Fixes/enhancements for devtool/recipetool/externalsrc
cover Fixes/enhancements for devtool/recipetool/externalsrc
Note in particular patch 6/11 which changes the behaviour of devtool modify without -x to extract by default which is a bit more logical (I'd been thinking about this for a while, but Khem suggested it most recently).
The following changes since commit 4bd658f7dae76ae8790fd1dfdd89fa58a456a0a4:
pseudo: uprev to 1.7.5 (2016-02-10 17:37:36 +0000)
are available in the git repository at:
git://git.openembedded.org/openembedded-core-contrib paule/devtool13
http://cgit.openembedded.org/cgit.cgi/openembedded-core-contrib/log/?h=paule/devtool13
Paul Eggleton (11):
classes/externalsrc: disable rm_work when active classes/externalsrc: create symlinks for workdir and logs
devtool: commit for extra tasks that modify source when extracting
recipetool: create: support cmake find_library directive
recipetool: create: determine name/version from github/bitbucket URLs
devtool: modify: make -x the default behaviour
devtool: add: fix adding from a local source directory
recipetool: create: ensure URL parameters don't make it into the name
recipetool: create: convert http git URLs that don't end in .git but contain /git/
recipetool: create: set S when we set SRC_URI from local git repo
oe-selftest: devtool: add another devtool add test
meta/classes/externalsrc.bbclass | 23 +++++++++++ meta/lib/oe/patch.py | 3 ++ meta/lib/oeqa/selftest/devtool.py | 40 ++++++++++++++++++- scripts/lib/devtool/standard.py | 64 +++++++++++++++++++++---------- scripts/lib/recipetool/create.py | 55 +++++++++++++++++++------- scripts/lib/recipetool/create_buildsys.py | 10 +++++ 6 files changed, 161 insertions(+), 34 deletions(-)
Signature · 1 line
2.5.0
PATCH 1/11 classes/externalsrc: disable rm_work when active
If you're using externalsrc, it's very likely that you're going to want to examine the intermediate build results even if the recipe builds successfully; therefore you won't want rm_work to delete those.
--- meta/classes/externalsrc.bbclass | 3 +++ 1 file changed, 3 insertions(+)
meta/classes/externalsrc.bbclass +3 -0
@@ -86,6 +86,9 @@ python () { # Ensure compilation happens every time d.setVarFlag('do_compile', 'nostamp', '1') + # We don't want the workdir to go away + d.appendVar('RM_WORK_EXCLUDE', ' ' + d.getVar('PN', True)) + # If B=S the same builddir is used even for different architectures. # Thus, use a shared CONFIGURESTAMPFILE so that change of do_configure # task hash is correctly detected if e.g. MACHINE changes. In addition,
Signature · 1 line
2.5.0
PATCH 2/11 classes/externalsrc: create symlinks for workdir and logs
In reply to Paul EggletonAuto-create symlinks in the source directory to the work directory (${WORKDIR}) and logs directory (${T}) so that they are easier for the user to find. This is particularly useful within the extensible SDK where the user is less likely to be familiar enough with the structure of the build system to know where to find these things, but otherwise they are a useful shortcut for anyone.
--- meta/classes/externalsrc.bbclass | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
meta/classes/externalsrc.bbclass +20 -0
@@ -25,6 +25,7 @@ # SRCTREECOVEREDTASKS ?= "do_patch do_unpack do_fetch" +EXTERNALSRC_SYMLINKS ?= "oe-workdir:${WORKDIR} oe-logs:${T}" python () { externalsrc = d.getVar('EXTERNALSRC', True)
@@ -82,6 +83,7 @@ python () { bb.build.deltask(task, d) d.prependVarFlag('do_compile', 'prefuncs', "externalsrc_compile_prefunc ") + d.prependVarFlag('do_configure', 'prefuncs', "externalsrc_configure_prefunc ") # Ensure compilation happens every time d.setVarFlag('do_compile', 'nostamp', '1')
@@ -101,6 +103,24 @@ python () { d.setVarFlag('do_configure', 'file-checksums', configstamp + ':True') } +python externalsrc_configure_prefunc() { + # Create desired symlinks + symlinks = (d.getVar('EXTERNALSRC_SYMLINKS', True) or '').split() + for symlink in symlinks: + symsplit = symlink.split(':', 1) + lnkfile = os.path.join(d.getVar('S', True), symsplit[0]) + target = d.expand(symsplit[1]) + if len(symsplit) > 1: + if os.path.islink(lnkfile): + # Link already exists, leave it if it points to the right location already + if os.readlink(lnkfile) == target: + continue + elif os.path.exists(lnkfile): + # File/dir exists with same name as link, just leave it alone + continue + os.symlink(target, lnkfile) +} + python externalsrc_compile_prefunc() { # Make it obvious that this is happening, since forgetting about it could lead to much confusion bb.plain('NOTE: %s: compiling from external source tree %s' % (d.getVar('PN', True), d.getVar('EXTERNALSRC', True)))
Signature · 1 line
2.5.0
PATCH 3/11 devtool: commit for extra tasks that modify source when extracting
In reply to Paul EggletonWhen extracting source for a recipe, if there are additional custom tasks run that make changes to the source, create a commit in the generated git branch so they are contained. This is particularly useful for tasks that come before do_patch since otherwise the changes might get incorporated in the first applied patch, but otherwise it helps avoid the tree being dirty at any point.
Fixes [YOCTO #7626].
--- meta/lib/oe/patch.py | 3 +++ scripts/lib/devtool/standard.py | 48 ++++++++++++++++++++++++++++++----------- 2 files changed, 38 insertions(+), 13 deletions(-)
meta/lib/oe/patch.py +3 -0
@@ -264,6 +264,7 @@ class PatchTree(PatchSet): class GitApplyTree(PatchTree): patch_line_prefix = '%% original patch' + ignore_commit_prefix = '%% ignore' def __init__(self, dir, d): PatchTree.__init__(self, dir, d)
@@ -384,6 +385,8 @@ class GitApplyTree(PatchTree): if line.startswith(GitApplyTree.patch_line_prefix): outfile = line.split()[-1].strip() continue + if line.startswith(GitApplyTree.ignore_commit_prefix): + continue patchlines.append(line) if not outfile: outfile = os.path.basename(srcfile)
scripts/lib/devtool/standard.py +35 -13
78 lines not on this page · raw message
Signature · 1 line
2.5.0
PATCH 4/11 recipetool: create: support cmake find_library directive
In reply to Paul EggletonCMake supports a find_library() directive to find named libraries, so detect dependencies from this.
--- scripts/lib/recipetool/create_buildsys.py | 10 ++++++++++ 1 file changed, 10 insertions(+)
scripts/lib/recipetool/create_buildsys.py +10 -0
@@ -137,6 +137,7 @@ class CmakeRecipeHandler(RecipeHandler): pkgcm_re = re.compile('pkg_check_modules\s*\(\s*[a-zA-Z0-9-_]+\s*(REQUIRED)?\s+([^)\s]+)\s*\)', re.IGNORECASE) pkgsm_re = re.compile('pkg_search_module\s*\(\s*[a-zA-Z0-9-_]+\s*(REQUIRED)?((\s+[^)\s]+)+)\s*\)', re.IGNORECASE) findpackage_re = re.compile('find_package\s*\(\s*([a-zA-Z0-9-_]+)\s*.*', re.IGNORECASE) + findlibrary_re = re.compile('find_library\s*\(\s*[a-zA-Z0-9-_]+\s*(NAMES\s+)?([a-zA-Z0-9-_ ]+)\s*.*') checklib_re = re.compile('check_library_exists\s*\(\s*([^\s)]+)\s*.*', re.IGNORECASE) include_re = re.compile('include\s*\(\s*([^)\s]*)\s*\)', re.IGNORECASE) subdir_re = re.compile('add_subdirectory\s*\(\s*([^)\s]*)\s*([^)\s]*)\s*\)', re.IGNORECASE)
@@ -215,6 +216,15 @@ class CmakeRecipeHandler(RecipeHandler): lib = interpret_value(res.group(1)) if not lib.startswith('$'): libdeps.append(lib) + res = findlibrary_re.match(line) + if res: + libs = res.group(2).split() + for lib in libs: + if lib in ['HINTS', 'PATHS', 'PATH_SUFFIXES', 'DOC', 'NAMES_PER_DIR'] or lib.startswith(('NO_', 'CMAKE_', 'ONLY_CMAKE_')): + break + lib = interpret_value(lib) + if not lib.startswith('$'): + libdeps.append(lib) if line.lower().startswith('useswig'): deps.append('swig-native') continue
Signature · 1 line
2.5.0
PATCH 5/11 recipetool: create: determine name/version from github/bitbucket URLs
In reply to Paul EggletonBoth BitBucket and GitHub provide "release" downloads that unfortunately don't include the actual project name. For recipetool this means we have to have special handling for these URLs or else the current code assumes the name of the project is something like "v0.9.1". Borrow and adapt some code from autospec to do this, and put it in its own function for clarity.
--- scripts/lib/recipetool/create.py | 44 ++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 9 deletions(-)
scripts/lib/recipetool/create.py +35 -9
@@ -247,6 +247,35 @@ def determine_from_filename(srcfile): pv = None return (pn, pv) +def determine_from_url(srcuri): + """Determine name and version from a URL""" + pn = None + pv = None + parseres = urlparse.urlparse(srcuri.lower()) + if parseres.path: + if 'github.com' in parseres.netloc: + res = re.search(r'.*/(.*?)/archive/(.*)-final\.(tar|zip)', parseres.path) + if res: + pn = res.group(1).strip().replace('_', '-') + pv = res.group(2).strip().replace('_', '.') + else: + res = re.search(r'.*/(.*?)/archive/v?(.*)\.(tar|zip)', parseres.path) + if res: + pn = res.group(1).strip().replace('_', '-') + pv = res.group(2).strip().replace('_', '.') + elif 'bitbucket.org' in parseres.netloc: + res = re.search(r'.*/(.*?)/get/[a-zA-Z_-]*([0-9][0-9a-zA-Z_.]*)\.(tar|zip)', parseres.path) + if res: + pn = res.group(1).strip().replace('_', '-') + pv = res.group(2).strip().replace('_', '.') + + if not pn and not pv: + srcfile = os.path.basename(parseres.path.rstrip('/')) + pn, pv = determine_from_filename(srcfile) + + logger.debug('Determined from source URL: name = "%s", version = "%s"' % (pn, pv)) + return (pn, pv) + def supports_srcrev(uri): localdata = bb.data.createCopy(tinfoil.config_data) # This is a bit sad, but if you don't have this set there can be some
@@ -430,15 +459,12 @@ def create_recipe(args): realpv = None if srcuri and not realpv or not pn: - parseres = urlparse.urlparse(srcuri) - if 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: - pn = name_pn - if name_pv and not realpv: - realpv = name_pv + name_pn, name_pv = determine_from_url(srcuri) + if name_pn and not pn: + pn = name_pn + if name_pv and not realpv: + realpv = name_pv + if not srcuri: lines_before.append('# No information for SRC_URI yet (only an external source tree was specified)')
Signature · 1 line
2.5.0
PATCH 6/11 devtool: modify: make -x the default behaviour
In reply to Paul EggletonIt's going to be more common for users not to have the prepared source tree for a recipe already, so the default behaviour ought to be to extract it for them from the recipe. Change the default to extract (effectively making the -x option a no-op) and add a --no-extract/-n option to disable it. Later we can look at trying to be smart and reusing an existing source tree instead of erroring out if it exists; for now this is just the default reversal.
--- meta/lib/oeqa/selftest/devtool.py | 3 ++- scripts/lib/devtool/standard.py | 14 ++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-)
meta/lib/oeqa/selftest/devtool.py +2 -1
@@ -570,7 +570,8 @@ class DevtoolTests(DevtoolBase): self.track_for_cleanup(self.workspacedir) self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') # (don't bother with cleaning the recipe on teardown, we won't be building it) - result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) + # We don't use -x here so that we test the behaviour of devtool modify without it + result = runCmd('devtool modify %s %s' % (testrecipe, tempdir)) # Check git repo self._check_src_repo(tempdir) # Add a couple of commits
scripts/lib/devtool/standard.py +8 -6
@@ -685,11 +685,11 @@ def modify(args, config, basepath, workspace): else: srctree = get_default_srctree(config, args.recipename) - if not args.extract and not os.path.isdir(srctree): - raise DevtoolError("directory %s does not exist or not a directory " - "(specify -x to extract source from recipe)" % + if args.no_extract and not os.path.isdir(srctree): + raise DevtoolError("--no-extract specified and source path %s does " + "not exist or is not a directory" % srctree) - if args.extract: + if not args.no_extract: tinfoil = _prep_extract_operation(config, basepath, args.recipename) if not tinfoil: # Error already shown
@@ -720,7 +720,7 @@ def modify(args, config, basepath, workspace): initial_rev = None commits = [] - if args.extract: + if not args.no_extract: initial_rev = _extract_source(srctree, False, args.branch, False, rd) if not initial_rev: return 1
@@ -1319,7 +1319,9 @@ def register_commands(subparsers, context): parser_modify.add_argument('recipename', help='Name of existing recipe to edit (just name - no version, path or extension)') parser_modify.add_argument('srctree', nargs='?', help='Path to external source tree. If not specified, a subdirectory of %s will be used.' % defsrctree) parser_modify.add_argument('--wildcard', '-w', action="store_true", help='Use wildcard for unversioned bbappend') - parser_modify.add_argument('--extract', '-x', action="store_true", help='Extract source as well') + group = parser_modify.add_mutually_exclusive_group() + group.add_argument('--extract', '-x', action="store_true", help='Extract source for recipe (default)') + group.add_argument('--no-extract', '-n', action="store_true", help='Do not extract source, expect it to exist') group = parser_modify.add_mutually_exclusive_group() group.add_argument('--same-dir', '-s', help='Build in same directory as source', action="store_true") group.add_argument('--no-same-dir', help='Force build in a separate build directory', action="store_true")
Signature · 1 line
2.5.0
PATCH 7/11 devtool: add: fix adding from a local source directory
In reply to Paul EggletonFix a regression introduced in in OE-Core revision aedfc5a5db1c4b2b80a36147c9a13b31764d91dd where specifying a local source tree without specifying a name resulted in a traceback.
Fixes [YOCTO #9086].
--- scripts/lib/devtool/standard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
scripts/lib/devtool/standard.py +1 -1
@@ -51,7 +51,7 @@ def add(args, config, basepath, workspace): raise DevtoolError('URI specified as positional argument as well as -f/--fetch') args.fetchuri = args.recipename args.recipename = '' - elif '://' in args.srctree: + elif args.srctree and '://' in args.srctree: if not args.fetchuri: if args.fetch: raise DevtoolError('URI specified as positional argument as well as -f/--fetch')
Signature · 1 line
2.5.0
PATCH 8/11 recipetool: create: ensure URL parameters don't make it into the name
In reply to Paul EggletonWhen auto-detecting the name for a recipe from the URL, strip off any parameters (";name=value...") before parsing the URL, otherwise this just ends up in the recipe name which is undesirable.
--- scripts/lib/recipetool/create.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
scripts/lib/recipetool/create.py +1 -1
@@ -251,7 +251,7 @@ def determine_from_url(srcuri): """Determine name and version from a URL""" pn = None pv = None - parseres = urlparse.urlparse(srcuri.lower()) + parseres = urlparse.urlparse(srcuri.lower().split(';', 1)[0]) if parseres.path: if 'github.com' in parseres.netloc: res = re.search(r'.*/(.*?)/archive/(.*)-final\.(tar|zip)', parseres.path)
Signature · 1 line
2.5.0
PATCH 9/11 recipetool: create: convert http git URLs that don't end in .git but contain /git/
In reply to Paul EggletonWhen recipetool create is given a URL that starts with http(s):// and contains /git/, such as the URLs at git.yoctoproject.org, it's fairly safe to assume it's a git repository and not something that should be fetched with wget, so rewrite the URL.
--- scripts/lib/recipetool/create.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
scripts/lib/recipetool/create.py +6 -4
@@ -291,10 +291,12 @@ def supports_srcrev(uri): 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 '') + checkuri = uri.split(';', 1)[0] + if checkuri.endswith('.git') or '/git/' in checkuri: + 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(4) or '') return uri def create_recipe(args):
Signature · 1 line
2.5.0
PATCH 10/11 recipetool: create: set S when we set SRC_URI from local git repo
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 as implemented by OE-Core revision b143d414846854dc8b3e0a47358daf5646eded38, however we also need to set S if the recipe is going to be of any use fetching from that SRC_URI later.
--- scripts/lib/recipetool/create.py | 1 + 1 file changed, 1 insertion(+)
scripts/lib/recipetool/create.py +1 -0
@@ -369,6 +369,7 @@ def create_recipe(args): if len(splitline) > 1: if splitline[0] == 'origin' and '://' in splitline[1]: srcuri = reformat_git_uri(splitline[1]) + srcsubdir = 'git' break if args.src_subdir:
Signature · 1 line
2.5.0
PATCH 11/11 oe-selftest: devtool: add another devtool add test
In reply to Paul EggletonAdd a test so we validate the following recently implemented/fixed aspects of "devtool add":
* Adding from a local directory with no name specified
* Auto-detecting the name and version from autoconf files
* Setting SRC_URI and S from the local git repo
* Showing the recipe file path in "devtool status" if it has been created within the workspace
Incidentally also tests:
* LICENSE and LIC_FILES_CHKSUM detection (though just for this piece of software)
* Extracting a dependency from autoconf (though just for this narrow case since there's only one)
--- meta/lib/oeqa/selftest/devtool.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
meta/lib/oeqa/selftest/devtool.py +37 -0
@@ -201,6 +201,43 @@ class DevtoolTests(DevtoolBase): bindir = bindir[1:] self.assertTrue(os.path.isfile(os.path.join(installdir, bindir, 'pv')), 'pv binary not found in D') + def test_devtool_add_git_local(self): + # Fetch source from a remote URL, but do it outside of devtool + tempdir = tempfile.mkdtemp(prefix='devtoolqa') + self.track_for_cleanup(tempdir) + pn = 'dbus-wait' + # We choose an https:// git URL here to check rewriting the URL works + url = 'https://git.yoctoproject.org/git/dbus-wait' + # Force fetching to "noname" subdir so we verify we're picking up the name from autoconf + # instead of the directory name + result = runCmd('git clone %s noname' % url, cwd=tempdir) + srcdir = os.path.join(tempdir, 'noname') + self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure.ac')), 'Unable to find configure script in source directory') + # Test devtool add + self.track_for_cleanup(self.workspacedir) + self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') + # Don't specify a name since we should be able to auto-detect it + result = runCmd('devtool add %s' % srcdir) + self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created') + # Check the recipe name is correct + recipefile = get_bb_var('FILE', pn) + self.assertIn('%s_git.bb' % pn, recipefile, 'Recipe file incorrectly named') + self.assertIn(recipefile, result.output) + # Test devtool status + result = runCmd('devtool status') + self.assertIn(pn, result.output) + self.assertIn(srcdir, result.output) + self.assertIn(recipefile, result.output) + checkvars = {} + checkvars['LICENSE'] = 'GPLv2' + checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263' + checkvars['S'] = '${WORKDIR}/git' + checkvars['PV'] = '0.1+git${SRCPV}' + checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/dbus-wait;protocol=https' + checkvars['SRCREV'] = '${AUTOREV}' + checkvars['DEPENDS'] = set(['dbus']) + self._test_recipe_contents(recipefile, checkvars, []) + @testcase(1162) def test_devtool_add_library(self): # We don't have the ability to pick up this dependency automatically yet...
Signature · 1 line
2.5.0