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__.py267
-rw-r--r--scripts/lib/devtool/ide_plugins/ide_code.py438
-rw-r--r--scripts/lib/devtool/ide_plugins/ide_none.py53
3 files changed, 758 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..3371b24264
--- /dev/null
+++ b/scripts/lib/devtool/ide_plugins/__init__.py
@@ -0,0 +1,267 @@
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 gdbinit_lines.append('set substitute-path "/usr/src/debug" "' + os.path.join(
138 self.image_recipe.rootfs_dbg, 'usr', 'src', 'debug') + '"')
139 gdbinit_lines.append(
140 '%s %s:%d' % (remote_cmd, self.gdb_cross.host, self.gdbserver_port))
141 gdbinit_lines.append('set remote exec-file ' + self.binary)
142 gdbinit_lines.append(
143 'run ' + os.path.join(self.modified_recipe.d, self.binary))
144
145 GdbCrossConfig.write_file(self.gdbinit, gdbinit_lines)
146
147 def _gen_gdb_start_script(self):
148 """Generate a script starting GDB with the corresponding gdbinit configuration."""
149 cmd_lines = ['#!/bin/sh']
150 cmd_lines.append('cd ' + self.modified_recipe.real_srctree)
151 cmd_lines.append(self.gdb_cross.gdb + ' -ix ' +
152 self.gdbinit + ' "$@"')
153 GdbCrossConfig.write_file(self.gdb_script, cmd_lines, True)
154
155 def initialize(self):
156 self._gen_gdbserver_start_script()
157 self._gen_gdbinit_config()
158 self._gen_gdb_start_script()
159
160 @staticmethod
161 def write_file(script_file, cmd_lines, executable=False):
162 script_dir = os.path.dirname(script_file)
163 mkdirhier(script_dir)
164 with open(script_file, 'w') as script_f:
165 script_f.write(os.linesep.join(cmd_lines))
166 script_f.write(os.linesep)
167 if executable:
168 st = os.stat(script_file)
169 os.chmod(script_file, st.st_mode | stat.S_IEXEC)
170 logger.info("Created: %s" % script_file)
171
172
173class IdeBase:
174 """Base class defining the interface for IDE plugins"""
175
176 def __init__(self):
177 self.ide_name = 'undefined'
178 self.gdb_cross_configs = []
179
180 @classmethod
181 def ide_plugin_priority(cls):
182 """Used to find the default ide handler if --ide is not passed"""
183 return 10
184
185 def setup_shared_sysroots(self, shared_env):
186 logger.warn("Shared sysroot mode is not supported for IDE %s" %
187 self.ide_name)
188
189 def setup_modified_recipe(self, args, image_recipe, modified_recipe):
190 logger.warn("Modified recipe mode is not supported for IDE %s" %
191 self.ide_name)
192
193 def initialize_gdb_cross_configs(self, image_recipe, modified_recipe, gdb_cross_config_class=GdbCrossConfig):
194 binaries = modified_recipe.find_installed_binaries()
195 for binary in binaries:
196 gdb_cross_config = gdb_cross_config_class(
197 image_recipe, modified_recipe, binary)
198 gdb_cross_config.initialize()
199 self.gdb_cross_configs.append(gdb_cross_config)
200
201 @staticmethod
202 def gen_oe_scrtips_sym_link(modified_recipe):
203 # create a sym-link from sources to the scripts directory
204 if os.path.isdir(modified_recipe.ide_sdk_scripts_dir):
205 IdeBase.symlink_force(modified_recipe.ide_sdk_scripts_dir,
206 os.path.join(modified_recipe.real_srctree, 'oe-scripts'))
207
208 @staticmethod
209 def update_json_file(json_dir, json_file, update_dict):
210 """Update a json file
211
212 By default it uses the dict.update function. If this is not sutiable
213 the update function might be passed via update_func parameter.
214 """
215 json_path = os.path.join(json_dir, json_file)
216 logger.info("Updating IDE config file: %s (%s)" %
217 (json_file, json_path))
218 if not os.path.exists(json_dir):
219 os.makedirs(json_dir)
220 try:
221 with open(json_path) as f:
222 orig_dict = json.load(f)
223 except json.decoder.JSONDecodeError:
224 logger.info(
225 "Decoding %s failed. Probably because of comments in the json file" % json_path)
226 orig_dict = {}
227 except FileNotFoundError:
228 orig_dict = {}
229 orig_dict.update(update_dict)
230 with open(json_path, 'w') as f:
231 json.dump(orig_dict, f, indent=4)
232
233 @staticmethod
234 def symlink_force(tgt, dst):
235 try:
236 os.symlink(tgt, dst)
237 except OSError as err:
238 if err.errno == errno.EEXIST:
239 if os.readlink(dst) != tgt:
240 os.remove(dst)
241 os.symlink(tgt, dst)
242 else:
243 raise err
244
245
246def get_devtool_deploy_opts(args):
247 """Filter args for devtool deploy-target args"""
248 if not args.target:
249 return None
250 devtool_deploy_opts = [args.target]
251 if args.no_host_check:
252 devtool_deploy_opts += ["-c"]
253 if args.show_status:
254 devtool_deploy_opts += ["-s"]
255 if args.no_preserve:
256 devtool_deploy_opts += ["-p"]
257 if args.no_check_space:
258 devtool_deploy_opts += ["--no-check-space"]
259 if args.ssh_exec:
260 devtool_deploy_opts += ["-e", args.ssh.exec]
261 if args.port:
262 devtool_deploy_opts += ["-P", args.port]
263 if args.key:
264 devtool_deploy_opts += ["-I", args.key]
265 if args.strip is False:
266 devtool_deploy_opts += ["--no-strip"]
267 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..b2193130d2
--- /dev/null
+++ b/scripts/lib/devtool/ide_plugins/ide_code.py
@@ -0,0 +1,438 @@
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):
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 settings_dict = {
142 "files.watcherExclude": files_excludes,
143 "files.exclude": files_excludes,
144 "python.analysis.exclude": python_exclude
145 }
146 self.__vscode_settings_cmake(settings_dict, modified_recipe)
147 self.__vscode_settings_meson(settings_dict, modified_recipe)
148
149 settings_file = 'settings.json'
150 IdeBase.update_json_file(
151 self.dot_code_dir(modified_recipe), settings_file, settings_dict)
152
153 def __vscode_extensions_cmake(self, modified_recipe, recommendations):
154 if modified_recipe.build_tool is not BuildTool.CMAKE:
155 return
156 recommendations += [
157 "twxs.cmake",
158 "ms-vscode.cmake-tools",
159 "ms-vscode.cpptools",
160 "ms-vscode.cpptools-extension-pack",
161 "ms-vscode.cpptools-themes"
162 ]
163
164 def __vscode_extensions_meson(self, modified_recipe, recommendations):
165 if modified_recipe.build_tool is not BuildTool.MESON:
166 return
167 recommendations += [
168 'mesonbuild.mesonbuild',
169 "ms-vscode.cpptools",
170 "ms-vscode.cpptools-extension-pack",
171 "ms-vscode.cpptools-themes"
172 ]
173
174 def vscode_extensions(self, modified_recipe):
175 recommendations = []
176 self.__vscode_extensions_cmake(modified_recipe, recommendations)
177 self.__vscode_extensions_meson(modified_recipe, recommendations)
178 extensions_file = 'extensions.json'
179 IdeBase.update_json_file(
180 self.dot_code_dir(modified_recipe), extensions_file, {"recommendations": recommendations})
181
182 def vscode_c_cpp_properties(self, modified_recipe):
183 properties_dict = {
184 "name": modified_recipe.recipe_id_pretty,
185 }
186 if modified_recipe.build_tool is BuildTool.CMAKE:
187 properties_dict["configurationProvider"] = "ms-vscode.cmake-tools"
188 elif modified_recipe.build_tool is BuildTool.MESON:
189 properties_dict["configurationProvider"] = "mesonbuild.mesonbuild"
190 else: # no C/C++ build
191 return
192
193 properties_dicts = {
194 "configurations": [
195 properties_dict
196 ],
197 "version": 4
198 }
199 prop_file = 'c_cpp_properties.json'
200 IdeBase.update_json_file(
201 self.dot_code_dir(modified_recipe), prop_file, properties_dicts)
202
203 def vscode_launch_bin_dbg(self, gdb_cross_config):
204 modified_recipe = gdb_cross_config.modified_recipe
205
206 launch_config = {
207 "name": gdb_cross_config.id_pretty,
208 "type": "cppdbg",
209 "request": "launch",
210 "program": os.path.join(modified_recipe.d, gdb_cross_config.binary.lstrip('/')),
211 "stopAtEntry": True,
212 "cwd": "${workspaceFolder}",
213 "environment": [],
214 "externalConsole": False,
215 "MIMode": "gdb",
216 "preLaunchTask": gdb_cross_config.id_pretty,
217 "miDebuggerPath": modified_recipe.gdb_cross.gdb,
218 "miDebuggerServerAddress": "%s:%d" % (modified_recipe.gdb_cross.host, gdb_cross_config.gdbserver_port)
219 }
220
221 # Search for header files in recipe-sysroot.
222 src_file_map = {
223 "/usr/include": os.path.join(modified_recipe.recipe_sysroot, "usr", "include")
224 }
225 # First of all search for not stripped binaries in the image folder.
226 # These binaries are copied (and optionally stripped) by deploy-target
227 setup_commands = [
228 {
229 "description": "sysroot",
230 "text": "set sysroot " + modified_recipe.d
231 }
232 ]
233
234 if gdb_cross_config.image_recipe.rootfs_dbg:
235 launch_config['additionalSOLibSearchPath'] = modified_recipe.solib_search_path_str(
236 gdb_cross_config.image_recipe)
237 src_file_map["/usr/src/debug"] = os.path.join(
238 gdb_cross_config.image_recipe.rootfs_dbg, "usr", "src", "debug")
239 else:
240 logger.warning(
241 "Cannot setup debug symbols configuration for GDB. IMAGE_GEN_DEBUGFS is not enabled.")
242
243 launch_config['sourceFileMap'] = src_file_map
244 launch_config['setupCommands'] = setup_commands
245 return launch_config
246
247 def vscode_launch(self, modified_recipe):
248 """GDB Launch configuration for binaries (elf files)"""
249
250 configurations = [self.vscode_launch_bin_dbg(
251 gdb_cross_config) for gdb_cross_config in self.gdb_cross_configs]
252 launch_dict = {
253 "version": "0.2.0",
254 "configurations": configurations
255 }
256 launch_file = 'launch.json'
257 IdeBase.update_json_file(
258 self.dot_code_dir(modified_recipe), launch_file, launch_dict)
259
260 def vscode_tasks_cpp(self, args, modified_recipe):
261 run_install_deploy = modified_recipe.gen_install_deploy_script(args)
262 install_task_name = "install && deploy-target %s" % modified_recipe.recipe_id_pretty
263 tasks_dict = {
264 "version": "2.0.0",
265 "tasks": [
266 {
267 "label": install_task_name,
268 "type": "shell",
269 "command": run_install_deploy,
270 "problemMatcher": []
271 }
272 ]
273 }
274 for gdb_cross_config in self.gdb_cross_configs:
275 tasks_dict['tasks'].append(
276 {
277 "label": gdb_cross_config.id_pretty,
278 "type": "shell",
279 "isBackground": True,
280 "dependsOn": [
281 install_task_name
282 ],
283 "command": gdb_cross_config.gdbserver_script,
284 "problemMatcher": [
285 {
286 "pattern": [
287 {
288 "regexp": ".",
289 "file": 1,
290 "location": 2,
291 "message": 3
292 }
293 ],
294 "background": {
295 "activeOnStart": True,
296 "beginsPattern": ".",
297 "endsPattern": ".",
298 }
299 }
300 ]
301 })
302 tasks_file = 'tasks.json'
303 IdeBase.update_json_file(
304 self.dot_code_dir(modified_recipe), tasks_file, tasks_dict)
305
306 def vscode_tasks_fallback(self, args, modified_recipe):
307 oe_init_dir = modified_recipe.oe_init_dir
308 oe_init = ". %s %s > /dev/null && " % (modified_recipe.oe_init_build_env, modified_recipe.topdir)
309 dt_build = "devtool build "
310 dt_build_label = dt_build + modified_recipe.recipe_id_pretty
311 dt_build_cmd = dt_build + modified_recipe.bpn
312 clean_opt = " --clean"
313 dt_build_clean_label = dt_build + modified_recipe.recipe_id_pretty + clean_opt
314 dt_build_clean_cmd = dt_build + modified_recipe.bpn + clean_opt
315 dt_deploy = "devtool deploy-target "
316 dt_deploy_label = dt_deploy + modified_recipe.recipe_id_pretty
317 dt_deploy_cmd = dt_deploy + modified_recipe.bpn
318 dt_build_deploy_label = "devtool build & deploy-target %s" % modified_recipe.recipe_id_pretty
319 deploy_opts = ' '.join(get_devtool_deploy_opts(args))
320 tasks_dict = {
321 "version": "2.0.0",
322 "tasks": [
323 {
324 "label": dt_build_label,
325 "type": "shell",
326 "command": "bash",
327 "linux": {
328 "options": {
329 "cwd": oe_init_dir
330 }
331 },
332 "args": [
333 "--login",
334 "-c",
335 "%s%s" % (oe_init, dt_build_cmd)
336 ],
337 "problemMatcher": []
338 },
339 {
340 "label": dt_deploy_label,
341 "type": "shell",
342 "command": "bash",
343 "linux": {
344 "options": {
345 "cwd": oe_init_dir
346 }
347 },
348 "args": [
349 "--login",
350 "-c",
351 "%s%s %s" % (
352 oe_init, dt_deploy_cmd, deploy_opts)
353 ],
354 "problemMatcher": []
355 },
356 {
357 "label": dt_build_deploy_label,
358 "dependsOrder": "sequence",
359 "dependsOn": [
360 dt_build_label,
361 dt_deploy_label
362 ],
363 "problemMatcher": [],
364 "group": {
365 "kind": "build",
366 "isDefault": True
367 }
368 },
369 {
370 "label": dt_build_clean_label,
371 "type": "shell",
372 "command": "bash",
373 "linux": {
374 "options": {
375 "cwd": oe_init_dir
376 }
377 },
378 "args": [
379 "--login",
380 "-c",
381 "%s%s" % (oe_init, dt_build_clean_cmd)
382 ],
383 "problemMatcher": []
384 }
385 ]
386 }
387 if modified_recipe.gdb_cross:
388 for gdb_cross_config in self.gdb_cross_configs:
389 tasks_dict['tasks'].append(
390 {
391 "label": gdb_cross_config.id_pretty,
392 "type": "shell",
393 "isBackground": True,
394 "dependsOn": [
395 dt_build_deploy_label
396 ],
397 "command": gdb_cross_config.gdbserver_script,
398 "problemMatcher": [
399 {
400 "pattern": [
401 {
402 "regexp": ".",
403 "file": 1,
404 "location": 2,
405 "message": 3
406 }
407 ],
408 "background": {
409 "activeOnStart": True,
410 "beginsPattern": ".",
411 "endsPattern": ".",
412 }
413 }
414 ]
415 })
416 tasks_file = 'tasks.json'
417 IdeBase.update_json_file(
418 self.dot_code_dir(modified_recipe), tasks_file, tasks_dict)
419
420 def vscode_tasks(self, args, modified_recipe):
421 if modified_recipe.build_tool.is_c_ccp:
422 self.vscode_tasks_cpp(args, modified_recipe)
423 else:
424 self.vscode_tasks_fallback(args, modified_recipe)
425
426 def setup_modified_recipe(self, args, image_recipe, modified_recipe):
427 self.vscode_settings(modified_recipe)
428 self.vscode_extensions(modified_recipe)
429 self.vscode_c_cpp_properties(modified_recipe)
430 if args.target:
431 self.initialize_gdb_cross_configs(
432 image_recipe, modified_recipe, gdb_cross_config_class=GdbCrossConfigVSCode)
433 self.vscode_launch(modified_recipe)
434 self.vscode_tasks(args, modified_recipe)
435
436
437def register_ide_plugin(ide_plugins):
438 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