Extensible SDK improvements redux
cover Extensible SDK improvements redux
* Fixes around ensuring pkgdata is present to help find dependencies
* Fixes for the SDK update process
* Add the ability to install prebuilt items into the SDK (from sstate)
* Fix a few other issues I noticed in the process
Note that the world pkgdata implementation relies on a BitBake patch I just sent.
The following changes since commit 8084c185c43cb7e7f5e29d31939dfd308f8a0f93:
udev: Add 2 patches to support 4.4 kernel (2016-01-20 09:20:06 +0000)
are available in the git repository at:
git://git.openembedded.org/openembedded-core-contrib paule/extsdkfixes5-oe
http://cgit.openembedded.org/cgit.cgi/openembedded-core-contrib/log/?h=paule/extsdkfixes5-oe
Paul Eggleton (14):
classes/populate_sdk_ext: add option to bring in pkgdata for world
devtool: build: ensure pkgdata is written out
devtool: sdk-update: improve temp directory handling
devtool: sdk-update: improve SDK update process robustness
devtool: add sdk-install subcommand classes/populate_sdk_ext: drop ext-sdk-prepare.py when installing classes/populate_sdk*: add dependencies on script files toolchain-shar-extract.sh: improve behaviour when xz is not installed
gen-lockedsig-cache: copy correct native sstate into ext SDK
recipetool: create: strip quotes from values extracted from
CMakeLists.txt
devtool / recipetool: support specifying a subdirectory within the
fetched source
recipetool: create: extract SRC_URI from local git repositories
recipetool: create: fix extraction of name from URLs ending in /
recipetool: create: better fix for fetch error handling
meta/classes/populate_sdk_base.bbclass | 3 + meta/classes/populate_sdk_ext.bbclass | 46 +++++- meta/files/toolchain-shar-extract.sh | 7 +- meta/lib/oe/copy_buildsystem.py | 61 +++++++- meta/lib/oeqa/selftest/devtool.py | 7 +- meta/recipes-core/meta/meta-world-pkgdata.bb | 50 +++++++ scripts/gen-lockedsig-cache | 6 +- scripts/lib/devtool/build.py | 16 +- scripts/lib/devtool/sdk.py | 212 ++++++++++++++++++++------- scripts/lib/devtool/standard.py | 6 + scripts/lib/recipetool/create.py | 47 ++++-- scripts/lib/recipetool/create_buildsys.py | 13 +- 12 files changed, 392 insertions(+), 82 deletions(-) create mode 100644 meta/recipes-core/meta/meta-world-pkgdata.bb
Signature · 1 line
2.5.0
PATCH 1/14 classes/populate_sdk_ext: add option to bring in pkgdata for world
Add a variable SDK_INCLUDE_PKGDATA which you can set to "1" to include pkgdata for all recipes in the world target. There are a couple of uses for this:
1) If you use "devtool add" to add a recipe that builds something which depends on anything in world, the dependency can then be correctly mapped to the recipe providing it and that recipe can be added to DEPENDS, since we have the pkg-config and shared library dependency data within pkgdata.
2) You'll be able to search for these recipes and any files they package for the target with "devtool search" since that also uses pkgdata
This of course assumes you've tailored world through EXCLUDE_FROM_WORLD to only include recipes you'd want built in your distro, but I think that's a reasonable assumption; failing that there is a WORLD_PKGDATA_EXCLUDE variable that you can set to exclude any recipes you don't want.
Note that this patch relies on functionality implemented in a recent BitBake patch and will not work without it.
Implements [YOCTO #8600].
--- meta/classes/populate_sdk_ext.bbclass | 24 +++++++++++- meta/lib/oe/copy_buildsystem.py | 58 +++++++++++++++++++++++++++- meta/recipes-core/meta/meta-world-pkgdata.bb | 50 ++++++++++++++++++++++++ scripts/lib/devtool/sdk.py | 6 +-- 4 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 meta/recipes-core/meta/meta-world-pkgdata.bb
meta/classes/populate_sdk_ext.bbclass +22 -2
@@ -24,6 +24,7 @@ SDK_INHERIT_BLACKLIST ?= "buildhistory icecc" SDK_UPDATE_URL ?= "" SDK_TARGETS ?= "${PN}" +SDK_INSTALL_TARGETS = "${SDK_TARGETS} ${@'meta-world-pkgdata:do_allpackagedata' if d.getVar('SDK_INCLUDE_PKGDATA', True) == '1' else ''}" OE_INIT_ENV_SCRIPT ?= "oe-init-build-env" # The files from COREBASE that you want preserved in the COREBASE copied
@@ -45,6 +46,7 @@ SDK_TITLE_task-populate-sdk-ext = "${@d.getVar('DISTRO_NAME', True) or d.getVar( python copy_buildsystem () { import re + import shutil import oe.copy_buildsystem oe_init_env_script = d.getVar('OE_INIT_ENV_SCRIPT', True)
@@ -91,6 +93,7 @@ python copy_buildsystem () { config.set('General', 'core_meta_subdir', core_meta_subdir) config.add_section('SDK') config.set('SDK', 'sdk_targets', d.getVar('SDK_TARGETS', True)) + config.set('SDK', 'sdk_update_targets', d.getVar('SDK_INSTALL_TARGETS', True)) updateurl = d.getVar('SDK_UPDATE_URL', True) if updateurl: config.set('SDK', 'updateserver', updateurl)
@@ -199,6 +202,22 @@ python copy_buildsystem () { d.getVar('SSTATE_DIR', True), sstate_out, d, fixedlsbstring) + + # Add packagedata if enabled + if d.getVar('SDK_INCLUDE_PKGDATA', True) == '1': + lockedsigs_base = d.getVar('WORKDIR', True) + '/locked-sigs-base.inc' + lockedsigs_copy = d.getVar('WORKDIR', True) + '/locked-sigs-copy.inc' + shutil.move(lockedsigs_pruned, lockedsigs_base) + oe.copy_buildsystem.merge_lockedsigs(['do_packagedata'], + lockedsigs_base, + d.getVar('STAGING_DIR_HOST', True) + '/world-pkgdata/locked-sigs-pkgdata.inc', + lockedsigs_pruned, + lockedsigs_copy) + oe.copy_buildsystem.create_locked_sstate_cache(lockedsigs_copy, + d.getVar('SSTATE_DIR', True), + sstate_out, d, + fixedlsbstring) + # We don't need sstate do_package files for root, dirs, files in os.walk(sstate_out): for name in files:
@@ -268,7 +287,7 @@ sdk_ext_postinst() { # current working directory when first ran, nor will it set $1 when # sourcing a script. That is why this has to look so ugly. LOGFILE="$target_sdk_dir/preparing_build_system.log" - sh -c ". buildtools/environment-setup* > $LOGFILE && cd $target_sdk_dir/`dirname ${oe_init_build_env_path}` && set $target_sdk_dir && . $target_sdk_dir/${oe_init_build_env_path} $target_sdk_dir >> $LOGFILE && python $target_sdk_dir/ext-sdk-prepare.py '${SDK_TARGETS}' >> $LOGFILE 2>&1" || { echo "ERROR: SDK preparation failed: see $LOGFILE"; echo "printf 'ERROR: this SDK was not fully installed and needs reinstalling\n'" >> $env_setup_script ; exit 1 ; } + sh -c ". buildtools/environment-setup* > $LOGFILE && cd $target_sdk_dir/`dirname ${oe_init_build_env_path}` && set $target_sdk_dir && . $target_sdk_dir/${oe_init_build_env_path} $target_sdk_dir >> $LOGFILE && python $target_sdk_dir/ext-sdk-prepare.py '${SDK_INSTALL_TARGETS}' >> $LOGFILE 2>&1" || { echo "ERROR: SDK preparation failed: see $LOGFILE"; echo "printf 'ERROR: this SDK was not fully installed and needs reinstalling\n'" >> $env_setup_script ; exit 1 ; } fi echo done }
@@ -314,7 +333,8 @@ def get_sdk_ext_rdepends(d): do_populate_sdk_ext[dirs] = "${@d.getVarFlag('do_populate_sdk', 'dirs', False)}" do_populate_sdk_ext[depends] = "${@d.getVarFlag('do_populate_sdk', 'depends', False)} \ - buildtools-tarball:do_populate_sdk uninative-tarball:do_populate_sdk" + buildtools-tarball:do_populate_sdk uninative-tarball:do_populate_sdk \ + ${@'meta-world-pkgdata:do_collect_packagedata' if d.getVar('SDK_INCLUDE_PKGDATA', True) == '1' else ''}" do_populate_sdk_ext[rdepends] += "${@' '.join([x + ':do_build' for x in d.getVar('SDK_TARGETS', True).split()])}"
meta/lib/oe/copy_buildsystem.py +56 -2
66 lines not on this page · raw message
meta/recipes-core/meta/meta-world-pkgdata.bb new file +50 -0
50 lines not on this page · raw message
scripts/lib/devtool/sdk.py +3 -3
@@ -175,12 +175,12 @@ def sdk_update(args, config, basepath, workspace): if not args.skip_prepare: # Run bitbake command for the whole SDK - sdk_targets = config.get('SDK', 'sdk_targets') + sdk_update_targets = config.get('SDK', 'sdk_update_targets', config.get('SDK', 'sdk_targets')) logger.info("Preparing build system... (This may take some time.)") try: - exec_build_env_command(config.init_path, basepath, 'bitbake %s --setscene-only' % sdk_targets) + exec_build_env_command(config.init_path, basepath, 'bitbake %s --setscene-only' % sdk_update_targets) except: - logger.error('bitbake %s failed' % sdk_targets) + logger.error('bitbake %s failed' % sdk_update_targets) return -1 return 0
Signature · 1 line
2.5.0
PATCH 2/14 devtool: build: ensure pkgdata is written out
In reply to Paul EggletonWhen you run devtool build, you need to have the pkgdata written out at the end, so that if what you're adding is a library and the next thing you add is something that depends on that library, the necessary information to map the dependency back to the recipe is present. In practical terms all this means is we need do_packagedata to run in addition to do_populate_sysroot.
This does mean that do_package needs to run which wasn't running before, and that means that the few package QA tests that run within do_package such as installed-vs-shipped will now be run. This may be a bit bothersome, and prompted a fix for one of our oe-selftest tests as a result, but I don't see an easy way around it. Ultimately if you care about using the recipe in an image you'll need to fix any such errors anyway.
Fixes [YOCTO #8887].
--- meta/lib/oeqa/selftest/devtool.py | 7 ++++++- scripts/lib/devtool/build.py | 16 +++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-)
meta/lib/oeqa/selftest/devtool.py +6 -1
@@ -226,8 +226,13 @@ class DevtoolTests(DevtoolBase): # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then) bitbake('libftdi -c cleansstate') # libftdi's python/CMakeLists.txt is a bit broken, so let's just disable it + # There's also the matter of it installing cmake files to a path we don't + # normally cover, which triggers the installed-vs-shipped QA test we have + # within do_package recipefile = '%s/recipes/libftdi/libftdi_%s.bb' % (self.workspacedir, version) - result = runCmd('recipetool setvar %s EXTRA_OECMAKE -- "-DPYTHON_BINDINGS=OFF"' % recipefile) + result = runCmd('recipetool setvar %s EXTRA_OECMAKE -- \'-DPYTHON_BINDINGS=OFF -DLIBFTDI_CMAKE_CONFIG_DIR=${datadir}/cmake/Modules\'' % recipefile) + with open(recipefile, 'a') as f: + f.write('\nFILES_${PN}-dev += "${datadir}/cmake/Modules"\n') # Test devtool build result = runCmd('devtool build libftdi') staging_libdir = get_bb_var('STAGING_LIBDIR', 'libftdi')
scripts/lib/devtool/build.py +11 -5
@@ -46,21 +46,27 @@ def _set_file_values(fn, values): f.writelines(newlines) return updated -def _get_build_task(config): - return config.get('Build', 'build_task', 'populate_sysroot') +def _get_build_tasks(config): + tasks = config.get('Build', 'build_task', 'populate_sysroot,packagedata').split(',') + return ['do_%s' % task.strip() for task in tasks] def build(args, config, basepath, workspace): """Entry point for the devtool 'build' subcommand""" workspacepn = check_workspace_recipe(workspace, args.recipename, bbclassextend=True) - build_task = _get_build_task(config) + build_tasks = _get_build_tasks(config) bbappend = workspace[workspacepn]['bbappend'] if args.disable_parallel_make: logger.info("Disabling 'make' parallelism") _set_file_values(bbappend, {'PARALLEL_MAKE': ''}) try: - exec_build_env_command(config.init_path, basepath, 'bitbake -c %s %s' % (build_task, args.recipename), watch=True) + bbargs = [] + for task in build_tasks: + if args.recipename.endswith('-native') and 'package' in task: + continue + bbargs.append('%s:%s' % (args.recipename, task)) + exec_build_env_command(config.init_path, basepath, 'bitbake %s' % ' '.join(bbargs), watch=True) except bb.process.ExecutionError as e: # We've already seen the output since watch=True, so just ensure we return something to the user return e.exitcode
@@ -73,7 +79,7 @@ def build(args, config, basepath, workspace): def register_commands(subparsers, context): """Register devtool subcommands from this plugin""" parser_build = subparsers.add_parser('build', help='Build a recipe', - description='Builds the specified recipe using bitbake (up to and including do_%s)' % _get_build_task(context.config)) + description='Builds the specified recipe using bitbake (up to and including %s)' % ', '.join(_get_build_tasks(context.config))) parser_build.add_argument('recipename', help='Recipe to build') parser_build.add_argument('-s', '--disable-parallel-make', action="store_true", help='Disable make parallelism') parser_build.set_defaults(func=build)
Signature · 1 line
2.5.0
PATCH 3/14 devtool: sdk-update: improve temp directory handling
In reply to Paul Eggleton* Use tempfile.mkdtemp() instead of hardcoding temp dir
* Set a variable early for the temp locked sigs file and use that everywhere
* Delete the temp dir at the end
--- scripts/lib/devtool/sdk.py | 75 +++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 37 deletions(-)
scripts/lib/devtool/sdk.py +38 -37
@@ -7,6 +7,7 @@ import glob import shutil import errno import sys +import tempfile from devtool import exec_build_env_command, setup_tinfoil, DevtoolError logger = logging.getLogger('devtool')
@@ -133,45 +134,45 @@ def sdk_update(args, config, basepath, workspace): return ret else: # devtool sdk-update http://myhost/sdk - tmpsdk_dir = '/tmp/sdk-ext' - if os.path.exists(tmpsdk_dir): - shutil.rmtree(tmpsdk_dir) - os.makedirs(tmpsdk_dir) - os.makedirs(os.path.join(tmpsdk_dir, 'conf')) - # Fetch locked-sigs.inc from update server - ret = subprocess.call("wget -q -O - %s/conf/locked-sigs.inc > %s/locked-sigs.inc" % (updateserver, os.path.join(tmpsdk_dir, 'conf')), shell=True) - if ret != 0: - logger.error("Fetching conf/locked-sigs.inc from %s to %s/locked-sigs.inc failed" % (updateserver, os.path.join(tmpsdk_dir, 'conf'))) - return ret - else: - logger.info("Fetching conf/locked-sigs.inc from %s to %s/locked-sigs.inc succeeded" % (updateserver, os.path.join(tmpsdk_dir, 'conf'))) - new_locked_sig_file_path = os.path.join(tmpsdk_dir, 'conf/locked-sigs.inc') - update_dict = generate_update_dict(new_locked_sig_file_path, old_locked_sig_file_path) - logger.debug("update_dict = %s" % update_dict) - if len(update_dict) == 0: - logger.info("No need to update.") - return 0 - # Update metadata - logger.debug("Updating meta data via git ...") - # Try using 'git pull', if failed, use 'git clone' - if os.path.exists(os.path.join(basepath, 'layers/.git')): - ret = subprocess.call("cd layers && git pull %s/layers/.git" % updateserver, shell=True) - else: - ret = -1 - if ret != 0: - ret = subprocess.call("rm -rf layers && git clone %s/layers/.git" % updateserver, shell=True) - if ret != 0: - logger.error("Updating meta data via git failed") - return ret - logger.debug("Updating conf files ...") - conf_files = ['local.conf', 'bblayers.conf', 'devtool.conf', 'locked-sigs.inc'] - for conf in conf_files: - ret = subprocess.call("wget -q -O - %s/conf/%s > conf/%s" % (updateserver, conf, conf), shell=True) + tmpsdk_dir = tempfile.mkdtemp() + try: + os.makedirs(os.path.join(tmpsdk_dir, 'conf')) + new_locked_sig_file_path = os.path.join(tmpsdk_dir, 'conf', 'locked-sigs.inc') + # Fetch locked-sigs.inc from update server + ret = subprocess.call("wget -q -O - %s/conf/locked-sigs.inc > %s" % (updateserver, new_locked_sig_file_path), shell=True) if ret != 0: - logger.error("Update %s failed" % conf) + logger.error("Fetching conf/locked-sigs.inc from %s to %s failed" % (updateserver, new_locked_sig_file_path)) return ret - with open(os.path.join(basepath, 'conf/local.conf'), 'a') as f: - f.write('SSTATE_MIRRORS_append = " file://.* %s/sstate-cache/PATH \\n "\n' % updateserver) + else: + logger.info("Fetching conf/locked-sigs.inc from %s to %s succeeded" % (updateserver, new_locked_sig_file_path)) + update_dict = generate_update_dict(new_locked_sig_file_path, old_locked_sig_file_path) + logger.debug("update_dict = %s" % update_dict) + if len(update_dict) == 0: + logger.info("No need to update.") + return 0 + # Update metadata + logger.debug("Updating meta data via git ...") + # Try using 'git pull', if failed, use 'git clone' + if os.path.exists(os.path.join(basepath, 'layers/.git')): + ret = subprocess.call("cd layers && git pull %s/layers/.git" % updateserver, shell=True) + else: + ret = -1 + if ret != 0: + ret = subprocess.call("rm -rf layers && git clone %s/layers/.git" % updateserver, shell=True) + if ret != 0: + logger.error("Updating meta data via git failed") + return ret + logger.debug("Updating conf files ...") + conf_files = ['local.conf', 'bblayers.conf', 'devtool.conf', 'locked-sigs.inc'] + for conf in conf_files: + ret = subprocess.call("wget -q -O - %s/conf/%s > conf/%s" % (updateserver, conf, conf), shell=True) + if ret != 0: + logger.error("Update %s failed" % conf) + return ret + with open(os.path.join(basepath, 'conf/local.conf'), 'a') as f: + f.write('SSTATE_MIRRORS_append = " file://.* %s/sstate-cache/PATH \\n "\n' % updateserver) + finally: + shutil.rmtree(tmpsdk_dir) if not args.skip_prepare: # Run bitbake command for the whole SDK
Signature · 1 line
2.5.0
PATCH 4/14 devtool: sdk-update: improve SDK update process robustness
In reply to Paul EggletonMake the following improvements to the SDK update process:
* Use a manifest file with sha256sums to track files other than sstate and metadata that we need to update - e.g. conf files. This allows us to handle where files such as auto.conf may or may not be present, as well as the configuration changing without affecting task signatures
- we still want the config files copied in that case rather than it saying nothing needs to be done.
* Write the SSTATE_MIRRORS_append to site.conf rather than local.conf so that local.conf remains static (since we don't want to trigger an update every time). Also, If there is an SSTATE_MIRRORS value already set in the configuration we can skip this and assume it contains the needed packages.
* Allow the update process to be run in any directory, don't assume we're already at the base of the SDK
* Where practical, fetch remote files into a temporary location and then move them to the desired location at the end, to avoid a failed update leaving the SDK in a broken state.
* Update all installed do_populate_sysroot / do_packagedata tasks instead of using the SDK targets. This ensures any item installed through dependencies after installation (e.g. when running "devtool build") won't go stale.
--- meta/classes/populate_sdk_ext.bbclass | 16 +++++ scripts/lib/devtool/sdk.py | 118 ++++++++++++++++++++++++---------- 2 files changed, 100 insertions(+), 34 deletions(-)
meta/classes/populate_sdk_ext.bbclass +16 -0
@@ -47,6 +47,7 @@ SDK_TITLE_task-populate-sdk-ext = "${@d.getVar('DISTRO_NAME', True) or d.getVar( python copy_buildsystem () { import re import shutil + import glob import oe.copy_buildsystem oe_init_env_script = d.getVar('OE_INIT_ENV_SCRIPT', True)
@@ -224,6 +225,21 @@ python copy_buildsystem () { if name.endswith("_package.tgz"): f = os.path.join(root, name) os.remove(f) + + # Write manifest file + # Note: at the moment we cannot include the env setup script here to keep + # it updated, since it gets modified during SDK installation (see + # sdk_ext_postinst() below) thus the checksum we take here would always + # be different. + manifest_file_list = ['conf/*'] + manifest_file = os.path.join(baseoutpath, 'conf', 'sdk-conf-manifest') + with open(manifest_file, 'w') as f: + for item in manifest_file_list: + for fn in glob.glob(os.path.join(baseoutpath, item)): + if fn == manifest_file: + continue + chksum = bb.utils.sha256_file(fn) + f.write('%s\t%s\n' % (chksum, os.path.relpath(fn, baseoutpath))) } def extsdk_get_buildtools_filename(d):
scripts/lib/devtool/sdk.py +84 -34
162 lines not on this page · raw message
Signature · 1 line
2.5.0
PATCH 5/14 devtool: add sdk-install subcommand
In reply to Paul EggletonAdd the ability to install additional pre-built items (from shared state) into the extensible SDK. This can already be done implicitly by adding something to DEPENDS within a recipe you're working on and then running "devtool build", but it's useful to be able to explicitly install things particularly if you're using the extensible SDK as a traditional toolchain.
Note that for this command to be useful you need to have SSTATE_MIRRORS set in your SDK configuration, and that mirror needs to be populated with sstate artifacts for recipes you wish to be able to install.
--- scripts/lib/devtool/sdk.py | 63 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-)
scripts/lib/devtool/sdk.py +62 -1
@@ -8,7 +8,7 @@ import shutil import errno import sys import tempfile -from devtool import exec_build_env_command, setup_tinfoil, DevtoolError +from devtool import exec_build_env_command, setup_tinfoil, parse_recipe, DevtoolError logger = logging.getLogger('devtool')
@@ -235,6 +235,64 @@ def sdk_update(args, config, basepath, workspace): return -1 return 0 +def sdk_install(args, config, basepath, workspace): + """Entry point for the devtool sdk-install command""" + + import oe.recipeutils + import bb.process + + for recipe in args.recipename: + if recipe in workspace: + raise DevtoolError('recipe %s is a recipe in your workspace' % recipe) + + tasks = ['do_populate_sysroot', 'do_packagedata'] + stampprefixes = {} + def checkstamp(recipe): + stampprefix = stampprefixes[recipe] + stamps = glob.glob(stampprefix + '*') + for stamp in stamps: + if '.sigdata.' not in stamp and stamp.startswith((stampprefix + '.', stampprefix + '_setscene.')): + return True + else: + return False + + install_recipes = [] + tinfoil = setup_tinfoil(config_only=False, basepath=basepath) + try: + for recipe in args.recipename: + rd = parse_recipe(config, tinfoil, recipe, True) + if not rd: + return 1 + stampprefixes[recipe] = '%s.%s' % (rd.getVar('STAMP', True), tasks[0]) + if checkstamp(recipe): + logger.info('%s is already installed' % recipe) + else: + install_recipes.append(recipe) + finally: + tinfoil.shutdown() + + if install_recipes: + logger.info('Installing %s...' % ', '.join(install_recipes)) + install_tasks = [] + for recipe in install_recipes: + for task in tasks: + if recipe.endswith('-native') and 'package' in task: + continue + install_tasks.append('%s:%s' % (recipe, task)) + try: + exec_build_env_command(config.init_path, basepath, 'bitbake --setscene-only %s' % ' '.join(install_tasks)) + except bb.process.ExecutionError as e: + raise DevtoolError('Failed to install %s:\n%s' % (recipe, str(e))) + failed = False + for recipe in install_recipes: + if checkstamp(recipe): + logger.info('Successfully installed %s' % recipe) + else: + raise DevtoolError('Failed to install %s - unavailable' % recipe) + failed = True + if failed: + return 2 + def register_commands(subparsers, context): """Register devtool subcommands from the sdk plugin""" if context.fixed_setup:
@@ -242,3 +300,6 @@ def register_commands(subparsers, context): parser_sdk.add_argument('updateserver', help='The update server to fetch latest SDK components from', nargs='?') parser_sdk.add_argument('--skip-prepare', action="store_true", help='Skip re-preparing the build system after updating (for debugging only)') parser_sdk.set_defaults(func=sdk_update) + parser_sdk_install = subparsers.add_parser('sdk-install', help='Install additional SDK components', description='Installs additional recipe development files into the SDK. (You can use "devtool search" to find available recipes.)') + parser_sdk_install.add_argument('recipename', help='Name of the recipe to install the development artifacts for', nargs='+') + parser_sdk_install.set_defaults(func=sdk_install)
Signature · 1 line
2.5.0
PATCH 6/14 classes/populate_sdk_ext: drop ext-sdk-prepare.py when installing
In reply to Paul EggletonAt the end of the extensible SDK installation, if we've successfully prepared the build system then we don't need ext-sdk-prepare.py. I had thought earlier that this would be used when updating, but a different mechanism was needed there so this script isn't used for that.
--- meta/classes/populate_sdk_ext.bbclass | 1 + 1 file changed, 1 insertion(+)
meta/classes/populate_sdk_ext.bbclass +1 -0
@@ -304,6 +304,7 @@ sdk_ext_postinst() { # sourcing a script. That is why this has to look so ugly. LOGFILE="$target_sdk_dir/preparing_build_system.log" sh -c ". buildtools/environment-setup* > $LOGFILE && cd $target_sdk_dir/`dirname ${oe_init_build_env_path}` && set $target_sdk_dir && . $target_sdk_dir/${oe_init_build_env_path} $target_sdk_dir >> $LOGFILE && python $target_sdk_dir/ext-sdk-prepare.py '${SDK_INSTALL_TARGETS}' >> $LOGFILE 2>&1" || { echo "ERROR: SDK preparation failed: see $LOGFILE"; echo "printf 'ERROR: this SDK was not fully installed and needs reinstalling\n'" >> $env_setup_script ; exit 1 ; } + rm $target_sdk_dir/ext-sdk-prepare.py fi echo done }
Signature · 1 line
2.5.0
PATCH 7/14 classes/populate_sdk*: add dependencies on script files
In reply to Paul EggletonIf the script files we use to construct the SDK installer change then that really ought to trigger re-execution of the do_populate_sdk(_ext) task, so add file-checksums varflags to ensure that happens.
--- meta/classes/populate_sdk_base.bbclass | 3 +++ meta/classes/populate_sdk_ext.bbclass | 5 +++++ 2 files changed, 8 insertions(+)
meta/classes/populate_sdk_base.bbclass +3 -0
@@ -243,6 +243,9 @@ populate_sdk_log_check() { done } +do_populate_sdk[file-checksums] += "${COREBASE}/meta/files/toolchain-shar-relocate.sh:True \ + ${COREBASE}/meta/files/toolchain-shar-extract.sh:True" + do_populate_sdk[dirs] = "${PKGDATA_DIR} ${TOPDIR}" do_populate_sdk[depends] += "${@' '.join([x + ':do_populate_sysroot' for x in d.getVar('SDK_DEPENDS', True).split()])} ${@d.getVarFlag('do_rootfs', 'depends', False)}" do_populate_sdk[rdepends] = "${@' '.join([x + ':do_populate_sysroot' for x in d.getVar('SDK_RDEPENDS', True).split()])}"
meta/classes/populate_sdk_ext.bbclass +5 -0
@@ -259,6 +259,7 @@ install_tools() { install -m 0644 ${COREBASE}/meta/files/ext-sdk-prepare.py ${SDK_OUTPUT}/${SDKPATH} } +do_populate_sdk_ext[file-checksums] += "${COREBASE}/meta/files/ext-sdk-prepare.py:True" # Since bitbake won't run as root it doesn't make sense to try and install # the extensible sdk as root.
@@ -358,4 +359,8 @@ do_populate_sdk_ext[rdepends] += "${@' '.join([x + ':do_build' for x in d.getVar # Make sure codes change in copy_buildsystem can result in rebuilt do_populate_sdk_ext[vardeps] += "copy_buildsystem" +do_populate_sdk_ext[file-checksums] += "${COREBASE}/meta/files/toolchain-shar-relocate.sh:True \ + ${COREBASE}/meta/files/toolchain-shar-extract.sh:True \ + ${COREBASE}/scripts/gen-lockedsig-cache:True" + addtask populate_sdk_ext after do_sdk_depends
Signature · 1 line
2.5.0
PATCH 8/14 toolchain-shar-extract.sh: improve behaviour when xz is not installed
In reply to Paul EggletonCurrently if xz isn't installed when installing the SDK you get an error from tar and then the SDK installation continues, cascading into a load of other failures since files are missing. Add a check to see if xz can be run and error out if it can't, and also exit if tar fails.
--- meta/files/toolchain-shar-extract.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
meta/files/toolchain-shar-extract.sh +6 -1
@@ -29,6 +29,11 @@ if [ "$INST_ARCH" != "$SDK_ARCH" ]; then fi fi +if ! xz -V > /dev/null 2>&1; then + echo "Error: xz is required for installation of this SDK, please install it first" + exit 1 +fi + DEFAULT_INSTALL_DIR="@SDKPATH@" SUDO_EXEC="" target_sdk_dir=""
@@ -168,7 +173,7 @@ fi payload_offset=$(($(grep -na -m1 "^MARKER:$" $0|cut -d':' -f1) + 1)) printf "Extracting SDK..." -tail -n +$payload_offset $0| $SUDO_EXEC tar xJ -C $target_sdk_dir --checkpoint=.2500 +tail -n +$payload_offset $0| $SUDO_EXEC tar xJ -C $target_sdk_dir --checkpoint=.2500 || exit 1 echo "done" printf "Setting it up..."
Signature · 1 line
2.5.0
PATCH 9/14 gen-lockedsig-cache: copy correct native sstate into ext SDK
In reply to Paul EggletonWhen constructing the sstate-cache directory for the extensible SDK, we were copying in any matching native sstate packages, and as the signature doesn't actually change when the distro changes (since NATIVELSBSTRING is just a path separator for the artifacts and is not part of the signature) we ended up copying duplicated packages when the distro changed e.g. upon host distro upgrade. Only search in the NATIVELSBSTRING-named subdirectory for native packages and the issue goes away.
Fixes [YOCTO #8885].
--- meta/lib/oe/copy_buildsystem.py | 5 +++-- scripts/gen-lockedsig-cache | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-)
meta/lib/oe/copy_buildsystem.py +3 -2
@@ -149,8 +149,9 @@ def merge_lockedsigs(copy_tasks, lockedsigs_main, lockedsigs_extra, merged_outpu def create_locked_sstate_cache(lockedsigs, input_sstate_cache, output_sstate_cache, d, fixedlsbstring=""): bb.note('Generating sstate-cache...') - bb.process.run("gen-lockedsig-cache %s %s %s" % (lockedsigs, input_sstate_cache, output_sstate_cache)) + nativelsbstring = d.getVar('NATIVELSBSTRING', True) + bb.process.run("gen-lockedsig-cache %s %s %s %s" % (lockedsigs, input_sstate_cache, output_sstate_cache, nativelsbstring)) if fixedlsbstring: - nativedir = output_sstate_cache + '/' + d.getVar('NATIVELSBSTRING', True) + nativedir = output_sstate_cache + '/' + nativelsbstring if os.path.isdir(nativedir): os.rename(nativedir, output_sstate_cache + '/' + fixedlsbstring)
scripts/gen-lockedsig-cache +3 -3
@@ -13,9 +13,9 @@ def mkdir(d): if e.errno != errno.EEXIST: raise e -if len(sys.argv) < 3: +if len(sys.argv) < 5: print("Incorrect number of arguments specified") - print("syntax: gen-lockedsig-cache <locked-sigs.inc> <input-cachedir> <output-cachedir>") + print("syntax: gen-lockedsig-cache <locked-sigs.inc> <input-cachedir> <output-cachedir> <nativelsbstring>") sys.exit(1) print('Reading %s' % sys.argv[1])
@@ -30,7 +30,7 @@ files = set() for s in sigs: p = sys.argv[2] + "/" + s[:2] + "/*" + s + "*" files |= set(glob.glob(p)) - p = sys.argv[2] + "/*/" + s[:2] + "/*" + s + "*" + p = sys.argv[2] + "/%s/" % sys.argv[4] + s[:2] + "/*" + s + "*" files |= set(glob.glob(p)) print('Processing files')
Signature · 1 line
2.5.0
PATCH 10/14 recipetool: create: strip quotes from values extracted from CMakeLists.txt
In reply to Paul EggletonQuoting is optional in CMakeLists.txt and is occasionally used, so strip out quotes if they are present.
--- scripts/lib/recipetool/create_buildsys.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
scripts/lib/recipetool/create_buildsys.py +8 -5
@@ -142,6 +142,9 @@ class CmakeRecipeHandler(RecipeHandler): subdir_re = re.compile('add_subdirectory\s*\(\s*([^)\s]*)\s*([^)\s]*)\s*\)', re.IGNORECASE) dep_re = re.compile('([^ ><=]+)( *[<>=]+ *[^ ><=]+)?') + def interpret_value(value): + return value.strip('"') + def parse_cmake_file(fn, paths=None): searchpaths = (paths or []) + [os.path.dirname(fn)] logger.debug('Parsing file %s' % fn)
@@ -166,13 +169,13 @@ class CmakeRecipeHandler(RecipeHandler): continue res = proj_re.match(line) if res: - extravalues['PN'] = res.group(1).split()[0] + extravalues['PN'] = interpret_value(res.group(1).split()[0]) continue res = pkgcm_re.match(line) if res: res = dep_re.findall(res.group(2)) if res: - pcdeps.extend([x[0] for x in res]) + pcdeps.extend([interpret_value(x[0]) for x in res]) inherits.append('pkgconfig') continue res = pkgsm_re.match(line)
@@ -180,7 +183,7 @@ class CmakeRecipeHandler(RecipeHandler): res = dep_re.findall(res.group(2)) if res: # Note: appending a tuple here! - item = tuple((x[0] for x in res)) + item = tuple((interpret_value(x[0]) for x in res)) if len(item) == 1: item = item[0] pcdeps.append(item)
@@ -189,7 +192,7 @@ class CmakeRecipeHandler(RecipeHandler): res = findpackage_re.match(line) if res: origpkg = res.group(1) - pkg = origpkg.lower() + pkg = interpret_value(origpkg.lower()) if pkg == 'gettext': inherits.append('gettext') elif pkg == 'perl':
@@ -209,7 +212,7 @@ class CmakeRecipeHandler(RecipeHandler): continue res = checklib_re.match(line) if res: - lib = res.group(1) + lib = interpret_value(res.group(1)) if not lib.startswith('$'): libdeps.append(lib) if line.lower().startswith('useswig'):
Signature · 1 line
2.5.0
PATCH 11/14 devtool / recipetool: support specifying a subdirectory within the fetched source
In reply to Paul EggletonSometimes you don't want to build an entire project, just a subdirectory of it; add a --src-subdir option to make that easier. (We still look for a single subdirectory in what gets unpacked, e.g. what you might find within a tarball, so whatever you specify with this option is added onto the end of that.)
--- scripts/lib/devtool/standard.py | 6 ++++++ scripts/lib/recipetool/create.py | 11 +++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-)
scripts/lib/devtool/standard.py +6 -0
@@ -141,6 +141,8 @@ def add(args, config, basepath, workspace): extracmdopts += ' -b' if args.also_native: extracmdopts += ' --also-native' + if args.src_subdir: + extracmdopts += ' --src-subdir "%s"' % args.src_subdir tempdir = tempfile.mkdtemp(prefix='devtool') try:
@@ -208,6 +210,9 @@ def add(args, config, basepath, workspace): if not rd: return 1 + if args.src_subdir: + srctree = os.path.join(srctree, args.src_subdir) + bb.utils.mkdirhier(os.path.dirname(appendfile)) with open(appendfile, 'w') as f: f.write('inherit externalsrc\n')
@@ -1308,6 +1313,7 @@ def register_commands(subparsers, context): parser_add.add_argument('--no-git', '-g', help='If fetching source, do not set up source tree as a git repository', action="store_true") 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') 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') + parser_add.add_argument('--src-subdir', help='Specify subdirectory within source tree to use', metavar='SUBDIR') parser_add.set_defaults(func=add) parser_modify = subparsers.add_parser('modify', help='Modify the source for an existing recipe',
scripts/lib/recipetool/create.py +9 -2
@@ -324,6 +324,12 @@ def create_recipe(args): srcuri = '' srctree = args.source + if args.src_subdir: + srcsubdir = os.path.join(srcsubdir, args.src_subdir) + srctree_use = os.path.join(srctree, args.src_subdir) + else: + srctree_use = srctree + if args.outfile and os.path.isdir(args.outfile): outfile = None outdir = args.outfile
@@ -343,7 +349,7 @@ def create_recipe(args): lines_before.append('# (Feel free to remove these comments when editing.)') lines_before.append('#') - licvalues = guess_license(srctree) + licvalues = guess_license(srctree_use) lic_files_chksum = [] if licvalues: licenses = []
@@ -472,7 +478,7 @@ def create_recipe(args): extravalues = {} for handler in handlers: - handler.process(srctree, classes, lines_before, lines_after, handled, extravalues) + handler.process(srctree_use, classes, lines_before, lines_after, handled, extravalues) if not realpv: realpv = extravalues.get('PV', None)
@@ -759,5 +765,6 @@ def register_commands(subparsers): parser_create.add_argument('-V', '--version', help='Version to use within recipe (PV)') parser_create.add_argument('-b', '--binary', help='Treat the source tree as something that should be installed verbatim (no compilation, same directory structure)', action='store_true') parser_create.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') + parser_create.add_argument('--src-subdir', help='Specify subdirectory within source tree to use', metavar='SUBDIR') parser_create.set_defaults(func=create_recipe)
Signature · 1 line
2.5.0
PATCH 12/14 recipetool: create: extract SRC_URI from local git repositories
In reply to Paul EggletonIf you specify a local directory which happens to be a git repository with an origin remote (and it is in fact remote), we can use that for SRC_URI rather than leaving it blank in the recipe.
--- scripts/lib/recipetool/create.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-)
scripts/lib/recipetool/create.py +23 -7
@@ -260,6 +260,14 @@ def supports_srcrev(uri): return True return False +def reformat_git_uri(uri): + '''Convert any http[s]://....git URI into git://...;protocol=http[s]''' + res = re.match('(https?)://([^;]+\.git)(;.*)?$', uri) + if res: + # Need to switch the URI around so that the git fetcher is used + return 'git://%s;protocol=%s%s' % (res.group(2), res.group(1), res.group(3) or '') + return uri + def create_recipe(args): import bb.process import tempfile
@@ -275,16 +283,11 @@ def create_recipe(args): srcrev = '${AUTOREV}' if '://' in args.source: # Fetch a URL - fetchuri = urlparse.urldefrag(args.source)[0] + fetchuri = reformat_git_uri(urlparse.urldefrag(args.source)[0]) if args.binary: # Assume the archive contains the directory structure verbatim # so we need to extract to a subdirectory fetchuri += ';subdir=%s' % os.path.splitext(os.path.basename(urlparse.urlsplit(fetchuri).path))[0] - git_re = re.compile('(https?)://([^;]+\.git)(;.*)?$') - res = git_re.match(fetchuri) - if res: - # Need to switch the URI around so that the git fetcher is used - fetchuri = 'git://%s;protocol=%s%s' % (res.group(2), res.group(1), res.group(3) or '') srcuri = fetchuri rev_re = re.compile(';rev=([^;]+)') res = rev_re.search(srcuri)
@@ -321,8 +324,21 @@ def create_recipe(args): if not os.path.isdir(args.source): logger.error('Invalid source directory %s' % args.source) sys.exit(1) - srcuri = '' srctree = args.source + srcuri = '' + if os.path.exists(os.path.join(srctree, '.git')): + # Try to get upstream repo location from origin remote + try: + stdout, _ = bb.process.run('git remote -v', cwd=srctree, shell=True) + except bb.process.ExecutionError as e: + stdout = None + if stdout: + for line in stdout.splitlines(): + splitline = line.split() + if len(splitline) > 1: + if splitline[0] == 'origin' and '://' in splitline[1]: + srcuri = reformat_git_uri(splitline[1]) + break if args.src_subdir: srcsubdir = os.path.join(srcsubdir, args.src_subdir)
Signature · 1 line
2.5.0
PATCH 13/14 recipetool: create: fix extraction of name from URLs ending in /
In reply to Paul EggletonIf the URL ends in a / then we want to strip that off the path we split out of the URL before calling os.path.basename() on it.
--- scripts/lib/recipetool/create.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
scripts/lib/recipetool/create.py +1 -1
@@ -432,7 +432,7 @@ def create_recipe(args): if srcuri and not realpv or not pn: parseres = urlparse.urlparse(srcuri) if parseres.path: - srcfile = os.path.basename(parseres.path) + srcfile = os.path.basename(parseres.path.rstrip('/')) name_pn, name_pv = determine_from_filename(srcfile) logger.debug('Determined from filename: name = "%s", version = "%s"' % (name_pn, name_pv)) if name_pn and not pn:
Signature · 1 line
2.5.0
PATCH 14/14 recipetool: create: better fix for fetch error handling
In reply to Paul EggletonI was a little bit hasty in OE-Core revision c2cc5abe34169eae92067d97ce1e747e7c1413f5 - it turns out BitBake's fetcher code is not consistent in whether it logs something useful or not; when fetching from an http URL it does but with a git repository it doesn't. In advance of any major reworking of fetch error handling in BitBake, let's just print the text of the exception and then we know we have shown something to the user.
Additionally, we were only catching FetchException here but there are several other classes of exception that the fetcher can raise (e.g. MalformedUrl); catch the parent BBFetchException class instead so we avoid tracebacks for those other classes as well.
--- scripts/lib/recipetool/create.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
scripts/lib/recipetool/create.py +2 -2
@@ -299,8 +299,8 @@ def create_recipe(args): logger.info('Fetching %s...' % srcuri) try: checksums = scriptutils.fetch_uri(tinfoil.config_data, fetchuri, srctree, srcrev) - except bb.fetch2.FetchError: - # Error already printed + except bb.fetch2.BBFetchException as e: + logger.error(str(e).rstrip()) sys.exit(1) dirlist = os.listdir(srctree) if 'git.indirectionsymlink' in dirlist:
Signature · 1 line
2.5.0