summaryrefslogtreecommitdiffstats
path: root/scripts/devtool
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/devtool')
-rwxr-xr-xscripts/devtool110
1 files changed, 64 insertions, 46 deletions
diff --git a/scripts/devtool b/scripts/devtool
index 8a4f41bc37..39cebec0d8 100755
--- a/scripts/devtool
+++ b/scripts/devtool
@@ -7,19 +7,17 @@
7# SPDX-License-Identifier: GPL-2.0-only 7# SPDX-License-Identifier: GPL-2.0-only
8# 8#
9 9
10import dataclasses
10import sys 11import sys
11import os 12import os
12import argparse 13import argparse
13import glob 14import glob
14import re 15import re
15import configparser 16import configparser
16import subprocess
17import logging 17import logging
18 18
19basepath = '' 19# This can be removed once our minimum is Python 3.9: https://docs.python.org/3/whatsnew/3.9.html#type-hinting-generics-in-standard-collections
20workspace = {} 20from typing import List
21config = None
22context = None
23 21
24 22
25scripts_path = os.path.dirname(os.path.realpath(__file__)) 23scripts_path = os.path.dirname(os.path.realpath(__file__))
@@ -30,16 +28,16 @@ import scriptutils
30import argparse_oe 28import argparse_oe
31logger = scriptutils.logger_create('devtool') 29logger = scriptutils.logger_create('devtool')
32 30
33plugins = []
34 31
35 32class ConfigHandler:
36class ConfigHandler(object): 33 basepath = None
37 config_file = '' 34 config_file = ''
38 config_obj = None 35 config_obj = None
39 init_path = '' 36 init_path = ''
40 workspace_path = '' 37 workspace_path = ''
41 38
42 def __init__(self, filename): 39 def __init__(self, basepath, filename):
40 self.basepath = basepath
43 self.config_file = filename 41 self.config_file = filename
44 self.config_obj = configparser.ConfigParser() 42 self.config_obj = configparser.ConfigParser()
45 43
@@ -47,7 +45,7 @@ class ConfigHandler(object):
47 try: 45 try:
48 ret = self.config_obj.get(section, option) 46 ret = self.config_obj.get(section, option)
49 except (configparser.NoOptionError, configparser.NoSectionError): 47 except (configparser.NoOptionError, configparser.NoSectionError):
50 if default != None: 48 if default is not None:
51 ret = default 49 ret = default
52 else: 50 else:
53 raise 51 raise
@@ -59,14 +57,14 @@ class ConfigHandler(object):
59 57
60 if self.config_obj.has_option('General', 'init_path'): 58 if self.config_obj.has_option('General', 'init_path'):
61 pth = self.get('General', 'init_path') 59 pth = self.get('General', 'init_path')
62 self.init_path = os.path.join(basepath, pth) 60 self.init_path = os.path.join(self.basepath, pth)
63 if not os.path.exists(self.init_path): 61 if not os.path.exists(self.init_path):
64 logger.error('init_path %s specified in config file cannot be found' % pth) 62 logger.error('init_path %s specified in config file cannot be found' % pth)
65 return False 63 return False
66 else: 64 else:
67 self.config_obj.add_section('General') 65 self.config_obj.add_section('General')
68 66
69 self.workspace_path = self.get('General', 'workspace_path', os.path.join(basepath, 'workspace')) 67 self.workspace_path = self.get('General', 'workspace_path', os.path.join(self.basepath, 'workspace'))
70 return True 68 return True
71 69
72 70
@@ -81,29 +79,32 @@ class ConfigHandler(object):
81 self.config_obj.add_section(section) 79 self.config_obj.add_section(section)
82 self.config_obj.set(section, option, value) 80 self.config_obj.set(section, option, value)
83 81
82
83@dataclasses.dataclass
84class Context: 84class Context:
85 def __init__(self, **kwargs): 85 fixed_setup: bool
86 self.__dict__.update(kwargs) 86 config: ConfigHandler
87 pluginpaths: List[str]
87 88
88 89
89def read_workspace(): 90def read_workspace(basepath, context):
90 global workspace
91 workspace = {} 91 workspace = {}
92 if not os.path.exists(os.path.join(config.workspace_path, 'conf', 'layer.conf')): 92 if not os.path.exists(os.path.join(context.config.workspace_path, 'conf', 'layer.conf')):
93 if context.fixed_setup: 93 if context.fixed_setup:
94 logger.error("workspace layer not set up") 94 logger.error("workspace layer not set up")
95 sys.exit(1) 95 sys.exit(1)
96 else: 96 else:
97 logger.info('Creating workspace layer in %s' % config.workspace_path) 97 logger.info('Creating workspace layer in %s' % context.config.workspace_path)
98 _create_workspace(config.workspace_path, config, basepath) 98 _create_workspace(context.config.workspace_path, basepath)
99 if not context.fixed_setup: 99 if not context.fixed_setup:
100 _enable_workspace_layer(config.workspace_path, config, basepath) 100 _enable_workspace_layer(context.config.workspace_path, context.config, basepath)
101 101
102 logger.debug('Reading workspace in %s' % config.workspace_path) 102 logger.debug('Reading workspace in %s' % context.config.workspace_path)
103 externalsrc_re = re.compile(r'^EXTERNALSRC(_pn-([^ =]+))? *= *"([^"]*)"$') 103 externalsrc_re = re.compile(r'^EXTERNALSRC(:pn-([^ =]+))? *= *"([^"]*)"$')
104 for fn in glob.glob(os.path.join(config.workspace_path, 'appends', '*.bbappend')): 104 for fn in glob.glob(os.path.join(context.config.workspace_path, 'appends', '*.bbappend')):
105 with open(fn, 'r') as f: 105 with open(fn, 'r') as f:
106 pnvalues = {} 106 pnvalues = {}
107 pn = None
107 for line in f: 108 for line in f:
108 res = externalsrc_re.match(line.rstrip()) 109 res = externalsrc_re.match(line.rstrip())
109 if res: 110 if res:
@@ -111,7 +112,7 @@ def read_workspace():
111 pn = res.group(2) or recipepn 112 pn = res.group(2) or recipepn
112 # Find the recipe file within the workspace, if any 113 # Find the recipe file within the workspace, if any
113 bbfile = os.path.basename(fn).replace('.bbappend', '.bb').replace('%', '*') 114 bbfile = os.path.basename(fn).replace('.bbappend', '.bb').replace('%', '*')
114 recipefile = glob.glob(os.path.join(config.workspace_path, 115 recipefile = glob.glob(os.path.join(context.config.workspace_path,
115 'recipes', 116 'recipes',
116 recipepn, 117 recipepn,
117 bbfile)) 118 bbfile))
@@ -123,27 +124,42 @@ def read_workspace():
123 elif line.startswith('# srctreebase: '): 124 elif line.startswith('# srctreebase: '):
124 pnvalues['srctreebase'] = line.split(':', 1)[1].strip() 125 pnvalues['srctreebase'] = line.split(':', 1)[1].strip()
125 if pnvalues: 126 if pnvalues:
127 if not pn:
128 raise DevtoolError("Found *.bbappend in %s, but could not determine EXTERNALSRC:pn-*. "
129 "Maybe still using old syntax?" % context.config.workspace_path)
126 if not pnvalues.get('srctreebase', None): 130 if not pnvalues.get('srctreebase', None):
127 pnvalues['srctreebase'] = pnvalues['srctree'] 131 pnvalues['srctreebase'] = pnvalues['srctree']
128 logger.debug('Found recipe %s' % pnvalues) 132 logger.debug('Found recipe %s' % pnvalues)
129 workspace[pn] = pnvalues 133 workspace[pn] = pnvalues
130 134
131def create_workspace(args, config, basepath, workspace): 135 return workspace
136
137def create_workspace(args, config, basepath, _workspace):
132 if args.layerpath: 138 if args.layerpath:
133 workspacedir = os.path.abspath(args.layerpath) 139 workspacedir = os.path.abspath(args.layerpath)
134 else: 140 else:
135 workspacedir = os.path.abspath(os.path.join(basepath, 'workspace')) 141 workspacedir = os.path.abspath(os.path.join(basepath, 'workspace'))
136 _create_workspace(workspacedir, config, basepath) 142 layerseries = None
143 if args.layerseries:
144 layerseries = args.layerseries
145 _create_workspace(workspacedir, basepath, layerseries)
137 if not args.create_only: 146 if not args.create_only:
138 _enable_workspace_layer(workspacedir, config, basepath) 147 _enable_workspace_layer(workspacedir, config, basepath)
139 148
140def _create_workspace(workspacedir, config, basepath): 149def _create_workspace(workspacedir, basepath, layerseries=None):
141 import bb 150 import bb.utils
142 151
143 confdir = os.path.join(workspacedir, 'conf') 152 confdir = os.path.join(workspacedir, 'conf')
144 if os.path.exists(os.path.join(confdir, 'layer.conf')): 153 if os.path.exists(os.path.join(confdir, 'layer.conf')):
145 logger.info('Specified workspace already set up, leaving as-is') 154 logger.info('Specified workspace already set up, leaving as-is')
146 else: 155 else:
156 if not layerseries:
157 tinfoil = setup_tinfoil(config_only=True, basepath=basepath)
158 try:
159 layerseries = tinfoil.config_data.getVar('LAYERSERIES_CORENAMES')
160 finally:
161 tinfoil.shutdown()
162
147 # Add a config file 163 # Add a config file
148 bb.utils.mkdirhier(confdir) 164 bb.utils.mkdirhier(confdir)
149 with open(os.path.join(confdir, 'layer.conf'), 'w') as f: 165 with open(os.path.join(confdir, 'layer.conf'), 'w') as f:
@@ -155,7 +171,7 @@ def _create_workspace(workspacedir, config, basepath):
155 f.write('BBFILE_PATTERN_workspacelayer = "^$' + '{LAYERDIR}/"\n') 171 f.write('BBFILE_PATTERN_workspacelayer = "^$' + '{LAYERDIR}/"\n')
156 f.write('BBFILE_PATTERN_IGNORE_EMPTY_workspacelayer = "1"\n') 172 f.write('BBFILE_PATTERN_IGNORE_EMPTY_workspacelayer = "1"\n')
157 f.write('BBFILE_PRIORITY_workspacelayer = "99"\n') 173 f.write('BBFILE_PRIORITY_workspacelayer = "99"\n')
158 f.write('LAYERSERIES_COMPAT_workspacelayer = "${LAYERSERIES_COMPAT_core}"\n') 174 f.write('LAYERSERIES_COMPAT_workspacelayer = "%s"\n' % layerseries)
159 # Add a README file 175 # Add a README file
160 with open(os.path.join(workspacedir, 'README'), 'w') as f: 176 with open(os.path.join(workspacedir, 'README'), 'w') as f:
161 f.write('This layer was created by the OpenEmbedded devtool utility in order to\n') 177 f.write('This layer was created by the OpenEmbedded devtool utility in order to\n')
@@ -176,7 +192,7 @@ def _create_workspace(workspacedir, config, basepath):
176 192
177def _enable_workspace_layer(workspacedir, config, basepath): 193def _enable_workspace_layer(workspacedir, config, basepath):
178 """Ensure the workspace layer is in bblayers.conf""" 194 """Ensure the workspace layer is in bblayers.conf"""
179 import bb 195 import bb.utils
180 bblayers_conf = os.path.join(basepath, 'conf', 'bblayers.conf') 196 bblayers_conf = os.path.join(basepath, 'conf', 'bblayers.conf')
181 if not os.path.exists(bblayers_conf): 197 if not os.path.exists(bblayers_conf):
182 logger.error('Unable to find bblayers.conf') 198 logger.error('Unable to find bblayers.conf')
@@ -195,15 +211,9 @@ def _enable_workspace_layer(workspacedir, config, basepath):
195 211
196 212
197def main(): 213def main():
198 global basepath
199 global config
200 global context
201
202 if sys.getfilesystemencoding() != "utf-8": 214 if sys.getfilesystemencoding() != "utf-8":
203 sys.exit("Please use a locale setting which supports utf-8.\nPython can't change the filesystem locale after loading so we need a utf-8 when python starts or things won't work.") 215 sys.exit("Please use a locale setting which supports utf-8.\nPython can't change the filesystem locale after loading so we need a utf-8 when python starts or things won't work.")
204 216
205 context = Context(fixed_setup=False)
206
207 # Default basepath 217 # Default basepath
208 basepath = os.path.dirname(os.path.abspath(__file__)) 218 basepath = os.path.dirname(os.path.abspath(__file__))
209 219
@@ -228,21 +238,23 @@ def main():
228 elif global_args.quiet: 238 elif global_args.quiet:
229 logger.setLevel(logging.ERROR) 239 logger.setLevel(logging.ERROR)
230 240
241 is_fixed_setup = False
242
231 if global_args.basepath: 243 if global_args.basepath:
232 # Override 244 # Override
233 basepath = global_args.basepath 245 basepath = global_args.basepath
234 if os.path.exists(os.path.join(basepath, '.devtoolbase')): 246 if os.path.exists(os.path.join(basepath, '.devtoolbase')):
235 context.fixed_setup = True 247 is_fixed_setup = True
236 else: 248 else:
237 pth = basepath 249 pth = basepath
238 while pth != '' and pth != os.sep: 250 while pth != '' and pth != os.sep:
239 if os.path.exists(os.path.join(pth, '.devtoolbase')): 251 if os.path.exists(os.path.join(pth, '.devtoolbase')):
240 context.fixed_setup = True 252 is_fixed_setup = True
241 basepath = pth 253 basepath = pth
242 break 254 break
243 pth = os.path.dirname(pth) 255 pth = os.path.dirname(pth)
244 256
245 if not context.fixed_setup: 257 if not is_fixed_setup:
246 basepath = os.environ.get('BUILDDIR') 258 basepath = os.environ.get('BUILDDIR')
247 if not basepath: 259 if not basepath:
248 logger.error("This script can only be run after initialising the build environment (e.g. by using oe-init-build-env)") 260 logger.error("This script can only be run after initialising the build environment (e.g. by using oe-init-build-env)")
@@ -250,10 +262,9 @@ def main():
250 262
251 logger.debug('Using basepath %s' % basepath) 263 logger.debug('Using basepath %s' % basepath)
252 264
253 config = ConfigHandler(os.path.join(basepath, 'conf', 'devtool.conf')) 265 config = ConfigHandler(basepath, os.path.join(basepath, 'conf', 'devtool.conf'))
254 if not config.read(): 266 if not config.read():
255 return -1 267 return -1
256 context.config = config
257 268
258 bitbake_subdir = config.get('General', 'bitbake_subdir', '') 269 bitbake_subdir = config.get('General', 'bitbake_subdir', '')
259 if bitbake_subdir: 270 if bitbake_subdir:
@@ -275,6 +286,7 @@ def main():
275 scriptutils.logger_setup_color(logger, global_args.color) 286 scriptutils.logger_setup_color(logger, global_args.color)
276 287
277 if global_args.bbpath is None: 288 if global_args.bbpath is None:
289 import bb
278 try: 290 try:
279 tinfoil = setup_tinfoil(config_only=True, basepath=basepath) 291 tinfoil = setup_tinfoil(config_only=True, basepath=basepath)
280 try: 292 try:
@@ -285,8 +297,12 @@ def main():
285 return 2 297 return 2
286 298
287 # Search BBPATH first to allow layers to override plugins in scripts_path 299 # Search BBPATH first to allow layers to override plugins in scripts_path
288 for path in global_args.bbpath.split(':') + [scripts_path]: 300 pluginpaths = [os.path.join(path, 'lib', 'devtool') for path in global_args.bbpath.split(':') + [scripts_path]]
289 pluginpath = os.path.join(path, 'lib', 'devtool') 301
302 context = Context(fixed_setup=is_fixed_setup, config=config, pluginpaths=pluginpaths)
303
304 plugins = []
305 for pluginpath in pluginpaths:
290 scriptutils.load_plugins(logger, plugins, pluginpath) 306 scriptutils.load_plugins(logger, plugins, pluginpath)
291 307
292 subparsers = parser.add_subparsers(dest="subparser_name", title='subcommands', metavar='<subcommand>') 308 subparsers = parser.add_subparsers(dest="subparser_name", title='subcommands', metavar='<subcommand>')
@@ -305,6 +321,7 @@ def main():
305 description='Sets up a new workspace. NOTE: other devtool subcommands will create a workspace automatically as needed, so you only need to use %(prog)s if you want to specify where the workspace should be located.', 321 description='Sets up a new workspace. NOTE: other devtool subcommands will create a workspace automatically as needed, so you only need to use %(prog)s if you want to specify where the workspace should be located.',
306 group='advanced') 322 group='advanced')
307 parser_create_workspace.add_argument('layerpath', nargs='?', help='Path in which the workspace layer should be created') 323 parser_create_workspace.add_argument('layerpath', nargs='?', help='Path in which the workspace layer should be created')
324 parser_create_workspace.add_argument('--layerseries', help='Layer series the workspace should be set to be compatible with')
308 parser_create_workspace.add_argument('--create-only', action="store_true", help='Only create the workspace layer, do not alter configuration') 325 parser_create_workspace.add_argument('--create-only', action="store_true", help='Only create the workspace layer, do not alter configuration')
309 parser_create_workspace.set_defaults(func=create_workspace, no_workspace=True) 326 parser_create_workspace.set_defaults(func=create_workspace, no_workspace=True)
310 327
@@ -314,10 +331,10 @@ def main():
314 331
315 args = parser.parse_args(unparsed_args, namespace=global_args) 332 args = parser.parse_args(unparsed_args, namespace=global_args)
316 333
317 if not getattr(args, 'no_workspace', False):
318 read_workspace()
319
320 try: 334 try:
335 workspace = {}
336 if not getattr(args, 'no_workspace', False):
337 workspace = read_workspace(basepath, context)
321 ret = args.func(args, config, basepath, workspace) 338 ret = args.func(args, config, basepath, workspace)
322 except DevtoolError as err: 339 except DevtoolError as err:
323 if str(err): 340 if str(err):
@@ -325,6 +342,7 @@ def main():
325 ret = err.exitcode 342 ret = err.exitcode
326 except argparse_oe.ArgumentUsageError as ae: 343 except argparse_oe.ArgumentUsageError as ae:
327 parser.error_subcommand(ae.message, ae.subcommand) 344 parser.error_subcommand(ae.message, ae.subcommand)
345 ret = 2
328 346
329 return ret 347 return ret
330 348