summaryrefslogtreecommitdiffstats
path: root/scripts/lib/devtool/ide_plugins
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/devtool/ide_plugins')
-rw-r--r--scripts/lib/devtool/ide_plugins/__init__.py282
-rw-r--r--scripts/lib/devtool/ide_plugins/ide_code.py462
-rw-r--r--scripts/lib/devtool/ide_plugins/ide_none.py53
3 files changed, 797 insertions, 0 deletions
diff --git a/scripts/lib/devtool/ide_plugins/__init__.py b/scripts/lib/devtool/ide_plugins/__init__.py
new file mode 100644
index 0000000000..19c2f61c5f
--- /dev/null
+++ b/scripts/lib/devtool/ide_plugins/__init__.py
@@ -0,0 +1,282 @@
1#
2# Copyright (C) 2023-2024 Siemens AG
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6"""Devtool ide-sdk IDE plugin interface definition and helper functions"""
7
8import errno
9import json
10import logging
11import os
12import stat
13from enum import Enum, auto
14from devtool import DevtoolError
15from bb.utils import mkdirhier
16
17logger = logging.getLogger('devtool')
18
19
20class BuildTool(Enum):
21 UNDEFINED = auto()
22 CMAKE = auto()
23 MESON = auto()
24
25 @property
26 def is_c_ccp(self):
27 if self is BuildTool.CMAKE:
28 return True
29 if self is BuildTool.MESON:
30 return True
31 return False
32
33
34class GdbCrossConfig:
35 """Base class defining the GDB configuration generator interface
36
37 Generate a GDB configuration for a binary on the target device.
38 Only one instance per binary is allowed. This allows to assign unique port
39 numbers for all gdbserver instances.
40 """
41 _gdbserver_port_next = 1234
42 _binaries = []
43
44 def __init__(self, image_recipe, modified_recipe, binary, gdbserver_multi=True):
45 self.image_recipe = image_recipe
46 self.modified_recipe = modified_recipe
47 self.gdb_cross = modified_recipe.gdb_cross
48 self.binary = binary
49 if binary in GdbCrossConfig._binaries:
50 raise DevtoolError(
51 "gdbserver config for binary %s is already generated" % binary)
52 GdbCrossConfig._binaries.append(binary)
53 self.script_dir = modified_recipe.ide_sdk_scripts_dir
54 self.gdbinit_dir = os.path.join(self.script_dir, 'gdbinit')
55 self.gdbserver_multi = gdbserver_multi
56 self.binary_pretty = self.binary.replace(os.sep, '-').lstrip('-')
57 self.gdbserver_port = GdbCrossConfig._gdbserver_port_next
58 GdbCrossConfig._gdbserver_port_next += 1
59 self.id_pretty = "%d_%s" % (self.gdbserver_port, self.binary_pretty)
60 # gdbserver start script
61 gdbserver_script_file = 'gdbserver_' + self.id_pretty
62 if self.gdbserver_multi:
63 gdbserver_script_file += "_m"
64 self.gdbserver_script = os.path.join(
65 self.script_dir, gdbserver_script_file)
66 # gdbinit file
67 self.gdbinit = os.path.join(
68 self.gdbinit_dir, 'gdbinit_' + self.id_pretty)
69 # gdb start script
70 self.gdb_script = os.path.join(
71 self.script_dir, 'gdb_' + self.id_pretty)
72
73 def _gen_gdbserver_start_script(self):
74 """Generate a shell command starting the gdbserver on the remote device via ssh
75
76 GDB supports two modes:
77 multi: gdbserver remains running over several debug sessions
78 once: gdbserver terminates after the debugged process terminates
79 """
80 cmd_lines = ['#!/bin/sh']
81 if self.gdbserver_multi:
82 temp_dir = "TEMP_DIR=/tmp/gdbserver_%s; " % self.id_pretty
83 gdbserver_cmd_start = temp_dir
84 gdbserver_cmd_start += "test -f \\$TEMP_DIR/pid && exit 0; "
85 gdbserver_cmd_start += "mkdir -p \\$TEMP_DIR; "
86 gdbserver_cmd_start += "%s --multi :%s > \\$TEMP_DIR/log 2>&1 & " % (
87 self.gdb_cross.gdbserver_path, self.gdbserver_port)
88 gdbserver_cmd_start += "echo \\$! > \\$TEMP_DIR/pid;"
89
90 gdbserver_cmd_stop = temp_dir
91 gdbserver_cmd_stop += "test -f \\$TEMP_DIR/pid && kill \\$(cat \\$TEMP_DIR/pid); "
92 gdbserver_cmd_stop += "rm -rf \\$TEMP_DIR; "
93
94 gdbserver_cmd_l = []
95 gdbserver_cmd_l.append('if [ "$1" = "stop" ]; then')
96 gdbserver_cmd_l.append(' shift')
97 gdbserver_cmd_l.append(" %s %s %s %s 'sh -c \"%s\"'" % (
98 self.gdb_cross.target_device.ssh_sshexec, self.gdb_cross.target_device.ssh_port, self.gdb_cross.target_device.extraoptions, self.gdb_cross.target_device.target, gdbserver_cmd_stop))
99 gdbserver_cmd_l.append('else')
100 gdbserver_cmd_l.append(" %s %s %s %s 'sh -c \"%s\"'" % (
101 self.gdb_cross.target_device.ssh_sshexec, self.gdb_cross.target_device.ssh_port, self.gdb_cross.target_device.extraoptions, self.gdb_cross.target_device.target, gdbserver_cmd_start))
102 gdbserver_cmd_l.append('fi')
103 gdbserver_cmd = os.linesep.join(gdbserver_cmd_l)
104 else:
105 gdbserver_cmd_start = "%s --once :%s %s" % (
106 self.gdb_cross.gdbserver_path, self.gdbserver_port, self.binary)
107 gdbserver_cmd = "%s %s %s %s 'sh -c \"%s\"'" % (
108 self.gdb_cross.target_device.ssh_sshexec, self.gdb_cross.target_device.ssh_port, self.gdb_cross.target_device.extraoptions, self.gdb_cross.target_device.target, gdbserver_cmd_start)
109 cmd_lines.append(gdbserver_cmd)
110 GdbCrossConfig.write_file(self.gdbserver_script, cmd_lines, True)
111
112 def _gen_gdbinit_config(self):
113 """Generate a gdbinit file for this binary and the corresponding gdbserver configuration"""
114 gdbinit_lines = ['# This file is generated by devtool ide-sdk']
115 if self.gdbserver_multi:
116 target_help = '# gdbserver --multi :%d' % self.gdbserver_port
117 remote_cmd = 'target extended-remote'
118 else:
119 target_help = '# gdbserver :%d %s' % (
120 self.gdbserver_port, self.binary)
121 remote_cmd = 'target remote'
122 gdbinit_lines.append('# On the remote target:')
123 gdbinit_lines.append(target_help)
124 gdbinit_lines.append('# On the build machine:')
125 gdbinit_lines.append('# cd ' + self.modified_recipe.real_srctree)
126 gdbinit_lines.append(
127 '# ' + self.gdb_cross.gdb + ' -ix ' + self.gdbinit)
128
129 gdbinit_lines.append('set sysroot ' + self.modified_recipe.d)
130 gdbinit_lines.append('set substitute-path "/usr/include" "' +
131 os.path.join(self.modified_recipe.recipe_sysroot, 'usr', 'include') + '"')
132 # Disable debuginfod for now, the IDE configuration uses rootfs-dbg from the image workdir.
133 gdbinit_lines.append('set debuginfod enabled off')
134 if self.image_recipe.rootfs_dbg:
135 gdbinit_lines.append(
136 'set solib-search-path "' + self.modified_recipe.solib_search_path_str(self.image_recipe) + '"')
137 # First: Search for sources of this recipe in the workspace folder
138 if self.modified_recipe.pn in self.modified_recipe.target_dbgsrc_dir:
139 gdbinit_lines.append('set substitute-path "%s" "%s"' %
140 (self.modified_recipe.target_dbgsrc_dir, self.modified_recipe.real_srctree))
141 else:
142 logger.error(
143 "TARGET_DBGSRC_DIR must contain the recipe name PN.")
144 # Second: Search for sources of other recipes in the rootfs-dbg
145 if self.modified_recipe.target_dbgsrc_dir.startswith("/usr/src/debug"):
146 gdbinit_lines.append('set substitute-path "/usr/src/debug" "%s"' % os.path.join(
147 self.image_recipe.rootfs_dbg, "usr", "src", "debug"))
148 else:
149 logger.error(
150 "TARGET_DBGSRC_DIR must start with /usr/src/debug.")
151 else:
152 logger.warning(
153 "Cannot setup debug symbols configuration for GDB. IMAGE_GEN_DEBUGFS is not enabled.")
154 gdbinit_lines.append(
155 '%s %s:%d' % (remote_cmd, self.gdb_cross.host, self.gdbserver_port))
156 gdbinit_lines.append('set remote exec-file ' + self.binary)
157 gdbinit_lines.append(
158 'run ' + os.path.join(self.modified_recipe.d, self.binary))
159
160 GdbCrossConfig.write_file(self.gdbinit, gdbinit_lines)
161
162 def _gen_gdb_start_script(self):
163 """Generate a script starting GDB with the corresponding gdbinit configuration."""
164 cmd_lines = ['#!/bin/sh']
165 cmd_lines.append('cd ' + self.modified_recipe.real_srctree)
166 cmd_lines.append(self.gdb_cross.gdb + ' -ix ' +
167 self.gdbinit + ' "$@"')
168 GdbCrossConfig.write_file(self.gdb_script, cmd_lines, True)
169
170 def initialize(self):
171 self._gen_gdbserver_start_script()
172 self._gen_gdbinit_config()
173 self._gen_gdb_start_script()
174
175 @staticmethod
176 def write_file(script_file, cmd_lines, executable=False):
177 script_dir = os.path.dirname(script_file)
178 mkdirhier(script_dir)
179 with open(script_file, 'w') as script_f:
180 script_f.write(os.linesep.join(cmd_lines))
181 script_f.write(os.linesep)
182 if executable:
183 st = os.stat(script_file)
184 os.chmod(script_file, st.st_mode | stat.S_IEXEC)
185 logger.info("Created: %s" % script_file)
186
187
188class IdeBase:
189 """Base class defining the interface for IDE plugins"""
190
191 def __init__(self):
192 self.ide_name = 'undefined'
193 self.gdb_cross_configs = []
194
195 @classmethod
196 def ide_plugin_priority(cls):
197 """Used to find the default ide handler if --ide is not passed"""
198 return 10
199
200 def setup_shared_sysroots(self, shared_env):
201 logger.warn("Shared sysroot mode is not supported for IDE %s" %
202 self.ide_name)
203
204 def setup_modified_recipe(self, args, image_recipe, modified_recipe):
205 logger.warn("Modified recipe mode is not supported for IDE %s" %
206 self.ide_name)
207
208 def initialize_gdb_cross_configs(self, image_recipe, modified_recipe, gdb_cross_config_class=GdbCrossConfig):
209 binaries = modified_recipe.find_installed_binaries()
210 for binary in binaries:
211 gdb_cross_config = gdb_cross_config_class(
212 image_recipe, modified_recipe, binary)
213 gdb_cross_config.initialize()
214 self.gdb_cross_configs.append(gdb_cross_config)
215
216 @staticmethod
217 def gen_oe_scrtips_sym_link(modified_recipe):
218 # create a sym-link from sources to the scripts directory
219 if os.path.isdir(modified_recipe.ide_sdk_scripts_dir):
220 IdeBase.symlink_force(modified_recipe.ide_sdk_scripts_dir,
221 os.path.join(modified_recipe.real_srctree, 'oe-scripts'))
222
223 @staticmethod
224 def update_json_file(json_dir, json_file, update_dict):
225 """Update a json file
226
227 By default it uses the dict.update function. If this is not sutiable
228 the update function might be passed via update_func parameter.
229 """
230 json_path = os.path.join(json_dir, json_file)
231 logger.info("Updating IDE config file: %s (%s)" %
232 (json_file, json_path))
233 if not os.path.exists(json_dir):
234 os.makedirs(json_dir)
235 try:
236 with open(json_path) as f:
237 orig_dict = json.load(f)
238 except json.decoder.JSONDecodeError:
239 logger.info(
240 "Decoding %s failed. Probably because of comments in the json file" % json_path)
241 orig_dict = {}
242 except FileNotFoundError:
243 orig_dict = {}
244 orig_dict.update(update_dict)
245 with open(json_path, 'w') as f:
246 json.dump(orig_dict, f, indent=4)
247
248 @staticmethod
249 def symlink_force(tgt, dst):
250 try:
251 os.symlink(tgt, dst)
252 except OSError as err:
253 if err.errno == errno.EEXIST:
254 if os.readlink(dst) != tgt:
255 os.remove(dst)
256 os.symlink(tgt, dst)
257 else:
258 raise err
259
260
261def get_devtool_deploy_opts(args):
262 """Filter args for devtool deploy-target args"""
263 if not args.target:
264 return None
265 devtool_deploy_opts = [args.target]
266 if args.no_host_check:
267 devtool_deploy_opts += ["-c"]
268 if args.show_status:
269 devtool_deploy_opts += ["-s"]
270 if args.no_preserve:
271 devtool_deploy_opts += ["-p"]
272 if args.no_check_space:
273 devtool_deploy_opts += ["--no-check-space"]
274 if args.ssh_exec:
275 devtool_deploy_opts += ["-e", args.ssh.exec]
276 if args.port:
277 devtool_deploy_opts += ["-P", args.port]
278 if args.key:
279 devtool_deploy_opts += ["-I", args.key]
280 if args.strip is False:
281 devtool_deploy_opts += ["--no-strip"]
282 return devtool_deploy_opts
diff --git a/scripts/lib/devtool/ide_plugins/ide_code.py b/scripts/lib/devtool/ide_plugins/ide_code.py
new file mode 100644
index 0000000000..ee5bb57265
--- /dev/null
+++ b/scripts/lib/devtool/ide_plugins/ide_code.py
@@ -0,0 +1,462 @@
1#
2# Copyright (C) 2023-2024 Siemens AG
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6"""Devtool ide-sdk IDE plugin for VSCode and VSCodium"""
7
8import json
9import logging
10import os
11import shutil
12from devtool.ide_plugins import BuildTool, IdeBase, GdbCrossConfig, get_devtool_deploy_opts
13
14logger = logging.getLogger('devtool')
15
16
17class GdbCrossConfigVSCode(GdbCrossConfig):
18 def __init__(self, image_recipe, modified_recipe, binary):
19 super().__init__(image_recipe, modified_recipe, binary, False)
20
21 def initialize(self):
22 self._gen_gdbserver_start_script()
23
24
25class IdeVSCode(IdeBase):
26 """Manage IDE configurations for VSCode
27
28 Modified recipe mode:
29 - cmake: use the cmake-preset generated by devtool ide-sdk
30 - meson: meson is called via a wrapper script generated by devtool ide-sdk
31
32 Shared sysroot mode:
33 In shared sysroot mode, the cross tool-chain is exported to the user's global configuration.
34 A workspace cannot be created because there is no recipe that defines how a workspace could
35 be set up.
36 - cmake: adds a cmake-kit to .local/share/CMakeTools/cmake-tools-kits.json
37 The cmake-kit uses the environment script and the tool-chain file
38 generated by meta-ide-support.
39 - meson: Meson needs manual workspace configuration.
40 """
41
42 @classmethod
43 def ide_plugin_priority(cls):
44 """If --ide is not passed this is the default plugin"""
45 if shutil.which('code'):
46 return 100
47 return 0
48
49 def setup_shared_sysroots(self, shared_env):
50 """Expose the toolchain of the shared sysroots SDK"""
51 datadir = shared_env.ide_support.datadir
52 deploy_dir_image = shared_env.ide_support.deploy_dir_image
53 real_multimach_target_sys = shared_env.ide_support.real_multimach_target_sys
54 standalone_sysroot_native = shared_env.build_sysroots.standalone_sysroot_native
55 vscode_ws_path = os.path.join(
56 os.environ['HOME'], '.local', 'share', 'CMakeTools')
57 cmake_kits_path = os.path.join(vscode_ws_path, 'cmake-tools-kits.json')
58 oecmake_generator = "Ninja"
59 env_script = os.path.join(
60 deploy_dir_image, 'environment-setup-' + real_multimach_target_sys)
61
62 if not os.path.isdir(vscode_ws_path):
63 os.makedirs(vscode_ws_path)
64 cmake_kits_old = []
65 if os.path.exists(cmake_kits_path):
66 with open(cmake_kits_path, 'r', encoding='utf-8') as cmake_kits_file:
67 cmake_kits_old = json.load(cmake_kits_file)
68 cmake_kits = cmake_kits_old.copy()
69
70 cmake_kit_new = {
71 "name": "OE " + real_multimach_target_sys,
72 "environmentSetupScript": env_script,
73 "toolchainFile": standalone_sysroot_native + datadir + "/cmake/OEToolchainConfig.cmake",
74 "preferredGenerator": {
75 "name": oecmake_generator
76 }
77 }
78
79 def merge_kit(cmake_kits, cmake_kit_new):
80 i = 0
81 while i < len(cmake_kits):
82 if 'environmentSetupScript' in cmake_kits[i] and \
83 cmake_kits[i]['environmentSetupScript'] == cmake_kit_new['environmentSetupScript']:
84 cmake_kits[i] = cmake_kit_new
85 return
86 i += 1
87 cmake_kits.append(cmake_kit_new)
88 merge_kit(cmake_kits, cmake_kit_new)
89
90 if cmake_kits != cmake_kits_old:
91 logger.info("Updating: %s" % cmake_kits_path)
92 with open(cmake_kits_path, 'w', encoding='utf-8') as cmake_kits_file:
93 json.dump(cmake_kits, cmake_kits_file, indent=4)
94 else:
95 logger.info("Already up to date: %s" % cmake_kits_path)
96
97 cmake_native = os.path.join(
98 shared_env.build_sysroots.standalone_sysroot_native, 'usr', 'bin', 'cmake')
99 if os.path.isfile(cmake_native):
100 logger.info('cmake-kits call cmake by default. If the cmake provided by this SDK should be used, please add the following line to ".vscode/settings.json" file: "cmake.cmakePath": "%s"' % cmake_native)
101 else:
102 logger.error("Cannot find cmake native at: %s" % cmake_native)
103
104 def dot_code_dir(self, modified_recipe):
105 return os.path.join(modified_recipe.srctree, '.vscode')
106
107 def __vscode_settings_meson(self, settings_dict, modified_recipe):
108 if modified_recipe.build_tool is not BuildTool.MESON:
109 return
110 settings_dict["mesonbuild.mesonPath"] = modified_recipe.meson_wrapper
111
112 confopts = modified_recipe.mesonopts.split()
113 confopts += modified_recipe.meson_cross_file.split()
114 confopts += modified_recipe.extra_oemeson.split()
115 settings_dict["mesonbuild.configureOptions"] = confopts
116 settings_dict["mesonbuild.buildFolder"] = modified_recipe.b
117
118 def __vscode_settings_cmake(self, settings_dict, modified_recipe):
119 """Add cmake specific settings to settings.json.
120
121 Note: most settings are passed to the cmake preset.
122 """
123 if modified_recipe.build_tool is not BuildTool.CMAKE:
124 return
125 settings_dict["cmake.configureOnOpen"] = True
126 settings_dict["cmake.sourceDirectory"] = modified_recipe.real_srctree
127
128 def vscode_settings(self, modified_recipe, image_recipe):
129 files_excludes = {
130 "**/.git/**": True,
131 "**/oe-logs/**": True,
132 "**/oe-workdir/**": True,
133 "**/source-date-epoch/**": True
134 }
135 python_exclude = [
136 "**/.git/**",
137 "**/oe-logs/**",
138 "**/oe-workdir/**",
139 "**/source-date-epoch/**"
140 ]
141 files_readonly = {
142 modified_recipe.recipe_sysroot + '/**': True,
143 modified_recipe.recipe_sysroot_native + '/**': True,
144 }
145 if image_recipe.rootfs_dbg is not None:
146 files_readonly[image_recipe.rootfs_dbg + '/**'] = True
147 settings_dict = {
148 "files.watcherExclude": files_excludes,
149 "files.exclude": files_excludes,
150 "files.readonlyInclude": files_readonly,
151 "python.analysis.exclude": python_exclude
152 }
153 self.__vscode_settings_cmake(settings_dict, modified_recipe)
154 self.__vscode_settings_meson(settings_dict, modified_recipe)
155
156 settings_file = 'settings.json'
157 IdeBase.update_json_file(
158 self.dot_code_dir(modified_recipe), settings_file, settings_dict)
159
160 def __vscode_extensions_cmake(self, modified_recipe, recommendations):
161 if modified_recipe.build_tool is not BuildTool.CMAKE:
162 return
163 recommendations += [
164 "ms-vscode.cmake-tools",
165 "ms-vscode.cpptools",
166 "ms-vscode.cpptools-extension-pack",
167 "ms-vscode.cpptools-themes"
168 ]
169
170 def __vscode_extensions_meson(self, modified_recipe, recommendations):
171 if modified_recipe.build_tool is not BuildTool.MESON:
172 return
173 recommendations += [
174 'mesonbuild.mesonbuild',
175 "ms-vscode.cpptools",
176 "ms-vscode.cpptools-extension-pack",
177 "ms-vscode.cpptools-themes"
178 ]
179
180 def vscode_extensions(self, modified_recipe):
181 recommendations = []
182 self.__vscode_extensions_cmake(modified_recipe, recommendations)
183 self.__vscode_extensions_meson(modified_recipe, recommendations)
184 extensions_file = 'extensions.json'
185 IdeBase.update_json_file(
186 self.dot_code_dir(modified_recipe), extensions_file, {"recommendations": recommendations})
187
188 def vscode_c_cpp_properties(self, modified_recipe):
189 properties_dict = {
190 "name": modified_recipe.recipe_id_pretty,
191 }
192 if modified_recipe.build_tool is BuildTool.CMAKE:
193 properties_dict["configurationProvider"] = "ms-vscode.cmake-tools"
194 elif modified_recipe.build_tool is BuildTool.MESON:
195 properties_dict["configurationProvider"] = "mesonbuild.mesonbuild"
196 properties_dict["compilerPath"] = os.path.join(modified_recipe.staging_bindir_toolchain, modified_recipe.cxx.split()[0])
197 else: # no C/C++ build
198 return
199
200 properties_dicts = {
201 "configurations": [
202 properties_dict
203 ],
204 "version": 4
205 }
206 prop_file = 'c_cpp_properties.json'
207 IdeBase.update_json_file(
208 self.dot_code_dir(modified_recipe), prop_file, properties_dicts)
209
210 def vscode_launch_bin_dbg(self, gdb_cross_config):
211 modified_recipe = gdb_cross_config.modified_recipe
212
213 launch_config = {
214 "name": gdb_cross_config.id_pretty,
215 "type": "cppdbg",
216 "request": "launch",
217 "program": os.path.join(modified_recipe.d, gdb_cross_config.binary.lstrip('/')),
218 "stopAtEntry": True,
219 "cwd": "${workspaceFolder}",
220 "environment": [],
221 "externalConsole": False,
222 "MIMode": "gdb",
223 "preLaunchTask": gdb_cross_config.id_pretty,
224 "miDebuggerPath": modified_recipe.gdb_cross.gdb,
225 "miDebuggerServerAddress": "%s:%d" % (modified_recipe.gdb_cross.host, gdb_cross_config.gdbserver_port)
226 }
227
228 # Search for header files in recipe-sysroot.
229 src_file_map = {
230 "/usr/include": os.path.join(modified_recipe.recipe_sysroot, "usr", "include")
231 }
232 # First of all search for not stripped binaries in the image folder.
233 # These binaries are copied (and optionally stripped) by deploy-target
234 setup_commands = [
235 {
236 "description": "sysroot",
237 "text": "set sysroot " + modified_recipe.d
238 }
239 ]
240
241 if gdb_cross_config.image_recipe.rootfs_dbg:
242 launch_config['additionalSOLibSearchPath'] = modified_recipe.solib_search_path_str(
243 gdb_cross_config.image_recipe)
244 # First: Search for sources of this recipe in the workspace folder
245 if modified_recipe.pn in modified_recipe.target_dbgsrc_dir:
246 src_file_map[modified_recipe.target_dbgsrc_dir] = "${workspaceFolder}"
247 else:
248 logger.error(
249 "TARGET_DBGSRC_DIR must contain the recipe name PN.")
250 # Second: Search for sources of other recipes in the rootfs-dbg
251 if modified_recipe.target_dbgsrc_dir.startswith("/usr/src/debug"):
252 src_file_map["/usr/src/debug"] = os.path.join(
253 gdb_cross_config.image_recipe.rootfs_dbg, "usr", "src", "debug")
254 else:
255 logger.error(
256 "TARGET_DBGSRC_DIR must start with /usr/src/debug.")
257 else:
258 logger.warning(
259 "Cannot setup debug symbols configuration for GDB. IMAGE_GEN_DEBUGFS is not enabled.")
260
261 launch_config['sourceFileMap'] = src_file_map
262 launch_config['setupCommands'] = setup_commands
263 return launch_config
264
265 def vscode_launch(self, modified_recipe):
266 """GDB Launch configuration for binaries (elf files)"""
267
268 configurations = []
269 for gdb_cross_config in self.gdb_cross_configs:
270 if gdb_cross_config.modified_recipe is modified_recipe:
271 configurations.append(self.vscode_launch_bin_dbg(gdb_cross_config))
272 launch_dict = {
273 "version": "0.2.0",
274 "configurations": configurations
275 }
276 launch_file = 'launch.json'
277 IdeBase.update_json_file(
278 self.dot_code_dir(modified_recipe), launch_file, launch_dict)
279
280 def vscode_tasks_cpp(self, args, modified_recipe):
281 run_install_deploy = modified_recipe.gen_install_deploy_script(args)
282 install_task_name = "install && deploy-target %s" % modified_recipe.recipe_id_pretty
283 tasks_dict = {
284 "version": "2.0.0",
285 "tasks": [
286 {
287 "label": install_task_name,
288 "type": "shell",
289 "command": run_install_deploy,
290 "problemMatcher": []
291 }
292 ]
293 }
294 for gdb_cross_config in self.gdb_cross_configs:
295 if gdb_cross_config.modified_recipe is not modified_recipe:
296 continue
297 tasks_dict['tasks'].append(
298 {
299 "label": gdb_cross_config.id_pretty,
300 "type": "shell",
301 "isBackground": True,
302 "dependsOn": [
303 install_task_name
304 ],
305 "command": gdb_cross_config.gdbserver_script,
306 "problemMatcher": [
307 {
308 "pattern": [
309 {
310 "regexp": ".",
311 "file": 1,
312 "location": 2,
313 "message": 3
314 }
315 ],
316 "background": {
317 "activeOnStart": True,
318 "beginsPattern": ".",
319 "endsPattern": ".",
320 }
321 }
322 ]
323 })
324 tasks_file = 'tasks.json'
325 IdeBase.update_json_file(
326 self.dot_code_dir(modified_recipe), tasks_file, tasks_dict)
327
328 def vscode_tasks_fallback(self, args, modified_recipe):
329 oe_init_dir = modified_recipe.oe_init_dir
330 oe_init = ". %s %s > /dev/null && " % (modified_recipe.oe_init_build_env, modified_recipe.topdir)
331 dt_build = "devtool build "
332 dt_build_label = dt_build + modified_recipe.recipe_id_pretty
333 dt_build_cmd = dt_build + modified_recipe.bpn
334 clean_opt = " --clean"
335 dt_build_clean_label = dt_build + modified_recipe.recipe_id_pretty + clean_opt
336 dt_build_clean_cmd = dt_build + modified_recipe.bpn + clean_opt
337 dt_deploy = "devtool deploy-target "
338 dt_deploy_label = dt_deploy + modified_recipe.recipe_id_pretty
339 dt_deploy_cmd = dt_deploy + modified_recipe.bpn
340 dt_build_deploy_label = "devtool build & deploy-target %s" % modified_recipe.recipe_id_pretty
341 deploy_opts = ' '.join(get_devtool_deploy_opts(args))
342 tasks_dict = {
343 "version": "2.0.0",
344 "tasks": [
345 {
346 "label": dt_build_label,
347 "type": "shell",
348 "command": "bash",
349 "linux": {
350 "options": {
351 "cwd": oe_init_dir
352 }
353 },
354 "args": [
355 "--login",
356 "-c",
357 "%s%s" % (oe_init, dt_build_cmd)
358 ],
359 "problemMatcher": []
360 },
361 {
362 "label": dt_deploy_label,
363 "type": "shell",
364 "command": "bash",
365 "linux": {
366 "options": {
367 "cwd": oe_init_dir
368 }
369 },
370 "args": [
371 "--login",
372 "-c",
373 "%s%s %s" % (
374 oe_init, dt_deploy_cmd, deploy_opts)
375 ],
376 "problemMatcher": []
377 },
378 {
379 "label": dt_build_deploy_label,
380 "dependsOrder": "sequence",
381 "dependsOn": [
382 dt_build_label,
383 dt_deploy_label
384 ],
385 "problemMatcher": [],
386 "group": {
387 "kind": "build",
388 "isDefault": True
389 }
390 },
391 {
392 "label": dt_build_clean_label,
393 "type": "shell",
394 "command": "bash",
395 "linux": {
396 "options": {
397 "cwd": oe_init_dir
398 }
399 },
400 "args": [
401 "--login",
402 "-c",
403 "%s%s" % (oe_init, dt_build_clean_cmd)
404 ],
405 "problemMatcher": []
406 }
407 ]
408 }
409 if modified_recipe.gdb_cross:
410 for gdb_cross_config in self.gdb_cross_configs:
411 if gdb_cross_config.modified_recipe is not modified_recipe:
412 continue
413 tasks_dict['tasks'].append(
414 {
415 "label": gdb_cross_config.id_pretty,
416 "type": "shell",
417 "isBackground": True,
418 "dependsOn": [
419 dt_build_deploy_label
420 ],
421 "command": gdb_cross_config.gdbserver_script,
422 "problemMatcher": [
423 {
424 "pattern": [
425 {
426 "regexp": ".",
427 "file": 1,
428 "location": 2,
429 "message": 3
430 }
431 ],
432 "background": {
433 "activeOnStart": True,
434 "beginsPattern": ".",
435 "endsPattern": ".",
436 }
437 }
438 ]
439 })
440 tasks_file = 'tasks.json'
441 IdeBase.update_json_file(
442 self.dot_code_dir(modified_recipe), tasks_file, tasks_dict)
443
444 def vscode_tasks(self, args, modified_recipe):
445 if modified_recipe.build_tool.is_c_ccp:
446 self.vscode_tasks_cpp(args, modified_recipe)
447 else:
448 self.vscode_tasks_fallback(args, modified_recipe)
449
450 def setup_modified_recipe(self, args, image_recipe, modified_recipe):
451 self.vscode_settings(modified_recipe, image_recipe)
452 self.vscode_extensions(modified_recipe)
453 self.vscode_c_cpp_properties(modified_recipe)
454 if args.target:
455 self.initialize_gdb_cross_configs(
456 image_recipe, modified_recipe, gdb_cross_config_class=GdbCrossConfigVSCode)
457 self.vscode_launch(modified_recipe)
458 self.vscode_tasks(args, modified_recipe)
459
460
461def register_ide_plugin(ide_plugins):
462 ide_plugins['code'] = IdeVSCode
diff --git a/scripts/lib/devtool/ide_plugins/ide_none.py b/scripts/lib/devtool/ide_plugins/ide_none.py
new file mode 100644
index 0000000000..f106c5a026
--- /dev/null
+++ b/scripts/lib/devtool/ide_plugins/ide_none.py
@@ -0,0 +1,53 @@
1#
2# Copyright (C) 2023-2024 Siemens AG
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6"""Devtool ide-sdk generic IDE plugin"""
7
8import os
9import logging
10from devtool.ide_plugins import IdeBase, GdbCrossConfig
11
12logger = logging.getLogger('devtool')
13
14
15class IdeNone(IdeBase):
16 """Generate some generic helpers for other IDEs
17
18 Modified recipe mode:
19 Generate some helper scripts for remote debugging with GDB
20
21 Shared sysroot mode:
22 A wrapper for bitbake meta-ide-support and bitbake build-sysroots
23 """
24
25 def __init__(self):
26 super().__init__()
27
28 def setup_shared_sysroots(self, shared_env):
29 real_multimach_target_sys = shared_env.ide_support.real_multimach_target_sys
30 deploy_dir_image = shared_env.ide_support.deploy_dir_image
31 env_script = os.path.join(
32 deploy_dir_image, 'environment-setup-' + real_multimach_target_sys)
33 logger.info(
34 "To use this SDK please source this: %s" % env_script)
35
36 def setup_modified_recipe(self, args, image_recipe, modified_recipe):
37 """generate some helper scripts and config files
38
39 - Execute the do_install task
40 - Execute devtool deploy-target
41 - Generate a gdbinit file per executable
42 - Generate the oe-scripts sym-link
43 """
44 script_path = modified_recipe.gen_install_deploy_script(args)
45 logger.info("Created: %s" % script_path)
46
47 self.initialize_gdb_cross_configs(image_recipe, modified_recipe)
48
49 IdeBase.gen_oe_scrtips_sym_link(modified_recipe)
50
51
52def register_ide_plugin(ide_plugins):
53 ide_plugins['none'] = IdeNone