blob: d64e18e179830f01606d17d676cc88621c0b0da4 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# Development tool - standard commands plugin
2#
Brad Bishopd7bf8c12018-02-25 22:55:05 -05003# Copyright (C) 2014-2017 Intel Corporation
Patrick Williamsc124f4f2015-09-15 14:41:29 -05004#
Brad Bishopc342db32019-05-15 21:57:59 -04005# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -05006#
Patrick Williamsc124f4f2015-09-15 14:41:29 -05007"""Devtool standard plugins"""
8
9import os
10import sys
11import re
12import shutil
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050013import subprocess
Patrick Williamsc124f4f2015-09-15 14:41:29 -050014import tempfile
15import logging
16import argparse
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050017import argparse_oe
Patrick Williamsc124f4f2015-09-15 14:41:29 -050018import scriptutils
19import errno
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050020import glob
Patrick Williamsc0f7c042017-02-23 20:41:17 -060021import filecmp
Patrick Williamsf1e5d692016-03-30 15:21:19 -050022from collections import OrderedDict
Brad Bishop316dfdd2018-06-25 12:45:53 -040023from devtool import exec_build_env_command, setup_tinfoil, check_workspace_recipe, use_external_build, setup_git_repo, recipe_to_append, get_bbclassextend_targets, update_unlockedsigs, check_prerelease_version, check_git_repo_dirty, check_git_repo_op, DevtoolError
Patrick Williamsc124f4f2015-09-15 14:41:29 -050024from devtool import parse_recipe
25
26logger = logging.getLogger('devtool')
27
Brad Bishop316dfdd2018-06-25 12:45:53 -040028override_branch_prefix = 'devtool-override-'
29
Patrick Williamsc124f4f2015-09-15 14:41:29 -050030
31def add(args, config, basepath, workspace):
32 """Entry point for the devtool 'add' subcommand"""
33 import bb
34 import oe.recipeutils
35
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050036 if not args.recipename and not args.srctree and not args.fetch and not args.fetchuri:
37 raise argparse_oe.ArgumentUsageError('At least one of recipename, srctree, fetchuri or -f/--fetch must be specified', 'add')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050038
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050039 # These are positional arguments, but because we're nice, allow
40 # specifying e.g. source tree without name, or fetch URI without name or
41 # source tree (if we can detect that that is what the user meant)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060042 if scriptutils.is_src_url(args.recipename):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050043 if not args.fetchuri:
44 if args.fetch:
45 raise DevtoolError('URI specified as positional argument as well as -f/--fetch')
46 args.fetchuri = args.recipename
47 args.recipename = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -060048 elif scriptutils.is_src_url(args.srctree):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050049 if not args.fetchuri:
50 if args.fetch:
51 raise DevtoolError('URI specified as positional argument as well as -f/--fetch')
52 args.fetchuri = args.srctree
53 args.srctree = ''
54 elif args.recipename and not args.srctree:
55 if os.sep in args.recipename:
56 args.srctree = args.recipename
57 args.recipename = None
58 elif os.path.isdir(args.recipename):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080059 logger.warning('Ambiguous argument "%s" - assuming you mean it to be the recipe name' % args.recipename)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060060
Brad Bishopd7bf8c12018-02-25 22:55:05 -050061 if not args.fetchuri:
62 if args.srcrev:
63 raise DevtoolError('The -S/--srcrev option is only valid when fetching from an SCM repository')
64 if args.srcbranch:
65 raise DevtoolError('The -B/--srcbranch option is only valid when fetching from an SCM repository')
66
Patrick Williamsc0f7c042017-02-23 20:41:17 -060067 if args.srctree and os.path.isfile(args.srctree):
68 args.fetchuri = 'file://' + os.path.abspath(args.srctree)
69 args.srctree = ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -050070
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050071 if args.fetch:
72 if args.fetchuri:
73 raise DevtoolError('URI specified as positional argument as well as -f/--fetch')
74 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080075 logger.warning('-f/--fetch option is deprecated - you can now simply specify the URL to fetch as a positional argument instead')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050076 args.fetchuri = args.fetch
Patrick Williamsf1e5d692016-03-30 15:21:19 -050077
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050078 if args.recipename:
79 if args.recipename in workspace:
80 raise DevtoolError("recipe %s is already in your workspace" %
81 args.recipename)
82 reason = oe.recipeutils.validate_pn(args.recipename)
83 if reason:
84 raise DevtoolError(reason)
85
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050086 if args.srctree:
87 srctree = os.path.abspath(args.srctree)
88 srctreeparent = None
89 tmpsrcdir = None
90 else:
91 srctree = None
92 srctreeparent = get_default_srctree(config)
93 bb.utils.mkdirhier(srctreeparent)
94 tmpsrcdir = tempfile.mkdtemp(prefix='devtoolsrc', dir=srctreeparent)
95
96 if srctree and os.path.exists(srctree):
97 if args.fetchuri:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050098 if not os.path.isdir(srctree):
99 raise DevtoolError("Cannot fetch into source tree path %s as "
100 "it exists and is not a directory" %
101 srctree)
102 elif os.listdir(srctree):
103 raise DevtoolError("Cannot fetch into source tree path %s as "
104 "it already exists and is non-empty" %
105 srctree)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500106 elif not args.fetchuri:
107 if args.srctree:
108 raise DevtoolError("Specified source tree %s could not be found" %
109 args.srctree)
110 elif srctree:
111 raise DevtoolError("No source tree exists at default path %s - "
112 "either create and populate this directory, "
113 "or specify a path to a source tree, or a "
114 "URI to fetch source from" % srctree)
115 else:
116 raise DevtoolError("You must either specify a source tree "
117 "or a URI to fetch source from")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500118
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500119 if args.version:
120 if '_' in args.version or ' ' in args.version:
121 raise DevtoolError('Invalid version string "%s"' % args.version)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500122
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500123 if args.color == 'auto' and sys.stdout.isatty():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500124 color = 'always'
125 else:
126 color = args.color
127 extracmdopts = ''
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500128 if args.fetchuri:
129 source = args.fetchuri
130 if srctree:
131 extracmdopts += ' -x %s' % srctree
132 else:
133 extracmdopts += ' -x %s' % tmpsrcdir
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500134 else:
135 source = srctree
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500136 if args.recipename:
137 extracmdopts += ' -N %s' % args.recipename
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500138 if args.version:
139 extracmdopts += ' -V %s' % args.version
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500140 if args.binary:
141 extracmdopts += ' -b'
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500142 if args.also_native:
143 extracmdopts += ' --also-native'
144 if args.src_subdir:
145 extracmdopts += ' --src-subdir "%s"' % args.src_subdir
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600146 if args.autorev:
147 extracmdopts += ' -a'
Andrew Geissler82c905d2020-04-13 13:39:40 -0500148 if args.npm_dev:
149 extracmdopts += ' --npm-dev'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500150 if args.mirrors:
151 extracmdopts += ' --mirrors'
152 if args.srcrev:
153 extracmdopts += ' --srcrev %s' % args.srcrev
154 if args.srcbranch:
155 extracmdopts += ' --srcbranch %s' % args.srcbranch
156 if args.provides:
157 extracmdopts += ' --provides %s' % args.provides
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500158
159 tempdir = tempfile.mkdtemp(prefix='devtool')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500160 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500161 try:
162 stdout, _ = exec_build_env_command(config.init_path, basepath, 'recipetool --color=%s create --devtool -o %s \'%s\' %s' % (color, tempdir, source, extracmdopts), watch=True)
163 except bb.process.ExecutionError as e:
164 if e.exitcode == 15:
165 raise DevtoolError('Could not auto-determine recipe name, please specify it on the command line')
166 else:
167 raise DevtoolError('Command \'%s\' failed' % e.command)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500168
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500169 recipes = glob.glob(os.path.join(tempdir, '*.bb'))
170 if recipes:
171 recipename = os.path.splitext(os.path.basename(recipes[0]))[0].split('_')[0]
172 if recipename in workspace:
173 raise DevtoolError('A recipe with the same name as the one being created (%s) already exists in your workspace' % recipename)
174 recipedir = os.path.join(config.workspace_path, 'recipes', recipename)
175 bb.utils.mkdirhier(recipedir)
176 recipefile = os.path.join(recipedir, os.path.basename(recipes[0]))
177 appendfile = recipe_to_append(recipefile, config)
178 if os.path.exists(appendfile):
179 # This shouldn't be possible, but just in case
180 raise DevtoolError('A recipe with the same name as the one being created already exists in your workspace')
181 if os.path.exists(recipefile):
182 raise DevtoolError('A recipe file %s already exists in your workspace; this shouldn\'t be there - please delete it before continuing' % recipefile)
183 if tmpsrcdir:
184 srctree = os.path.join(srctreeparent, recipename)
185 if os.path.exists(tmpsrcdir):
186 if os.path.exists(srctree):
187 if os.path.isdir(srctree):
188 try:
189 os.rmdir(srctree)
190 except OSError as e:
191 if e.errno == errno.ENOTEMPTY:
192 raise DevtoolError('Source tree path %s already exists and is not empty' % srctree)
193 else:
194 raise
195 else:
196 raise DevtoolError('Source tree path %s already exists and is not a directory' % srctree)
197 logger.info('Using default source tree path %s' % srctree)
198 shutil.move(tmpsrcdir, srctree)
199 else:
200 raise DevtoolError('Couldn\'t find source tree created by recipetool')
201 bb.utils.mkdirhier(recipedir)
202 shutil.move(recipes[0], recipefile)
203 # Move any additional files created by recipetool
204 for fn in os.listdir(tempdir):
205 shutil.move(os.path.join(tempdir, fn), recipedir)
206 else:
207 raise DevtoolError('Command \'%s\' did not create any recipe file:\n%s' % (e.command, e.stdout))
208 attic_recipe = os.path.join(config.workspace_path, 'attic', recipename, os.path.basename(recipefile))
209 if os.path.exists(attic_recipe):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800210 logger.warning('A modified recipe from a previous invocation exists in %s - you may wish to move this over the top of the new recipe if you had changes in it that you want to continue with' % attic_recipe)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500211 finally:
212 if tmpsrcdir and os.path.exists(tmpsrcdir):
213 shutil.rmtree(tmpsrcdir)
214 shutil.rmtree(tempdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500215
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500216 for fn in os.listdir(recipedir):
217 _add_md5(config, recipename, os.path.join(recipedir, fn))
218
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500219 tinfoil = setup_tinfoil(config_only=True, basepath=basepath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600220 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500221 try:
222 rd = tinfoil.parse_recipe_file(recipefile, False)
223 except Exception as e:
224 logger.error(str(e))
225 rd = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600226 if not rd:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500227 # Parsing failed. We just created this recipe and we shouldn't
228 # leave it in the workdir or it'll prevent bitbake from starting
229 movefn = '%s.parsefailed' % recipefile
230 logger.error('Parsing newly created recipe failed, moving recipe to %s for reference. If this looks to be caused by the recipe itself, please report this error.' % movefn)
231 shutil.move(recipefile, movefn)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600232 return 1
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500233
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600234 if args.fetchuri and not args.no_git:
235 setup_git_repo(srctree, args.version, 'devtool', d=tinfoil.config_data)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500236
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600237 initial_rev = None
238 if os.path.exists(os.path.join(srctree, '.git')):
239 (stdout, _) = bb.process.run('git rev-parse HEAD', cwd=srctree)
240 initial_rev = stdout.rstrip()
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500241
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600242 if args.src_subdir:
243 srctree = os.path.join(srctree, args.src_subdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500244
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600245 bb.utils.mkdirhier(os.path.dirname(appendfile))
246 with open(appendfile, 'w') as f:
247 f.write('inherit externalsrc\n')
248 f.write('EXTERNALSRC = "%s"\n' % srctree)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500249
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600250 b_is_s = use_external_build(args.same_dir, args.no_same_dir, rd)
251 if b_is_s:
252 f.write('EXTERNALSRC_BUILD = "%s"\n' % srctree)
253 if initial_rev:
254 f.write('\n# initial_rev: %s\n' % initial_rev)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500255
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600256 if args.binary:
Patrick Williams213cb262021-08-07 19:21:33 -0500257 f.write('do_install:append() {\n')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600258 f.write(' rm -rf ${D}/.git\n')
259 f.write(' rm -f ${D}/singletask.lock\n')
260 f.write('}\n')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500261
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600262 if bb.data.inherits_class('npm', rd):
Patrick Williams213cb262021-08-07 19:21:33 -0500263 f.write('python do_configure:append() {\n')
Andrew Geissler82c905d2020-04-13 13:39:40 -0500264 f.write(' pkgdir = d.getVar("NPM_PACKAGE")\n')
265 f.write(' lockfile = os.path.join(pkgdir, "singletask.lock")\n')
266 f.write(' bb.utils.remove(lockfile)\n')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600267 f.write('}\n')
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500268
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500269 # Check if the new layer provides recipes whose priorities have been
270 # overriden by PREFERRED_PROVIDER.
271 recipe_name = rd.getVar('PN')
272 provides = rd.getVar('PROVIDES')
273 # Search every item defined in PROVIDES
274 for recipe_provided in provides.split():
275 preferred_provider = 'PREFERRED_PROVIDER_' + recipe_provided
276 current_pprovider = rd.getVar(preferred_provider)
277 if current_pprovider and current_pprovider != recipe_name:
278 if args.fixed_setup:
279 #if we are inside the eSDK add the new PREFERRED_PROVIDER in the workspace layer.conf
280 layerconf_file = os.path.join(config.workspace_path, "conf", "layer.conf")
281 with open(layerconf_file, 'a') as f:
282 f.write('%s = "%s"\n' % (preferred_provider, recipe_name))
283 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800284 logger.warning('Set \'%s\' in order to use the recipe' % preferred_provider)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500285 break
286
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600287 _add_md5(config, recipename, appendfile)
288
Brad Bishop316dfdd2018-06-25 12:45:53 -0400289 check_prerelease_version(rd.getVar('PV'), 'devtool add')
290
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600291 logger.info('Recipe %s has been automatically created; further editing may be required to make it fully functional' % recipefile)
292
293 finally:
294 tinfoil.shutdown()
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500295
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500296 return 0
297
298
299def _check_compatible_recipe(pn, d):
300 """Check if the recipe is supported by devtool"""
301 if pn == 'perf':
302 raise DevtoolError("The perf recipe does not actually check out "
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600303 "source and thus cannot be supported by this tool",
304 4)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500305
306 if pn in ['kernel-devsrc', 'package-index'] or pn.startswith('gcc-source'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600307 raise DevtoolError("The %s recipe is not supported by this tool" % pn, 4)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500308
309 if bb.data.inherits_class('image', d):
310 raise DevtoolError("The %s recipe is an image, and therefore is not "
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600311 "supported by this tool" % pn, 4)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500312
313 if bb.data.inherits_class('populate_sdk', d):
314 raise DevtoolError("The %s recipe is an SDK, and therefore is not "
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600315 "supported by this tool" % pn, 4)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500316
317 if bb.data.inherits_class('packagegroup', d):
318 raise DevtoolError("The %s recipe is a packagegroup, and therefore is "
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600319 "not supported by this tool" % pn, 4)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500320
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500321 if bb.data.inherits_class('externalsrc', d) and d.getVar('EXTERNALSRC'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600322 # Not an incompatibility error per se, so we don't pass the error code
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500323 raise DevtoolError("externalsrc is currently enabled for the %s "
324 "recipe. This prevents the normal do_patch task "
325 "from working. You will need to disable this "
326 "first." % pn)
327
Brad Bishop316dfdd2018-06-25 12:45:53 -0400328def _dry_run_copy(src, dst, dry_run_outdir, base_outdir):
329 """Common function for copying a file to the dry run output directory"""
330 relpath = os.path.relpath(dst, base_outdir)
331 if relpath.startswith('..'):
332 raise Exception('Incorrect base path %s for path %s' % (base_outdir, dst))
333 dst = os.path.join(dry_run_outdir, relpath)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500334 dst_d = os.path.dirname(dst)
335 if dst_d:
336 bb.utils.mkdirhier(dst_d)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400337 # Don't overwrite existing files, otherwise in the case of an upgrade
338 # the dry-run written out recipe will be overwritten with an unmodified
339 # version
340 if not os.path.exists(dst):
341 shutil.copy(src, dst)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500342
Brad Bishop316dfdd2018-06-25 12:45:53 -0400343def _move_file(src, dst, dry_run_outdir=None, base_outdir=None):
344 """Move a file. Creates all the directory components of destination path."""
345 dry_run_suffix = ' (dry-run)' if dry_run_outdir else ''
346 logger.debug('Moving %s to %s%s' % (src, dst, dry_run_suffix))
347 if dry_run_outdir:
348 # We want to copy here, not move
349 _dry_run_copy(src, dst, dry_run_outdir, base_outdir)
350 else:
351 dst_d = os.path.dirname(dst)
352 if dst_d:
353 bb.utils.mkdirhier(dst_d)
354 shutil.move(src, dst)
355
Andrew Geissler78b72792022-06-14 06:47:25 -0500356def _copy_file(src, dst, dry_run_outdir=None, base_outdir=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600357 """Copy a file. Creates all the directory components of destination path."""
Brad Bishop316dfdd2018-06-25 12:45:53 -0400358 dry_run_suffix = ' (dry-run)' if dry_run_outdir else ''
359 logger.debug('Copying %s to %s%s' % (src, dst, dry_run_suffix))
360 if dry_run_outdir:
361 _dry_run_copy(src, dst, dry_run_outdir, base_outdir)
362 else:
363 dst_d = os.path.dirname(dst)
364 if dst_d:
365 bb.utils.mkdirhier(dst_d)
366 shutil.copy(src, dst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600367
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500368def _git_ls_tree(repodir, treeish='HEAD', recursive=False):
369 """List contents of a git treeish"""
370 import bb
371 cmd = ['git', 'ls-tree', '-z', treeish]
372 if recursive:
373 cmd.append('-r')
374 out, _ = bb.process.run(cmd, cwd=repodir)
375 ret = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500376 if out:
377 for line in out.split('\0'):
378 if line:
379 split = line.split(None, 4)
380 ret[split[3]] = split[0:3]
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500381 return ret
382
383def _git_exclude_path(srctree, path):
384 """Return pathspec (list of paths) that excludes certain path"""
385 # NOTE: "Filtering out" files/paths in this way is not entirely reliable -
386 # we don't catch files that are deleted, for example. A more reliable way
387 # to implement this would be to use "negative pathspecs" which were
388 # introduced in Git v1.9.0. Revisit this when/if the required Git version
389 # becomes greater than that.
390 path = os.path.normpath(path)
391 recurse = True if len(path.split(os.path.sep)) > 1 else False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600392 git_files = list(_git_ls_tree(srctree, 'HEAD', recurse).keys())
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500393 if path in git_files:
394 git_files.remove(path)
395 return git_files
396 else:
397 return ['.']
398
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500399def _ls_tree(directory):
400 """Recursive listing of files in a directory"""
401 ret = []
402 for root, dirs, files in os.walk(directory):
403 ret.extend([os.path.relpath(os.path.join(root, fname), directory) for
404 fname in files])
405 return ret
406
407
408def extract(args, config, basepath, workspace):
409 """Entry point for the devtool 'extract' subcommand"""
410 import bb
411
Brad Bishop316dfdd2018-06-25 12:45:53 -0400412 tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500413 if not tinfoil:
414 # Error already shown
415 return 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600416 try:
417 rd = parse_recipe(config, tinfoil, args.recipename, True)
418 if not rd:
419 return 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600421 srctree = os.path.abspath(args.srctree)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400422 initial_rev, _ = _extract_source(srctree, args.keep_temp, args.branch, False, config, basepath, workspace, args.fixed_setup, rd, tinfoil, no_overrides=args.no_overrides)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600423 logger.info('Source tree extracted to %s' % srctree)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500424
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600425 if initial_rev:
426 return 0
427 else:
428 return 1
429 finally:
430 tinfoil.shutdown()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500431
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500432def sync(args, config, basepath, workspace):
433 """Entry point for the devtool 'sync' subcommand"""
434 import bb
435
Brad Bishop316dfdd2018-06-25 12:45:53 -0400436 tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500437 if not tinfoil:
438 # Error already shown
439 return 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600440 try:
441 rd = parse_recipe(config, tinfoil, args.recipename, True)
442 if not rd:
443 return 1
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500444
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600445 srctree = os.path.abspath(args.srctree)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400446 initial_rev, _ = _extract_source(srctree, args.keep_temp, args.branch, True, config, basepath, workspace, args.fixed_setup, rd, tinfoil, no_overrides=True)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600447 logger.info('Source tree %s synchronized' % srctree)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500448
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600449 if initial_rev:
450 return 0
451 else:
452 return 1
453 finally:
454 tinfoil.shutdown()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500455
Brad Bishop96ff1982019-08-19 13:50:42 -0400456def symlink_oelocal_files_srctree(rd,srctree):
457 import oe.patch
458 if os.path.abspath(rd.getVar('S')) == os.path.abspath(rd.getVar('WORKDIR')):
459 # If recipe extracts to ${WORKDIR}, symlink the files into the srctree
460 # (otherwise the recipe won't build as expected)
461 local_files_dir = os.path.join(srctree, 'oe-local-files')
462 addfiles = []
463 for root, _, files in os.walk(local_files_dir):
464 relpth = os.path.relpath(root, local_files_dir)
465 if relpth != '.':
466 bb.utils.mkdirhier(os.path.join(srctree, relpth))
467 for fn in files:
468 if fn == '.gitignore':
469 continue
470 destpth = os.path.join(srctree, relpth, fn)
471 if os.path.exists(destpth):
472 os.unlink(destpth)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600473 if relpth != '.':
474 back_relpth = os.path.relpath(local_files_dir, root)
475 os.symlink('%s/oe-local-files/%s/%s' % (back_relpth, relpth, fn), destpth)
476 else:
477 os.symlink('oe-local-files/%s' % fn, destpth)
Brad Bishop96ff1982019-08-19 13:50:42 -0400478 addfiles.append(os.path.join(relpth, fn))
479 if addfiles:
480 bb.process.run('git add %s' % ' '.join(addfiles), cwd=srctree)
Brad Bishop79641f22019-09-10 07:20:22 -0400481 useroptions = []
482 oe.patch.GitApplyTree.gitCommandUserOptions(useroptions, d=rd)
483 bb.process.run('git %s commit -m "Committing local file symlinks\n\n%s"' % (' '.join(useroptions), oe.patch.GitApplyTree.ignore_commit_prefix), cwd=srctree)
Brad Bishop96ff1982019-08-19 13:50:42 -0400484
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500485
Brad Bishop316dfdd2018-06-25 12:45:53 -0400486def _extract_source(srctree, keep_temp, devbranch, sync, config, basepath, workspace, fixed_setup, d, tinfoil, no_overrides=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500487 """Extract sources of a recipe"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500488 import oe.recipeutils
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500489 import oe.patch
Brad Bishop96ff1982019-08-19 13:50:42 -0400490 import oe.path
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500491
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500492 pn = d.getVar('PN')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500493
494 _check_compatible_recipe(pn, d)
495
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500496 if sync:
497 if not os.path.exists(srctree):
498 raise DevtoolError("output path %s does not exist" % srctree)
499 else:
500 if os.path.exists(srctree):
501 if not os.path.isdir(srctree):
502 raise DevtoolError("output path %s exists and is not a directory" %
503 srctree)
504 elif os.listdir(srctree):
505 raise DevtoolError("output path %s already exists and is "
506 "non-empty" % srctree)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500507
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500508 if 'noexec' in (d.getVarFlags('do_unpack', False) or []):
509 raise DevtoolError("The %s recipe has do_unpack disabled, unable to "
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600510 "extract source" % pn, 4)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500511
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500512 if not sync:
513 # Prepare for shutil.move later on
514 bb.utils.mkdirhier(srctree)
515 os.rmdir(srctree)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500516
Brad Bishop316dfdd2018-06-25 12:45:53 -0400517 extra_overrides = []
518 if not no_overrides:
519 history = d.varhistory.variable('SRC_URI')
520 for event in history:
521 if not 'flag' in event:
Patrick Williams213cb262021-08-07 19:21:33 -0500522 if event['op'].startswith((':append[', ':prepend[')):
Andrew Geissler615f2f12022-07-15 14:00:58 -0500523 override = event['op'].split('[')[1].split(']')[0]
524 if not override.startswith('pn-'):
525 extra_overrides.append(override)
Andrew Geissler99467da2019-02-25 18:54:23 -0600526 # We want to remove duplicate overrides. If a recipe had multiple
527 # SRC_URI_override += values it would cause mulitple instances of
528 # overrides. This doesn't play nicely with things like creating a
529 # branch for every instance of DEVTOOL_EXTRA_OVERRIDES.
530 extra_overrides = list(set(extra_overrides))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400531 if extra_overrides:
532 logger.info('SRC_URI contains some conditional appends/prepends - will create branches to represent these')
533
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500534 initial_rev = None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500535
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500536 recipefile = d.getVar('FILE')
537 appendfile = recipe_to_append(recipefile, config)
538 is_kernel_yocto = bb.data.inherits_class('kernel-yocto', d)
539
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500540 # We need to redirect WORKDIR, STAMPS_DIR etc. under a temporary
541 # directory so that:
542 # (a) we pick up all files that get unpacked to the WORKDIR, and
543 # (b) we don't disturb the existing build
544 # However, with recipe-specific sysroots the sysroots for the recipe
545 # will be prepared under WORKDIR, and if we used the system temporary
546 # directory (i.e. usually /tmp) as used by mkdtemp by default, then
547 # our attempts to hardlink files into the recipe-specific sysroots
548 # will fail on systems where /tmp is a different filesystem, and it
549 # would have to fall back to copying the files which is a waste of
550 # time. Put the temp directory under the WORKDIR to prevent that from
551 # being a problem.
552 tempbasedir = d.getVar('WORKDIR')
553 bb.utils.mkdirhier(tempbasedir)
554 tempdir = tempfile.mkdtemp(prefix='devtooltmp-', dir=tempbasedir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500555 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500556 tinfoil.logger.setLevel(logging.WARNING)
557
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500558 # FIXME this results in a cache reload under control of tinfoil, which is fine
559 # except we don't get the knotty progress bar
560
561 if os.path.exists(appendfile):
562 appendbackup = os.path.join(tempdir, os.path.basename(appendfile) + '.bak')
563 shutil.copyfile(appendfile, appendbackup)
564 else:
565 appendbackup = None
566 bb.utils.mkdirhier(os.path.dirname(appendfile))
567 logger.debug('writing append file %s' % appendfile)
568 with open(appendfile, 'a') as f:
569 f.write('###--- _extract_source\n')
570 f.write('DEVTOOL_TEMPDIR = "%s"\n' % tempdir)
571 f.write('DEVTOOL_DEVBRANCH = "%s"\n' % devbranch)
572 if not is_kernel_yocto:
573 f.write('PATCHTOOL = "git"\n')
574 f.write('PATCH_COMMIT_FUNCTIONS = "1"\n')
Brad Bishop316dfdd2018-06-25 12:45:53 -0400575 if extra_overrides:
576 f.write('DEVTOOL_EXTRA_OVERRIDES = "%s"\n' % ':'.join(extra_overrides))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500577 f.write('inherit devtool-source\n')
578 f.write('###--- _extract_source\n')
579
580 update_unlockedsigs(basepath, workspace, fixed_setup, [pn])
581
582 sstate_manifests = d.getVar('SSTATE_MANIFESTS')
583 bb.utils.mkdirhier(sstate_manifests)
584 preservestampfile = os.path.join(sstate_manifests, 'preserve-stamps')
585 with open(preservestampfile, 'w') as f:
586 f.write(d.getVar('STAMP'))
587 try:
Brad Bishop96ff1982019-08-19 13:50:42 -0400588 if is_kernel_yocto:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500589 # We need to generate the kernel config
590 task = 'do_configure'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500591 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500592 task = 'do_patch'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500593
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600594 if 'noexec' in (d.getVarFlags(task, False) or []) or 'task' not in (d.getVarFlags(task, False) or []):
595 logger.info('The %s recipe has %s disabled. Running only '
596 'do_configure task dependencies' % (pn, task))
597
598 if 'depends' in d.getVarFlags('do_configure', False):
599 pn = d.getVarFlags('do_configure', False)['depends']
600 pn = pn.replace('${PV}', d.getVar('PV'))
601 pn = pn.replace('${COMPILERDEP}', d.getVar('COMPILERDEP'))
602 task = None
603
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500604 # Run the fetch + unpack tasks
605 res = tinfoil.build_targets(pn,
606 task,
607 handle_events=True)
608 finally:
609 if os.path.exists(preservestampfile):
610 os.remove(preservestampfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500611
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500612 if not res:
613 raise DevtoolError('Extracting source for %s failed' % pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500614
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600615 if not is_kernel_yocto and ('noexec' in (d.getVarFlags('do_patch', False) or []) or 'task' not in (d.getVarFlags('do_patch', False) or [])):
616 workshareddir = d.getVar('S')
617 if os.path.islink(srctree):
618 os.unlink(srctree)
619
620 os.symlink(workshareddir, srctree)
621
622 # The initial_rev file is created in devtool_post_unpack function that will not be executed if
623 # do_unpack/do_patch tasks are disabled so we have to directly say that source extraction was successful
624 return True, True
625
Brad Bishop316dfdd2018-06-25 12:45:53 -0400626 try:
627 with open(os.path.join(tempdir, 'initial_rev'), 'r') as f:
628 initial_rev = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500629
Brad Bishop316dfdd2018-06-25 12:45:53 -0400630 with open(os.path.join(tempdir, 'srcsubdir'), 'r') as f:
631 srcsubdir = f.read()
632 except FileNotFoundError as e:
633 raise DevtoolError('Something went wrong with source extraction - the devtool-source class was not active or did not function correctly:\n%s' % str(e))
634 srcsubdir_rel = os.path.relpath(srcsubdir, os.path.join(tempdir, 'workdir'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500635
Brad Bishop96ff1982019-08-19 13:50:42 -0400636 # Check if work-shared is empty, if yes
637 # find source and copy to work-shared
638 if is_kernel_yocto:
639 workshareddir = d.getVar('STAGING_KERNEL_DIR')
640 staging_kerVer = get_staging_kver(workshareddir)
641 kernelVersion = d.getVar('LINUX_VERSION')
642
643 # handle dangling symbolic link in work-shared:
644 if os.path.islink(workshareddir):
645 os.unlink(workshareddir)
646
647 if os.path.exists(workshareddir) and (not os.listdir(workshareddir) or kernelVersion != staging_kerVer):
648 shutil.rmtree(workshareddir)
649 oe.path.copyhardlinktree(srcsubdir,workshareddir)
650 elif not os.path.exists(workshareddir):
651 oe.path.copyhardlinktree(srcsubdir,workshareddir)
652
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500653 tempdir_localdir = os.path.join(tempdir, 'oe-local-files')
654 srctree_localdir = os.path.join(srctree, 'oe-local-files')
655
656 if sync:
657 bb.process.run('git fetch file://' + srcsubdir + ' ' + devbranch + ':' + devbranch, cwd=srctree)
658
659 # Move oe-local-files directory to srctree
660 # As the oe-local-files is not part of the constructed git tree,
661 # remove them directly during the synchrounizating might surprise
662 # the users. Instead, we move it to oe-local-files.bak and remind
663 # user in the log message.
664 if os.path.exists(srctree_localdir + '.bak'):
665 shutil.rmtree(srctree_localdir, srctree_localdir + '.bak')
666
667 if os.path.exists(srctree_localdir):
668 logger.info('Backing up current local file directory %s' % srctree_localdir)
669 shutil.move(srctree_localdir, srctree_localdir + '.bak')
670
671 if os.path.exists(tempdir_localdir):
672 logger.info('Syncing local source files to srctree...')
673 shutil.copytree(tempdir_localdir, srctree_localdir)
674 else:
675 # Move oe-local-files directory to srctree
676 if os.path.exists(tempdir_localdir):
677 logger.info('Adding local source files to srctree...')
678 shutil.move(tempdir_localdir, srcsubdir)
679
680 shutil.move(srcsubdir, srctree)
Brad Bishop96ff1982019-08-19 13:50:42 -0400681 symlink_oelocal_files_srctree(d,srctree)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500682
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500683 if is_kernel_yocto:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500684 logger.info('Copying kernel config to srctree')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500685 shutil.copy2(os.path.join(tempdir, '.config'), srctree)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500686
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500687 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500688 if appendbackup:
689 shutil.copyfile(appendbackup, appendfile)
690 elif os.path.exists(appendfile):
691 os.remove(appendfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500692 if keep_temp:
693 logger.info('Preserving temporary directory %s' % tempdir)
694 else:
695 shutil.rmtree(tempdir)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400696 return initial_rev, srcsubdir_rel
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500697
698def _add_md5(config, recipename, filename):
699 """Record checksum of a file (or recursively for a directory) to the md5-file of the workspace"""
700 import bb.utils
701
702 def addfile(fn):
703 md5 = bb.utils.md5_file(fn)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500704 with open(os.path.join(config.workspace_path, '.devtool_md5'), 'a+') as f:
705 md5_str = '%s|%s|%s\n' % (recipename, os.path.relpath(fn, config.workspace_path), md5)
706 f.seek(0, os.SEEK_SET)
707 if not md5_str in f.read():
708 f.write(md5_str)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500709
710 if os.path.isdir(filename):
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500711 for root, _, files in os.walk(filename):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500712 for f in files:
713 addfile(os.path.join(root, f))
714 else:
715 addfile(filename)
716
717def _check_preserve(config, recipename):
718 """Check if a file was manually changed and needs to be saved in 'attic'
719 directory"""
720 import bb.utils
721 origfile = os.path.join(config.workspace_path, '.devtool_md5')
722 newfile = os.path.join(config.workspace_path, '.devtool_md5_new')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500723 preservepath = os.path.join(config.workspace_path, 'attic', recipename)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500724 with open(origfile, 'r') as f:
725 with open(newfile, 'w') as tf:
726 for line in f.readlines():
727 splitline = line.rstrip().split('|')
728 if splitline[0] == recipename:
729 removefile = os.path.join(config.workspace_path, splitline[1])
730 try:
731 md5 = bb.utils.md5_file(removefile)
732 except IOError as err:
733 if err.errno == 2:
734 # File no longer exists, skip it
735 continue
736 else:
737 raise
738 if splitline[2] != md5:
739 bb.utils.mkdirhier(preservepath)
740 preservefile = os.path.basename(removefile)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800741 logger.warning('File %s modified since it was written, preserving in %s' % (preservefile, preservepath))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500742 shutil.move(removefile, os.path.join(preservepath, preservefile))
743 else:
744 os.remove(removefile)
745 else:
746 tf.write(line)
Andrew Geisslerc926e172021-05-07 16:11:35 -0500747 bb.utils.rename(newfile, origfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500748
Brad Bishop96ff1982019-08-19 13:50:42 -0400749def get_staging_kver(srcdir):
750 # Kernel version from work-shared
751 kerver = []
752 staging_kerVer=""
753 if os.path.exists(srcdir) and os.listdir(srcdir):
754 with open(os.path.join(srcdir,"Makefile")) as f:
755 version = [next(f) for x in range(5)][1:4]
756 for word in version:
757 kerver.append(word.split('= ')[1].split('\n')[0])
758 staging_kerVer = ".".join(kerver)
759 return staging_kerVer
760
761def get_staging_kbranch(srcdir):
762 staging_kbranch = ""
763 if os.path.exists(srcdir) and os.listdir(srcdir):
764 (branch, _) = bb.process.run('git branch | grep \* | cut -d \' \' -f2', cwd=srcdir)
765 staging_kbranch = "".join(branch.split('\n')[0])
766 return staging_kbranch
767
Andrew Geissler517393d2023-01-13 08:55:19 -0600768def get_real_srctree(srctree, s, workdir):
769 # Check that recipe isn't using a shared workdir
770 s = os.path.abspath(s)
771 workdir = os.path.abspath(workdir)
772 if s.startswith(workdir) and s != workdir and os.path.dirname(s) != workdir:
773 # Handle if S is set to a subdirectory of the source
774 srcsubdir = os.path.relpath(s, workdir).split(os.sep, 1)[1]
775 srctree = os.path.join(srctree, srcsubdir)
776 return srctree
777
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500778def modify(args, config, basepath, workspace):
779 """Entry point for the devtool 'modify' subcommand"""
780 import bb
781 import oe.recipeutils
Brad Bishop316dfdd2018-06-25 12:45:53 -0400782 import oe.patch
Brad Bishop96ff1982019-08-19 13:50:42 -0400783 import oe.path
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500784
785 if args.recipename in workspace:
786 raise DevtoolError("recipe %s is already in your workspace" %
787 args.recipename)
788
Brad Bishop316dfdd2018-06-25 12:45:53 -0400789 tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600790 try:
791 rd = parse_recipe(config, tinfoil, args.recipename, True)
792 if not rd:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500793 return 1
794
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500795 pn = rd.getVar('PN')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600796 if pn != args.recipename:
797 logger.info('Mapping %s to %s' % (args.recipename, pn))
798 if pn in workspace:
799 raise DevtoolError("recipe %s is already in your workspace" %
800 pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500801
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600802 if args.srctree:
803 srctree = os.path.abspath(args.srctree)
804 else:
805 srctree = get_default_srctree(config, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500806
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600807 if args.no_extract and not os.path.isdir(srctree):
808 raise DevtoolError("--no-extract specified and source path %s does "
809 "not exist or is not a directory" %
810 srctree)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600811
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500812 recipefile = rd.getVar('FILE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600813 appendfile = recipe_to_append(recipefile, config, args.wildcard)
814 if os.path.exists(appendfile):
815 raise DevtoolError("Another variant of recipe %s is already in your "
816 "workspace (only one variant of a recipe can "
817 "currently be worked on at once)"
818 % pn)
819
820 _check_compatible_recipe(pn, rd)
821
822 initial_rev = None
823 commits = []
Brad Bishop316dfdd2018-06-25 12:45:53 -0400824 check_commits = False
Brad Bishop96ff1982019-08-19 13:50:42 -0400825
826 if bb.data.inherits_class('kernel-yocto', rd):
827 # Current set kernel version
828 kernelVersion = rd.getVar('LINUX_VERSION')
829 srcdir = rd.getVar('STAGING_KERNEL_DIR')
830 kbranch = rd.getVar('KBRANCH')
831
832 staging_kerVer = get_staging_kver(srcdir)
833 staging_kbranch = get_staging_kbranch(srcdir)
834 if (os.path.exists(srcdir) and os.listdir(srcdir)) and (kernelVersion in staging_kerVer and staging_kbranch == kbranch):
835 oe.path.copyhardlinktree(srcdir,srctree)
836 workdir = rd.getVar('WORKDIR')
837 srcsubdir = rd.getVar('S')
838 localfilesdir = os.path.join(srctree,'oe-local-files')
839 # Move local source files into separate subdir
840 recipe_patches = [os.path.basename(patch) for patch in oe.recipeutils.get_recipe_patches(rd)]
841 local_files = oe.recipeutils.get_recipe_local_files(rd)
842
843 for key in local_files.copy():
844 if key.endswith('scc'):
845 sccfile = open(local_files[key], 'r')
846 for l in sccfile:
847 line = l.split()
848 if line and line[0] in ('kconf', 'patch'):
849 cfg = os.path.join(os.path.dirname(local_files[key]), line[-1])
850 if not cfg in local_files.values():
851 local_files[line[-1]] = cfg
852 shutil.copy2(cfg, workdir)
853 sccfile.close()
854
855 # Ignore local files with subdir={BP}
856 srcabspath = os.path.abspath(srcsubdir)
857 local_files = [fname for fname in local_files if os.path.exists(os.path.join(workdir, fname)) and (srcabspath == workdir or not os.path.join(workdir, fname).startswith(srcabspath + os.sep))]
858 if local_files:
859 for fname in local_files:
860 _move_file(os.path.join(workdir, fname), os.path.join(srctree, 'oe-local-files', fname))
861 with open(os.path.join(srctree, 'oe-local-files', '.gitignore'), 'w') as f:
862 f.write('# Ignore local files, by default. Remove this file ''if you want to commit the directory to Git\n*\n')
863
864 symlink_oelocal_files_srctree(rd,srctree)
865
866 task = 'do_configure'
867 res = tinfoil.build_targets(pn, task, handle_events=True)
868
869 # Copy .config to workspace
870 kconfpath = rd.getVar('B')
871 logger.info('Copying kernel config to workspace')
872 shutil.copy2(os.path.join(kconfpath, '.config'),srctree)
873
874 # Set this to true, we still need to get initial_rev
875 # by parsing the git repo
876 args.no_extract = True
877
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600878 if not args.no_extract:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400879 initial_rev, _ = _extract_source(srctree, args.keep_temp, args.branch, False, config, basepath, workspace, args.fixed_setup, rd, tinfoil, no_overrides=args.no_overrides)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500880 if not initial_rev:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600881 return 1
882 logger.info('Source tree extracted to %s' % srctree)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600883 if os.path.exists(os.path.join(srctree, '.git')):
884 # Get list of commits since this revision
885 (stdout, _) = bb.process.run('git rev-list --reverse %s..HEAD' % initial_rev, cwd=srctree)
886 commits = stdout.split()
887 check_commits = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600888 else:
889 if os.path.exists(os.path.join(srctree, '.git')):
Andrew Geissler99467da2019-02-25 18:54:23 -0600890 # Check if it's a tree previously extracted by us. This is done
891 # by ensuring that devtool-base and args.branch (devtool) exist.
892 # The check_commits logic will cause an exception if either one
893 # of these doesn't exist
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600894 try:
895 (stdout, _) = bb.process.run('git branch --contains devtool-base', cwd=srctree)
Andrew Geissler99467da2019-02-25 18:54:23 -0600896 bb.process.run('git rev-parse %s' % args.branch, cwd=srctree)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600897 except bb.process.ExecutionError:
898 stdout = ''
Brad Bishop316dfdd2018-06-25 12:45:53 -0400899 if stdout:
900 check_commits = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600901 for line in stdout.splitlines():
902 if line.startswith('*'):
903 (stdout, _) = bb.process.run('git rev-parse devtool-base', cwd=srctree)
904 initial_rev = stdout.rstrip()
905 if not initial_rev:
906 # Otherwise, just grab the head revision
907 (stdout, _) = bb.process.run('git rev-parse HEAD', cwd=srctree)
908 initial_rev = stdout.rstrip()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500909
Brad Bishop316dfdd2018-06-25 12:45:53 -0400910 branch_patches = {}
911 if check_commits:
912 # Check if there are override branches
913 (stdout, _) = bb.process.run('git branch', cwd=srctree)
914 branches = []
915 for line in stdout.rstrip().splitlines():
916 branchname = line[2:].rstrip()
917 if branchname.startswith(override_branch_prefix):
918 branches.append(branchname)
919 if branches:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800920 logger.warning('SRC_URI is conditionally overridden in this recipe, thus several %s* branches have been created, one for each override that makes changes to SRC_URI. It is recommended that you make changes to the %s branch first, then checkout and rebase each %s* branch and update any unique patches there (duplicates on those branches will be ignored by devtool finish/update-recipe)' % (override_branch_prefix, args.branch, override_branch_prefix))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400921 branches.insert(0, args.branch)
922 seen_patches = []
923 for branch in branches:
924 branch_patches[branch] = []
925 (stdout, _) = bb.process.run('git log devtool-base..%s' % branch, cwd=srctree)
926 for line in stdout.splitlines():
927 line = line.strip()
928 if line.startswith(oe.patch.GitApplyTree.patch_line_prefix):
929 origpatch = line[len(oe.patch.GitApplyTree.patch_line_prefix):].split(':', 1)[-1].strip()
930 if not origpatch in seen_patches:
931 seen_patches.append(origpatch)
932 branch_patches[branch].append(origpatch)
933
934 # Need to grab this here in case the source is within a subdirectory
935 srctreebase = srctree
Andrew Geissler517393d2023-01-13 08:55:19 -0600936 srctree = get_real_srctree(srctree, rd.getVar('S'), rd.getVar('WORKDIR'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500937
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600938 bb.utils.mkdirhier(os.path.dirname(appendfile))
939 with open(appendfile, 'w') as f:
Patrick Williams213cb262021-08-07 19:21:33 -0500940 f.write('FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600941 # Local files can be modified/tracked in separate subdir under srctree
942 # Mostly useful for packages with S != WORKDIR
Patrick Williams213cb262021-08-07 19:21:33 -0500943 f.write('FILESPATH:prepend := "%s:"\n' %
Brad Bishop316dfdd2018-06-25 12:45:53 -0400944 os.path.join(srctreebase, 'oe-local-files'))
945 f.write('# srctreebase: %s\n' % srctreebase)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500946
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600947 f.write('\ninherit externalsrc\n')
948 f.write('# NOTE: We use pn- overrides here to avoid affecting multiple variants in the case where the recipe uses BBCLASSEXTEND\n')
Patrick Williams213cb262021-08-07 19:21:33 -0500949 f.write('EXTERNALSRC:pn-%s = "%s"\n' % (pn, srctree))
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500950
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600951 b_is_s = use_external_build(args.same_dir, args.no_same_dir, rd)
952 if b_is_s:
Patrick Williams213cb262021-08-07 19:21:33 -0500953 f.write('EXTERNALSRC_BUILD:pn-%s = "%s"\n' % (pn, srctree))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500954
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600955 if bb.data.inherits_class('kernel', rd):
956 f.write('SRCTREECOVEREDTASKS = "do_validate_branches do_kernel_checkout '
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500957 'do_fetch do_unpack do_kernel_configcheck"\n')
Brad Bishop19323692019-04-05 15:28:33 -0400958 f.write('\ndo_patch[noexec] = "1"\n')
Patrick Williams213cb262021-08-07 19:21:33 -0500959 f.write('\ndo_configure:append() {\n'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600960 ' cp ${B}/.config ${S}/.config.baseline\n'
961 ' ln -sfT ${B}/.config ${S}/.config.new\n'
962 '}\n')
Patrick Williams213cb262021-08-07 19:21:33 -0500963 f.write('\ndo_kernel_configme:prepend() {\n'
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500964 ' if [ -e ${S}/.config ]; then\n'
965 ' mv ${S}/.config ${S}/.config.old\n'
966 ' fi\n'
967 '}\n')
Brad Bishop96ff1982019-08-19 13:50:42 -0400968 if rd.getVarFlag('do_menuconfig','task'):
Patrick Williams213cb262021-08-07 19:21:33 -0500969 f.write('\ndo_configure:append() {\n'
Andrew Geissler82c905d2020-04-13 13:39:40 -0500970 ' if [ ! ${DEVTOOL_DISABLE_MENUCONFIG} ]; then\n'
971 ' cp ${B}/.config ${S}/.config.baseline\n'
972 ' ln -sfT ${B}/.config ${S}/.config.new\n'
973 ' fi\n'
Brad Bishop96ff1982019-08-19 13:50:42 -0400974 '}\n')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600975 if initial_rev:
976 f.write('\n# initial_rev: %s\n' % initial_rev)
977 for commit in commits:
978 f.write('# commit: %s\n' % commit)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400979 if branch_patches:
980 for branch in branch_patches:
981 if branch == args.branch:
982 continue
983 f.write('# patches_%s: %s\n' % (branch, ','.join(branch_patches[branch])))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500984
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500985 update_unlockedsigs(basepath, workspace, args.fixed_setup, [pn])
986
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600987 _add_md5(config, pn, appendfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500988
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600989 logger.info('Recipe %s now set up to build from %s' % (pn, srctree))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500990
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600991 finally:
992 tinfoil.shutdown()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500993
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500994 return 0
995
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500996
997def rename(args, config, basepath, workspace):
998 """Entry point for the devtool 'rename' subcommand"""
999 import bb
1000 import oe.recipeutils
1001
1002 check_workspace_recipe(workspace, args.recipename)
1003
1004 if not (args.newname or args.version):
1005 raise DevtoolError('You must specify a new name, a version with -V/--version, or both')
1006
1007 recipefile = workspace[args.recipename]['recipefile']
1008 if not recipefile:
1009 raise DevtoolError('devtool rename can only be used where the recipe file itself is in the workspace (e.g. after devtool add)')
1010
1011 if args.newname and args.newname != args.recipename:
1012 reason = oe.recipeutils.validate_pn(args.newname)
1013 if reason:
1014 raise DevtoolError(reason)
1015 newname = args.newname
1016 else:
1017 newname = args.recipename
1018
1019 append = workspace[args.recipename]['bbappend']
1020 appendfn = os.path.splitext(os.path.basename(append))[0]
1021 splitfn = appendfn.split('_')
1022 if len(splitfn) > 1:
1023 origfnver = appendfn.split('_')[1]
1024 else:
1025 origfnver = ''
1026
1027 recipefilemd5 = None
1028 tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
1029 try:
1030 rd = parse_recipe(config, tinfoil, args.recipename, True)
1031 if not rd:
1032 return 1
1033
1034 bp = rd.getVar('BP')
1035 bpn = rd.getVar('BPN')
1036 if newname != args.recipename:
1037 localdata = rd.createCopy()
1038 localdata.setVar('PN', newname)
1039 newbpn = localdata.getVar('BPN')
1040 else:
1041 newbpn = bpn
1042 s = rd.getVar('S', False)
1043 src_uri = rd.getVar('SRC_URI', False)
1044 pv = rd.getVar('PV')
1045
1046 # Correct variable values that refer to the upstream source - these
1047 # values must stay the same, so if the name/version are changing then
1048 # we need to fix them up
1049 new_s = s
1050 new_src_uri = src_uri
1051 if newbpn != bpn:
1052 # ${PN} here is technically almost always incorrect, but people do use it
1053 new_s = new_s.replace('${BPN}', bpn)
1054 new_s = new_s.replace('${PN}', bpn)
1055 new_s = new_s.replace('${BP}', '%s-${PV}' % bpn)
1056 new_src_uri = new_src_uri.replace('${BPN}', bpn)
1057 new_src_uri = new_src_uri.replace('${PN}', bpn)
1058 new_src_uri = new_src_uri.replace('${BP}', '%s-${PV}' % bpn)
1059 if args.version and origfnver == pv:
1060 new_s = new_s.replace('${PV}', pv)
1061 new_s = new_s.replace('${BP}', '${BPN}-%s' % pv)
1062 new_src_uri = new_src_uri.replace('${PV}', pv)
1063 new_src_uri = new_src_uri.replace('${BP}', '${BPN}-%s' % pv)
1064 patchfields = {}
1065 if new_s != s:
1066 patchfields['S'] = new_s
1067 if new_src_uri != src_uri:
1068 patchfields['SRC_URI'] = new_src_uri
1069 if patchfields:
1070 recipefilemd5 = bb.utils.md5_file(recipefile)
1071 oe.recipeutils.patch_recipe(rd, recipefile, patchfields)
1072 newrecipefilemd5 = bb.utils.md5_file(recipefile)
1073 finally:
1074 tinfoil.shutdown()
1075
1076 if args.version:
1077 newver = args.version
1078 else:
1079 newver = origfnver
1080
1081 if newver:
1082 newappend = '%s_%s.bbappend' % (newname, newver)
1083 newfile = '%s_%s.bb' % (newname, newver)
1084 else:
1085 newappend = '%s.bbappend' % newname
1086 newfile = '%s.bb' % newname
1087
1088 oldrecipedir = os.path.dirname(recipefile)
1089 newrecipedir = os.path.join(config.workspace_path, 'recipes', newname)
1090 if oldrecipedir != newrecipedir:
1091 bb.utils.mkdirhier(newrecipedir)
1092
1093 newappend = os.path.join(os.path.dirname(append), newappend)
1094 newfile = os.path.join(newrecipedir, newfile)
1095
1096 # Rename bbappend
1097 logger.info('Renaming %s to %s' % (append, newappend))
Andrew Geisslerc926e172021-05-07 16:11:35 -05001098 bb.utils.rename(append, newappend)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001099 # Rename recipe file
1100 logger.info('Renaming %s to %s' % (recipefile, newfile))
Andrew Geisslerc926e172021-05-07 16:11:35 -05001101 bb.utils.rename(recipefile, newfile)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001102
1103 # Rename source tree if it's the default path
1104 appendmd5 = None
1105 if not args.no_srctree:
1106 srctree = workspace[args.recipename]['srctree']
1107 if os.path.abspath(srctree) == os.path.join(config.workspace_path, 'sources', args.recipename):
1108 newsrctree = os.path.join(config.workspace_path, 'sources', newname)
1109 logger.info('Renaming %s to %s' % (srctree, newsrctree))
1110 shutil.move(srctree, newsrctree)
1111 # Correct any references (basically EXTERNALSRC*) in the .bbappend
1112 appendmd5 = bb.utils.md5_file(newappend)
1113 appendlines = []
1114 with open(newappend, 'r') as f:
1115 for line in f:
1116 appendlines.append(line)
1117 with open(newappend, 'w') as f:
1118 for line in appendlines:
1119 if srctree in line:
1120 line = line.replace(srctree, newsrctree)
1121 f.write(line)
1122 newappendmd5 = bb.utils.md5_file(newappend)
1123
1124 bpndir = None
1125 newbpndir = None
1126 if newbpn != bpn:
1127 bpndir = os.path.join(oldrecipedir, bpn)
1128 if os.path.exists(bpndir):
1129 newbpndir = os.path.join(newrecipedir, newbpn)
1130 logger.info('Renaming %s to %s' % (bpndir, newbpndir))
1131 shutil.move(bpndir, newbpndir)
1132
1133 bpdir = None
1134 newbpdir = None
1135 if newver != origfnver or newbpn != bpn:
1136 bpdir = os.path.join(oldrecipedir, bp)
1137 if os.path.exists(bpdir):
1138 newbpdir = os.path.join(newrecipedir, '%s-%s' % (newbpn, newver))
1139 logger.info('Renaming %s to %s' % (bpdir, newbpdir))
1140 shutil.move(bpdir, newbpdir)
1141
1142 if oldrecipedir != newrecipedir:
1143 # Move any stray files and delete the old recipe directory
1144 for entry in os.listdir(oldrecipedir):
1145 oldpath = os.path.join(oldrecipedir, entry)
1146 newpath = os.path.join(newrecipedir, entry)
1147 logger.info('Renaming %s to %s' % (oldpath, newpath))
1148 shutil.move(oldpath, newpath)
1149 os.rmdir(oldrecipedir)
1150
1151 # Now take care of entries in .devtool_md5
1152 md5entries = []
1153 with open(os.path.join(config.workspace_path, '.devtool_md5'), 'r') as f:
1154 for line in f:
1155 md5entries.append(line)
1156
1157 if bpndir and newbpndir:
1158 relbpndir = os.path.relpath(bpndir, config.workspace_path) + '/'
1159 else:
1160 relbpndir = None
1161 if bpdir and newbpdir:
1162 relbpdir = os.path.relpath(bpdir, config.workspace_path) + '/'
1163 else:
1164 relbpdir = None
1165
1166 with open(os.path.join(config.workspace_path, '.devtool_md5'), 'w') as f:
1167 for entry in md5entries:
1168 splitentry = entry.rstrip().split('|')
1169 if len(splitentry) > 2:
1170 if splitentry[0] == args.recipename:
1171 splitentry[0] = newname
1172 if splitentry[1] == os.path.relpath(append, config.workspace_path):
1173 splitentry[1] = os.path.relpath(newappend, config.workspace_path)
1174 if appendmd5 and splitentry[2] == appendmd5:
1175 splitentry[2] = newappendmd5
1176 elif splitentry[1] == os.path.relpath(recipefile, config.workspace_path):
1177 splitentry[1] = os.path.relpath(newfile, config.workspace_path)
1178 if recipefilemd5 and splitentry[2] == recipefilemd5:
1179 splitentry[2] = newrecipefilemd5
1180 elif relbpndir and splitentry[1].startswith(relbpndir):
1181 splitentry[1] = os.path.relpath(os.path.join(newbpndir, splitentry[1][len(relbpndir):]), config.workspace_path)
1182 elif relbpdir and splitentry[1].startswith(relbpdir):
1183 splitentry[1] = os.path.relpath(os.path.join(newbpdir, splitentry[1][len(relbpdir):]), config.workspace_path)
1184 entry = '|'.join(splitentry) + '\n'
1185 f.write(entry)
1186 return 0
1187
1188
Brad Bishop316dfdd2018-06-25 12:45:53 -04001189def _get_patchset_revs(srctree, recipe_path, initial_rev=None, force_patch_refresh=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001190 """Get initial and update rev of a recipe. These are the start point of the
1191 whole patchset and start point for the patches to be re-generated/updated.
1192 """
1193 import bb
1194
Brad Bishop316dfdd2018-06-25 12:45:53 -04001195 # Get current branch
1196 stdout, _ = bb.process.run('git rev-parse --abbrev-ref HEAD',
1197 cwd=srctree)
1198 branchname = stdout.rstrip()
1199
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001200 # Parse initial rev from recipe if not specified
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001201 commits = []
Brad Bishop316dfdd2018-06-25 12:45:53 -04001202 patches = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001203 with open(recipe_path, 'r') as f:
1204 for line in f:
1205 if line.startswith('# initial_rev:'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001206 if not initial_rev:
1207 initial_rev = line.split(':')[-1].strip()
Brad Bishop316dfdd2018-06-25 12:45:53 -04001208 elif line.startswith('# commit:') and not force_patch_refresh:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001209 commits.append(line.split(':')[-1].strip())
Brad Bishop316dfdd2018-06-25 12:45:53 -04001210 elif line.startswith('# patches_%s:' % branchname):
1211 patches = line.split(':')[-1].strip().split(',')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001212
1213 update_rev = initial_rev
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001214 changed_revs = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001215 if initial_rev:
1216 # Find first actually changed revision
1217 stdout, _ = bb.process.run('git rev-list --reverse %s..HEAD' %
1218 initial_rev, cwd=srctree)
1219 newcommits = stdout.split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001220 for i in range(min(len(commits), len(newcommits))):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001221 if newcommits[i] == commits[i]:
1222 update_rev = commits[i]
1223
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001224 try:
1225 stdout, _ = bb.process.run('git cherry devtool-patched',
1226 cwd=srctree)
1227 except bb.process.ExecutionError as err:
1228 stdout = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001229
Brad Bishop316dfdd2018-06-25 12:45:53 -04001230 if stdout is not None and not force_patch_refresh:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001231 changed_revs = []
1232 for line in stdout.splitlines():
1233 if line.startswith('+ '):
1234 rev = line.split()[1]
1235 if rev in newcommits:
1236 changed_revs.append(rev)
1237
Brad Bishop316dfdd2018-06-25 12:45:53 -04001238 return initial_rev, update_rev, changed_revs, patches
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001239
1240def _remove_file_entries(srcuri, filelist):
1241 """Remove file:// entries from SRC_URI"""
1242 remaining = filelist[:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001243 entries = []
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001244 for fname in filelist:
1245 basename = os.path.basename(fname)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001246 for i in range(len(srcuri)):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001247 if (srcuri[i].startswith('file://') and
1248 os.path.basename(srcuri[i].split(';')[0]) == basename):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001249 entries.append(srcuri[i])
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001250 remaining.remove(fname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001251 srcuri.pop(i)
1252 break
1253 return entries, remaining
1254
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001255def _replace_srcuri_entry(srcuri, filename, newentry):
1256 """Replace entry corresponding to specified file with a new entry"""
1257 basename = os.path.basename(filename)
1258 for i in range(len(srcuri)):
1259 if os.path.basename(srcuri[i].split(';')[0]) == basename:
1260 srcuri.pop(i)
1261 srcuri.insert(i, newentry)
1262 break
1263
Brad Bishop316dfdd2018-06-25 12:45:53 -04001264def _remove_source_files(append, files, destpath, no_report_remove=False, dry_run=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001265 """Unlink existing patch files"""
Brad Bishop316dfdd2018-06-25 12:45:53 -04001266
1267 dry_run_suffix = ' (dry-run)' if dry_run else ''
1268
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001269 for path in files:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001270 if append:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001271 if not destpath:
1272 raise Exception('destpath should be set here')
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001273 path = os.path.join(destpath, os.path.basename(path))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001274
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001275 if os.path.exists(path):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001276 if not no_report_remove:
1277 logger.info('Removing file %s%s' % (path, dry_run_suffix))
1278 if not dry_run:
1279 # FIXME "git rm" here would be nice if the file in question is
1280 # tracked
1281 # FIXME there's a chance that this file is referred to by
1282 # another recipe, in which case deleting wouldn't be the
1283 # right thing to do
1284 os.remove(path)
1285 # Remove directory if empty
1286 try:
1287 os.rmdir(os.path.dirname(path))
1288 except OSError as ose:
1289 if ose.errno != errno.ENOTEMPTY:
1290 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001291
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001292
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001293def _export_patches(srctree, rd, start_rev, destdir, changed_revs=None):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001294 """Export patches from srctree to given location.
1295 Returns three-tuple of dicts:
1296 1. updated - patches that already exist in SRCURI
1297 2. added - new patches that don't exist in SRCURI
1298 3 removed - patches that exist in SRCURI but not in exported patches
1299 In each dict the key is the 'basepath' of the URI and value is the
1300 absolute path to the existing file in recipe space (if any).
1301 """
1302 import oe.recipeutils
1303 from oe.patch import GitApplyTree
1304 updated = OrderedDict()
1305 added = OrderedDict()
1306 seqpatch_re = re.compile('^([0-9]{4}-)?(.+)')
1307
1308 existing_patches = dict((os.path.basename(path), path) for path in
1309 oe.recipeutils.get_recipe_patches(rd))
Brad Bishop316dfdd2018-06-25 12:45:53 -04001310 logger.debug('Existing patches: %s' % existing_patches)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001311
1312 # Generate patches from Git, exclude local files directory
1313 patch_pathspec = _git_exclude_path(srctree, 'oe-local-files')
1314 GitApplyTree.extractPatches(srctree, start_rev, destdir, patch_pathspec)
1315
1316 new_patches = sorted(os.listdir(destdir))
1317 for new_patch in new_patches:
1318 # Strip numbering from patch names. If it's a git sequence named patch,
1319 # the numbers might not match up since we are starting from a different
1320 # revision This does assume that people are using unique shortlog
1321 # values, but they ought to be anyway...
1322 new_basename = seqpatch_re.match(new_patch).group(2)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001323 match_name = None
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001324 for old_patch in existing_patches:
1325 old_basename = seqpatch_re.match(old_patch).group(2)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001326 old_basename_splitext = os.path.splitext(old_basename)
1327 if old_basename.endswith(('.gz', '.bz2', '.Z')) and old_basename_splitext[0] == new_basename:
1328 old_patch_noext = os.path.splitext(old_patch)[0]
1329 match_name = old_patch_noext
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001330 break
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001331 elif new_basename == old_basename:
1332 match_name = old_patch
1333 break
1334 if match_name:
1335 # Rename patch files
1336 if new_patch != match_name:
Andrew Geisslerc926e172021-05-07 16:11:35 -05001337 bb.utils.rename(os.path.join(destdir, new_patch),
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001338 os.path.join(destdir, match_name))
1339 # Need to pop it off the list now before checking changed_revs
1340 oldpath = existing_patches.pop(old_patch)
1341 if changed_revs is not None:
1342 # Avoid updating patches that have not actually changed
1343 with open(os.path.join(destdir, match_name), 'r') as f:
1344 firstlineitems = f.readline().split()
1345 # Looking for "From <hash>" line
1346 if len(firstlineitems) > 1 and len(firstlineitems[1]) == 40:
1347 if not firstlineitems[1] in changed_revs:
1348 continue
1349 # Recompress if necessary
1350 if oldpath.endswith(('.gz', '.Z')):
1351 bb.process.run(['gzip', match_name], cwd=destdir)
1352 if oldpath.endswith('.gz'):
1353 match_name += '.gz'
1354 else:
1355 match_name += '.Z'
1356 elif oldpath.endswith('.bz2'):
1357 bb.process.run(['bzip2', match_name], cwd=destdir)
1358 match_name += '.bz2'
1359 updated[match_name] = oldpath
1360 else:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001361 added[new_patch] = None
1362 return (updated, added, existing_patches)
1363
1364
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001365def _create_kconfig_diff(srctree, rd, outfile):
1366 """Create a kconfig fragment"""
1367 # Only update config fragment if both config files exist
1368 orig_config = os.path.join(srctree, '.config.baseline')
1369 new_config = os.path.join(srctree, '.config.new')
1370 if os.path.exists(orig_config) and os.path.exists(new_config):
1371 cmd = ['diff', '--new-line-format=%L', '--old-line-format=',
1372 '--unchanged-line-format=', orig_config, new_config]
1373 pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
1374 stderr=subprocess.PIPE)
1375 stdout, stderr = pipe.communicate()
1376 if pipe.returncode == 1:
1377 logger.info("Updating config fragment %s" % outfile)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001378 with open(outfile, 'wb') as fobj:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001379 fobj.write(stdout)
1380 elif pipe.returncode == 0:
1381 logger.info("Would remove config fragment %s" % outfile)
1382 if os.path.exists(outfile):
1383 # Remove fragment file in case of empty diff
1384 logger.info("Removing config fragment %s" % outfile)
1385 os.unlink(outfile)
1386 else:
1387 raise bb.process.ExecutionError(cmd, pipe.returncode, stdout, stderr)
1388 return True
1389 return False
1390
1391
Brad Bishop316dfdd2018-06-25 12:45:53 -04001392def _export_local_files(srctree, rd, destdir, srctreebase):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001393 """Copy local files from srctree to given location.
1394 Returns three-tuple of dicts:
1395 1. updated - files that already exist in SRCURI
1396 2. added - new files files that don't exist in SRCURI
1397 3 removed - files that exist in SRCURI but not in exported files
1398 In each dict the key is the 'basepath' of the URI and value is the
1399 absolute path to the existing file in recipe space (if any).
1400 """
1401 import oe.recipeutils
1402
1403 # Find out local files (SRC_URI files that exist in the "recipe space").
1404 # Local files that reside in srctree are not included in patch generation.
1405 # Instead they are directly copied over the original source files (in
1406 # recipe space).
1407 existing_files = oe.recipeutils.get_recipe_local_files(rd)
1408 new_set = None
1409 updated = OrderedDict()
1410 added = OrderedDict()
1411 removed = OrderedDict()
Andrew Geissler517393d2023-01-13 08:55:19 -06001412
1413 # Get current branch and return early with empty lists
1414 # if on one of the override branches
1415 # (local files are provided only for the main branch and processing
1416 # them against lists from recipe overrides will result in mismatches
1417 # and broken modifications to recipes).
1418 stdout, _ = bb.process.run('git rev-parse --abbrev-ref HEAD',
1419 cwd=srctree)
1420 branchname = stdout.rstrip()
1421 if branchname.startswith(override_branch_prefix):
1422 return (updated, added, removed)
1423
Brad Bishop316dfdd2018-06-25 12:45:53 -04001424 local_files_dir = os.path.join(srctreebase, 'oe-local-files')
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001425 git_files = _git_ls_tree(srctree)
1426 if 'oe-local-files' in git_files:
1427 # If tracked by Git, take the files from srctree HEAD. First get
1428 # the tree object of the directory
1429 tmp_index = os.path.join(srctree, '.git', 'index.tmp.devtool')
1430 tree = git_files['oe-local-files'][2]
1431 bb.process.run(['git', 'checkout', tree, '--', '.'], cwd=srctree,
1432 env=dict(os.environ, GIT_WORK_TREE=destdir,
1433 GIT_INDEX_FILE=tmp_index))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001434 new_set = list(_git_ls_tree(srctree, tree, True).keys())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001435 elif os.path.isdir(local_files_dir):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001436 # If not tracked by Git, just copy from working copy
Brad Bishop316dfdd2018-06-25 12:45:53 -04001437 new_set = _ls_tree(local_files_dir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001438 bb.process.run(['cp', '-ax',
Brad Bishop316dfdd2018-06-25 12:45:53 -04001439 os.path.join(local_files_dir, '.'), destdir])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001440 else:
1441 new_set = []
1442
1443 # Special handling for kernel config
1444 if bb.data.inherits_class('kernel-yocto', rd):
1445 fragment_fn = 'devtool-fragment.cfg'
1446 fragment_path = os.path.join(destdir, fragment_fn)
1447 if _create_kconfig_diff(srctree, rd, fragment_path):
1448 if os.path.exists(fragment_path):
1449 if fragment_fn not in new_set:
1450 new_set.append(fragment_fn)
1451 # Copy fragment to local-files
1452 if os.path.isdir(local_files_dir):
1453 shutil.copy2(fragment_path, local_files_dir)
1454 else:
1455 if fragment_fn in new_set:
1456 new_set.remove(fragment_fn)
1457 # Remove fragment from local-files
1458 if os.path.exists(os.path.join(local_files_dir, fragment_fn)):
1459 os.unlink(os.path.join(local_files_dir, fragment_fn))
1460
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001461 # Special handling for cml1, ccmake, etc bbclasses that generated
1462 # configuration fragment files that are consumed as source files
1463 for frag_class, frag_name in [("cml1", "fragment.cfg"), ("ccmake", "site-file.cmake")]:
1464 if bb.data.inherits_class(frag_class, rd):
1465 srcpath = os.path.join(rd.getVar('WORKDIR'), frag_name)
1466 if os.path.exists(srcpath):
1467 if frag_name not in new_set:
1468 new_set.append(frag_name)
1469 # copy fragment into destdir
1470 shutil.copy2(srcpath, destdir)
1471 # copy fragment into local files if exists
1472 if os.path.isdir(local_files_dir):
1473 shutil.copy2(srcpath, local_files_dir)
1474
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001475 if new_set is not None:
1476 for fname in new_set:
1477 if fname in existing_files:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001478 origpath = existing_files.pop(fname)
1479 workpath = os.path.join(local_files_dir, fname)
1480 if not filecmp.cmp(origpath, workpath):
1481 updated[fname] = origpath
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001482 elif fname != '.gitignore':
1483 added[fname] = None
1484
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001485 workdir = rd.getVar('WORKDIR')
1486 s = rd.getVar('S')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001487 if not s.endswith(os.sep):
1488 s += os.sep
1489
1490 if workdir != s:
1491 # Handle files where subdir= was specified
1492 for fname in list(existing_files.keys()):
1493 # FIXME handle both subdir starting with BP and not?
1494 fworkpath = os.path.join(workdir, fname)
1495 if fworkpath.startswith(s):
1496 fpath = os.path.join(srctree, os.path.relpath(fworkpath, s))
1497 if os.path.exists(fpath):
1498 origpath = existing_files.pop(fname)
1499 if not filecmp.cmp(origpath, fpath):
1500 updated[fpath] = origpath
1501
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001502 removed = existing_files
1503 return (updated, added, removed)
1504
1505
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001506def _determine_files_dir(rd):
1507 """Determine the appropriate files directory for a recipe"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001508 recipedir = rd.getVar('FILE_DIRNAME')
1509 for entry in rd.getVar('FILESPATH').split(':'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001510 relpth = os.path.relpath(entry, recipedir)
1511 if not os.sep in relpth:
1512 # One (or zero) levels below only, so we don't put anything in machine-specific directories
1513 if os.path.isdir(entry):
1514 return entry
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001515 return os.path.join(recipedir, rd.getVar('BPN'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001516
1517
Brad Bishop316dfdd2018-06-25 12:45:53 -04001518def _update_recipe_srcrev(recipename, workspace, srctree, rd, appendlayerdir, wildcard_version, no_remove, no_report_remove, dry_run_outdir=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001519 """Implement the 'srcrev' mode of update-recipe"""
1520 import bb
1521 import oe.recipeutils
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001522
Brad Bishop316dfdd2018-06-25 12:45:53 -04001523 dry_run_suffix = ' (dry-run)' if dry_run_outdir else ''
1524
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001525 recipefile = rd.getVar('FILE')
Brad Bishop316dfdd2018-06-25 12:45:53 -04001526 recipedir = os.path.basename(recipefile)
1527 logger.info('Updating SRCREV in recipe %s%s' % (recipedir, dry_run_suffix))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001528
1529 # Get HEAD revision
1530 try:
1531 stdout, _ = bb.process.run('git rev-parse HEAD', cwd=srctree)
1532 except bb.process.ExecutionError as err:
1533 raise DevtoolError('Failed to get HEAD revision in %s: %s' %
1534 (srctree, err))
1535 srcrev = stdout.strip()
1536 if len(srcrev) != 40:
1537 raise DevtoolError('Invalid hash returned by git: %s' % stdout)
1538
1539 destpath = None
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001540 remove_files = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001541 patchfields = {}
1542 patchfields['SRCREV'] = srcrev
1543 orig_src_uri = rd.getVar('SRC_URI', False) or ''
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001544 srcuri = orig_src_uri.split()
1545 tempdir = tempfile.mkdtemp(prefix='devtool')
1546 update_srcuri = False
Brad Bishop316dfdd2018-06-25 12:45:53 -04001547 appendfile = None
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001548 try:
1549 local_files_dir = tempfile.mkdtemp(dir=tempdir)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001550 srctreebase = workspace[recipename]['srctreebase']
1551 upd_f, new_f, del_f = _export_local_files(srctree, rd, local_files_dir, srctreebase)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001552 if not no_remove:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001553 # Find list of existing patches in recipe file
1554 patches_dir = tempfile.mkdtemp(dir=tempdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001555 old_srcrev = rd.getVar('SRCREV') or ''
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001556 upd_p, new_p, del_p = _export_patches(srctree, rd, old_srcrev,
1557 patches_dir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001558 logger.debug('Patches: update %s, new %s, delete %s' % (dict(upd_p), dict(new_p), dict(del_p)))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001559
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001560 # Remove deleted local files and "overlapping" patches
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001561 remove_files = list(del_f.values()) + list(upd_p.values()) + list(del_p.values())
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001562 if remove_files:
1563 removedentries = _remove_file_entries(srcuri, remove_files)[0]
1564 update_srcuri = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001565
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001566 if appendlayerdir:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001567 files = dict((os.path.join(local_files_dir, key), val) for
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001568 key, val in list(upd_f.items()) + list(new_f.items()))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001569 removevalues = {}
1570 if update_srcuri:
1571 removevalues = {'SRC_URI': removedentries}
1572 patchfields['SRC_URI'] = '\\\n '.join(srcuri)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001573 if dry_run_outdir:
1574 logger.info('Creating bbappend (dry-run)')
1575 else:
1576 appendfile, destpath = oe.recipeutils.bbappend_recipe(
1577 rd, appendlayerdir, files, wildcardver=wildcard_version,
1578 extralines=patchfields, removevalues=removevalues,
1579 redirect_output=dry_run_outdir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001580 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001581 files_dir = _determine_files_dir(rd)
1582 for basepath, path in upd_f.items():
Brad Bishop316dfdd2018-06-25 12:45:53 -04001583 logger.info('Updating file %s%s' % (basepath, dry_run_suffix))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001584 if os.path.isabs(basepath):
1585 # Original file (probably with subdir pointing inside source tree)
1586 # so we do not want to move it, just copy
Brad Bishop316dfdd2018-06-25 12:45:53 -04001587 _copy_file(basepath, path, dry_run_outdir=dry_run_outdir, base_outdir=recipedir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001588 else:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001589 _move_file(os.path.join(local_files_dir, basepath), path,
1590 dry_run_outdir=dry_run_outdir, base_outdir=recipedir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001591 update_srcuri= True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001592 for basepath, path in new_f.items():
Brad Bishop316dfdd2018-06-25 12:45:53 -04001593 logger.info('Adding new file %s%s' % (basepath, dry_run_suffix))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001594 _move_file(os.path.join(local_files_dir, basepath),
Brad Bishop316dfdd2018-06-25 12:45:53 -04001595 os.path.join(files_dir, basepath),
1596 dry_run_outdir=dry_run_outdir,
1597 base_outdir=recipedir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001598 srcuri.append('file://%s' % basepath)
1599 update_srcuri = True
1600 if update_srcuri:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001601 patchfields['SRC_URI'] = ' '.join(srcuri)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001602 ret = oe.recipeutils.patch_recipe(rd, recipefile, patchfields, redirect_output=dry_run_outdir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001603 finally:
1604 shutil.rmtree(tempdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001605 if not 'git://' in orig_src_uri:
1606 logger.info('You will need to update SRC_URI within the recipe to '
1607 'point to a git repository where you have pushed your '
1608 'changes')
1609
Brad Bishop316dfdd2018-06-25 12:45:53 -04001610 _remove_source_files(appendlayerdir, remove_files, destpath, no_report_remove, dry_run=dry_run_outdir)
1611 return True, appendfile, remove_files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001612
Brad Bishop316dfdd2018-06-25 12:45:53 -04001613def _update_recipe_patch(recipename, workspace, srctree, rd, appendlayerdir, wildcard_version, no_remove, no_report_remove, initial_rev, dry_run_outdir=None, force_patch_refresh=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001614 """Implement the 'patch' mode of update-recipe"""
1615 import bb
1616 import oe.recipeutils
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001617
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001618 recipefile = rd.getVar('FILE')
Brad Bishop316dfdd2018-06-25 12:45:53 -04001619 recipedir = os.path.dirname(recipefile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001620 append = workspace[recipename]['bbappend']
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001621 if not os.path.exists(append):
1622 raise DevtoolError('unable to find workspace bbappend for recipe %s' %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001623 recipename)
Andrew Geissler615f2f12022-07-15 14:00:58 -05001624 srctreebase = workspace[recipename]['srctreebase']
1625 relpatchdir = os.path.relpath(srctreebase, srctree)
1626 if relpatchdir == '.':
1627 patchdir_params = {}
1628 else:
1629 patchdir_params = {'patchdir': relpatchdir}
1630
1631 def srcuri_entry(fname):
1632 if patchdir_params:
1633 paramstr = ';' + ';'.join('%s=%s' % (k,v) for k,v in patchdir_params.items())
1634 else:
1635 paramstr = ''
1636 return 'file://%s%s' % (basepath, paramstr)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001637
Brad Bishop316dfdd2018-06-25 12:45:53 -04001638 initial_rev, update_rev, changed_revs, filter_patches = _get_patchset_revs(srctree, append, initial_rev, force_patch_refresh)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001639 if not initial_rev:
1640 raise DevtoolError('Unable to find initial revision - please specify '
1641 'it with --initial-rev')
1642
Brad Bishop316dfdd2018-06-25 12:45:53 -04001643 appendfile = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001644 dl_dir = rd.getVar('DL_DIR')
1645 if not dl_dir.endswith('/'):
1646 dl_dir += '/'
1647
Brad Bishop316dfdd2018-06-25 12:45:53 -04001648 dry_run_suffix = ' (dry-run)' if dry_run_outdir else ''
1649
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001650 tempdir = tempfile.mkdtemp(prefix='devtool')
1651 try:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001652 local_files_dir = tempfile.mkdtemp(dir=tempdir)
Andrew Geissler517393d2023-01-13 08:55:19 -06001653 upd_f, new_f, del_f = _export_local_files(srctree, rd, local_files_dir, srctreebase)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001654
1655 # Get updated patches from source tree
1656 patches_dir = tempfile.mkdtemp(dir=tempdir)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001657 upd_p, new_p, _ = _export_patches(srctree, rd, update_rev,
1658 patches_dir, changed_revs)
Andrew Geissler517393d2023-01-13 08:55:19 -06001659 # Get all patches from source tree and check if any should be removed
1660 all_patches_dir = tempfile.mkdtemp(dir=tempdir)
1661 _, _, del_p = _export_patches(srctree, rd, initial_rev,
1662 all_patches_dir)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001663 logger.debug('Pre-filtering: update: %s, new: %s' % (dict(upd_p), dict(new_p)))
1664 if filter_patches:
Brad Bishop00e122a2019-10-05 11:10:57 -04001665 new_p = OrderedDict()
1666 upd_p = OrderedDict((k,v) for k,v in upd_p.items() if k in filter_patches)
Andrew Geissler517393d2023-01-13 08:55:19 -06001667 del_p = OrderedDict((k,v) for k,v in del_p.items() if k in filter_patches)
1668 remove_files = []
1669 if not no_remove:
1670 # Remove deleted local files and patches
1671 remove_files = list(del_f.values()) + list(del_p.values())
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001672 updatefiles = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001673 updaterecipe = False
1674 destpath = None
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001675 srcuri = (rd.getVar('SRC_URI', False) or '').split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001676 if appendlayerdir:
Brad Bishop00e122a2019-10-05 11:10:57 -04001677 files = OrderedDict((os.path.join(local_files_dir, key), val) for
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001678 key, val in list(upd_f.items()) + list(new_f.items()))
Brad Bishop00e122a2019-10-05 11:10:57 -04001679 files.update(OrderedDict((os.path.join(patches_dir, key), val) for
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001680 key, val in list(upd_p.items()) + list(new_p.items())))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001681 if files or remove_files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001682 removevalues = None
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001683 if remove_files:
1684 removedentries, remaining = _remove_file_entries(
1685 srcuri, remove_files)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001686 if removedentries or remaining:
Andrew Geissler615f2f12022-07-15 14:00:58 -05001687 remaining = [srcuri_entry(os.path.basename(item)) for
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001688 item in remaining]
1689 removevalues = {'SRC_URI': removedentries + remaining}
Brad Bishop316dfdd2018-06-25 12:45:53 -04001690 appendfile, destpath = oe.recipeutils.bbappend_recipe(
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001691 rd, appendlayerdir, files,
1692 wildcardver=wildcard_version,
Brad Bishop316dfdd2018-06-25 12:45:53 -04001693 removevalues=removevalues,
Andrew Geissler615f2f12022-07-15 14:00:58 -05001694 redirect_output=dry_run_outdir,
1695 params=[patchdir_params] * len(files))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001696 else:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001697 logger.info('No patches or local source files needed updating')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001698 else:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001699 # Update existing files
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001700 files_dir = _determine_files_dir(rd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001701 for basepath, path in upd_f.items():
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001702 logger.info('Updating file %s' % basepath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001703 if os.path.isabs(basepath):
1704 # Original file (probably with subdir pointing inside source tree)
1705 # so we do not want to move it, just copy
Brad Bishop316dfdd2018-06-25 12:45:53 -04001706 _copy_file(basepath, path,
1707 dry_run_outdir=dry_run_outdir, base_outdir=recipedir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001708 else:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001709 _move_file(os.path.join(local_files_dir, basepath), path,
1710 dry_run_outdir=dry_run_outdir, base_outdir=recipedir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001711 updatefiles = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001712 for basepath, path in upd_p.items():
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001713 patchfn = os.path.join(patches_dir, basepath)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001714 if os.path.dirname(path) + '/' == dl_dir:
1715 # This is a a downloaded patch file - we now need to
1716 # replace the entry in SRC_URI with our local version
1717 logger.info('Replacing remote patch %s with updated local version' % basepath)
1718 path = os.path.join(files_dir, basepath)
Andrew Geissler615f2f12022-07-15 14:00:58 -05001719 _replace_srcuri_entry(srcuri, basepath, srcuri_entry(basepath))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001720 updaterecipe = True
1721 else:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001722 logger.info('Updating patch %s%s' % (basepath, dry_run_suffix))
1723 _move_file(patchfn, path,
1724 dry_run_outdir=dry_run_outdir, base_outdir=recipedir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001725 updatefiles = True
1726 # Add any new files
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001727 for basepath, path in new_f.items():
Brad Bishop316dfdd2018-06-25 12:45:53 -04001728 logger.info('Adding new file %s%s' % (basepath, dry_run_suffix))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001729 _move_file(os.path.join(local_files_dir, basepath),
Brad Bishop316dfdd2018-06-25 12:45:53 -04001730 os.path.join(files_dir, basepath),
1731 dry_run_outdir=dry_run_outdir,
1732 base_outdir=recipedir)
Andrew Geissler615f2f12022-07-15 14:00:58 -05001733 srcuri.append(srcuri_entry(basepath))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001734 updaterecipe = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001735 for basepath, path in new_p.items():
Brad Bishop316dfdd2018-06-25 12:45:53 -04001736 logger.info('Adding new patch %s%s' % (basepath, dry_run_suffix))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001737 _move_file(os.path.join(patches_dir, basepath),
Brad Bishop316dfdd2018-06-25 12:45:53 -04001738 os.path.join(files_dir, basepath),
1739 dry_run_outdir=dry_run_outdir,
1740 base_outdir=recipedir)
Andrew Geissler615f2f12022-07-15 14:00:58 -05001741 srcuri.append(srcuri_entry(basepath))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001742 updaterecipe = True
1743 # Update recipe, if needed
1744 if _remove_file_entries(srcuri, remove_files)[0]:
1745 updaterecipe = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001746 if updaterecipe:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001747 if not dry_run_outdir:
1748 logger.info('Updating recipe %s' % os.path.basename(recipefile))
1749 ret = oe.recipeutils.patch_recipe(rd, recipefile,
1750 {'SRC_URI': ' '.join(srcuri)},
1751 redirect_output=dry_run_outdir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001752 elif not updatefiles:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001753 # Neither patches nor recipe were updated
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001754 logger.info('No patches or files need updating')
Brad Bishop316dfdd2018-06-25 12:45:53 -04001755 return False, None, []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001756 finally:
1757 shutil.rmtree(tempdir)
1758
Brad Bishop316dfdd2018-06-25 12:45:53 -04001759 _remove_source_files(appendlayerdir, remove_files, destpath, no_report_remove, dry_run=dry_run_outdir)
1760 return True, appendfile, remove_files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001761
1762def _guess_recipe_update_mode(srctree, rdata):
1763 """Guess the recipe update mode to use"""
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001764 src_uri = (rdata.getVar('SRC_URI') or '').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001765 git_uris = [uri for uri in src_uri if uri.startswith('git://')]
1766 if not git_uris:
1767 return 'patch'
1768 # Just use the first URI for now
1769 uri = git_uris[0]
1770 # Check remote branch
1771 params = bb.fetch.decodeurl(uri)[5]
1772 upstr_branch = params['branch'] if 'branch' in params else 'master'
1773 # Check if current branch HEAD is found in upstream branch
1774 stdout, _ = bb.process.run('git rev-parse HEAD', cwd=srctree)
1775 head_rev = stdout.rstrip()
1776 stdout, _ = bb.process.run('git branch -r --contains %s' % head_rev,
1777 cwd=srctree)
1778 remote_brs = [branch.strip() for branch in stdout.splitlines()]
1779 if 'origin/' + upstr_branch in remote_brs:
1780 return 'srcrev'
1781
1782 return 'patch'
1783
Brad Bishop316dfdd2018-06-25 12:45:53 -04001784def _update_recipe(recipename, workspace, rd, mode, appendlayerdir, wildcard_version, no_remove, initial_rev, no_report_remove=False, dry_run_outdir=None, no_overrides=False, force_patch_refresh=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001785 srctree = workspace[recipename]['srctree']
1786 if mode == 'auto':
1787 mode = _guess_recipe_update_mode(srctree, rd)
1788
Brad Bishop316dfdd2018-06-25 12:45:53 -04001789 override_branches = []
1790 mainbranch = None
1791 startbranch = None
1792 if not no_overrides:
1793 stdout, _ = bb.process.run('git branch', cwd=srctree)
1794 other_branches = []
1795 for line in stdout.splitlines():
1796 branchname = line[2:]
1797 if line.startswith('* '):
1798 startbranch = branchname
1799 if branchname.startswith(override_branch_prefix):
1800 override_branches.append(branchname)
1801 else:
1802 other_branches.append(branchname)
1803
1804 if override_branches:
1805 logger.debug('_update_recipe: override branches: %s' % override_branches)
1806 logger.debug('_update_recipe: other branches: %s' % other_branches)
1807 if startbranch.startswith(override_branch_prefix):
1808 if len(other_branches) == 1:
1809 mainbranch = other_branches[1]
1810 else:
1811 raise DevtoolError('Unable to determine main branch - please check out the main branch in source tree first')
1812 else:
1813 mainbranch = startbranch
1814
1815 checkedout = None
1816 anyupdated = False
1817 appendfile = None
1818 allremoved = []
1819 if override_branches:
1820 logger.info('Handling main branch (%s)...' % mainbranch)
1821 if startbranch != mainbranch:
1822 bb.process.run('git checkout %s' % mainbranch, cwd=srctree)
1823 checkedout = mainbranch
1824 try:
1825 branchlist = [mainbranch] + override_branches
1826 for branch in branchlist:
1827 crd = bb.data.createCopy(rd)
1828 if branch != mainbranch:
1829 logger.info('Handling branch %s...' % branch)
1830 override = branch[len(override_branch_prefix):]
1831 crd.appendVar('OVERRIDES', ':%s' % override)
1832 bb.process.run('git checkout %s' % branch, cwd=srctree)
1833 checkedout = branch
1834
1835 if mode == 'srcrev':
1836 updated, appendf, removed = _update_recipe_srcrev(recipename, workspace, srctree, crd, appendlayerdir, wildcard_version, no_remove, no_report_remove, dry_run_outdir)
1837 elif mode == 'patch':
1838 updated, appendf, removed = _update_recipe_patch(recipename, workspace, srctree, crd, appendlayerdir, wildcard_version, no_remove, no_report_remove, initial_rev, dry_run_outdir, force_patch_refresh)
1839 else:
1840 raise DevtoolError('update_recipe: invalid mode %s' % mode)
1841 if updated:
1842 anyupdated = True
1843 if appendf:
1844 appendfile = appendf
1845 allremoved.extend(removed)
1846 finally:
1847 if startbranch and checkedout != startbranch:
1848 bb.process.run('git checkout %s' % startbranch, cwd=srctree)
1849
1850 return anyupdated, appendfile, allremoved
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001851
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001852def update_recipe(args, config, basepath, workspace):
1853 """Entry point for the devtool 'update-recipe' subcommand"""
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001854 check_workspace_recipe(workspace, args.recipename)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001855
1856 if args.append:
1857 if not os.path.exists(args.append):
1858 raise DevtoolError('bbappend destination layer directory "%s" '
1859 'does not exist' % args.append)
1860 if not os.path.exists(os.path.join(args.append, 'conf', 'layer.conf')):
1861 raise DevtoolError('conf/layer.conf not found in bbappend '
1862 'destination layer "%s"' % args.append)
1863
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001864 tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001865 try:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001866
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001867 rd = parse_recipe(config, tinfoil, args.recipename, True)
1868 if not rd:
1869 return 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001870
Brad Bishop316dfdd2018-06-25 12:45:53 -04001871 dry_run_output = None
1872 dry_run_outdir = None
1873 if args.dry_run:
1874 dry_run_output = tempfile.TemporaryDirectory(prefix='devtool')
1875 dry_run_outdir = dry_run_output.name
1876 updated, _, _ = _update_recipe(args.recipename, workspace, rd, args.mode, args.append, args.wildcard_version, args.no_remove, args.initial_rev, dry_run_outdir=dry_run_outdir, no_overrides=args.no_overrides, force_patch_refresh=args.force_patch_refresh)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001877
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001878 if updated:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001879 rf = rd.getVar('FILE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001880 if rf.startswith(config.workspace_path):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001881 logger.warning('Recipe file %s has been updated but is inside the workspace - you will need to move it (and any associated files next to it) out to the desired layer before using "devtool reset" in order to keep any changes' % rf)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001882 finally:
1883 tinfoil.shutdown()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001884
1885 return 0
1886
1887
1888def status(args, config, basepath, workspace):
1889 """Entry point for the devtool 'status' subcommand"""
1890 if workspace:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001891 for recipe, value in sorted(workspace.items()):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001892 recipefile = value['recipefile']
1893 if recipefile:
1894 recipestr = ' (%s)' % recipefile
1895 else:
1896 recipestr = ''
1897 print("%s: %s%s" % (recipe, value['srctree'], recipestr))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001898 else:
1899 logger.info('No recipes currently in your workspace - you can use "devtool modify" to work on an existing recipe or "devtool add" to add a new one')
1900 return 0
1901
1902
Brad Bishop64c979e2019-11-04 13:55:29 -05001903def _reset(recipes, no_clean, remove_work, config, basepath, workspace):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001904 """Reset one or more recipes"""
Brad Bishop316dfdd2018-06-25 12:45:53 -04001905 import oe.path
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001906
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001907 def clean_preferred_provider(pn, layerconf_path):
1908 """Remove PREFERRED_PROVIDER from layer.conf'"""
1909 import re
1910 layerconf_file = os.path.join(layerconf_path, 'conf', 'layer.conf')
1911 new_layerconf_file = os.path.join(layerconf_path, 'conf', '.layer.conf')
1912 pprovider_found = False
1913 with open(layerconf_file, 'r') as f:
1914 lines = f.readlines()
1915 with open(new_layerconf_file, 'a') as nf:
1916 for line in lines:
1917 pprovider_exp = r'^PREFERRED_PROVIDER_.*? = "' + pn + r'"$'
1918 if not re.match(pprovider_exp, line):
1919 nf.write(line)
1920 else:
1921 pprovider_found = True
1922 if pprovider_found:
1923 shutil.move(new_layerconf_file, layerconf_file)
1924 else:
1925 os.remove(new_layerconf_file)
1926
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001927 if recipes and not no_clean:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001928 if len(recipes) == 1:
1929 logger.info('Cleaning sysroot for recipe %s...' % recipes[0])
1930 else:
1931 logger.info('Cleaning sysroot for recipes %s...' % ', '.join(recipes))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001932 # If the recipe file itself was created in the workspace, and
1933 # it uses BBCLASSEXTEND, then we need to also clean the other
1934 # variants
1935 targets = []
1936 for recipe in recipes:
1937 targets.append(recipe)
1938 recipefile = workspace[recipe]['recipefile']
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001939 if recipefile and os.path.exists(recipefile):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001940 targets.extend(get_bbclassextend_targets(recipefile, recipe))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001941 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001942 exec_build_env_command(config.init_path, basepath, 'bitbake -c clean %s' % ' '.join(targets))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001943 except bb.process.ExecutionError as e:
1944 raise DevtoolError('Command \'%s\' failed, output:\n%s\nIf you '
1945 'wish, you may specify -n/--no-clean to '
1946 'skip running this command when resetting' %
1947 (e.command, e.stdout))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001948
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001949 for pn in recipes:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001950 _check_preserve(config, pn)
1951
Brad Bishop316dfdd2018-06-25 12:45:53 -04001952 appendfile = workspace[pn]['bbappend']
1953 if os.path.exists(appendfile):
1954 # This shouldn't happen, but is possible if devtool errored out prior to
1955 # writing the md5 file. We need to delete this here or the recipe won't
1956 # actually be reset
1957 os.remove(appendfile)
1958
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001959 preservepath = os.path.join(config.workspace_path, 'attic', pn, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001960 def preservedir(origdir):
1961 if os.path.exists(origdir):
1962 for root, dirs, files in os.walk(origdir):
1963 for fn in files:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001964 logger.warning('Preserving %s in %s' % (fn, preservepath))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001965 _move_file(os.path.join(origdir, fn),
1966 os.path.join(preservepath, fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001967 for dn in dirs:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001968 preservedir(os.path.join(root, dn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001969 os.rmdir(origdir)
1970
Brad Bishop316dfdd2018-06-25 12:45:53 -04001971 recipefile = workspace[pn]['recipefile']
1972 if recipefile and oe.path.is_path_parent(config.workspace_path, recipefile):
1973 # This should always be true if recipefile is set, but just in case
1974 preservedir(os.path.dirname(recipefile))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001975 # We don't automatically create this dir next to appends, but the user can
1976 preservedir(os.path.join(config.workspace_path, 'appends', pn))
1977
Brad Bishop316dfdd2018-06-25 12:45:53 -04001978 srctreebase = workspace[pn]['srctreebase']
1979 if os.path.isdir(srctreebase):
1980 if os.listdir(srctreebase):
Brad Bishop64c979e2019-11-04 13:55:29 -05001981 if remove_work:
1982 logger.info('-r argument used on %s, removing source tree.'
1983 ' You will lose any unsaved work' %pn)
1984 shutil.rmtree(srctreebase)
1985 else:
1986 # We don't want to risk wiping out any work in progress
Patrick Williams92b42cb2022-09-03 06:53:57 -05001987 if srctreebase.startswith(os.path.join(config.workspace_path, 'sources')):
1988 from datetime import datetime
1989 preservesrc = os.path.join(config.workspace_path, 'attic', 'sources', "{}.{}".format(pn,datetime.now().strftime("%Y%m%d%H%M%S")))
1990 logger.info('Preserving source tree in %s\nIf you no '
1991 'longer need it then please delete it manually.\n'
1992 'It is also possible to reuse it via devtool source tree argument.'
1993 % preservesrc)
1994 bb.utils.mkdirhier(os.path.dirname(preservesrc))
1995 shutil.move(srctreebase, preservesrc)
1996 else:
1997 logger.info('Leaving source tree %s as-is; if you no '
1998 'longer need it then please delete it manually'
1999 % srctreebase)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002000 else:
2001 # This is unlikely, but if it's empty we can just remove it
Brad Bishop316dfdd2018-06-25 12:45:53 -04002002 os.rmdir(srctreebase)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002003
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002004 clean_preferred_provider(pn, config.workspace_path)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002005
2006def reset(args, config, basepath, workspace):
2007 """Entry point for the devtool 'reset' subcommand"""
2008 import bb
Brad Bishop64c979e2019-11-04 13:55:29 -05002009 import shutil
2010
2011 recipes = ""
2012
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002013 if args.recipename:
2014 if args.all:
2015 raise DevtoolError("Recipe cannot be specified if -a/--all is used")
2016 else:
2017 for recipe in args.recipename:
2018 check_workspace_recipe(workspace, recipe, checksrc=False)
2019 elif not args.all:
2020 raise DevtoolError("Recipe must be specified, or specify -a/--all to "
2021 "reset all recipes")
2022 if args.all:
2023 recipes = list(workspace.keys())
2024 else:
2025 recipes = args.recipename
2026
Brad Bishop64c979e2019-11-04 13:55:29 -05002027 _reset(recipes, args.no_clean, args.remove_work, config, basepath, workspace)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002028
2029 return 0
2030
2031
2032def _get_layer(layername, d):
2033 """Determine the base layer path for the specified layer name/path"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002034 layerdirs = d.getVar('BBLAYERS').split()
Brad Bishop96ff1982019-08-19 13:50:42 -04002035 layers = {} # {basename: layer_paths}
2036 for p in layerdirs:
2037 bn = os.path.basename(p)
2038 if bn not in layers:
2039 layers[bn] = [p]
2040 else:
2041 layers[bn].append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002042 # Provide some shortcuts
2043 if layername.lower() in ['oe-core', 'openembedded-core']:
Brad Bishop96ff1982019-08-19 13:50:42 -04002044 layername = 'meta'
2045 layer_paths = layers.get(layername, None)
2046 if not layer_paths:
2047 return os.path.abspath(layername)
2048 elif len(layer_paths) == 1:
2049 return os.path.abspath(layer_paths[0])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002050 else:
Brad Bishop96ff1982019-08-19 13:50:42 -04002051 # multiple layers having the same base name
2052 logger.warning("Multiple layers have the same base name '%s', use the first one '%s'." % (layername, layer_paths[0]))
2053 logger.warning("Consider using path instead of base name to specify layer:\n\t\t%s" % '\n\t\t'.join(layer_paths))
2054 return os.path.abspath(layer_paths[0])
2055
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002056
2057def finish(args, config, basepath, workspace):
2058 """Entry point for the devtool 'finish' subcommand"""
2059 import bb
2060 import oe.recipeutils
2061
2062 check_workspace_recipe(workspace, args.recipename)
2063
Brad Bishop316dfdd2018-06-25 12:45:53 -04002064 dry_run_suffix = ' (dry-run)' if args.dry_run else ''
2065
2066 # Grab the equivalent of COREBASE without having to initialise tinfoil
2067 corebasedir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
2068
2069 srctree = workspace[args.recipename]['srctree']
2070 check_git_repo_op(srctree, [corebasedir])
2071 dirty = check_git_repo_dirty(srctree)
2072 if dirty:
2073 if args.force:
2074 logger.warning('Source tree is not clean, continuing as requested by -f/--force')
2075 else:
2076 raise DevtoolError('Source tree is not clean:\n\n%s\nEnsure you have committed your changes or use -f/--force if you are sure there\'s nothing that needs to be committed' % dirty)
2077
Brad Bishop00e122a2019-10-05 11:10:57 -04002078 no_clean = args.no_clean
Brad Bishop64c979e2019-11-04 13:55:29 -05002079 remove_work=args.remove_work
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002080 tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
2081 try:
Brad Bishop6dbb3162019-11-25 09:41:34 -05002082 rd = parse_recipe(config, tinfoil, args.recipename, True)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002083 if not rd:
2084 return 1
2085
2086 destlayerdir = _get_layer(args.destination, tinfoil.config_data)
Brad Bishop316dfdd2018-06-25 12:45:53 -04002087 recipefile = rd.getVar('FILE')
2088 recipedir = os.path.dirname(recipefile)
2089 origlayerdir = oe.recipeutils.find_layerdir(recipefile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002090
2091 if not os.path.isdir(destlayerdir):
2092 raise DevtoolError('Unable to find layer or directory matching "%s"' % args.destination)
2093
2094 if os.path.abspath(destlayerdir) == config.workspace_path:
2095 raise DevtoolError('"%s" specifies the workspace layer - that is not a valid destination' % args.destination)
2096
2097 # If it's an upgrade, grab the original path
2098 origpath = None
2099 origfilelist = None
2100 append = workspace[args.recipename]['bbappend']
2101 with open(append, 'r') as f:
2102 for line in f:
2103 if line.startswith('# original_path:'):
2104 origpath = line.split(':')[1].strip()
2105 elif line.startswith('# original_files:'):
2106 origfilelist = line.split(':')[1].split()
2107
Brad Bishop316dfdd2018-06-25 12:45:53 -04002108 destlayerbasedir = oe.recipeutils.find_layerdir(destlayerdir)
2109
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002110 if origlayerdir == config.workspace_path:
2111 # Recipe file itself is in workspace, update it there first
2112 appendlayerdir = None
2113 origrelpath = None
2114 if origpath:
2115 origlayerpath = oe.recipeutils.find_layerdir(origpath)
2116 if origlayerpath:
2117 origrelpath = os.path.relpath(origpath, origlayerpath)
2118 destpath = oe.recipeutils.get_bbfile_path(rd, destlayerdir, origrelpath)
2119 if not destpath:
2120 raise DevtoolError("Unable to determine destination layer path - check that %s specifies an actual layer and %s/conf/layer.conf specifies BBFILES. You may also need to specify a more complete path." % (args.destination, destlayerdir))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002121 # Warn if the layer isn't in bblayers.conf (the code to create a bbappend will do this in other cases)
2122 layerdirs = [os.path.abspath(layerdir) for layerdir in rd.getVar('BBLAYERS').split()]
Brad Bishop316dfdd2018-06-25 12:45:53 -04002123 if not os.path.abspath(destlayerbasedir) in layerdirs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002124 bb.warn('Specified destination layer is not currently enabled in bblayers.conf, so the %s recipe will now be unavailable in your current configuration until you add the layer there' % args.recipename)
2125
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002126 elif destlayerdir == origlayerdir:
2127 # Same layer, update the original recipe
2128 appendlayerdir = None
2129 destpath = None
2130 else:
2131 # Create/update a bbappend in the specified layer
2132 appendlayerdir = destlayerdir
2133 destpath = None
2134
Brad Bishop316dfdd2018-06-25 12:45:53 -04002135 # Actually update the recipe / bbappend
2136 removing_original = (origpath and origfilelist and oe.recipeutils.find_layerdir(origpath) == destlayerbasedir)
2137 dry_run_output = None
2138 dry_run_outdir = None
2139 if args.dry_run:
2140 dry_run_output = tempfile.TemporaryDirectory(prefix='devtool')
2141 dry_run_outdir = dry_run_output.name
2142 updated, appendfile, removed = _update_recipe(args.recipename, workspace, rd, args.mode, appendlayerdir, wildcard_version=True, no_remove=False, no_report_remove=removing_original, initial_rev=args.initial_rev, dry_run_outdir=dry_run_outdir, no_overrides=args.no_overrides, force_patch_refresh=args.force_patch_refresh)
2143 removed = [os.path.relpath(pth, recipedir) for pth in removed]
2144
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002145 # Remove any old files in the case of an upgrade
Brad Bishop316dfdd2018-06-25 12:45:53 -04002146 if removing_original:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002147 for fn in origfilelist:
2148 fnp = os.path.join(origpath, fn)
Brad Bishop316dfdd2018-06-25 12:45:53 -04002149 if fn in removed or not os.path.exists(os.path.join(recipedir, fn)):
2150 logger.info('Removing file %s%s' % (fnp, dry_run_suffix))
2151 if not args.dry_run:
2152 try:
2153 os.remove(fnp)
2154 except FileNotFoundError:
2155 pass
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002156
2157 if origlayerdir == config.workspace_path and destpath:
2158 # Recipe file itself is in the workspace - need to move it and any
2159 # associated files to the specified layer
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002160 no_clean = True
Brad Bishop316dfdd2018-06-25 12:45:53 -04002161 logger.info('Moving recipe file to %s%s' % (destpath, dry_run_suffix))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002162 for root, _, files in os.walk(recipedir):
2163 for fn in files:
2164 srcpath = os.path.join(root, fn)
2165 relpth = os.path.relpath(os.path.dirname(srcpath), recipedir)
2166 destdir = os.path.abspath(os.path.join(destpath, relpth))
Brad Bishop316dfdd2018-06-25 12:45:53 -04002167 destfp = os.path.join(destdir, fn)
2168 _move_file(srcpath, destfp, dry_run_outdir=dry_run_outdir, base_outdir=destpath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002169
Brad Bishop316dfdd2018-06-25 12:45:53 -04002170 if dry_run_outdir:
2171 import difflib
2172 comparelist = []
2173 for root, _, files in os.walk(dry_run_outdir):
2174 for fn in files:
2175 outf = os.path.join(root, fn)
2176 relf = os.path.relpath(outf, dry_run_outdir)
2177 logger.debug('dry-run: output file %s' % relf)
2178 if fn.endswith('.bb'):
2179 if origfilelist and origpath and destpath:
2180 # Need to match this up with the pre-upgrade recipe file
2181 for origf in origfilelist:
2182 if origf.endswith('.bb'):
2183 comparelist.append((os.path.abspath(os.path.join(origpath, origf)),
2184 outf,
2185 os.path.abspath(os.path.join(destpath, relf))))
2186 break
2187 else:
2188 # Compare to the existing recipe
2189 comparelist.append((recipefile, outf, recipefile))
2190 elif fn.endswith('.bbappend'):
2191 if appendfile:
2192 if os.path.exists(appendfile):
2193 comparelist.append((appendfile, outf, appendfile))
2194 else:
2195 comparelist.append((None, outf, appendfile))
2196 else:
2197 if destpath:
2198 recipedest = destpath
2199 elif appendfile:
2200 recipedest = os.path.dirname(appendfile)
2201 else:
2202 recipedest = os.path.dirname(recipefile)
2203 destfp = os.path.join(recipedest, relf)
2204 if os.path.exists(destfp):
2205 comparelist.append((destfp, outf, destfp))
2206 output = ''
2207 for oldfile, newfile, newfileshow in comparelist:
2208 if oldfile:
2209 with open(oldfile, 'r') as f:
2210 oldlines = f.readlines()
2211 else:
2212 oldfile = '/dev/null'
2213 oldlines = []
2214 with open(newfile, 'r') as f:
2215 newlines = f.readlines()
2216 if not newfileshow:
2217 newfileshow = newfile
2218 diff = difflib.unified_diff(oldlines, newlines, oldfile, newfileshow)
2219 difflines = list(diff)
2220 if difflines:
2221 output += ''.join(difflines)
2222 if output:
2223 logger.info('Diff of changed files:\n%s' % output)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002224 finally:
2225 tinfoil.shutdown()
2226
2227 # Everything else has succeeded, we can now reset
Brad Bishop316dfdd2018-06-25 12:45:53 -04002228 if args.dry_run:
2229 logger.info('Resetting recipe (dry-run)')
2230 else:
Brad Bishop64c979e2019-11-04 13:55:29 -05002231 _reset([args.recipename], no_clean=no_clean, remove_work=remove_work, config=config, basepath=basepath, workspace=workspace)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002232
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002233 return 0
2234
2235
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002236def get_default_srctree(config, recipename=''):
2237 """Get the default srctree path"""
2238 srctreeparent = config.get('General', 'default_source_parent_dir', config.workspace_path)
2239 if recipename:
2240 return os.path.join(srctreeparent, 'sources', recipename)
2241 else:
2242 return os.path.join(srctreeparent, 'sources')
2243
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002244def register_commands(subparsers, context):
2245 """Register devtool subcommands from this plugin"""
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002246
2247 defsrctree = get_default_srctree(context.config)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002248 parser_add = subparsers.add_parser('add', help='Add a new recipe',
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002249 description='Adds a new recipe to the workspace to build a specified source tree. Can optionally fetch a remote URI and unpack it to create the source tree.',
2250 group='starting', order=100)
2251 parser_add.add_argument('recipename', nargs='?', help='Name for new recipe to add (just name - no version, path or extension). If not specified, will attempt to auto-detect it.')
2252 parser_add.add_argument('srctree', nargs='?', help='Path to external source tree. If not specified, a subdirectory of %s will be used.' % defsrctree)
2253 parser_add.add_argument('fetchuri', nargs='?', help='Fetch the specified URI and extract it to create the source tree')
Patrick Williamsf1e5d692016-03-30 15:21:19 -05002254 group = parser_add.add_mutually_exclusive_group()
2255 group.add_argument('--same-dir', '-s', help='Build in same directory as source', action="store_true")
2256 group.add_argument('--no-same-dir', help='Force build in a separate build directory', action="store_true")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002257 parser_add.add_argument('--fetch', '-f', help='Fetch the specified URI and extract it to create the source tree (deprecated - pass as positional argument instead)', metavar='URI')
Andrew Geissler82c905d2020-04-13 13:39:40 -05002258 parser_add.add_argument('--npm-dev', help='For npm, also fetch devDependencies', action="store_true")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002259 parser_add.add_argument('--version', '-V', help='Version to use within recipe (PV)')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002260 parser_add.add_argument('--no-git', '-g', help='If fetching source, do not set up source tree as a git repository', action="store_true")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002261 group = parser_add.add_mutually_exclusive_group()
2262 group.add_argument('--srcrev', '-S', help='Source revision to fetch if fetching from an SCM such as git (default latest)')
2263 group.add_argument('--autorev', '-a', help='When fetching from a git repository, set SRCREV in the recipe to a floating revision instead of fixed', action="store_true")
2264 parser_add.add_argument('--srcbranch', '-B', help='Branch in source repository if fetching from an SCM such as git (default master)')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002265 parser_add.add_argument('--binary', '-b', help='Treat the source tree as something that should be installed verbatim (no compilation, same directory structure). Useful with binary packages e.g. RPMs.', action='store_true')
2266 parser_add.add_argument('--also-native', help='Also add native variant (i.e. support building recipe for the build host as well as the target machine)', action='store_true')
2267 parser_add.add_argument('--src-subdir', help='Specify subdirectory within source tree to use', metavar='SUBDIR')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002268 parser_add.add_argument('--mirrors', help='Enable PREMIRRORS and MIRRORS for source tree fetching (disable by default).', action="store_true")
2269 parser_add.add_argument('--provides', '-p', help='Specify an alias for the item provided by the recipe. E.g. virtual/libgl')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002270 parser_add.set_defaults(func=add, fixed_setup=context.fixed_setup)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002271
2272 parser_modify = subparsers.add_parser('modify', help='Modify the source for an existing recipe',
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002273 description='Sets up the build environment to modify the source for an existing recipe. The default behaviour is to extract the source being fetched by the recipe into a git tree so you can work on it; alternatively if you already have your own pre-prepared source tree you can specify -n/--no-extract.',
2274 group='starting', order=90)
2275 parser_modify.add_argument('recipename', help='Name of existing recipe to edit (just name - no version, path or extension)')
2276 parser_modify.add_argument('srctree', nargs='?', help='Path to external source tree. If not specified, a subdirectory of %s will be used.' % defsrctree)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002277 parser_modify.add_argument('--wildcard', '-w', action="store_true", help='Use wildcard for unversioned bbappend')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002278 group = parser_modify.add_mutually_exclusive_group()
2279 group.add_argument('--extract', '-x', action="store_true", help='Extract source for recipe (default)')
2280 group.add_argument('--no-extract', '-n', action="store_true", help='Do not extract source, expect it to exist')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002281 group = parser_modify.add_mutually_exclusive_group()
2282 group.add_argument('--same-dir', '-s', help='Build in same directory as source', action="store_true")
2283 group.add_argument('--no-same-dir', help='Force build in a separate build directory', action="store_true")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002284 parser_modify.add_argument('--branch', '-b', default="devtool", help='Name for development branch to checkout (when not using -n/--no-extract) (default "%(default)s")')
Brad Bishop316dfdd2018-06-25 12:45:53 -04002285 parser_modify.add_argument('--no-overrides', '-O', action="store_true", help='Do not create branches for other override configurations')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002286 parser_modify.add_argument('--keep-temp', help='Keep temporary directory (for debugging)', action="store_true")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002287 parser_modify.set_defaults(func=modify, fixed_setup=context.fixed_setup)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002288
2289 parser_extract = subparsers.add_parser('extract', help='Extract the source for an existing recipe',
2290 description='Extracts the source for an existing recipe',
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002291 group='advanced')
2292 parser_extract.add_argument('recipename', help='Name of recipe to extract the source for')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002293 parser_extract.add_argument('srctree', help='Path to where to extract the source tree')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002294 parser_extract.add_argument('--branch', '-b', default="devtool", help='Name for development branch to checkout (default "%(default)s")')
Brad Bishop316dfdd2018-06-25 12:45:53 -04002295 parser_extract.add_argument('--no-overrides', '-O', action="store_true", help='Do not create branches for other override configurations')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002296 parser_extract.add_argument('--keep-temp', action="store_true", help='Keep temporary directory (for debugging)')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002297 parser_extract.set_defaults(func=extract, fixed_setup=context.fixed_setup)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002298
2299 parser_sync = subparsers.add_parser('sync', help='Synchronize the source tree for an existing recipe',
2300 description='Synchronize the previously extracted source tree for an existing recipe',
2301 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
2302 group='advanced')
2303 parser_sync.add_argument('recipename', help='Name of recipe to sync the source for')
2304 parser_sync.add_argument('srctree', help='Path to the source tree')
2305 parser_sync.add_argument('--branch', '-b', default="devtool", help='Name for development branch to checkout')
2306 parser_sync.add_argument('--keep-temp', action="store_true", help='Keep temporary directory (for debugging)')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002307 parser_sync.set_defaults(func=sync, fixed_setup=context.fixed_setup)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002308
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002309 parser_rename = subparsers.add_parser('rename', help='Rename a recipe file in the workspace',
2310 description='Renames the recipe file for a recipe in the workspace, changing the name or version part or both, ensuring that all references within the workspace are updated at the same time. Only works when the recipe file itself is in the workspace, e.g. after devtool add. Particularly useful when devtool add did not automatically determine the correct name.',
2311 group='working', order=10)
2312 parser_rename.add_argument('recipename', help='Current name of recipe to rename')
2313 parser_rename.add_argument('newname', nargs='?', help='New name for recipe (optional, not needed if you only want to change the version)')
2314 parser_rename.add_argument('--version', '-V', help='Change the version (NOTE: this does not change the version fetched by the recipe, just the version in the recipe file name)')
2315 parser_rename.add_argument('--no-srctree', '-s', action='store_true', help='Do not rename the source tree directory (if the default source tree path has been used) - keeping the old name may be desirable if there are internal/other external references to this path')
2316 parser_rename.set_defaults(func=rename)
2317
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002318 parser_update_recipe = subparsers.add_parser('update-recipe', help='Apply changes from external source tree to recipe',
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002319 description='Applies changes from external source tree to a recipe (updating/adding/removing patches as necessary, or by updating SRCREV). Note that these changes need to have been committed to the git repository in order to be recognised.',
2320 group='working', order=-90)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002321 parser_update_recipe.add_argument('recipename', help='Name of recipe to update')
2322 parser_update_recipe.add_argument('--mode', '-m', choices=['patch', 'srcrev', 'auto'], default='auto', help='Update mode (where %(metavar)s is %(choices)s; default is %(default)s)', metavar='MODE')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002323 parser_update_recipe.add_argument('--initial-rev', help='Override starting revision for patches')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002324 parser_update_recipe.add_argument('--append', '-a', help='Write changes to a bbappend in the specified layer instead of the recipe', metavar='LAYERDIR')
2325 parser_update_recipe.add_argument('--wildcard-version', '-w', help='In conjunction with -a/--append, use a wildcard to make the bbappend apply to any recipe version', action='store_true')
2326 parser_update_recipe.add_argument('--no-remove', '-n', action="store_true", help='Don\'t remove patches, only add or update')
Brad Bishop316dfdd2018-06-25 12:45:53 -04002327 parser_update_recipe.add_argument('--no-overrides', '-O', action="store_true", help='Do not handle other override branches (if they exist)')
2328 parser_update_recipe.add_argument('--dry-run', '-N', action="store_true", help='Dry-run (just report changes instead of writing them)')
2329 parser_update_recipe.add_argument('--force-patch-refresh', action="store_true", help='Update patches in the layer even if they have not been modified (useful for refreshing patch context)')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002330 parser_update_recipe.set_defaults(func=update_recipe)
2331
2332 parser_status = subparsers.add_parser('status', help='Show workspace status',
2333 description='Lists recipes currently in your workspace and the paths to their respective external source trees',
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002334 group='info', order=100)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002335 parser_status.set_defaults(func=status)
2336
2337 parser_reset = subparsers.add_parser('reset', help='Remove a recipe from your workspace',
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002338 description='Removes the specified recipe(s) from your workspace (resetting its state back to that defined by the metadata).',
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002339 group='working', order=-100)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002340 parser_reset.add_argument('recipename', nargs='*', help='Recipe to reset')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002341 parser_reset.add_argument('--all', '-a', action="store_true", help='Reset all recipes (clear workspace)')
2342 parser_reset.add_argument('--no-clean', '-n', action="store_true", help='Don\'t clean the sysroot to remove recipe output')
Brad Bishop64c979e2019-11-04 13:55:29 -05002343 parser_reset.add_argument('--remove-work', '-r', action="store_true", help='Clean the sources directory along with append')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002344 parser_reset.set_defaults(func=reset)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002345
2346 parser_finish = subparsers.add_parser('finish', help='Finish working on a recipe in your workspace',
Brad Bishop316dfdd2018-06-25 12:45:53 -04002347 description='Pushes any committed changes to the specified recipe to the specified layer and removes it from your workspace. Roughly equivalent to an update-recipe followed by reset, except the update-recipe step will do the "right thing" depending on the recipe and the destination layer specified. Note that your changes must have been committed to the git repository in order to be recognised.',
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002348 group='working', order=-100)
2349 parser_finish.add_argument('recipename', help='Recipe to finish')
2350 parser_finish.add_argument('destination', help='Layer/path to put recipe into. Can be the name of a layer configured in your bblayers.conf, the path to the base of a layer, or a partial path inside a layer. %(prog)s will attempt to complete the path based on the layer\'s structure.')
2351 parser_finish.add_argument('--mode', '-m', choices=['patch', 'srcrev', 'auto'], default='auto', help='Update mode (where %(metavar)s is %(choices)s; default is %(default)s)', metavar='MODE')
2352 parser_finish.add_argument('--initial-rev', help='Override starting revision for patches')
Brad Bishop316dfdd2018-06-25 12:45:53 -04002353 parser_finish.add_argument('--force', '-f', action="store_true", help='Force continuing even if there are uncommitted changes in the source tree repository')
Brad Bishop64c979e2019-11-04 13:55:29 -05002354 parser_finish.add_argument('--remove-work', '-r', action="store_true", help='Clean the sources directory under workspace')
Brad Bishop00e122a2019-10-05 11:10:57 -04002355 parser_finish.add_argument('--no-clean', '-n', action="store_true", help='Don\'t clean the sysroot to remove recipe output')
Brad Bishop316dfdd2018-06-25 12:45:53 -04002356 parser_finish.add_argument('--no-overrides', '-O', action="store_true", help='Do not handle other override branches (if they exist)')
2357 parser_finish.add_argument('--dry-run', '-N', action="store_true", help='Dry-run (just report changes instead of writing them)')
2358 parser_finish.add_argument('--force-patch-refresh', action="store_true", help='Update patches in the layer even if they have not been modified (useful for refreshing patch context)')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002359 parser_finish.set_defaults(func=finish)