blob: 01fb5ad96ff6f746ade06d690c8a098dfa6b7c69 [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
356def _copy_file(src, dst, dry_run_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[')):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400523 extra_overrides.append(event['op'].split('[')[1].split(']')[0])
Andrew Geissler99467da2019-02-25 18:54:23 -0600524 # We want to remove duplicate overrides. If a recipe had multiple
525 # SRC_URI_override += values it would cause mulitple instances of
526 # overrides. This doesn't play nicely with things like creating a
527 # branch for every instance of DEVTOOL_EXTRA_OVERRIDES.
528 extra_overrides = list(set(extra_overrides))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400529 if extra_overrides:
530 logger.info('SRC_URI contains some conditional appends/prepends - will create branches to represent these')
531
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500532 initial_rev = None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500533
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500534 recipefile = d.getVar('FILE')
535 appendfile = recipe_to_append(recipefile, config)
536 is_kernel_yocto = bb.data.inherits_class('kernel-yocto', d)
537
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500538 # We need to redirect WORKDIR, STAMPS_DIR etc. under a temporary
539 # directory so that:
540 # (a) we pick up all files that get unpacked to the WORKDIR, and
541 # (b) we don't disturb the existing build
542 # However, with recipe-specific sysroots the sysroots for the recipe
543 # will be prepared under WORKDIR, and if we used the system temporary
544 # directory (i.e. usually /tmp) as used by mkdtemp by default, then
545 # our attempts to hardlink files into the recipe-specific sysroots
546 # will fail on systems where /tmp is a different filesystem, and it
547 # would have to fall back to copying the files which is a waste of
548 # time. Put the temp directory under the WORKDIR to prevent that from
549 # being a problem.
550 tempbasedir = d.getVar('WORKDIR')
551 bb.utils.mkdirhier(tempbasedir)
552 tempdir = tempfile.mkdtemp(prefix='devtooltmp-', dir=tempbasedir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500553 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500554 tinfoil.logger.setLevel(logging.WARNING)
555
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500556 # FIXME this results in a cache reload under control of tinfoil, which is fine
557 # except we don't get the knotty progress bar
558
559 if os.path.exists(appendfile):
560 appendbackup = os.path.join(tempdir, os.path.basename(appendfile) + '.bak')
561 shutil.copyfile(appendfile, appendbackup)
562 else:
563 appendbackup = None
564 bb.utils.mkdirhier(os.path.dirname(appendfile))
565 logger.debug('writing append file %s' % appendfile)
566 with open(appendfile, 'a') as f:
567 f.write('###--- _extract_source\n')
568 f.write('DEVTOOL_TEMPDIR = "%s"\n' % tempdir)
569 f.write('DEVTOOL_DEVBRANCH = "%s"\n' % devbranch)
570 if not is_kernel_yocto:
571 f.write('PATCHTOOL = "git"\n')
572 f.write('PATCH_COMMIT_FUNCTIONS = "1"\n')
Brad Bishop316dfdd2018-06-25 12:45:53 -0400573 if extra_overrides:
574 f.write('DEVTOOL_EXTRA_OVERRIDES = "%s"\n' % ':'.join(extra_overrides))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500575 f.write('inherit devtool-source\n')
576 f.write('###--- _extract_source\n')
577
578 update_unlockedsigs(basepath, workspace, fixed_setup, [pn])
579
580 sstate_manifests = d.getVar('SSTATE_MANIFESTS')
581 bb.utils.mkdirhier(sstate_manifests)
582 preservestampfile = os.path.join(sstate_manifests, 'preserve-stamps')
583 with open(preservestampfile, 'w') as f:
584 f.write(d.getVar('STAMP'))
585 try:
Brad Bishop96ff1982019-08-19 13:50:42 -0400586 if is_kernel_yocto:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500587 # We need to generate the kernel config
588 task = 'do_configure'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500589 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500590 task = 'do_patch'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500591
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600592 if 'noexec' in (d.getVarFlags(task, False) or []) or 'task' not in (d.getVarFlags(task, False) or []):
593 logger.info('The %s recipe has %s disabled. Running only '
594 'do_configure task dependencies' % (pn, task))
595
596 if 'depends' in d.getVarFlags('do_configure', False):
597 pn = d.getVarFlags('do_configure', False)['depends']
598 pn = pn.replace('${PV}', d.getVar('PV'))
599 pn = pn.replace('${COMPILERDEP}', d.getVar('COMPILERDEP'))
600 task = None
601
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500602 # Run the fetch + unpack tasks
603 res = tinfoil.build_targets(pn,
604 task,
605 handle_events=True)
606 finally:
607 if os.path.exists(preservestampfile):
608 os.remove(preservestampfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500609
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500610 if not res:
611 raise DevtoolError('Extracting source for %s failed' % pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500612
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600613 if not is_kernel_yocto and ('noexec' in (d.getVarFlags('do_patch', False) or []) or 'task' not in (d.getVarFlags('do_patch', False) or [])):
614 workshareddir = d.getVar('S')
615 if os.path.islink(srctree):
616 os.unlink(srctree)
617
618 os.symlink(workshareddir, srctree)
619
620 # The initial_rev file is created in devtool_post_unpack function that will not be executed if
621 # do_unpack/do_patch tasks are disabled so we have to directly say that source extraction was successful
622 return True, True
623
Brad Bishop316dfdd2018-06-25 12:45:53 -0400624 try:
625 with open(os.path.join(tempdir, 'initial_rev'), 'r') as f:
626 initial_rev = f.read()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500627
Brad Bishop316dfdd2018-06-25 12:45:53 -0400628 with open(os.path.join(tempdir, 'srcsubdir'), 'r') as f:
629 srcsubdir = f.read()
630 except FileNotFoundError as e:
631 raise DevtoolError('Something went wrong with source extraction - the devtool-source class was not active or did not function correctly:\n%s' % str(e))
632 srcsubdir_rel = os.path.relpath(srcsubdir, os.path.join(tempdir, 'workdir'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500633
Brad Bishop96ff1982019-08-19 13:50:42 -0400634 # Check if work-shared is empty, if yes
635 # find source and copy to work-shared
636 if is_kernel_yocto:
637 workshareddir = d.getVar('STAGING_KERNEL_DIR')
638 staging_kerVer = get_staging_kver(workshareddir)
639 kernelVersion = d.getVar('LINUX_VERSION')
640
641 # handle dangling symbolic link in work-shared:
642 if os.path.islink(workshareddir):
643 os.unlink(workshareddir)
644
645 if os.path.exists(workshareddir) and (not os.listdir(workshareddir) or kernelVersion != staging_kerVer):
646 shutil.rmtree(workshareddir)
647 oe.path.copyhardlinktree(srcsubdir,workshareddir)
648 elif not os.path.exists(workshareddir):
649 oe.path.copyhardlinktree(srcsubdir,workshareddir)
650
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500651 tempdir_localdir = os.path.join(tempdir, 'oe-local-files')
652 srctree_localdir = os.path.join(srctree, 'oe-local-files')
653
654 if sync:
655 bb.process.run('git fetch file://' + srcsubdir + ' ' + devbranch + ':' + devbranch, cwd=srctree)
656
657 # Move oe-local-files directory to srctree
658 # As the oe-local-files is not part of the constructed git tree,
659 # remove them directly during the synchrounizating might surprise
660 # the users. Instead, we move it to oe-local-files.bak and remind
661 # user in the log message.
662 if os.path.exists(srctree_localdir + '.bak'):
663 shutil.rmtree(srctree_localdir, srctree_localdir + '.bak')
664
665 if os.path.exists(srctree_localdir):
666 logger.info('Backing up current local file directory %s' % srctree_localdir)
667 shutil.move(srctree_localdir, srctree_localdir + '.bak')
668
669 if os.path.exists(tempdir_localdir):
670 logger.info('Syncing local source files to srctree...')
671 shutil.copytree(tempdir_localdir, srctree_localdir)
672 else:
673 # Move oe-local-files directory to srctree
674 if os.path.exists(tempdir_localdir):
675 logger.info('Adding local source files to srctree...')
676 shutil.move(tempdir_localdir, srcsubdir)
677
678 shutil.move(srcsubdir, srctree)
Brad Bishop96ff1982019-08-19 13:50:42 -0400679 symlink_oelocal_files_srctree(d,srctree)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500680
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500681 if is_kernel_yocto:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500682 logger.info('Copying kernel config to srctree')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500683 shutil.copy2(os.path.join(tempdir, '.config'), srctree)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500684
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500685 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500686 if appendbackup:
687 shutil.copyfile(appendbackup, appendfile)
688 elif os.path.exists(appendfile):
689 os.remove(appendfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500690 if keep_temp:
691 logger.info('Preserving temporary directory %s' % tempdir)
692 else:
693 shutil.rmtree(tempdir)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400694 return initial_rev, srcsubdir_rel
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500695
696def _add_md5(config, recipename, filename):
697 """Record checksum of a file (or recursively for a directory) to the md5-file of the workspace"""
698 import bb.utils
699
700 def addfile(fn):
701 md5 = bb.utils.md5_file(fn)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500702 with open(os.path.join(config.workspace_path, '.devtool_md5'), 'a+') as f:
703 md5_str = '%s|%s|%s\n' % (recipename, os.path.relpath(fn, config.workspace_path), md5)
704 f.seek(0, os.SEEK_SET)
705 if not md5_str in f.read():
706 f.write(md5_str)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707
708 if os.path.isdir(filename):
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500709 for root, _, files in os.walk(filename):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500710 for f in files:
711 addfile(os.path.join(root, f))
712 else:
713 addfile(filename)
714
715def _check_preserve(config, recipename):
716 """Check if a file was manually changed and needs to be saved in 'attic'
717 directory"""
718 import bb.utils
719 origfile = os.path.join(config.workspace_path, '.devtool_md5')
720 newfile = os.path.join(config.workspace_path, '.devtool_md5_new')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500721 preservepath = os.path.join(config.workspace_path, 'attic', recipename)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500722 with open(origfile, 'r') as f:
723 with open(newfile, 'w') as tf:
724 for line in f.readlines():
725 splitline = line.rstrip().split('|')
726 if splitline[0] == recipename:
727 removefile = os.path.join(config.workspace_path, splitline[1])
728 try:
729 md5 = bb.utils.md5_file(removefile)
730 except IOError as err:
731 if err.errno == 2:
732 # File no longer exists, skip it
733 continue
734 else:
735 raise
736 if splitline[2] != md5:
737 bb.utils.mkdirhier(preservepath)
738 preservefile = os.path.basename(removefile)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800739 logger.warning('File %s modified since it was written, preserving in %s' % (preservefile, preservepath))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500740 shutil.move(removefile, os.path.join(preservepath, preservefile))
741 else:
742 os.remove(removefile)
743 else:
744 tf.write(line)
Andrew Geisslerc926e172021-05-07 16:11:35 -0500745 bb.utils.rename(newfile, origfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746
Brad Bishop96ff1982019-08-19 13:50:42 -0400747def get_staging_kver(srcdir):
748 # Kernel version from work-shared
749 kerver = []
750 staging_kerVer=""
751 if os.path.exists(srcdir) and os.listdir(srcdir):
752 with open(os.path.join(srcdir,"Makefile")) as f:
753 version = [next(f) for x in range(5)][1:4]
754 for word in version:
755 kerver.append(word.split('= ')[1].split('\n')[0])
756 staging_kerVer = ".".join(kerver)
757 return staging_kerVer
758
759def get_staging_kbranch(srcdir):
760 staging_kbranch = ""
761 if os.path.exists(srcdir) and os.listdir(srcdir):
762 (branch, _) = bb.process.run('git branch | grep \* | cut -d \' \' -f2', cwd=srcdir)
763 staging_kbranch = "".join(branch.split('\n')[0])
764 return staging_kbranch
765
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500766def modify(args, config, basepath, workspace):
767 """Entry point for the devtool 'modify' subcommand"""
768 import bb
769 import oe.recipeutils
Brad Bishop316dfdd2018-06-25 12:45:53 -0400770 import oe.patch
Brad Bishop96ff1982019-08-19 13:50:42 -0400771 import oe.path
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500772
773 if args.recipename in workspace:
774 raise DevtoolError("recipe %s is already in your workspace" %
775 args.recipename)
776
Brad Bishop316dfdd2018-06-25 12:45:53 -0400777 tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600778 try:
779 rd = parse_recipe(config, tinfoil, args.recipename, True)
780 if not rd:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500781 return 1
782
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500783 pn = rd.getVar('PN')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600784 if pn != args.recipename:
785 logger.info('Mapping %s to %s' % (args.recipename, pn))
786 if pn in workspace:
787 raise DevtoolError("recipe %s is already in your workspace" %
788 pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500789
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600790 if args.srctree:
791 srctree = os.path.abspath(args.srctree)
792 else:
793 srctree = get_default_srctree(config, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500794
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600795 if args.no_extract and not os.path.isdir(srctree):
796 raise DevtoolError("--no-extract specified and source path %s does "
797 "not exist or is not a directory" %
798 srctree)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600799
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500800 recipefile = rd.getVar('FILE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600801 appendfile = recipe_to_append(recipefile, config, args.wildcard)
802 if os.path.exists(appendfile):
803 raise DevtoolError("Another variant of recipe %s is already in your "
804 "workspace (only one variant of a recipe can "
805 "currently be worked on at once)"
806 % pn)
807
808 _check_compatible_recipe(pn, rd)
809
810 initial_rev = None
811 commits = []
Brad Bishop316dfdd2018-06-25 12:45:53 -0400812 check_commits = False
Brad Bishop96ff1982019-08-19 13:50:42 -0400813
814 if bb.data.inherits_class('kernel-yocto', rd):
815 # Current set kernel version
816 kernelVersion = rd.getVar('LINUX_VERSION')
817 srcdir = rd.getVar('STAGING_KERNEL_DIR')
818 kbranch = rd.getVar('KBRANCH')
819
820 staging_kerVer = get_staging_kver(srcdir)
821 staging_kbranch = get_staging_kbranch(srcdir)
822 if (os.path.exists(srcdir) and os.listdir(srcdir)) and (kernelVersion in staging_kerVer and staging_kbranch == kbranch):
823 oe.path.copyhardlinktree(srcdir,srctree)
824 workdir = rd.getVar('WORKDIR')
825 srcsubdir = rd.getVar('S')
826 localfilesdir = os.path.join(srctree,'oe-local-files')
827 # Move local source files into separate subdir
828 recipe_patches = [os.path.basename(patch) for patch in oe.recipeutils.get_recipe_patches(rd)]
829 local_files = oe.recipeutils.get_recipe_local_files(rd)
830
831 for key in local_files.copy():
832 if key.endswith('scc'):
833 sccfile = open(local_files[key], 'r')
834 for l in sccfile:
835 line = l.split()
836 if line and line[0] in ('kconf', 'patch'):
837 cfg = os.path.join(os.path.dirname(local_files[key]), line[-1])
838 if not cfg in local_files.values():
839 local_files[line[-1]] = cfg
840 shutil.copy2(cfg, workdir)
841 sccfile.close()
842
843 # Ignore local files with subdir={BP}
844 srcabspath = os.path.abspath(srcsubdir)
845 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))]
846 if local_files:
847 for fname in local_files:
848 _move_file(os.path.join(workdir, fname), os.path.join(srctree, 'oe-local-files', fname))
849 with open(os.path.join(srctree, 'oe-local-files', '.gitignore'), 'w') as f:
850 f.write('# Ignore local files, by default. Remove this file ''if you want to commit the directory to Git\n*\n')
851
852 symlink_oelocal_files_srctree(rd,srctree)
853
854 task = 'do_configure'
855 res = tinfoil.build_targets(pn, task, handle_events=True)
856
857 # Copy .config to workspace
858 kconfpath = rd.getVar('B')
859 logger.info('Copying kernel config to workspace')
860 shutil.copy2(os.path.join(kconfpath, '.config'),srctree)
861
862 # Set this to true, we still need to get initial_rev
863 # by parsing the git repo
864 args.no_extract = True
865
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600866 if not args.no_extract:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400867 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 -0500868 if not initial_rev:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600869 return 1
870 logger.info('Source tree extracted to %s' % srctree)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600871 if os.path.exists(os.path.join(srctree, '.git')):
872 # Get list of commits since this revision
873 (stdout, _) = bb.process.run('git rev-list --reverse %s..HEAD' % initial_rev, cwd=srctree)
874 commits = stdout.split()
875 check_commits = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600876 else:
877 if os.path.exists(os.path.join(srctree, '.git')):
Andrew Geissler99467da2019-02-25 18:54:23 -0600878 # Check if it's a tree previously extracted by us. This is done
879 # by ensuring that devtool-base and args.branch (devtool) exist.
880 # The check_commits logic will cause an exception if either one
881 # of these doesn't exist
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600882 try:
883 (stdout, _) = bb.process.run('git branch --contains devtool-base', cwd=srctree)
Andrew Geissler99467da2019-02-25 18:54:23 -0600884 bb.process.run('git rev-parse %s' % args.branch, cwd=srctree)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600885 except bb.process.ExecutionError:
886 stdout = ''
Brad Bishop316dfdd2018-06-25 12:45:53 -0400887 if stdout:
888 check_commits = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600889 for line in stdout.splitlines():
890 if line.startswith('*'):
891 (stdout, _) = bb.process.run('git rev-parse devtool-base', cwd=srctree)
892 initial_rev = stdout.rstrip()
893 if not initial_rev:
894 # Otherwise, just grab the head revision
895 (stdout, _) = bb.process.run('git rev-parse HEAD', cwd=srctree)
896 initial_rev = stdout.rstrip()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500897
Brad Bishop316dfdd2018-06-25 12:45:53 -0400898 branch_patches = {}
899 if check_commits:
900 # Check if there are override branches
901 (stdout, _) = bb.process.run('git branch', cwd=srctree)
902 branches = []
903 for line in stdout.rstrip().splitlines():
904 branchname = line[2:].rstrip()
905 if branchname.startswith(override_branch_prefix):
906 branches.append(branchname)
907 if branches:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800908 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 -0400909 branches.insert(0, args.branch)
910 seen_patches = []
911 for branch in branches:
912 branch_patches[branch] = []
913 (stdout, _) = bb.process.run('git log devtool-base..%s' % branch, cwd=srctree)
914 for line in stdout.splitlines():
915 line = line.strip()
916 if line.startswith(oe.patch.GitApplyTree.patch_line_prefix):
917 origpatch = line[len(oe.patch.GitApplyTree.patch_line_prefix):].split(':', 1)[-1].strip()
918 if not origpatch in seen_patches:
919 seen_patches.append(origpatch)
920 branch_patches[branch].append(origpatch)
921
922 # Need to grab this here in case the source is within a subdirectory
923 srctreebase = srctree
924
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600925 # Check that recipe isn't using a shared workdir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500926 s = os.path.abspath(rd.getVar('S'))
927 workdir = os.path.abspath(rd.getVar('WORKDIR'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600928 if s.startswith(workdir) and s != workdir and os.path.dirname(s) != workdir:
929 # Handle if S is set to a subdirectory of the source
930 srcsubdir = os.path.relpath(s, workdir).split(os.sep, 1)[1]
931 srctree = os.path.join(srctree, srcsubdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500932
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600933 bb.utils.mkdirhier(os.path.dirname(appendfile))
934 with open(appendfile, 'w') as f:
Patrick Williams213cb262021-08-07 19:21:33 -0500935 f.write('FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600936 # Local files can be modified/tracked in separate subdir under srctree
937 # Mostly useful for packages with S != WORKDIR
Patrick Williams213cb262021-08-07 19:21:33 -0500938 f.write('FILESPATH:prepend := "%s:"\n' %
Brad Bishop316dfdd2018-06-25 12:45:53 -0400939 os.path.join(srctreebase, 'oe-local-files'))
940 f.write('# srctreebase: %s\n' % srctreebase)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500941
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600942 f.write('\ninherit externalsrc\n')
943 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 -0500944 f.write('EXTERNALSRC:pn-%s = "%s"\n' % (pn, srctree))
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500945
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600946 b_is_s = use_external_build(args.same_dir, args.no_same_dir, rd)
947 if b_is_s:
Patrick Williams213cb262021-08-07 19:21:33 -0500948 f.write('EXTERNALSRC_BUILD:pn-%s = "%s"\n' % (pn, srctree))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500949
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600950 if bb.data.inherits_class('kernel', rd):
951 f.write('SRCTREECOVEREDTASKS = "do_validate_branches do_kernel_checkout '
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500952 'do_fetch do_unpack do_kernel_configcheck"\n')
Brad Bishop19323692019-04-05 15:28:33 -0400953 f.write('\ndo_patch[noexec] = "1"\n')
Patrick Williams213cb262021-08-07 19:21:33 -0500954 f.write('\ndo_configure:append() {\n'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600955 ' cp ${B}/.config ${S}/.config.baseline\n'
956 ' ln -sfT ${B}/.config ${S}/.config.new\n'
957 '}\n')
Patrick Williams213cb262021-08-07 19:21:33 -0500958 f.write('\ndo_kernel_configme:prepend() {\n'
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500959 ' if [ -e ${S}/.config ]; then\n'
960 ' mv ${S}/.config ${S}/.config.old\n'
961 ' fi\n'
962 '}\n')
Brad Bishop96ff1982019-08-19 13:50:42 -0400963 if rd.getVarFlag('do_menuconfig','task'):
Patrick Williams213cb262021-08-07 19:21:33 -0500964 f.write('\ndo_configure:append() {\n'
Andrew Geissler82c905d2020-04-13 13:39:40 -0500965 ' if [ ! ${DEVTOOL_DISABLE_MENUCONFIG} ]; then\n'
966 ' cp ${B}/.config ${S}/.config.baseline\n'
967 ' ln -sfT ${B}/.config ${S}/.config.new\n'
968 ' fi\n'
Brad Bishop96ff1982019-08-19 13:50:42 -0400969 '}\n')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600970 if initial_rev:
971 f.write('\n# initial_rev: %s\n' % initial_rev)
972 for commit in commits:
973 f.write('# commit: %s\n' % commit)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400974 if branch_patches:
975 for branch in branch_patches:
976 if branch == args.branch:
977 continue
978 f.write('# patches_%s: %s\n' % (branch, ','.join(branch_patches[branch])))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500979
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500980 update_unlockedsigs(basepath, workspace, args.fixed_setup, [pn])
981
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600982 _add_md5(config, pn, appendfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500983
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600984 logger.info('Recipe %s now set up to build from %s' % (pn, srctree))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500985
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600986 finally:
987 tinfoil.shutdown()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500988
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500989 return 0
990
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500991
992def rename(args, config, basepath, workspace):
993 """Entry point for the devtool 'rename' subcommand"""
994 import bb
995 import oe.recipeutils
996
997 check_workspace_recipe(workspace, args.recipename)
998
999 if not (args.newname or args.version):
1000 raise DevtoolError('You must specify a new name, a version with -V/--version, or both')
1001
1002 recipefile = workspace[args.recipename]['recipefile']
1003 if not recipefile:
1004 raise DevtoolError('devtool rename can only be used where the recipe file itself is in the workspace (e.g. after devtool add)')
1005
1006 if args.newname and args.newname != args.recipename:
1007 reason = oe.recipeutils.validate_pn(args.newname)
1008 if reason:
1009 raise DevtoolError(reason)
1010 newname = args.newname
1011 else:
1012 newname = args.recipename
1013
1014 append = workspace[args.recipename]['bbappend']
1015 appendfn = os.path.splitext(os.path.basename(append))[0]
1016 splitfn = appendfn.split('_')
1017 if len(splitfn) > 1:
1018 origfnver = appendfn.split('_')[1]
1019 else:
1020 origfnver = ''
1021
1022 recipefilemd5 = None
1023 tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
1024 try:
1025 rd = parse_recipe(config, tinfoil, args.recipename, True)
1026 if not rd:
1027 return 1
1028
1029 bp = rd.getVar('BP')
1030 bpn = rd.getVar('BPN')
1031 if newname != args.recipename:
1032 localdata = rd.createCopy()
1033 localdata.setVar('PN', newname)
1034 newbpn = localdata.getVar('BPN')
1035 else:
1036 newbpn = bpn
1037 s = rd.getVar('S', False)
1038 src_uri = rd.getVar('SRC_URI', False)
1039 pv = rd.getVar('PV')
1040
1041 # Correct variable values that refer to the upstream source - these
1042 # values must stay the same, so if the name/version are changing then
1043 # we need to fix them up
1044 new_s = s
1045 new_src_uri = src_uri
1046 if newbpn != bpn:
1047 # ${PN} here is technically almost always incorrect, but people do use it
1048 new_s = new_s.replace('${BPN}', bpn)
1049 new_s = new_s.replace('${PN}', bpn)
1050 new_s = new_s.replace('${BP}', '%s-${PV}' % bpn)
1051 new_src_uri = new_src_uri.replace('${BPN}', bpn)
1052 new_src_uri = new_src_uri.replace('${PN}', bpn)
1053 new_src_uri = new_src_uri.replace('${BP}', '%s-${PV}' % bpn)
1054 if args.version and origfnver == pv:
1055 new_s = new_s.replace('${PV}', pv)
1056 new_s = new_s.replace('${BP}', '${BPN}-%s' % pv)
1057 new_src_uri = new_src_uri.replace('${PV}', pv)
1058 new_src_uri = new_src_uri.replace('${BP}', '${BPN}-%s' % pv)
1059 patchfields = {}
1060 if new_s != s:
1061 patchfields['S'] = new_s
1062 if new_src_uri != src_uri:
1063 patchfields['SRC_URI'] = new_src_uri
1064 if patchfields:
1065 recipefilemd5 = bb.utils.md5_file(recipefile)
1066 oe.recipeutils.patch_recipe(rd, recipefile, patchfields)
1067 newrecipefilemd5 = bb.utils.md5_file(recipefile)
1068 finally:
1069 tinfoil.shutdown()
1070
1071 if args.version:
1072 newver = args.version
1073 else:
1074 newver = origfnver
1075
1076 if newver:
1077 newappend = '%s_%s.bbappend' % (newname, newver)
1078 newfile = '%s_%s.bb' % (newname, newver)
1079 else:
1080 newappend = '%s.bbappend' % newname
1081 newfile = '%s.bb' % newname
1082
1083 oldrecipedir = os.path.dirname(recipefile)
1084 newrecipedir = os.path.join(config.workspace_path, 'recipes', newname)
1085 if oldrecipedir != newrecipedir:
1086 bb.utils.mkdirhier(newrecipedir)
1087
1088 newappend = os.path.join(os.path.dirname(append), newappend)
1089 newfile = os.path.join(newrecipedir, newfile)
1090
1091 # Rename bbappend
1092 logger.info('Renaming %s to %s' % (append, newappend))
Andrew Geisslerc926e172021-05-07 16:11:35 -05001093 bb.utils.rename(append, newappend)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001094 # Rename recipe file
1095 logger.info('Renaming %s to %s' % (recipefile, newfile))
Andrew Geisslerc926e172021-05-07 16:11:35 -05001096 bb.utils.rename(recipefile, newfile)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001097
1098 # Rename source tree if it's the default path
1099 appendmd5 = None
1100 if not args.no_srctree:
1101 srctree = workspace[args.recipename]['srctree']
1102 if os.path.abspath(srctree) == os.path.join(config.workspace_path, 'sources', args.recipename):
1103 newsrctree = os.path.join(config.workspace_path, 'sources', newname)
1104 logger.info('Renaming %s to %s' % (srctree, newsrctree))
1105 shutil.move(srctree, newsrctree)
1106 # Correct any references (basically EXTERNALSRC*) in the .bbappend
1107 appendmd5 = bb.utils.md5_file(newappend)
1108 appendlines = []
1109 with open(newappend, 'r') as f:
1110 for line in f:
1111 appendlines.append(line)
1112 with open(newappend, 'w') as f:
1113 for line in appendlines:
1114 if srctree in line:
1115 line = line.replace(srctree, newsrctree)
1116 f.write(line)
1117 newappendmd5 = bb.utils.md5_file(newappend)
1118
1119 bpndir = None
1120 newbpndir = None
1121 if newbpn != bpn:
1122 bpndir = os.path.join(oldrecipedir, bpn)
1123 if os.path.exists(bpndir):
1124 newbpndir = os.path.join(newrecipedir, newbpn)
1125 logger.info('Renaming %s to %s' % (bpndir, newbpndir))
1126 shutil.move(bpndir, newbpndir)
1127
1128 bpdir = None
1129 newbpdir = None
1130 if newver != origfnver or newbpn != bpn:
1131 bpdir = os.path.join(oldrecipedir, bp)
1132 if os.path.exists(bpdir):
1133 newbpdir = os.path.join(newrecipedir, '%s-%s' % (newbpn, newver))
1134 logger.info('Renaming %s to %s' % (bpdir, newbpdir))
1135 shutil.move(bpdir, newbpdir)
1136
1137 if oldrecipedir != newrecipedir:
1138 # Move any stray files and delete the old recipe directory
1139 for entry in os.listdir(oldrecipedir):
1140 oldpath = os.path.join(oldrecipedir, entry)
1141 newpath = os.path.join(newrecipedir, entry)
1142 logger.info('Renaming %s to %s' % (oldpath, newpath))
1143 shutil.move(oldpath, newpath)
1144 os.rmdir(oldrecipedir)
1145
1146 # Now take care of entries in .devtool_md5
1147 md5entries = []
1148 with open(os.path.join(config.workspace_path, '.devtool_md5'), 'r') as f:
1149 for line in f:
1150 md5entries.append(line)
1151
1152 if bpndir and newbpndir:
1153 relbpndir = os.path.relpath(bpndir, config.workspace_path) + '/'
1154 else:
1155 relbpndir = None
1156 if bpdir and newbpdir:
1157 relbpdir = os.path.relpath(bpdir, config.workspace_path) + '/'
1158 else:
1159 relbpdir = None
1160
1161 with open(os.path.join(config.workspace_path, '.devtool_md5'), 'w') as f:
1162 for entry in md5entries:
1163 splitentry = entry.rstrip().split('|')
1164 if len(splitentry) > 2:
1165 if splitentry[0] == args.recipename:
1166 splitentry[0] = newname
1167 if splitentry[1] == os.path.relpath(append, config.workspace_path):
1168 splitentry[1] = os.path.relpath(newappend, config.workspace_path)
1169 if appendmd5 and splitentry[2] == appendmd5:
1170 splitentry[2] = newappendmd5
1171 elif splitentry[1] == os.path.relpath(recipefile, config.workspace_path):
1172 splitentry[1] = os.path.relpath(newfile, config.workspace_path)
1173 if recipefilemd5 and splitentry[2] == recipefilemd5:
1174 splitentry[2] = newrecipefilemd5
1175 elif relbpndir and splitentry[1].startswith(relbpndir):
1176 splitentry[1] = os.path.relpath(os.path.join(newbpndir, splitentry[1][len(relbpndir):]), config.workspace_path)
1177 elif relbpdir and splitentry[1].startswith(relbpdir):
1178 splitentry[1] = os.path.relpath(os.path.join(newbpdir, splitentry[1][len(relbpdir):]), config.workspace_path)
1179 entry = '|'.join(splitentry) + '\n'
1180 f.write(entry)
1181 return 0
1182
1183
Brad Bishop316dfdd2018-06-25 12:45:53 -04001184def _get_patchset_revs(srctree, recipe_path, initial_rev=None, force_patch_refresh=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001185 """Get initial and update rev of a recipe. These are the start point of the
1186 whole patchset and start point for the patches to be re-generated/updated.
1187 """
1188 import bb
1189
Brad Bishop316dfdd2018-06-25 12:45:53 -04001190 # Get current branch
1191 stdout, _ = bb.process.run('git rev-parse --abbrev-ref HEAD',
1192 cwd=srctree)
1193 branchname = stdout.rstrip()
1194
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001195 # Parse initial rev from recipe if not specified
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001196 commits = []
Brad Bishop316dfdd2018-06-25 12:45:53 -04001197 patches = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001198 with open(recipe_path, 'r') as f:
1199 for line in f:
1200 if line.startswith('# initial_rev:'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001201 if not initial_rev:
1202 initial_rev = line.split(':')[-1].strip()
Brad Bishop316dfdd2018-06-25 12:45:53 -04001203 elif line.startswith('# commit:') and not force_patch_refresh:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001204 commits.append(line.split(':')[-1].strip())
Brad Bishop316dfdd2018-06-25 12:45:53 -04001205 elif line.startswith('# patches_%s:' % branchname):
1206 patches = line.split(':')[-1].strip().split(',')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001207
1208 update_rev = initial_rev
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001209 changed_revs = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001210 if initial_rev:
1211 # Find first actually changed revision
1212 stdout, _ = bb.process.run('git rev-list --reverse %s..HEAD' %
1213 initial_rev, cwd=srctree)
1214 newcommits = stdout.split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001215 for i in range(min(len(commits), len(newcommits))):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001216 if newcommits[i] == commits[i]:
1217 update_rev = commits[i]
1218
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001219 try:
1220 stdout, _ = bb.process.run('git cherry devtool-patched',
1221 cwd=srctree)
1222 except bb.process.ExecutionError as err:
1223 stdout = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001224
Brad Bishop316dfdd2018-06-25 12:45:53 -04001225 if stdout is not None and not force_patch_refresh:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001226 changed_revs = []
1227 for line in stdout.splitlines():
1228 if line.startswith('+ '):
1229 rev = line.split()[1]
1230 if rev in newcommits:
1231 changed_revs.append(rev)
1232
Brad Bishop316dfdd2018-06-25 12:45:53 -04001233 return initial_rev, update_rev, changed_revs, patches
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001234
1235def _remove_file_entries(srcuri, filelist):
1236 """Remove file:// entries from SRC_URI"""
1237 remaining = filelist[:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001238 entries = []
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001239 for fname in filelist:
1240 basename = os.path.basename(fname)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001241 for i in range(len(srcuri)):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001242 if (srcuri[i].startswith('file://') and
1243 os.path.basename(srcuri[i].split(';')[0]) == basename):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001244 entries.append(srcuri[i])
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001245 remaining.remove(fname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001246 srcuri.pop(i)
1247 break
1248 return entries, remaining
1249
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001250def _replace_srcuri_entry(srcuri, filename, newentry):
1251 """Replace entry corresponding to specified file with a new entry"""
1252 basename = os.path.basename(filename)
1253 for i in range(len(srcuri)):
1254 if os.path.basename(srcuri[i].split(';')[0]) == basename:
1255 srcuri.pop(i)
1256 srcuri.insert(i, newentry)
1257 break
1258
Brad Bishop316dfdd2018-06-25 12:45:53 -04001259def _remove_source_files(append, files, destpath, no_report_remove=False, dry_run=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001260 """Unlink existing patch files"""
Brad Bishop316dfdd2018-06-25 12:45:53 -04001261
1262 dry_run_suffix = ' (dry-run)' if dry_run else ''
1263
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001264 for path in files:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001265 if append:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001266 if not destpath:
1267 raise Exception('destpath should be set here')
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001268 path = os.path.join(destpath, os.path.basename(path))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001269
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001270 if os.path.exists(path):
Brad Bishop316dfdd2018-06-25 12:45:53 -04001271 if not no_report_remove:
1272 logger.info('Removing file %s%s' % (path, dry_run_suffix))
1273 if not dry_run:
1274 # FIXME "git rm" here would be nice if the file in question is
1275 # tracked
1276 # FIXME there's a chance that this file is referred to by
1277 # another recipe, in which case deleting wouldn't be the
1278 # right thing to do
1279 os.remove(path)
1280 # Remove directory if empty
1281 try:
1282 os.rmdir(os.path.dirname(path))
1283 except OSError as ose:
1284 if ose.errno != errno.ENOTEMPTY:
1285 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001286
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001287
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001288def _export_patches(srctree, rd, start_rev, destdir, changed_revs=None):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001289 """Export patches from srctree to given location.
1290 Returns three-tuple of dicts:
1291 1. updated - patches that already exist in SRCURI
1292 2. added - new patches that don't exist in SRCURI
1293 3 removed - patches that exist in SRCURI but not in exported patches
1294 In each dict the key is the 'basepath' of the URI and value is the
1295 absolute path to the existing file in recipe space (if any).
1296 """
1297 import oe.recipeutils
1298 from oe.patch import GitApplyTree
1299 updated = OrderedDict()
1300 added = OrderedDict()
1301 seqpatch_re = re.compile('^([0-9]{4}-)?(.+)')
1302
1303 existing_patches = dict((os.path.basename(path), path) for path in
1304 oe.recipeutils.get_recipe_patches(rd))
Brad Bishop316dfdd2018-06-25 12:45:53 -04001305 logger.debug('Existing patches: %s' % existing_patches)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001306
1307 # Generate patches from Git, exclude local files directory
1308 patch_pathspec = _git_exclude_path(srctree, 'oe-local-files')
1309 GitApplyTree.extractPatches(srctree, start_rev, destdir, patch_pathspec)
1310
1311 new_patches = sorted(os.listdir(destdir))
1312 for new_patch in new_patches:
1313 # Strip numbering from patch names. If it's a git sequence named patch,
1314 # the numbers might not match up since we are starting from a different
1315 # revision This does assume that people are using unique shortlog
1316 # values, but they ought to be anyway...
1317 new_basename = seqpatch_re.match(new_patch).group(2)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001318 match_name = None
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001319 for old_patch in existing_patches:
1320 old_basename = seqpatch_re.match(old_patch).group(2)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001321 old_basename_splitext = os.path.splitext(old_basename)
1322 if old_basename.endswith(('.gz', '.bz2', '.Z')) and old_basename_splitext[0] == new_basename:
1323 old_patch_noext = os.path.splitext(old_patch)[0]
1324 match_name = old_patch_noext
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001325 break
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001326 elif new_basename == old_basename:
1327 match_name = old_patch
1328 break
1329 if match_name:
1330 # Rename patch files
1331 if new_patch != match_name:
Andrew Geisslerc926e172021-05-07 16:11:35 -05001332 bb.utils.rename(os.path.join(destdir, new_patch),
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001333 os.path.join(destdir, match_name))
1334 # Need to pop it off the list now before checking changed_revs
1335 oldpath = existing_patches.pop(old_patch)
1336 if changed_revs is not None:
1337 # Avoid updating patches that have not actually changed
1338 with open(os.path.join(destdir, match_name), 'r') as f:
1339 firstlineitems = f.readline().split()
1340 # Looking for "From <hash>" line
1341 if len(firstlineitems) > 1 and len(firstlineitems[1]) == 40:
1342 if not firstlineitems[1] in changed_revs:
1343 continue
1344 # Recompress if necessary
1345 if oldpath.endswith(('.gz', '.Z')):
1346 bb.process.run(['gzip', match_name], cwd=destdir)
1347 if oldpath.endswith('.gz'):
1348 match_name += '.gz'
1349 else:
1350 match_name += '.Z'
1351 elif oldpath.endswith('.bz2'):
1352 bb.process.run(['bzip2', match_name], cwd=destdir)
1353 match_name += '.bz2'
1354 updated[match_name] = oldpath
1355 else:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001356 added[new_patch] = None
1357 return (updated, added, existing_patches)
1358
1359
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001360def _create_kconfig_diff(srctree, rd, outfile):
1361 """Create a kconfig fragment"""
1362 # Only update config fragment if both config files exist
1363 orig_config = os.path.join(srctree, '.config.baseline')
1364 new_config = os.path.join(srctree, '.config.new')
1365 if os.path.exists(orig_config) and os.path.exists(new_config):
1366 cmd = ['diff', '--new-line-format=%L', '--old-line-format=',
1367 '--unchanged-line-format=', orig_config, new_config]
1368 pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
1369 stderr=subprocess.PIPE)
1370 stdout, stderr = pipe.communicate()
1371 if pipe.returncode == 1:
1372 logger.info("Updating config fragment %s" % outfile)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001373 with open(outfile, 'wb') as fobj:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001374 fobj.write(stdout)
1375 elif pipe.returncode == 0:
1376 logger.info("Would remove config fragment %s" % outfile)
1377 if os.path.exists(outfile):
1378 # Remove fragment file in case of empty diff
1379 logger.info("Removing config fragment %s" % outfile)
1380 os.unlink(outfile)
1381 else:
1382 raise bb.process.ExecutionError(cmd, pipe.returncode, stdout, stderr)
1383 return True
1384 return False
1385
1386
Brad Bishop316dfdd2018-06-25 12:45:53 -04001387def _export_local_files(srctree, rd, destdir, srctreebase):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001388 """Copy local files from srctree to given location.
1389 Returns three-tuple of dicts:
1390 1. updated - files that already exist in SRCURI
1391 2. added - new files files that don't exist in SRCURI
1392 3 removed - files that exist in SRCURI but not in exported files
1393 In each dict the key is the 'basepath' of the URI and value is the
1394 absolute path to the existing file in recipe space (if any).
1395 """
1396 import oe.recipeutils
1397
1398 # Find out local files (SRC_URI files that exist in the "recipe space").
1399 # Local files that reside in srctree are not included in patch generation.
1400 # Instead they are directly copied over the original source files (in
1401 # recipe space).
1402 existing_files = oe.recipeutils.get_recipe_local_files(rd)
1403 new_set = None
1404 updated = OrderedDict()
1405 added = OrderedDict()
1406 removed = OrderedDict()
Brad Bishop316dfdd2018-06-25 12:45:53 -04001407 local_files_dir = os.path.join(srctreebase, 'oe-local-files')
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001408 git_files = _git_ls_tree(srctree)
1409 if 'oe-local-files' in git_files:
1410 # If tracked by Git, take the files from srctree HEAD. First get
1411 # the tree object of the directory
1412 tmp_index = os.path.join(srctree, '.git', 'index.tmp.devtool')
1413 tree = git_files['oe-local-files'][2]
1414 bb.process.run(['git', 'checkout', tree, '--', '.'], cwd=srctree,
1415 env=dict(os.environ, GIT_WORK_TREE=destdir,
1416 GIT_INDEX_FILE=tmp_index))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001417 new_set = list(_git_ls_tree(srctree, tree, True).keys())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001418 elif os.path.isdir(local_files_dir):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001419 # If not tracked by Git, just copy from working copy
Brad Bishop316dfdd2018-06-25 12:45:53 -04001420 new_set = _ls_tree(local_files_dir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001421 bb.process.run(['cp', '-ax',
Brad Bishop316dfdd2018-06-25 12:45:53 -04001422 os.path.join(local_files_dir, '.'), destdir])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001423 else:
1424 new_set = []
1425
1426 # Special handling for kernel config
1427 if bb.data.inherits_class('kernel-yocto', rd):
1428 fragment_fn = 'devtool-fragment.cfg'
1429 fragment_path = os.path.join(destdir, fragment_fn)
1430 if _create_kconfig_diff(srctree, rd, fragment_path):
1431 if os.path.exists(fragment_path):
1432 if fragment_fn not in new_set:
1433 new_set.append(fragment_fn)
1434 # Copy fragment to local-files
1435 if os.path.isdir(local_files_dir):
1436 shutil.copy2(fragment_path, local_files_dir)
1437 else:
1438 if fragment_fn in new_set:
1439 new_set.remove(fragment_fn)
1440 # Remove fragment from local-files
1441 if os.path.exists(os.path.join(local_files_dir, fragment_fn)):
1442 os.unlink(os.path.join(local_files_dir, fragment_fn))
1443
Brad Bishopd89cb5f2019-04-10 09:02:41 -04001444 # Special handling for cml1, ccmake, etc bbclasses that generated
1445 # configuration fragment files that are consumed as source files
1446 for frag_class, frag_name in [("cml1", "fragment.cfg"), ("ccmake", "site-file.cmake")]:
1447 if bb.data.inherits_class(frag_class, rd):
1448 srcpath = os.path.join(rd.getVar('WORKDIR'), frag_name)
1449 if os.path.exists(srcpath):
1450 if frag_name not in new_set:
1451 new_set.append(frag_name)
1452 # copy fragment into destdir
1453 shutil.copy2(srcpath, destdir)
1454 # copy fragment into local files if exists
1455 if os.path.isdir(local_files_dir):
1456 shutil.copy2(srcpath, local_files_dir)
1457
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001458 if new_set is not None:
1459 for fname in new_set:
1460 if fname in existing_files:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001461 origpath = existing_files.pop(fname)
1462 workpath = os.path.join(local_files_dir, fname)
1463 if not filecmp.cmp(origpath, workpath):
1464 updated[fname] = origpath
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001465 elif fname != '.gitignore':
1466 added[fname] = None
1467
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001468 workdir = rd.getVar('WORKDIR')
1469 s = rd.getVar('S')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001470 if not s.endswith(os.sep):
1471 s += os.sep
1472
1473 if workdir != s:
1474 # Handle files where subdir= was specified
1475 for fname in list(existing_files.keys()):
1476 # FIXME handle both subdir starting with BP and not?
1477 fworkpath = os.path.join(workdir, fname)
1478 if fworkpath.startswith(s):
1479 fpath = os.path.join(srctree, os.path.relpath(fworkpath, s))
1480 if os.path.exists(fpath):
1481 origpath = existing_files.pop(fname)
1482 if not filecmp.cmp(origpath, fpath):
1483 updated[fpath] = origpath
1484
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001485 removed = existing_files
1486 return (updated, added, removed)
1487
1488
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001489def _determine_files_dir(rd):
1490 """Determine the appropriate files directory for a recipe"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001491 recipedir = rd.getVar('FILE_DIRNAME')
1492 for entry in rd.getVar('FILESPATH').split(':'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001493 relpth = os.path.relpath(entry, recipedir)
1494 if not os.sep in relpth:
1495 # One (or zero) levels below only, so we don't put anything in machine-specific directories
1496 if os.path.isdir(entry):
1497 return entry
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001498 return os.path.join(recipedir, rd.getVar('BPN'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001499
1500
Brad Bishop316dfdd2018-06-25 12:45:53 -04001501def _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 -05001502 """Implement the 'srcrev' mode of update-recipe"""
1503 import bb
1504 import oe.recipeutils
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001505
Brad Bishop316dfdd2018-06-25 12:45:53 -04001506 dry_run_suffix = ' (dry-run)' if dry_run_outdir else ''
1507
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001508 recipefile = rd.getVar('FILE')
Brad Bishop316dfdd2018-06-25 12:45:53 -04001509 recipedir = os.path.basename(recipefile)
1510 logger.info('Updating SRCREV in recipe %s%s' % (recipedir, dry_run_suffix))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001511
1512 # Get HEAD revision
1513 try:
1514 stdout, _ = bb.process.run('git rev-parse HEAD', cwd=srctree)
1515 except bb.process.ExecutionError as err:
1516 raise DevtoolError('Failed to get HEAD revision in %s: %s' %
1517 (srctree, err))
1518 srcrev = stdout.strip()
1519 if len(srcrev) != 40:
1520 raise DevtoolError('Invalid hash returned by git: %s' % stdout)
1521
1522 destpath = None
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001523 remove_files = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001524 patchfields = {}
1525 patchfields['SRCREV'] = srcrev
1526 orig_src_uri = rd.getVar('SRC_URI', False) or ''
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001527 srcuri = orig_src_uri.split()
1528 tempdir = tempfile.mkdtemp(prefix='devtool')
1529 update_srcuri = False
Brad Bishop316dfdd2018-06-25 12:45:53 -04001530 appendfile = None
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001531 try:
1532 local_files_dir = tempfile.mkdtemp(dir=tempdir)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001533 srctreebase = workspace[recipename]['srctreebase']
1534 upd_f, new_f, del_f = _export_local_files(srctree, rd, local_files_dir, srctreebase)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001535 if not no_remove:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001536 # Find list of existing patches in recipe file
1537 patches_dir = tempfile.mkdtemp(dir=tempdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001538 old_srcrev = rd.getVar('SRCREV') or ''
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001539 upd_p, new_p, del_p = _export_patches(srctree, rd, old_srcrev,
1540 patches_dir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001541 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 -05001542
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001543 # Remove deleted local files and "overlapping" patches
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001544 remove_files = list(del_f.values()) + list(upd_p.values()) + list(del_p.values())
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001545 if remove_files:
1546 removedentries = _remove_file_entries(srcuri, remove_files)[0]
1547 update_srcuri = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001548
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001549 if appendlayerdir:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001550 files = dict((os.path.join(local_files_dir, key), val) for
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001551 key, val in list(upd_f.items()) + list(new_f.items()))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001552 removevalues = {}
1553 if update_srcuri:
1554 removevalues = {'SRC_URI': removedentries}
1555 patchfields['SRC_URI'] = '\\\n '.join(srcuri)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001556 if dry_run_outdir:
1557 logger.info('Creating bbappend (dry-run)')
1558 else:
1559 appendfile, destpath = oe.recipeutils.bbappend_recipe(
1560 rd, appendlayerdir, files, wildcardver=wildcard_version,
1561 extralines=patchfields, removevalues=removevalues,
1562 redirect_output=dry_run_outdir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001563 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001564 files_dir = _determine_files_dir(rd)
1565 for basepath, path in upd_f.items():
Brad Bishop316dfdd2018-06-25 12:45:53 -04001566 logger.info('Updating file %s%s' % (basepath, dry_run_suffix))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001567 if os.path.isabs(basepath):
1568 # Original file (probably with subdir pointing inside source tree)
1569 # so we do not want to move it, just copy
Brad Bishop316dfdd2018-06-25 12:45:53 -04001570 _copy_file(basepath, path, dry_run_outdir=dry_run_outdir, base_outdir=recipedir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001571 else:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001572 _move_file(os.path.join(local_files_dir, basepath), path,
1573 dry_run_outdir=dry_run_outdir, base_outdir=recipedir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001574 update_srcuri= True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001575 for basepath, path in new_f.items():
Brad Bishop316dfdd2018-06-25 12:45:53 -04001576 logger.info('Adding new file %s%s' % (basepath, dry_run_suffix))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001577 _move_file(os.path.join(local_files_dir, basepath),
Brad Bishop316dfdd2018-06-25 12:45:53 -04001578 os.path.join(files_dir, basepath),
1579 dry_run_outdir=dry_run_outdir,
1580 base_outdir=recipedir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001581 srcuri.append('file://%s' % basepath)
1582 update_srcuri = True
1583 if update_srcuri:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001584 patchfields['SRC_URI'] = ' '.join(srcuri)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001585 ret = oe.recipeutils.patch_recipe(rd, recipefile, patchfields, redirect_output=dry_run_outdir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001586 finally:
1587 shutil.rmtree(tempdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001588 if not 'git://' in orig_src_uri:
1589 logger.info('You will need to update SRC_URI within the recipe to '
1590 'point to a git repository where you have pushed your '
1591 'changes')
1592
Brad Bishop316dfdd2018-06-25 12:45:53 -04001593 _remove_source_files(appendlayerdir, remove_files, destpath, no_report_remove, dry_run=dry_run_outdir)
1594 return True, appendfile, remove_files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001595
Brad Bishop316dfdd2018-06-25 12:45:53 -04001596def _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 -05001597 """Implement the 'patch' mode of update-recipe"""
1598 import bb
1599 import oe.recipeutils
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001600
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001601 recipefile = rd.getVar('FILE')
Brad Bishop316dfdd2018-06-25 12:45:53 -04001602 recipedir = os.path.dirname(recipefile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001603 append = workspace[recipename]['bbappend']
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001604 if not os.path.exists(append):
1605 raise DevtoolError('unable to find workspace bbappend for recipe %s' %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001606 recipename)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001607
Brad Bishop316dfdd2018-06-25 12:45:53 -04001608 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 -05001609 if not initial_rev:
1610 raise DevtoolError('Unable to find initial revision - please specify '
1611 'it with --initial-rev')
1612
Brad Bishop316dfdd2018-06-25 12:45:53 -04001613 appendfile = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001614 dl_dir = rd.getVar('DL_DIR')
1615 if not dl_dir.endswith('/'):
1616 dl_dir += '/'
1617
Brad Bishop316dfdd2018-06-25 12:45:53 -04001618 dry_run_suffix = ' (dry-run)' if dry_run_outdir else ''
1619
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001620 tempdir = tempfile.mkdtemp(prefix='devtool')
1621 try:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001622 local_files_dir = tempfile.mkdtemp(dir=tempdir)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001623 if filter_patches:
1624 upd_f = {}
1625 new_f = {}
1626 del_f = {}
1627 else:
1628 srctreebase = workspace[recipename]['srctreebase']
1629 upd_f, new_f, del_f = _export_local_files(srctree, rd, local_files_dir, srctreebase)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001630
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001631 remove_files = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001632 if not no_remove:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001633 # Get all patches from source tree and check if any should be removed
1634 all_patches_dir = tempfile.mkdtemp(dir=tempdir)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001635 _, _, del_p = _export_patches(srctree, rd, initial_rev,
1636 all_patches_dir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001637 # Remove deleted local files and patches
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001638 remove_files = list(del_f.values()) + list(del_p.values())
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001639
1640 # Get updated patches from source tree
1641 patches_dir = tempfile.mkdtemp(dir=tempdir)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001642 upd_p, new_p, _ = _export_patches(srctree, rd, update_rev,
1643 patches_dir, changed_revs)
1644 logger.debug('Pre-filtering: update: %s, new: %s' % (dict(upd_p), dict(new_p)))
1645 if filter_patches:
Brad Bishop00e122a2019-10-05 11:10:57 -04001646 new_p = OrderedDict()
1647 upd_p = OrderedDict((k,v) for k,v in upd_p.items() if k in filter_patches)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001648 remove_files = [f for f in remove_files if f in filter_patches]
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001649 updatefiles = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001650 updaterecipe = False
1651 destpath = None
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001652 srcuri = (rd.getVar('SRC_URI', False) or '').split()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001653 if appendlayerdir:
Brad Bishop00e122a2019-10-05 11:10:57 -04001654 files = OrderedDict((os.path.join(local_files_dir, key), val) for
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001655 key, val in list(upd_f.items()) + list(new_f.items()))
Brad Bishop00e122a2019-10-05 11:10:57 -04001656 files.update(OrderedDict((os.path.join(patches_dir, key), val) for
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001657 key, val in list(upd_p.items()) + list(new_p.items())))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001658 if files or remove_files:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001659 removevalues = None
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001660 if remove_files:
1661 removedentries, remaining = _remove_file_entries(
1662 srcuri, remove_files)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001663 if removedentries or remaining:
1664 remaining = ['file://' + os.path.basename(item) for
1665 item in remaining]
1666 removevalues = {'SRC_URI': removedentries + remaining}
Brad Bishop316dfdd2018-06-25 12:45:53 -04001667 appendfile, destpath = oe.recipeutils.bbappend_recipe(
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001668 rd, appendlayerdir, files,
1669 wildcardver=wildcard_version,
Brad Bishop316dfdd2018-06-25 12:45:53 -04001670 removevalues=removevalues,
1671 redirect_output=dry_run_outdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001672 else:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001673 logger.info('No patches or local source files needed updating')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001674 else:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001675 # Update existing files
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001676 files_dir = _determine_files_dir(rd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001677 for basepath, path in upd_f.items():
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001678 logger.info('Updating file %s' % basepath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001679 if os.path.isabs(basepath):
1680 # Original file (probably with subdir pointing inside source tree)
1681 # so we do not want to move it, just copy
Brad Bishop316dfdd2018-06-25 12:45:53 -04001682 _copy_file(basepath, path,
1683 dry_run_outdir=dry_run_outdir, base_outdir=recipedir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001684 else:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001685 _move_file(os.path.join(local_files_dir, basepath), path,
1686 dry_run_outdir=dry_run_outdir, base_outdir=recipedir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001687 updatefiles = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001688 for basepath, path in upd_p.items():
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001689 patchfn = os.path.join(patches_dir, basepath)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001690 if os.path.dirname(path) + '/' == dl_dir:
1691 # This is a a downloaded patch file - we now need to
1692 # replace the entry in SRC_URI with our local version
1693 logger.info('Replacing remote patch %s with updated local version' % basepath)
1694 path = os.path.join(files_dir, basepath)
1695 _replace_srcuri_entry(srcuri, basepath, 'file://%s' % basepath)
1696 updaterecipe = True
1697 else:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001698 logger.info('Updating patch %s%s' % (basepath, dry_run_suffix))
1699 _move_file(patchfn, path,
1700 dry_run_outdir=dry_run_outdir, base_outdir=recipedir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001701 updatefiles = True
1702 # Add any new files
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001703 for basepath, path in new_f.items():
Brad Bishop316dfdd2018-06-25 12:45:53 -04001704 logger.info('Adding new file %s%s' % (basepath, dry_run_suffix))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001705 _move_file(os.path.join(local_files_dir, basepath),
Brad Bishop316dfdd2018-06-25 12:45:53 -04001706 os.path.join(files_dir, basepath),
1707 dry_run_outdir=dry_run_outdir,
1708 base_outdir=recipedir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001709 srcuri.append('file://%s' % basepath)
1710 updaterecipe = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001711 for basepath, path in new_p.items():
Brad Bishop316dfdd2018-06-25 12:45:53 -04001712 logger.info('Adding new patch %s%s' % (basepath, dry_run_suffix))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001713 _move_file(os.path.join(patches_dir, basepath),
Brad Bishop316dfdd2018-06-25 12:45:53 -04001714 os.path.join(files_dir, basepath),
1715 dry_run_outdir=dry_run_outdir,
1716 base_outdir=recipedir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001717 srcuri.append('file://%s' % basepath)
1718 updaterecipe = True
1719 # Update recipe, if needed
1720 if _remove_file_entries(srcuri, remove_files)[0]:
1721 updaterecipe = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001722 if updaterecipe:
Brad Bishop316dfdd2018-06-25 12:45:53 -04001723 if not dry_run_outdir:
1724 logger.info('Updating recipe %s' % os.path.basename(recipefile))
1725 ret = oe.recipeutils.patch_recipe(rd, recipefile,
1726 {'SRC_URI': ' '.join(srcuri)},
1727 redirect_output=dry_run_outdir)
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001728 elif not updatefiles:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001729 # Neither patches nor recipe were updated
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001730 logger.info('No patches or files need updating')
Brad Bishop316dfdd2018-06-25 12:45:53 -04001731 return False, None, []
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001732 finally:
1733 shutil.rmtree(tempdir)
1734
Brad Bishop316dfdd2018-06-25 12:45:53 -04001735 _remove_source_files(appendlayerdir, remove_files, destpath, no_report_remove, dry_run=dry_run_outdir)
1736 return True, appendfile, remove_files
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001737
1738def _guess_recipe_update_mode(srctree, rdata):
1739 """Guess the recipe update mode to use"""
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001740 src_uri = (rdata.getVar('SRC_URI') or '').split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001741 git_uris = [uri for uri in src_uri if uri.startswith('git://')]
1742 if not git_uris:
1743 return 'patch'
1744 # Just use the first URI for now
1745 uri = git_uris[0]
1746 # Check remote branch
1747 params = bb.fetch.decodeurl(uri)[5]
1748 upstr_branch = params['branch'] if 'branch' in params else 'master'
1749 # Check if current branch HEAD is found in upstream branch
1750 stdout, _ = bb.process.run('git rev-parse HEAD', cwd=srctree)
1751 head_rev = stdout.rstrip()
1752 stdout, _ = bb.process.run('git branch -r --contains %s' % head_rev,
1753 cwd=srctree)
1754 remote_brs = [branch.strip() for branch in stdout.splitlines()]
1755 if 'origin/' + upstr_branch in remote_brs:
1756 return 'srcrev'
1757
1758 return 'patch'
1759
Brad Bishop316dfdd2018-06-25 12:45:53 -04001760def _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 -06001761 srctree = workspace[recipename]['srctree']
1762 if mode == 'auto':
1763 mode = _guess_recipe_update_mode(srctree, rd)
1764
Brad Bishop316dfdd2018-06-25 12:45:53 -04001765 override_branches = []
1766 mainbranch = None
1767 startbranch = None
1768 if not no_overrides:
1769 stdout, _ = bb.process.run('git branch', cwd=srctree)
1770 other_branches = []
1771 for line in stdout.splitlines():
1772 branchname = line[2:]
1773 if line.startswith('* '):
1774 startbranch = branchname
1775 if branchname.startswith(override_branch_prefix):
1776 override_branches.append(branchname)
1777 else:
1778 other_branches.append(branchname)
1779
1780 if override_branches:
1781 logger.debug('_update_recipe: override branches: %s' % override_branches)
1782 logger.debug('_update_recipe: other branches: %s' % other_branches)
1783 if startbranch.startswith(override_branch_prefix):
1784 if len(other_branches) == 1:
1785 mainbranch = other_branches[1]
1786 else:
1787 raise DevtoolError('Unable to determine main branch - please check out the main branch in source tree first')
1788 else:
1789 mainbranch = startbranch
1790
1791 checkedout = None
1792 anyupdated = False
1793 appendfile = None
1794 allremoved = []
1795 if override_branches:
1796 logger.info('Handling main branch (%s)...' % mainbranch)
1797 if startbranch != mainbranch:
1798 bb.process.run('git checkout %s' % mainbranch, cwd=srctree)
1799 checkedout = mainbranch
1800 try:
1801 branchlist = [mainbranch] + override_branches
1802 for branch in branchlist:
1803 crd = bb.data.createCopy(rd)
1804 if branch != mainbranch:
1805 logger.info('Handling branch %s...' % branch)
1806 override = branch[len(override_branch_prefix):]
1807 crd.appendVar('OVERRIDES', ':%s' % override)
1808 bb.process.run('git checkout %s' % branch, cwd=srctree)
1809 checkedout = branch
1810
1811 if mode == 'srcrev':
1812 updated, appendf, removed = _update_recipe_srcrev(recipename, workspace, srctree, crd, appendlayerdir, wildcard_version, no_remove, no_report_remove, dry_run_outdir)
1813 elif mode == 'patch':
1814 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)
1815 else:
1816 raise DevtoolError('update_recipe: invalid mode %s' % mode)
1817 if updated:
1818 anyupdated = True
1819 if appendf:
1820 appendfile = appendf
1821 allremoved.extend(removed)
1822 finally:
1823 if startbranch and checkedout != startbranch:
1824 bb.process.run('git checkout %s' % startbranch, cwd=srctree)
1825
1826 return anyupdated, appendfile, allremoved
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001827
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001828def update_recipe(args, config, basepath, workspace):
1829 """Entry point for the devtool 'update-recipe' subcommand"""
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001830 check_workspace_recipe(workspace, args.recipename)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001831
1832 if args.append:
1833 if not os.path.exists(args.append):
1834 raise DevtoolError('bbappend destination layer directory "%s" '
1835 'does not exist' % args.append)
1836 if not os.path.exists(os.path.join(args.append, 'conf', 'layer.conf')):
1837 raise DevtoolError('conf/layer.conf not found in bbappend '
1838 'destination layer "%s"' % args.append)
1839
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001840 tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001841 try:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001842
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001843 rd = parse_recipe(config, tinfoil, args.recipename, True)
1844 if not rd:
1845 return 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001846
Brad Bishop316dfdd2018-06-25 12:45:53 -04001847 dry_run_output = None
1848 dry_run_outdir = None
1849 if args.dry_run:
1850 dry_run_output = tempfile.TemporaryDirectory(prefix='devtool')
1851 dry_run_outdir = dry_run_output.name
1852 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 -05001853
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001854 if updated:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001855 rf = rd.getVar('FILE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001856 if rf.startswith(config.workspace_path):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001857 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 -06001858 finally:
1859 tinfoil.shutdown()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001860
1861 return 0
1862
1863
1864def status(args, config, basepath, workspace):
1865 """Entry point for the devtool 'status' subcommand"""
1866 if workspace:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001867 for recipe, value in sorted(workspace.items()):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001868 recipefile = value['recipefile']
1869 if recipefile:
1870 recipestr = ' (%s)' % recipefile
1871 else:
1872 recipestr = ''
1873 print("%s: %s%s" % (recipe, value['srctree'], recipestr))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001874 else:
1875 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')
1876 return 0
1877
1878
Brad Bishop64c979e2019-11-04 13:55:29 -05001879def _reset(recipes, no_clean, remove_work, config, basepath, workspace):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001880 """Reset one or more recipes"""
Brad Bishop316dfdd2018-06-25 12:45:53 -04001881 import oe.path
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001882
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001883 def clean_preferred_provider(pn, layerconf_path):
1884 """Remove PREFERRED_PROVIDER from layer.conf'"""
1885 import re
1886 layerconf_file = os.path.join(layerconf_path, 'conf', 'layer.conf')
1887 new_layerconf_file = os.path.join(layerconf_path, 'conf', '.layer.conf')
1888 pprovider_found = False
1889 with open(layerconf_file, 'r') as f:
1890 lines = f.readlines()
1891 with open(new_layerconf_file, 'a') as nf:
1892 for line in lines:
1893 pprovider_exp = r'^PREFERRED_PROVIDER_.*? = "' + pn + r'"$'
1894 if not re.match(pprovider_exp, line):
1895 nf.write(line)
1896 else:
1897 pprovider_found = True
1898 if pprovider_found:
1899 shutil.move(new_layerconf_file, layerconf_file)
1900 else:
1901 os.remove(new_layerconf_file)
1902
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001903 if recipes and not no_clean:
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001904 if len(recipes) == 1:
1905 logger.info('Cleaning sysroot for recipe %s...' % recipes[0])
1906 else:
1907 logger.info('Cleaning sysroot for recipes %s...' % ', '.join(recipes))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001908 # If the recipe file itself was created in the workspace, and
1909 # it uses BBCLASSEXTEND, then we need to also clean the other
1910 # variants
1911 targets = []
1912 for recipe in recipes:
1913 targets.append(recipe)
1914 recipefile = workspace[recipe]['recipefile']
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001915 if recipefile and os.path.exists(recipefile):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001916 targets.extend(get_bbclassextend_targets(recipefile, recipe))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001917 try:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001918 exec_build_env_command(config.init_path, basepath, 'bitbake -c clean %s' % ' '.join(targets))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001919 except bb.process.ExecutionError as e:
1920 raise DevtoolError('Command \'%s\' failed, output:\n%s\nIf you '
1921 'wish, you may specify -n/--no-clean to '
1922 'skip running this command when resetting' %
1923 (e.command, e.stdout))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001924
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001925 for pn in recipes:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001926 _check_preserve(config, pn)
1927
Brad Bishop316dfdd2018-06-25 12:45:53 -04001928 appendfile = workspace[pn]['bbappend']
1929 if os.path.exists(appendfile):
1930 # This shouldn't happen, but is possible if devtool errored out prior to
1931 # writing the md5 file. We need to delete this here or the recipe won't
1932 # actually be reset
1933 os.remove(appendfile)
1934
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001935 preservepath = os.path.join(config.workspace_path, 'attic', pn, pn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001936 def preservedir(origdir):
1937 if os.path.exists(origdir):
1938 for root, dirs, files in os.walk(origdir):
1939 for fn in files:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001940 logger.warning('Preserving %s in %s' % (fn, preservepath))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001941 _move_file(os.path.join(origdir, fn),
1942 os.path.join(preservepath, fn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001943 for dn in dirs:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001944 preservedir(os.path.join(root, dn))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001945 os.rmdir(origdir)
1946
Brad Bishop316dfdd2018-06-25 12:45:53 -04001947 recipefile = workspace[pn]['recipefile']
1948 if recipefile and oe.path.is_path_parent(config.workspace_path, recipefile):
1949 # This should always be true if recipefile is set, but just in case
1950 preservedir(os.path.dirname(recipefile))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001951 # We don't automatically create this dir next to appends, but the user can
1952 preservedir(os.path.join(config.workspace_path, 'appends', pn))
1953
Brad Bishop316dfdd2018-06-25 12:45:53 -04001954 srctreebase = workspace[pn]['srctreebase']
1955 if os.path.isdir(srctreebase):
1956 if os.listdir(srctreebase):
Brad Bishop64c979e2019-11-04 13:55:29 -05001957 if remove_work:
1958 logger.info('-r argument used on %s, removing source tree.'
1959 ' You will lose any unsaved work' %pn)
1960 shutil.rmtree(srctreebase)
1961 else:
1962 # We don't want to risk wiping out any work in progress
1963 logger.info('Leaving source tree %s as-is; if you no '
1964 'longer need it then please delete it manually'
1965 % srctreebase)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001966 else:
1967 # This is unlikely, but if it's empty we can just remove it
Brad Bishop316dfdd2018-06-25 12:45:53 -04001968 os.rmdir(srctreebase)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001969
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001970 clean_preferred_provider(pn, config.workspace_path)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001971
1972def reset(args, config, basepath, workspace):
1973 """Entry point for the devtool 'reset' subcommand"""
1974 import bb
Brad Bishop64c979e2019-11-04 13:55:29 -05001975 import shutil
1976
1977 recipes = ""
1978
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001979 if args.recipename:
1980 if args.all:
1981 raise DevtoolError("Recipe cannot be specified if -a/--all is used")
1982 else:
1983 for recipe in args.recipename:
1984 check_workspace_recipe(workspace, recipe, checksrc=False)
1985 elif not args.all:
1986 raise DevtoolError("Recipe must be specified, or specify -a/--all to "
1987 "reset all recipes")
1988 if args.all:
1989 recipes = list(workspace.keys())
1990 else:
1991 recipes = args.recipename
1992
Brad Bishop64c979e2019-11-04 13:55:29 -05001993 _reset(recipes, args.no_clean, args.remove_work, config, basepath, workspace)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001994
1995 return 0
1996
1997
1998def _get_layer(layername, d):
1999 """Determine the base layer path for the specified layer name/path"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002000 layerdirs = d.getVar('BBLAYERS').split()
Brad Bishop96ff1982019-08-19 13:50:42 -04002001 layers = {} # {basename: layer_paths}
2002 for p in layerdirs:
2003 bn = os.path.basename(p)
2004 if bn not in layers:
2005 layers[bn] = [p]
2006 else:
2007 layers[bn].append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002008 # Provide some shortcuts
2009 if layername.lower() in ['oe-core', 'openembedded-core']:
Brad Bishop96ff1982019-08-19 13:50:42 -04002010 layername = 'meta'
2011 layer_paths = layers.get(layername, None)
2012 if not layer_paths:
2013 return os.path.abspath(layername)
2014 elif len(layer_paths) == 1:
2015 return os.path.abspath(layer_paths[0])
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002016 else:
Brad Bishop96ff1982019-08-19 13:50:42 -04002017 # multiple layers having the same base name
2018 logger.warning("Multiple layers have the same base name '%s', use the first one '%s'." % (layername, layer_paths[0]))
2019 logger.warning("Consider using path instead of base name to specify layer:\n\t\t%s" % '\n\t\t'.join(layer_paths))
2020 return os.path.abspath(layer_paths[0])
2021
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002022
2023def finish(args, config, basepath, workspace):
2024 """Entry point for the devtool 'finish' subcommand"""
2025 import bb
2026 import oe.recipeutils
2027
2028 check_workspace_recipe(workspace, args.recipename)
2029
Brad Bishop316dfdd2018-06-25 12:45:53 -04002030 dry_run_suffix = ' (dry-run)' if args.dry_run else ''
2031
2032 # Grab the equivalent of COREBASE without having to initialise tinfoil
2033 corebasedir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
2034
2035 srctree = workspace[args.recipename]['srctree']
2036 check_git_repo_op(srctree, [corebasedir])
2037 dirty = check_git_repo_dirty(srctree)
2038 if dirty:
2039 if args.force:
2040 logger.warning('Source tree is not clean, continuing as requested by -f/--force')
2041 else:
2042 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)
2043
Brad Bishop00e122a2019-10-05 11:10:57 -04002044 no_clean = args.no_clean
Brad Bishop64c979e2019-11-04 13:55:29 -05002045 remove_work=args.remove_work
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002046 tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
2047 try:
Brad Bishop6dbb3162019-11-25 09:41:34 -05002048 rd = parse_recipe(config, tinfoil, args.recipename, True)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002049 if not rd:
2050 return 1
2051
2052 destlayerdir = _get_layer(args.destination, tinfoil.config_data)
Brad Bishop316dfdd2018-06-25 12:45:53 -04002053 recipefile = rd.getVar('FILE')
2054 recipedir = os.path.dirname(recipefile)
2055 origlayerdir = oe.recipeutils.find_layerdir(recipefile)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002056
2057 if not os.path.isdir(destlayerdir):
2058 raise DevtoolError('Unable to find layer or directory matching "%s"' % args.destination)
2059
2060 if os.path.abspath(destlayerdir) == config.workspace_path:
2061 raise DevtoolError('"%s" specifies the workspace layer - that is not a valid destination' % args.destination)
2062
2063 # If it's an upgrade, grab the original path
2064 origpath = None
2065 origfilelist = None
2066 append = workspace[args.recipename]['bbappend']
2067 with open(append, 'r') as f:
2068 for line in f:
2069 if line.startswith('# original_path:'):
2070 origpath = line.split(':')[1].strip()
2071 elif line.startswith('# original_files:'):
2072 origfilelist = line.split(':')[1].split()
2073
Brad Bishop316dfdd2018-06-25 12:45:53 -04002074 destlayerbasedir = oe.recipeutils.find_layerdir(destlayerdir)
2075
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002076 if origlayerdir == config.workspace_path:
2077 # Recipe file itself is in workspace, update it there first
2078 appendlayerdir = None
2079 origrelpath = None
2080 if origpath:
2081 origlayerpath = oe.recipeutils.find_layerdir(origpath)
2082 if origlayerpath:
2083 origrelpath = os.path.relpath(origpath, origlayerpath)
2084 destpath = oe.recipeutils.get_bbfile_path(rd, destlayerdir, origrelpath)
2085 if not destpath:
2086 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 -05002087 # Warn if the layer isn't in bblayers.conf (the code to create a bbappend will do this in other cases)
2088 layerdirs = [os.path.abspath(layerdir) for layerdir in rd.getVar('BBLAYERS').split()]
Brad Bishop316dfdd2018-06-25 12:45:53 -04002089 if not os.path.abspath(destlayerbasedir) in layerdirs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002090 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)
2091
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002092 elif destlayerdir == origlayerdir:
2093 # Same layer, update the original recipe
2094 appendlayerdir = None
2095 destpath = None
2096 else:
2097 # Create/update a bbappend in the specified layer
2098 appendlayerdir = destlayerdir
2099 destpath = None
2100
Brad Bishop316dfdd2018-06-25 12:45:53 -04002101 # Actually update the recipe / bbappend
2102 removing_original = (origpath and origfilelist and oe.recipeutils.find_layerdir(origpath) == destlayerbasedir)
2103 dry_run_output = None
2104 dry_run_outdir = None
2105 if args.dry_run:
2106 dry_run_output = tempfile.TemporaryDirectory(prefix='devtool')
2107 dry_run_outdir = dry_run_output.name
2108 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)
2109 removed = [os.path.relpath(pth, recipedir) for pth in removed]
2110
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002111 # Remove any old files in the case of an upgrade
Brad Bishop316dfdd2018-06-25 12:45:53 -04002112 if removing_original:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002113 for fn in origfilelist:
2114 fnp = os.path.join(origpath, fn)
Brad Bishop316dfdd2018-06-25 12:45:53 -04002115 if fn in removed or not os.path.exists(os.path.join(recipedir, fn)):
2116 logger.info('Removing file %s%s' % (fnp, dry_run_suffix))
2117 if not args.dry_run:
2118 try:
2119 os.remove(fnp)
2120 except FileNotFoundError:
2121 pass
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002122
2123 if origlayerdir == config.workspace_path and destpath:
2124 # Recipe file itself is in the workspace - need to move it and any
2125 # associated files to the specified layer
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002126 no_clean = True
Brad Bishop316dfdd2018-06-25 12:45:53 -04002127 logger.info('Moving recipe file to %s%s' % (destpath, dry_run_suffix))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002128 for root, _, files in os.walk(recipedir):
2129 for fn in files:
2130 srcpath = os.path.join(root, fn)
2131 relpth = os.path.relpath(os.path.dirname(srcpath), recipedir)
2132 destdir = os.path.abspath(os.path.join(destpath, relpth))
Brad Bishop316dfdd2018-06-25 12:45:53 -04002133 destfp = os.path.join(destdir, fn)
2134 _move_file(srcpath, destfp, dry_run_outdir=dry_run_outdir, base_outdir=destpath)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002135
Brad Bishop316dfdd2018-06-25 12:45:53 -04002136 if dry_run_outdir:
2137 import difflib
2138 comparelist = []
2139 for root, _, files in os.walk(dry_run_outdir):
2140 for fn in files:
2141 outf = os.path.join(root, fn)
2142 relf = os.path.relpath(outf, dry_run_outdir)
2143 logger.debug('dry-run: output file %s' % relf)
2144 if fn.endswith('.bb'):
2145 if origfilelist and origpath and destpath:
2146 # Need to match this up with the pre-upgrade recipe file
2147 for origf in origfilelist:
2148 if origf.endswith('.bb'):
2149 comparelist.append((os.path.abspath(os.path.join(origpath, origf)),
2150 outf,
2151 os.path.abspath(os.path.join(destpath, relf))))
2152 break
2153 else:
2154 # Compare to the existing recipe
2155 comparelist.append((recipefile, outf, recipefile))
2156 elif fn.endswith('.bbappend'):
2157 if appendfile:
2158 if os.path.exists(appendfile):
2159 comparelist.append((appendfile, outf, appendfile))
2160 else:
2161 comparelist.append((None, outf, appendfile))
2162 else:
2163 if destpath:
2164 recipedest = destpath
2165 elif appendfile:
2166 recipedest = os.path.dirname(appendfile)
2167 else:
2168 recipedest = os.path.dirname(recipefile)
2169 destfp = os.path.join(recipedest, relf)
2170 if os.path.exists(destfp):
2171 comparelist.append((destfp, outf, destfp))
2172 output = ''
2173 for oldfile, newfile, newfileshow in comparelist:
2174 if oldfile:
2175 with open(oldfile, 'r') as f:
2176 oldlines = f.readlines()
2177 else:
2178 oldfile = '/dev/null'
2179 oldlines = []
2180 with open(newfile, 'r') as f:
2181 newlines = f.readlines()
2182 if not newfileshow:
2183 newfileshow = newfile
2184 diff = difflib.unified_diff(oldlines, newlines, oldfile, newfileshow)
2185 difflines = list(diff)
2186 if difflines:
2187 output += ''.join(difflines)
2188 if output:
2189 logger.info('Diff of changed files:\n%s' % output)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002190 finally:
2191 tinfoil.shutdown()
2192
2193 # Everything else has succeeded, we can now reset
Brad Bishop316dfdd2018-06-25 12:45:53 -04002194 if args.dry_run:
2195 logger.info('Resetting recipe (dry-run)')
2196 else:
Brad Bishop64c979e2019-11-04 13:55:29 -05002197 _reset([args.recipename], no_clean=no_clean, remove_work=remove_work, config=config, basepath=basepath, workspace=workspace)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002198
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002199 return 0
2200
2201
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002202def get_default_srctree(config, recipename=''):
2203 """Get the default srctree path"""
2204 srctreeparent = config.get('General', 'default_source_parent_dir', config.workspace_path)
2205 if recipename:
2206 return os.path.join(srctreeparent, 'sources', recipename)
2207 else:
2208 return os.path.join(srctreeparent, 'sources')
2209
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002210def register_commands(subparsers, context):
2211 """Register devtool subcommands from this plugin"""
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002212
2213 defsrctree = get_default_srctree(context.config)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002214 parser_add = subparsers.add_parser('add', help='Add a new recipe',
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002215 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.',
2216 group='starting', order=100)
2217 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.')
2218 parser_add.add_argument('srctree', nargs='?', help='Path to external source tree. If not specified, a subdirectory of %s will be used.' % defsrctree)
2219 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 -05002220 group = parser_add.add_mutually_exclusive_group()
2221 group.add_argument('--same-dir', '-s', help='Build in same directory as source', action="store_true")
2222 group.add_argument('--no-same-dir', help='Force build in a separate build directory', action="store_true")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002223 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 -05002224 parser_add.add_argument('--npm-dev', help='For npm, also fetch devDependencies', action="store_true")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002225 parser_add.add_argument('--version', '-V', help='Version to use within recipe (PV)')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002226 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 -05002227 group = parser_add.add_mutually_exclusive_group()
2228 group.add_argument('--srcrev', '-S', help='Source revision to fetch if fetching from an SCM such as git (default latest)')
2229 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")
2230 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 -05002231 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')
2232 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')
2233 parser_add.add_argument('--src-subdir', help='Specify subdirectory within source tree to use', metavar='SUBDIR')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002234 parser_add.add_argument('--mirrors', help='Enable PREMIRRORS and MIRRORS for source tree fetching (disable by default).', action="store_true")
2235 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 -06002236 parser_add.set_defaults(func=add, fixed_setup=context.fixed_setup)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002237
2238 parser_modify = subparsers.add_parser('modify', help='Modify the source for an existing recipe',
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002239 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.',
2240 group='starting', order=90)
2241 parser_modify.add_argument('recipename', help='Name of existing recipe to edit (just name - no version, path or extension)')
2242 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 -05002243 parser_modify.add_argument('--wildcard', '-w', action="store_true", help='Use wildcard for unversioned bbappend')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002244 group = parser_modify.add_mutually_exclusive_group()
2245 group.add_argument('--extract', '-x', action="store_true", help='Extract source for recipe (default)')
2246 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 -05002247 group = parser_modify.add_mutually_exclusive_group()
2248 group.add_argument('--same-dir', '-s', help='Build in same directory as source', action="store_true")
2249 group.add_argument('--no-same-dir', help='Force build in a separate build directory', action="store_true")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002250 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 -04002251 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 -05002252 parser_modify.add_argument('--keep-temp', help='Keep temporary directory (for debugging)', action="store_true")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002253 parser_modify.set_defaults(func=modify, fixed_setup=context.fixed_setup)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002254
2255 parser_extract = subparsers.add_parser('extract', help='Extract the source for an existing recipe',
2256 description='Extracts the source for an existing recipe',
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002257 group='advanced')
2258 parser_extract.add_argument('recipename', help='Name of recipe to extract the source for')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002259 parser_extract.add_argument('srctree', help='Path to where to extract the source tree')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002260 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 -04002261 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 -05002262 parser_extract.add_argument('--keep-temp', action="store_true", help='Keep temporary directory (for debugging)')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002263 parser_extract.set_defaults(func=extract, fixed_setup=context.fixed_setup)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002264
2265 parser_sync = subparsers.add_parser('sync', help='Synchronize the source tree for an existing recipe',
2266 description='Synchronize the previously extracted source tree for an existing recipe',
2267 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
2268 group='advanced')
2269 parser_sync.add_argument('recipename', help='Name of recipe to sync the source for')
2270 parser_sync.add_argument('srctree', help='Path to the source tree')
2271 parser_sync.add_argument('--branch', '-b', default="devtool", help='Name for development branch to checkout')
2272 parser_sync.add_argument('--keep-temp', action="store_true", help='Keep temporary directory (for debugging)')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05002273 parser_sync.set_defaults(func=sync, fixed_setup=context.fixed_setup)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002274
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002275 parser_rename = subparsers.add_parser('rename', help='Rename a recipe file in the workspace',
2276 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.',
2277 group='working', order=10)
2278 parser_rename.add_argument('recipename', help='Current name of recipe to rename')
2279 parser_rename.add_argument('newname', nargs='?', help='New name for recipe (optional, not needed if you only want to change the version)')
2280 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)')
2281 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')
2282 parser_rename.set_defaults(func=rename)
2283
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002284 parser_update_recipe = subparsers.add_parser('update-recipe', help='Apply changes from external source tree to recipe',
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002285 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.',
2286 group='working', order=-90)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002287 parser_update_recipe.add_argument('recipename', help='Name of recipe to update')
2288 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 -05002289 parser_update_recipe.add_argument('--initial-rev', help='Override starting revision for patches')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002290 parser_update_recipe.add_argument('--append', '-a', help='Write changes to a bbappend in the specified layer instead of the recipe', metavar='LAYERDIR')
2291 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')
2292 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 -04002293 parser_update_recipe.add_argument('--no-overrides', '-O', action="store_true", help='Do not handle other override branches (if they exist)')
2294 parser_update_recipe.add_argument('--dry-run', '-N', action="store_true", help='Dry-run (just report changes instead of writing them)')
2295 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 -05002296 parser_update_recipe.set_defaults(func=update_recipe)
2297
2298 parser_status = subparsers.add_parser('status', help='Show workspace status',
2299 description='Lists recipes currently in your workspace and the paths to their respective external source trees',
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05002300 group='info', order=100)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002301 parser_status.set_defaults(func=status)
2302
2303 parser_reset = subparsers.add_parser('reset', help='Remove a recipe from your workspace',
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002304 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 -05002305 group='working', order=-100)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002306 parser_reset.add_argument('recipename', nargs='*', help='Recipe to reset')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002307 parser_reset.add_argument('--all', '-a', action="store_true", help='Reset all recipes (clear workspace)')
2308 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 -05002309 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 -05002310 parser_reset.set_defaults(func=reset)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06002311
2312 parser_finish = subparsers.add_parser('finish', help='Finish working on a recipe in your workspace',
Brad Bishop316dfdd2018-06-25 12:45:53 -04002313 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 -06002314 group='working', order=-100)
2315 parser_finish.add_argument('recipename', help='Recipe to finish')
2316 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.')
2317 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')
2318 parser_finish.add_argument('--initial-rev', help='Override starting revision for patches')
Brad Bishop316dfdd2018-06-25 12:45:53 -04002319 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 -05002320 parser_finish.add_argument('--remove-work', '-r', action="store_true", help='Clean the sources directory under workspace')
Brad Bishop00e122a2019-10-05 11:10:57 -04002321 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 -04002322 parser_finish.add_argument('--no-overrides', '-O', action="store_true", help='Do not handle other override branches (if they exist)')
2323 parser_finish.add_argument('--dry-run', '-N', action="store_true", help='Dry-run (just report changes instead of writing them)')
2324 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 -06002325 parser_finish.set_defaults(func=finish)