* [PATCH v2 0/1] devtool: upgrade feature
@ 2015-08-26 7:43 leonardo.sandoval.gonzalez
2015-08-26 7:43 ` [PATCH v2 1/2] " leonardo.sandoval.gonzalez
2015-08-26 7:43 ` [PATCH v2 2/2] oeqa/selftest: new tests for devtool upgrage feature leonardo.sandoval.gonzalez
0 siblings, 2 replies; 13+ messages in thread
From: leonardo.sandoval.gonzalez @ 2015-08-26 7:43 UTC (permalink / raw)
To: openembedded-core; +Cc: paul.eggleton
From: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
Complete RE-IMPLEMENTATION since v1. This patch does not use any of the
auto-upgrade-helper code [1] and the reason is that this code needs
some (i.e. use the oe.recipeutils module, auh touchs tracked recipes, etc.)
work before it can be reused into the devtool framework. Instead
of AUH, the recipetool script is used to get the new checksums.
Bugzilla Tracking page is [2]
[1] http://git.yoctoproject.org/cgit/cgit.cgi/auto-upgrade-helper/
[2] https://bugzilla.yoctoproject.org/show_bug.cgi?id=7642
The following changes since commit 645435a645a0817cec94ce1433eb74fbe7388416:
bitbake: toastergui: Added IDs to elements used in testing (2015-08-17 08:48:28 +0100)
are available in the git repository at:
git://git.yoctoproject.org/poky-contrib lsandov1/devtool-upgrade
http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=lsandov1/devtool-upgrade
Leonardo Sandoval (2):
devtool: upgrade feature
oeqa/selftest: new tests for devtool upgrage feature
meta/lib/oeqa/selftest/devtool.py | 36 +++++
scripts/lib/devtool/standard.py | 4 +-
scripts/lib/devtool/upgrade.py | 314 ++++++++++++++++++++++++++++++++++++++
3 files changed, 353 insertions(+), 1 deletion(-)
create mode 100644 scripts/lib/devtool/upgrade.py
--
1.8.4.5
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 1/2] devtool: upgrade feature
2015-08-26 7:43 [PATCH v2 0/1] devtool: upgrade feature leonardo.sandoval.gonzalez
@ 2015-08-26 7:43 ` leonardo.sandoval.gonzalez
2015-08-26 16:09 ` Aníbal Limón
2015-08-27 0:04 ` Paul Eggleton
2015-08-26 7:43 ` [PATCH v2 2/2] oeqa/selftest: new tests for devtool upgrage feature leonardo.sandoval.gonzalez
1 sibling, 2 replies; 13+ messages in thread
From: leonardo.sandoval.gonzalez @ 2015-08-26 7:43 UTC (permalink / raw)
To: openembedded-core; +Cc: paul.eggleton
From: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
Upgrades a recipe to a particular version and downloads the source code
into srctree. User can avoid patching the source code. These are the
general steps of the upgrade function:
* Extract current recipe source code into srctree and create branch
* Extract upgrade recipe source code into srctree and rebase with
previous branch. This step also creates a temporal recipe (created
using recipetool), containing the correct checksums.
* Creates the new recipe under the workspace
[YOCTO #7642]
Signed-off-by: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
---
scripts/lib/devtool/standard.py | 4 +-
scripts/lib/devtool/upgrade.py | 314 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 317 insertions(+), 1 deletion(-)
create mode 100644 scripts/lib/devtool/upgrade.py
diff --git a/scripts/lib/devtool/standard.py b/scripts/lib/devtool/standard.py
index ea21877..cd5a3ed 100644
--- a/scripts/lib/devtool/standard.py
+++ b/scripts/lib/devtool/standard.py
@@ -205,6 +205,8 @@ def extract(args, config, basepath, workspace):
srctree = os.path.abspath(args.srctree)
initial_rev = _extract_source(srctree, args.keep_temp, args.branch, rd)
+ logger.info('Source tree extracted to %s' % srctree)
+
if initial_rev:
return 0
else:
@@ -360,7 +362,6 @@ def _extract_source(srctree, keep_temp, devbranch, d):
bb.process.run('git checkout patches', cwd=srcsubdir)
shutil.move(srcsubdir, srctree)
- logger.info('Source tree extracted to %s' % srctree)
finally:
bb.logger.setLevel(origlevel)
@@ -439,6 +440,7 @@ def modify(args, config, basepath, workspace):
initial_rev = _extract_source(args.srctree, False, args.branch, rd)
if not initial_rev:
return 1
+ logger.info('Source tree extracted to %s' % srctree)
# Get list of commits since this revision
(stdout, _) = bb.process.run('git rev-list --reverse %s..HEAD' % initial_rev, cwd=args.srctree)
commits = stdout.split()
diff --git a/scripts/lib/devtool/upgrade.py b/scripts/lib/devtool/upgrade.py
new file mode 100644
index 0000000..9bef984
--- /dev/null
+++ b/scripts/lib/devtool/upgrade.py
@@ -0,0 +1,314 @@
+# Development tool - upgrade command plugin
+#
+# Copyright (C) 2014-2015 Intel Corporation
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# DESCRIPTION
+# Created a new recipe under workspace/recipes/<recipename> and place the
+# source code into <srctree>.
+# The upgrade feature executes the following steps:
+# * Extract current recipe source code into srctree and create branch
+# * Extract upgrade recipe source code into srctree and rebase with
+# previous branch. This step also creates a temporal recipe (created
+# using recipetool), containing the correct checksums.
+# * Creates the new recipe under the workspace
+"""Devtool upgrade plugin"""
+
+import os
+import sys
+import re
+import shutil
+import tempfile
+import logging
+import argparse
+import scriptutils
+import errno
+from devtool import standard
+from devtool import exec_build_env_command, setup_tinfoil, DevtoolError
+
+logger = logging.getLogger('devtool')
+
+def plugin_init(pluginlist):
+ """Plugin initialization"""
+ pass
+
+def _extract_upgrade_source(args, devbranch, config, basepath, d, recipepostfix='tmp'):
+ """Extract sources of a recipe with PV given on args.version
+
+ On the target source tree folder, a new branch (<devbranch>_<args.version>)
+ and tag (<devbranch>-base_<args.version>) will be created. In case patches
+ are applied, another tag is created (<devbranch>-patched_<args.version>).
+
+ Returns: 1) The (git) initial revision ID
+ 2) The full path of a temporal recipe containing the correct checksums
+ """
+ import oe.recipeutils
+
+ initial_rev = None
+ srctree = os.path.abspath(args.srctree)
+
+ pn = d.getVar('PN', True)
+
+ standard._check_compatible_recipe(pn, d)
+
+ recipepath = os.path.join(config.workspace_path, 'recipes', pn)
+ bb.utils.mkdirhier(recipepath)
+ recipefile = os.path.join(recipepath, "%s-%s.bb" % (pn,recipepostfix))
+ tmpsrcbasetree = tempfile.mkdtemp(prefix='devtool')
+
+ # Change PV and get URL
+ crd = d.createCopy()
+ pv = d.getVar('PV', True)
+ crd.setVar('PV', args.version)
+ src_uri = crd.getVar('SRC_URI', True)
+ if src_uri:
+ url = src_uri.split()[0]
+
+ # Generate recipe and fetch new source
+ try:
+ cmdopts = "-o %s -x %s -V %s" % (recipefile, tmpsrcbasetree, args.version)
+ cmd = 'recipetool create "%s" %s' % (url, cmdopts)
+ stdout, _ = exec_build_env_command(config.init_path, basepath, cmd)
+ except bb.process.ExecutionError as e:
+ raise DevtoolError('Command \'%s\' failed:\n%s' % (e.command, e.stdout))
+
+ tmpsrctree = os.path.join(tmpsrcbasetree, pn + '-' + args.version)
+
+ try:
+ # branch from devtool-base (original source code without patches) before copying new source code
+ bb.process.run('git checkout -b %s devtool-base' % devbranch, cwd=srctree)
+ bb.process.run('git tag -f devtool-base_%s' % args.version, cwd=srctree)
+
+ # Copy tmpsrctree into srctree
+ src_files = standard._ls_tree(tmpsrctree)
+ for path in src_files:
+ tgt_dir = os.path.join(srctree, os.path.dirname(path))
+ bb.utils.mkdirhier(tgt_dir)
+ tgt_path = os.path.join(srctree, path)
+ os.rename(os.path.join(tmpsrctree, path), tgt_path)
+
+ # Track modified and untracked files
+ (stdout,_) = bb.process.run('git ls-files --modified --others --exclude-standard', cwd=srctree)
+ add_files = stdout.splitlines()
+ for add_file in add_files:
+ bb.process.run('git add "%s"' % add_file, cwd=srctree)
+
+ if len(add_files):
+ bb.process.run('git commit -q -m "Initial commit from upstream at version %s"' % args.version, cwd=srctree)
+ (stdout, _) = bb.process.run('git rev-parse HEAD', cwd=srctree)
+ initial_rev = stdout.rstrip()
+
+ if args.no_patch:
+ patches = oe.recipeutils.get_recipe_patches(crd)
+ if len(patches):
+ logger.warn('By user choice, the following (%s_%s) patches will NOT be applied into' %(pn,pv))
+ for patch in patches:
+ logger.warn("\t%s" % os.path.basename(patch))
+ else:
+ try:
+ bb.process.run('git rebase devtool-patched', cwd=srctree)
+ except bb.process.ExecutionError as e:
+ # this is the only case where we do not propagate the error
+ # user will have the change to correct merges
+ logger.error('Command \'%s\' failed:\n%s' % (e.command, e.stdout))
+ bb.process.run('git tag -f devtool-patched_%s' % args.version, cwd=srctree)
+ except bb.process.ExecutionError as e:
+ raise DevtoolError('Command \'%s\' failed:\n%s' % (e.command, e.stdout))
+ finally:
+ if args.keep_temp:
+ logger.info('Preserving temporary directory %s' % tmpsrcbasetree)
+ else:
+ shutil.rmtree(tmpsrcbasetree)
+ return initial_rev, recipefile
+
+def _create_upgrade_recipe(args, config, recipetmp, config_data, d):
+ """Creates the new recipe under workspace
+
+ Returns the full path on the new created recipe
+ """
+ import oe.recipeutils
+
+ def _get_checksums(recipefile):
+ import re
+ checksums = {}
+ with open(recipefile) as rf:
+ for line in rf:
+ for cs in ['md5sum', 'sha256sum']:
+ m = re.match("^SRC_URI\[%s\].*=.*\"(.*)\"" % cs, line)
+ if m:
+ checksums[cs] = m.group(1)
+ return checksums
+
+ def _replace_checksums(recipefile, checksums):
+ import re
+ with open(recipefile + ".tmp", "w+") as tmprf:
+ with open(recipefile) as rf:
+ for line in rf:
+ m = None
+ for cs in ['md5sum', 'sha256sum']:
+ m = re.match("^SRC_URI\[%s\].*=.*\"(.*)\"" % cs, line)
+ if m:
+ if cs in checksums:
+ oldcheck = m.group(1)
+ newcheck = checksums[cs]
+ line = line.replace(oldcheck, newcheck)
+ break
+ tmprf.write(line)
+
+ os.rename(recipefile + ".tmp", recipefile)
+
+ def _rename_patch_dirs(recipefolder, oldpv, newpv):
+ for root, dirs, files in os.walk(recipefolder):
+ for olddir in dirs:
+ if olddir.find(oldpv) != -1:
+ newdir = olddir.replace(oldpv, newpv)
+ bb.process.run('mv %s %s' % (olddir, newdir))
+
+ def _remove_patch_dirs(recipefolder):
+ for root, dirs, files in os.walk(recipefolder):
+ for d in dirs:
+ shutil.rmtree(os.path.join(root,d))
+
+ def _recipe_contains(recipefile, var):
+ import re
+ found = False
+ with open(recipefile) as rf:
+ for line in rf:
+ if re.match("^%s.*=.*" % var, line):
+ found = True
+ break
+ return found
+
+ if not os.path.exists(recipetmp):
+ raise DevtoolError("Temporal recipe %s does not exist" % recipetmp)
+
+ # Copy current recipe into workspace
+ pn = d.getVar('PN', True)
+ recipepath = os.path.join(config.workspace_path, 'recipes', pn)
+ oe.recipeutils.copy_recipe_files(d, recipepath)
+
+ # Rename recipe
+ pv = d.getVar('PV', True)
+ recipename = "%s_%s.bb" % (pn, pv)
+ newrecipename = "%s_%s.bb" % (pn, args.version)
+ if os.path.isfile(os.path.join(recipepath, recipename)):
+ bb.process.run('mv %s %s' % (recipename, newrecipename), cwd=recipepath)
+ else:
+ # Check if it is a git recipe
+ recipename = newrecipename = "%s_git.bb" % pn
+ if not os.path.isfile(os.path.join(recipepath, recipename)):
+ raise DevtoolError("Original recipe not found on workspace")
+
+ # Rename folders
+ _rename_patch_dirs(recipepath, pv, args.version)
+
+ # Update PV, just in case it is present
+ if _recipe_contains(os.path.join(recipepath, newrecipename), 'PV'):
+ oe.recipeutils.patch_recipe(d, os.path.join(recipepath, newrecipename), {'PV':args.version})
+
+ # Update checksums
+ checksums = _get_checksums(os.path.join(recipepath, recipetmp))
+ _replace_checksums(os.path.join(recipepath, newrecipename), checksums)
+
+ # Remove recipe created by the recipe-tool
+ bb.process.run('rm %s' % recipetmp)
+
+ return os.path.join(recipepath,newrecipename)
+
+def upgrade(args, config, basepath, workspace):
+ """Entry point for the devtool 'upgrade' subcommand"""
+ import bb
+ import oe.recipeutils
+
+ if args.recipename in workspace:
+ raise DevtoolError("recipe %s is already in your workspace" %
+ args.recipename)
+ if not args.version:
+ raise DevtoolError("Provide a version through the parameter --version/-V")
+
+ reason = oe.recipeutils.validate_pn(args.recipename)
+ if reason:
+ raise DevtoolError(reason)
+
+ tinfoil = setup_tinfoil()
+
+ rd = standard._parse_recipe(config, tinfoil, args.recipename, True)
+ if not rd:
+ return 1
+
+ pn = rd.getVar('PV', True)
+ if pn == args.version:
+ raise DevtoolError("Current and upgrade versions are the same %s" % pn)
+
+ srctree = os.path.abspath(args.srctree)
+
+ try:
+ # Extract source from current recipe
+ initial_rev_base = standard._extract_source(srctree, False, args.branch, rd)
+
+ # We need to shutdown tinfoil temporally because recipetool will be used
+ tinfoil.shutdown()
+
+ # Extract new source code
+ branch_upgrade = "%s_%s" % (args.branch, args.version)
+ initial_rev_upgrade, recipe_file_tmp = _extract_upgrade_source(args, branch_upgrade, config, basepath, rd)
+ # Start again tinfoil
+ tinfoil = setup_tinfoil()
+
+ recipe_file = _create_upgrade_recipe(args, config, recipe_file_tmp, tinfoil.config_data, rd)
+
+ except DevtoolError as e:
+ logger.error(e)
+ # remove workspace recipe and srctree
+ pn = rd.getVar('PN', True)
+ recipespath = os.path.join(config.workspace_path, 'recipes')
+ recipepath = os.path.join(recipespath, pn)
+ if os.path.exists(recipepath):
+ shutil.rmtree(recipepath)
+ if not len(os.listdir(recipespath)):
+ os.rmdir(recipespath)
+ # remove srctree
+ if os.path.exists(srctree):
+ shutil.rmtree(srctree)
+ raise DevtoolError(e)
+
+ appendpath = os.path.join(config.workspace_path, 'appends')
+ if not os.path.exists(appendpath):
+ os.makedirs(appendpath)
+
+ recipe_file_base = os.path.basename(os.path.splitext(recipe_file)[0])
+ appendfile = os.path.join(appendpath, '%s.bbappend' % recipe_file_base)
+ with open(appendfile, 'w') as f:
+ f.write('inherit externalsrc\n')
+ f.write('EXTERNALSRC = "%s"\n' % srctree)
+ f.write('EXTERNALSRC_BUILD = "%s"\n' % srctree)
+ f.write('\n# initial_rev: %s\n' % initial_rev_upgrade)
+
+ standard._add_md5(config, args.recipename, appendfile)
+ logger.info('Source tree extracted to %s' % srctree)
+ return 0
+
+def register_commands(subparsers, context):
+ """Register devtool subcommands from this plugin"""
+ parser_upgrade = subparsers.add_parser('upgrade', help='Upgrade an existing recipe',
+ description='Upgrades an existing recipe',
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ parser_upgrade.add_argument('recipename', help='Name for recipe to extract the source for')
+ parser_upgrade.add_argument('srctree', help='Path to where to extract the source tree')
+ parser_upgrade.add_argument('--version', '-V', help='Version to upgrade (PV)')
+ parser_upgrade.add_argument('--branch', '-b', default="devtool", help='Name for development branch to checkout')
+ parser_upgrade.add_argument('--keep-temp', action="store_true", help='Keep temporary directory (for debugging)')
+ parser_upgrade.add_argument('--no-patch', action="store_true", help='Do not patch the new source code')
+ parser_upgrade.set_defaults(func=upgrade)
--
1.8.4.5
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 2/2] oeqa/selftest: new tests for devtool upgrage feature
2015-08-26 7:43 [PATCH v2 0/1] devtool: upgrade feature leonardo.sandoval.gonzalez
2015-08-26 7:43 ` [PATCH v2 1/2] " leonardo.sandoval.gonzalez
@ 2015-08-26 7:43 ` leonardo.sandoval.gonzalez
2015-08-26 16:15 ` Aníbal Limón
1 sibling, 1 reply; 13+ messages in thread
From: leonardo.sandoval.gonzalez @ 2015-08-26 7:43 UTC (permalink / raw)
To: openembedded-core; +Cc: paul.eggleton
From: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
Basic tests for the devtool's upgrade feature, including:
* Parameter check
* Upgrading a real recipe (e2fsprogrs) without patching and
checing its output
* Devtool status after upgrade
Signed-off-by: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
---
meta/lib/oeqa/selftest/devtool.py | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/meta/lib/oeqa/selftest/devtool.py b/meta/lib/oeqa/selftest/devtool.py
index b59db15..f72e010 100644
--- a/meta/lib/oeqa/selftest/devtool.py
+++ b/meta/lib/oeqa/selftest/devtool.py
@@ -857,3 +857,39 @@ class DevtoolTests(DevtoolBase):
result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, qemu.ip))
result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True)
self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have')
+
+ def test_devtool_upgrade(self):
+ # Check preconditions
+ workspacedir = os.path.join(self.builddir, 'workspace')
+ self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ # Check parameters
+ result = runCmd('devtool upgrade -h')
+ for param in 'recipename srctree --version -V --branch -b --keep-temp --no-patch'.split():
+ self.assertIn(param, result.output)
+ # For the moment, we are using a real recipe.
+ recipe='e2fsprogs'
+ version='1.42.13'
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ # Check that recipe is not already under devtool control
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output)
+ # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
+ # we are downgrading instead of upgrading.
+ result = runCmd('devtool upgrade %s %s -V %s --no-patch' % (recipe, tempdir, version))
+ # Check if srctree at least is populated
+ self.assertTrue(len(os.listdir(tempdir)) > 0, 'scrtree (%s) should be populated with new (%s) source code' % (tempdir, version))
+ # Check new recipe folder is present
+ self.assertTrue(os.path.exists(os.path.join(workspacedir,'recipes',recipe)), 'Recipe folder should exist')
+ # Check new recipe file is present
+ self.assertTrue(os.path.exists(os.path.join(workspacedir,'recipes',recipe,"%s_%s.bb" % (recipe,version))), 'Recipe folder should exist')
+ # Check devtool status and make sure recipe is present
+ result = runCmd('devtool status')
+ self.assertIn(recipe, result.output)
+ self.assertIn(tempdir, result.output)
+ # Check devtool reset recipe
+ result = runCmd('devtool reset %s -n' % recipe)
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output)
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
--
1.8.4.5
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/2] devtool: upgrade feature
2015-08-26 7:43 ` [PATCH v2 1/2] " leonardo.sandoval.gonzalez
@ 2015-08-26 16:09 ` Aníbal Limón
2015-08-26 16:23 ` Leonardo Sandoval
2015-08-27 0:04 ` Paul Eggleton
1 sibling, 1 reply; 13+ messages in thread
From: Aníbal Limón @ 2015-08-26 16:09 UTC (permalink / raw)
To: leonardo.sandoval.gonzalez, openembedded-core; +Cc: paul.eggleton
Comments below,
On 26/08/15 02:43, leonardo.sandoval.gonzalez@linux.intel.com wrote:
> From: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
>
> Upgrades a recipe to a particular version and downloads the source code
> into srctree. User can avoid patching the source code. These are the
> general steps of the upgrade function:
> * Extract current recipe source code into srctree and create branch
> * Extract upgrade recipe source code into srctree and rebase with
> previous branch. This step also creates a temporal recipe (created
> using recipetool), containing the correct checksums.
> * Creates the new recipe under the workspace
>
> [YOCTO #7642]
>
> Signed-off-by: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
> ---
> scripts/lib/devtool/standard.py | 4 +-
> scripts/lib/devtool/upgrade.py | 314 ++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 317 insertions(+), 1 deletion(-)
> create mode 100644 scripts/lib/devtool/upgrade.py
>
> diff --git a/scripts/lib/devtool/standard.py b/scripts/lib/devtool/standard.py
> index ea21877..cd5a3ed 100644
> --- a/scripts/lib/devtool/standard.py
> +++ b/scripts/lib/devtool/standard.py
> @@ -205,6 +205,8 @@ def extract(args, config, basepath, workspace):
>
> srctree = os.path.abspath(args.srctree)
> initial_rev = _extract_source(srctree, args.keep_temp, args.branch, rd)
> + logger.info('Source tree extracted to %s' % srctree)
> +
> if initial_rev:
> return 0
> else:
> @@ -360,7 +362,6 @@ def _extract_source(srctree, keep_temp, devbranch, d):
> bb.process.run('git checkout patches', cwd=srcsubdir)
>
> shutil.move(srcsubdir, srctree)
> - logger.info('Source tree extracted to %s' % srctree)
Why you move this logging outside?
> finally:
> bb.logger.setLevel(origlevel)
>
> @@ -439,6 +440,7 @@ def modify(args, config, basepath, workspace):
> initial_rev = _extract_source(args.srctree, False, args.branch, rd)
> if not initial_rev:
> return 1
> + logger.info('Source tree extracted to %s' % srctree)
> # Get list of commits since this revision
> (stdout, _) = bb.process.run('git rev-list --reverse %s..HEAD' % initial_rev, cwd=args.srctree)
> commits = stdout.split()
> diff --git a/scripts/lib/devtool/upgrade.py b/scripts/lib/devtool/upgrade.py
> new file mode 100644
> index 0000000..9bef984
> --- /dev/null
> +++ b/scripts/lib/devtool/upgrade.py
> @@ -0,0 +1,314 @@
> +# Development tool - upgrade command plugin
> +#
> +# Copyright (C) 2014-2015 Intel Corporation
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License version 2 as
> +# published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License along
> +# with this program; if not, write to the Free Software Foundation, Inc.,
> +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> +#
> +# DESCRIPTION
> +# Created a new recipe under workspace/recipes/<recipename> and place the
> +# source code into <srctree>.
> +# The upgrade feature executes the following steps:
> +# * Extract current recipe source code into srctree and create branch
> +# * Extract upgrade recipe source code into srctree and rebase with
> +# previous branch. This step also creates a temporal recipe (created
> +# using recipetool), containing the correct checksums.
> +# * Creates the new recipe under the workspace
> +"""Devtool upgrade plugin"""
> +
> +import os
> +import sys
> +import re
> +import shutil
> +import tempfile
> +import logging
> +import argparse
> +import scriptutils
> +import errno
> +from devtool import standard
> +from devtool import exec_build_env_command, setup_tinfoil, DevtoolError
> +
> +logger = logging.getLogger('devtool')
> +
> +def plugin_init(pluginlist):
> + """Plugin initialization"""
> + pass
> +
> +def _extract_upgrade_source(args, devbranch, config, basepath, d, recipepostfix='tmp'):
> + """Extract sources of a recipe with PV given on args.version
> +
> + On the target source tree folder, a new branch (<devbranch>_<args.version>)
> + and tag (<devbranch>-base_<args.version>) will be created. In case patches
> + are applied, another tag is created (<devbranch>-patched_<args.version>).
> +
> + Returns: 1) The (git) initial revision ID
> + 2) The full path of a temporal recipe containing the correct checksums
> + """
> + import oe.recipeutils
> +
> + initial_rev = None
> + srctree = os.path.abspath(args.srctree)
> +
> + pn = d.getVar('PN', True)
> +
> + standard._check_compatible_recipe(pn, d)
> +
> + recipepath = os.path.join(config.workspace_path, 'recipes', pn)
> + bb.utils.mkdirhier(recipepath)
> + recipefile = os.path.join(recipepath, "%s-%s.bb" % (pn,recipepostfix))
> + tmpsrcbasetree = tempfile.mkdtemp(prefix='devtool')
> +
> + # Change PV and get URL
> + crd = d.createCopy()
> + pv = d.getVar('PV', True)
> + crd.setVar('PV', args.version)
> + src_uri = crd.getVar('SRC_URI', True)
> + if src_uri:
> + url = src_uri.split()[0]
> +
> + # Generate recipe and fetch new source
> + try:
> + cmdopts = "-o %s -x %s -V %s" % (recipefile, tmpsrcbasetree, args.version)
> + cmd = 'recipetool create "%s" %s' % (url, cmdopts)
> + stdout, _ = exec_build_env_command(config.init_path, basepath, cmd)
> + except bb.process.ExecutionError as e:
> + raise DevtoolError('Command \'%s\' failed:\n%s' % (e.command, e.stdout))
> +
> + tmpsrctree = os.path.join(tmpsrcbasetree, pn + '-' + args.version)
> +
> + try:
> + # branch from devtool-base (original source code without patches) before copying new source code
> + bb.process.run('git checkout -b %s devtool-base' % devbranch, cwd=srctree)
> + bb.process.run('git tag -f devtool-base_%s' % args.version, cwd=srctree)
> +
> + # Copy tmpsrctree into srctree
> + src_files = standard._ls_tree(tmpsrctree)
> + for path in src_files:
> + tgt_dir = os.path.join(srctree, os.path.dirname(path))
> + bb.utils.mkdirhier(tgt_dir)
> + tgt_path = os.path.join(srctree, path)
> + os.rename(os.path.join(tmpsrctree, path), tgt_path)
> +
> + # Track modified and untracked files
> + (stdout,_) = bb.process.run('git ls-files --modified --others --exclude-standard', cwd=srctree)
> + add_files = stdout.splitlines()
> + for add_file in add_files:
> + bb.process.run('git add "%s"' % add_file, cwd=srctree)
> +
> + if len(add_files):
> + bb.process.run('git commit -q -m "Initial commit from upstream at version %s"' % args.version, cwd=srctree)
I don't know if is good to commit changes without user intervention.
This tool is an interactive one may be ask to the
user?
> + (stdout, _) = bb.process.run('git rev-parse HEAD', cwd=srctree)
> + initial_rev = stdout.rstrip()
> +
> + if args.no_patch:
> + patches = oe.recipeutils.get_recipe_patches(crd)
> + if len(patches):
> + logger.warn('By user choice, the following (%s_%s) patches will NOT be applied into' %(pn,pv))
> + for patch in patches:
> + logger.warn("\t%s" % os.path.basename(patch))
> + else:
> + try:
> + bb.process.run('git rebase devtool-patched', cwd=srctree)
> + except bb.process.ExecutionError as e:
> + # this is the only case where we do not propagate the error
> + # user will have the change to correct merges
> + logger.error('Command \'%s\' failed:\n%s' % (e.command, e.stdout))
> + bb.process.run('git tag -f devtool-patched_%s' % args.version, cwd=srctree)
> + except bb.process.ExecutionError as e:
> + raise DevtoolError('Command \'%s\' failed:\n%s' % (e.command, e.stdout))
> + finally:
> + if args.keep_temp:
> + logger.info('Preserving temporary directory %s' % tmpsrcbasetree)
> + else:
> + shutil.rmtree(tmpsrcbasetree)
> + return initial_rev, recipefile
> +
> +def _create_upgrade_recipe(args, config, recipetmp, config_data, d):
> + """Creates the new recipe under workspace
> +
> + Returns the full path on the new created recipe
> + """
> + import oe.recipeutils
> +
> + def _get_checksums(recipefile):
> + import re
> + checksums = {}
> + with open(recipefile) as rf:
> + for line in rf:
> + for cs in ['md5sum', 'sha256sum']:
> + m = re.match("^SRC_URI\[%s\].*=.*\"(.*)\"" % cs, line)
> + if m:
> + checksums[cs] = m.group(1)
> + return checksums
> +
> + def _replace_checksums(recipefile, checksums):
> + import re
> + with open(recipefile + ".tmp", "w+") as tmprf:
> + with open(recipefile) as rf:
> + for line in rf:
> + m = None
> + for cs in ['md5sum', 'sha256sum']:
> + m = re.match("^SRC_URI\[%s\].*=.*\"(.*)\"" % cs, line)
> + if m:
> + if cs in checksums:
> + oldcheck = m.group(1)
> + newcheck = checksums[cs]
> + line = line.replace(oldcheck, newcheck)
> + break
> + tmprf.write(line)
> +
> + os.rename(recipefile + ".tmp", recipefile)
> +
> + def _rename_patch_dirs(recipefolder, oldpv, newpv):
> + for root, dirs, files in os.walk(recipefolder):
> + for olddir in dirs:
> + if olddir.find(oldpv) != -1:
> + newdir = olddir.replace(oldpv, newpv)
> + bb.process.run('mv %s %s' % (olddir, newdir))
> +
> + def _remove_patch_dirs(recipefolder):
> + for root, dirs, files in os.walk(recipefolder):
> + for d in dirs:
> + shutil.rmtree(os.path.join(root,d))
> +
> + def _recipe_contains(recipefile, var):
> + import re
> + found = False
> + with open(recipefile) as rf:
> + for line in rf:
> + if re.match("^%s.*=.*" % var, line):
> + found = True
> + break
> + return found
> +
> + if not os.path.exists(recipetmp):
> + raise DevtoolError("Temporal recipe %s does not exist" % recipetmp)
> +
> + # Copy current recipe into workspace
> + pn = d.getVar('PN', True)
> + recipepath = os.path.join(config.workspace_path, 'recipes', pn)
> + oe.recipeutils.copy_recipe_files(d, recipepath)
> +
> + # Rename recipe
> + pv = d.getVar('PV', True)
> + recipename = "%s_%s.bb" % (pn, pv)
> + newrecipename = "%s_%s.bb" % (pn, args.version)
> + if os.path.isfile(os.path.join(recipepath, recipename)):
> + bb.process.run('mv %s %s' % (recipename, newrecipename), cwd=recipepath)
> + else:
> + # Check if it is a git recipe
> + recipename = newrecipename = "%s_git.bb" % pn
> + if not os.path.isfile(os.path.join(recipepath, recipename)):
> + raise DevtoolError("Original recipe not found on workspace")
> +
> + # Rename folders
> + _rename_patch_dirs(recipepath, pv, args.version)
> +
> + # Update PV, just in case it is present
> + if _recipe_contains(os.path.join(recipepath, newrecipename), 'PV'):
> + oe.recipeutils.patch_recipe(d, os.path.join(recipepath, newrecipename), {'PV':args.version})
> +
> + # Update checksums
> + checksums = _get_checksums(os.path.join(recipepath, recipetmp))
> + _replace_checksums(os.path.join(recipepath, newrecipename), checksums)
> +
> + # Remove recipe created by the recipe-tool
> + bb.process.run('rm %s' % recipetmp)
> +
> + return os.path.join(recipepath,newrecipename)
> +
> +def upgrade(args, config, basepath, workspace):
> + """Entry point for the devtool 'upgrade' subcommand"""
> + import bb
> + import oe.recipeutils
> +
> + if args.recipename in workspace:
> + raise DevtoolError("recipe %s is already in your workspace" %
> + args.recipename)
> + if not args.version:
> + raise DevtoolError("Provide a version through the parameter --version/-V")
> +
> + reason = oe.recipeutils.validate_pn(args.recipename)
> + if reason:
> + raise DevtoolError(reason)
> +
> + tinfoil = setup_tinfoil()
> +
> + rd = standard._parse_recipe(config, tinfoil, args.recipename, True)
> + if not rd:
> + return 1
> +
> + pn = rd.getVar('PV', True)
> + if pn == args.version:
> + raise DevtoolError("Current and upgrade versions are the same %s" % pn)
> +
> + srctree = os.path.abspath(args.srctree)
> +
> + try:
> + # Extract source from current recipe
> + initial_rev_base = standard._extract_source(srctree, False, args.branch, rd)
> +
> + # We need to shutdown tinfoil temporally because recipetool will be used
> + tinfoil.shutdown()
> +
> + # Extract new source code
> + branch_upgrade = "%s_%s" % (args.branch, args.version)
> + initial_rev_upgrade, recipe_file_tmp = _extract_upgrade_source(args, branch_upgrade, config, basepath, rd)
> + # Start again tinfoil
> + tinfoil = setup_tinfoil()
> +
> + recipe_file = _create_upgrade_recipe(args, config, recipe_file_tmp, tinfoil.config_data, rd)
> +
> + except DevtoolError as e:
> + logger.error(e)
> + # remove workspace recipe and srctree
> + pn = rd.getVar('PN', True)
> + recipespath = os.path.join(config.workspace_path, 'recipes')
> + recipepath = os.path.join(recipespath, pn)
> + if os.path.exists(recipepath):
> + shutil.rmtree(recipepath)
> + if not len(os.listdir(recipespath)):
> + os.rmdir(recipespath)
> + # remove srctree
> + if os.path.exists(srctree):
> + shutil.rmtree(srctree)
May be will be better to put this inside a clean function?
> + raise DevtoolError(e)
> +
> + appendpath = os.path.join(config.workspace_path, 'appends')
> + if not os.path.exists(appendpath):
> + os.makedirs(appendpath)
> +
> + recipe_file_base = os.path.basename(os.path.splitext(recipe_file)[0])
> + appendfile = os.path.join(appendpath, '%s.bbappend' % recipe_file_base)
> + with open(appendfile, 'w') as f:
> + f.write('inherit externalsrc\n')
> + f.write('EXTERNALSRC = "%s"\n' % srctree)
> + f.write('EXTERNALSRC_BUILD = "%s"\n' % srctree)
> + f.write('\n# initial_rev: %s\n' % initial_rev_upgrade)
> +
> + standard._add_md5(config, args.recipename, appendfile)
> + logger.info('Source tree extracted to %s' % srctree)
> + return 0
> +
> +def register_commands(subparsers, context):
> + """Register devtool subcommands from this plugin"""
> + parser_upgrade = subparsers.add_parser('upgrade', help='Upgrade an existing recipe',
> + description='Upgrades an existing recipe',
> + formatter_class=argparse.ArgumentDefaultsHelpFormatter)
> + parser_upgrade.add_argument('recipename', help='Name for recipe to extract the source for')
> + parser_upgrade.add_argument('srctree', help='Path to where to extract the source tree')
> + parser_upgrade.add_argument('--version', '-V', help='Version to upgrade (PV)')
> + parser_upgrade.add_argument('--branch', '-b', default="devtool", help='Name for development branch to checkout')
> + parser_upgrade.add_argument('--keep-temp', action="store_true", help='Keep temporary directory (for debugging)')
> + parser_upgrade.add_argument('--no-patch', action="store_true", help='Do not patch the new source code')
> + parser_upgrade.set_defaults(func=upgrade)
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 2/2] oeqa/selftest: new tests for devtool upgrage feature
2015-08-26 7:43 ` [PATCH v2 2/2] oeqa/selftest: new tests for devtool upgrage feature leonardo.sandoval.gonzalez
@ 2015-08-26 16:15 ` Aníbal Limón
2015-08-26 16:29 ` Leonardo Sandoval
0 siblings, 1 reply; 13+ messages in thread
From: Aníbal Limón @ 2015-08-26 16:15 UTC (permalink / raw)
To: leonardo.sandoval.gonzalez, openembedded-core; +Cc: paul.eggleton
Comments below.
On 26/08/15 02:43, leonardo.sandoval.gonzalez@linux.intel.com wrote:
> From: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
>
> Basic tests for the devtool's upgrade feature, including:
> * Parameter check
> * Upgrading a real recipe (e2fsprogrs) without patching and
> checing its output
> * Devtool status after upgrade
>
> Signed-off-by: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
> ---
> meta/lib/oeqa/selftest/devtool.py | 36 ++++++++++++++++++++++++++++++++++++
> 1 file changed, 36 insertions(+)
>
> diff --git a/meta/lib/oeqa/selftest/devtool.py b/meta/lib/oeqa/selftest/devtool.py
> index b59db15..f72e010 100644
> --- a/meta/lib/oeqa/selftest/devtool.py
> +++ b/meta/lib/oeqa/selftest/devtool.py
> @@ -857,3 +857,39 @@ class DevtoolTests(DevtoolBase):
> result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, qemu.ip))
> result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True)
> self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have')
> +
> + def test_devtool_upgrade(self):
> + # Check preconditions
> + workspacedir = os.path.join(self.builddir, 'workspace')
> + self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
> + # Check parameters
> + result = runCmd('devtool upgrade -h')
> + for param in 'recipename srctree --version -V --branch -b --keep-temp --no-patch'.split():
> + self.assertIn(param, result.output)
> + # For the moment, we are using a real recipe.
> + recipe='e2fsprogs'
> + version='1.42.13'
Isn't a guarantee that this version will be newer ever (i.e. if someone
upgrade e2fsprogs to 1.42.13) what happen in this case?
> + tempdir = tempfile.mkdtemp(prefix='devtoolqa')
> + # Check that recipe is not already under devtool control
> + result = runCmd('devtool status')
> + self.assertNotIn(recipe, result.output)
> + # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
> + # we are downgrading instead of upgrading.
> + result = runCmd('devtool upgrade %s %s -V %s --no-patch' % (recipe, tempdir, version))
> + # Check if srctree at least is populated
> + self.assertTrue(len(os.listdir(tempdir)) > 0, 'scrtree (%s) should be populated with new (%s) source code' % (tempdir, version))
> + # Check new recipe folder is present
> + self.assertTrue(os.path.exists(os.path.join(workspacedir,'recipes',recipe)), 'Recipe folder should exist')
> + # Check new recipe file is present
> + self.assertTrue(os.path.exists(os.path.join(workspacedir,'recipes',recipe,"%s_%s.bb" % (recipe,version))), 'Recipe folder should exist')
> + # Check devtool status and make sure recipe is present
> + result = runCmd('devtool status')
> + self.assertIn(recipe, result.output)
> + self.assertIn(tempdir, result.output)
> + # Check devtool reset recipe
> + result = runCmd('devtool reset %s -n' % recipe)
> + result = runCmd('devtool status')
> + self.assertNotIn(recipe, result.output)
> + self.track_for_cleanup(tempdir)
> + self.track_for_cleanup(workspacedir)
> + self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/2] devtool: upgrade feature
2015-08-26 16:09 ` Aníbal Limón
@ 2015-08-26 16:23 ` Leonardo Sandoval
0 siblings, 0 replies; 13+ messages in thread
From: Leonardo Sandoval @ 2015-08-26 16:23 UTC (permalink / raw)
To: Aníbal Limón, openembedded-core; +Cc: paul.eggleton
On 08/26/2015 11:09 AM, Aníbal Limón wrote:
> Comments below,
>
> On 26/08/15 02:43, leonardo.sandoval.gonzalez@linux.intel.com wrote:
>> From: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
>>
>> Upgrades a recipe to a particular version and downloads the source code
>> into srctree. User can avoid patching the source code. These are the
>> general steps of the upgrade function:
>> * Extract current recipe source code into srctree and create branch
>> * Extract upgrade recipe source code into srctree and rebase with
>> previous branch. This step also creates a temporal recipe (created
>> using recipetool), containing the correct checksums.
>> * Creates the new recipe under the workspace
>>
>> [YOCTO #7642]
>>
>> Signed-off-by: Leonardo Sandoval
>> <leonardo.sandoval.gonzalez@linux.intel.com>
>> ---
>> scripts/lib/devtool/standard.py | 4 +-
>> scripts/lib/devtool/upgrade.py | 314
>> ++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 317 insertions(+), 1 deletion(-)
>> create mode 100644 scripts/lib/devtool/upgrade.py
>>
>> diff --git a/scripts/lib/devtool/standard.py
>> b/scripts/lib/devtool/standard.py
>> index ea21877..cd5a3ed 100644
>> --- a/scripts/lib/devtool/standard.py
>> +++ b/scripts/lib/devtool/standard.py
>> @@ -205,6 +205,8 @@ def extract(args, config, basepath, workspace):
>> srctree = os.path.abspath(args.srctree)
>> initial_rev = _extract_source(srctree, args.keep_temp,
>> args.branch, rd)
>> + logger.info('Source tree extracted to %s' % srctree)
>> +
>> if initial_rev:
>> return 0
>> else:
>> @@ -360,7 +362,6 @@ def _extract_source(srctree, keep_temp, devbranch,
>> d):
>> bb.process.run('git checkout patches', cwd=srcsubdir)
>> shutil.move(srcsubdir, srctree)
>> - logger.info('Source tree extracted to %s' % srctree)
> Why you move this logging outside?
the upgrade functions does two extractions, so I did not want two prints
indicating the same message.
>> finally:
>> bb.logger.setLevel(origlevel)
>> @@ -439,6 +440,7 @@ def modify(args, config, basepath, workspace):
>> initial_rev = _extract_source(args.srctree, False,
>> args.branch, rd)
>> if not initial_rev:
>> return 1
>> + logger.info('Source tree extracted to %s' % srctree)
>> # Get list of commits since this revision
>> (stdout, _) = bb.process.run('git rev-list --reverse
>> %s..HEAD' % initial_rev, cwd=args.srctree)
>> commits = stdout.split()
>> diff --git a/scripts/lib/devtool/upgrade.py
>> b/scripts/lib/devtool/upgrade.py
>> new file mode 100644
>> index 0000000..9bef984
>> --- /dev/null
>> +++ b/scripts/lib/devtool/upgrade.py
>> @@ -0,0 +1,314 @@
>> +# Development tool - upgrade command plugin
>> +#
>> +# Copyright (C) 2014-2015 Intel Corporation
>> +#
>> +# This program is free software; you can redistribute it and/or modify
>> +# it under the terms of the GNU General Public License version 2 as
>> +# published by the Free Software Foundation.
>> +#
>> +# This program is distributed in the hope that it will be useful,
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> +# GNU General Public License for more details.
>> +#
>> +# You should have received a copy of the GNU General Public License
>> along
>> +# with this program; if not, write to the Free Software Foundation,
>> Inc.,
>> +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
>> +#
>> +# DESCRIPTION
>> +# Created a new recipe under workspace/recipes/<recipename> and place
>> the
>> +# source code into <srctree>.
>> +# The upgrade feature executes the following steps:
>> +# * Extract current recipe source code into srctree and create
>> branch
>> +# * Extract upgrade recipe source code into srctree and rebase with
>> +# previous branch. This step also creates a temporal recipe
>> (created
>> +# using recipetool), containing the correct checksums.
>> +# * Creates the new recipe under the workspace
>> +"""Devtool upgrade plugin"""
>> +
>> +import os
>> +import sys
>> +import re
>> +import shutil
>> +import tempfile
>> +import logging
>> +import argparse
>> +import scriptutils
>> +import errno
>> +from devtool import standard
>> +from devtool import exec_build_env_command, setup_tinfoil, DevtoolError
>> +
>> +logger = logging.getLogger('devtool')
>> +
>> +def plugin_init(pluginlist):
>> + """Plugin initialization"""
>> + pass
>> +
>> +def _extract_upgrade_source(args, devbranch, config, basepath, d,
>> recipepostfix='tmp'):
>> + """Extract sources of a recipe with PV given on args.version
>> +
>> + On the target source tree folder, a new branch
>> (<devbranch>_<args.version>)
>> + and tag (<devbranch>-base_<args.version>) will be created. In
>> case patches
>> + are applied, another tag is created
>> (<devbranch>-patched_<args.version>).
>> +
>> + Returns: 1) The (git) initial revision ID
>> + 2) The full path of a temporal recipe containing the
>> correct checksums
>> + """
>> + import oe.recipeutils
>> +
>> + initial_rev = None
>> + srctree = os.path.abspath(args.srctree)
>> +
>> + pn = d.getVar('PN', True)
>> +
>> + standard._check_compatible_recipe(pn, d)
>> +
>> + recipepath = os.path.join(config.workspace_path, 'recipes', pn)
>> + bb.utils.mkdirhier(recipepath)
>> + recipefile = os.path.join(recipepath, "%s-%s.bb" %
>> (pn,recipepostfix))
>> + tmpsrcbasetree = tempfile.mkdtemp(prefix='devtool')
>> +
>> + # Change PV and get URL
>> + crd = d.createCopy()
>> + pv = d.getVar('PV', True)
>> + crd.setVar('PV', args.version)
>> + src_uri = crd.getVar('SRC_URI', True)
>> + if src_uri:
>> + url = src_uri.split()[0]
>> +
>> + # Generate recipe and fetch new source
>> + try:
>> + cmdopts = "-o %s -x %s -V %s" % (recipefile, tmpsrcbasetree,
>> args.version)
>> + cmd = 'recipetool create "%s" %s' % (url, cmdopts)
>> + stdout, _ = exec_build_env_command(config.init_path,
>> basepath, cmd)
>> + except bb.process.ExecutionError as e:
>> + raise DevtoolError('Command \'%s\' failed:\n%s' % (e.command,
>> e.stdout))
>> +
>> + tmpsrctree = os.path.join(tmpsrcbasetree, pn + '-' + args.version)
>> +
>> + try:
>> + # branch from devtool-base (original source code without
>> patches) before copying new source code
>> + bb.process.run('git checkout -b %s devtool-base' % devbranch,
>> cwd=srctree)
>> + bb.process.run('git tag -f devtool-base_%s' % args.version,
>> cwd=srctree)
>> +
>> + # Copy tmpsrctree into srctree
>> + src_files = standard._ls_tree(tmpsrctree)
>> + for path in src_files:
>> + tgt_dir = os.path.join(srctree, os.path.dirname(path))
>> + bb.utils.mkdirhier(tgt_dir)
>> + tgt_path = os.path.join(srctree, path)
>> + os.rename(os.path.join(tmpsrctree, path), tgt_path)
>> +
>> + # Track modified and untracked files
>> + (stdout,_) = bb.process.run('git ls-files --modified --others
>> --exclude-standard', cwd=srctree)
>> + add_files = stdout.splitlines()
>> + for add_file in add_files:
>> + bb.process.run('git add "%s"' % add_file, cwd=srctree)
>> +
>> + if len(add_files):
>> + bb.process.run('git commit -q -m "Initial commit from
>> upstream at version %s"' % args.version, cwd=srctree)
> I don't know if is good to commit changes without user intervention.
> This tool is an interactive one may be ask to the
> user?
I do not get your question, can you please elaborate a more?
Let me explain a bit more: Internally, code creates a git repository and
branches, so the user can start pushing commits from it. This does not
require user intervention; at least this is how is done with the rest of
the commands.
>> + (stdout, _) = bb.process.run('git rev-parse HEAD', cwd=srctree)
>> + initial_rev = stdout.rstrip()
>> +
>> + if args.no_patch:
>> + patches = oe.recipeutils.get_recipe_patches(crd)
>> + if len(patches):
>> + logger.warn('By user choice, the following (%s_%s)
>> patches will NOT be applied into' %(pn,pv))
>> + for patch in patches:
>> + logger.warn("\t%s" % os.path.basename(patch))
>> + else:
>> + try:
>> + bb.process.run('git rebase devtool-patched',
>> cwd=srctree)
>> + except bb.process.ExecutionError as e:
>> + # this is the only case where we do not propagate the
>> error
>> + # user will have the change to correct merges
>> + logger.error('Command \'%s\' failed:\n%s' %
>> (e.command, e.stdout))
>> + bb.process.run('git tag -f devtool-patched_%s' %
>> args.version, cwd=srctree)
>> + except bb.process.ExecutionError as e:
>> + raise DevtoolError('Command \'%s\' failed:\n%s' % (e.command,
>> e.stdout))
>> + finally:
>> + if args.keep_temp:
>> + logger.info('Preserving temporary directory %s' %
>> tmpsrcbasetree)
>> + else:
>> + shutil.rmtree(tmpsrcbasetree)
>> + return initial_rev, recipefile
>> +
>> +def _create_upgrade_recipe(args, config, recipetmp, config_data, d):
>> + """Creates the new recipe under workspace
>> +
>> + Returns the full path on the new created recipe
>> + """
>> + import oe.recipeutils
>> +
>> + def _get_checksums(recipefile):
>> + import re
>> + checksums = {}
>> + with open(recipefile) as rf:
>> + for line in rf:
>> + for cs in ['md5sum', 'sha256sum']:
>> + m = re.match("^SRC_URI\[%s\].*=.*\"(.*)\"" % cs,
>> line)
>> + if m:
>> + checksums[cs] = m.group(1)
>> + return checksums
>> +
>> + def _replace_checksums(recipefile, checksums):
>> + import re
>> + with open(recipefile + ".tmp", "w+") as tmprf:
>> + with open(recipefile) as rf:
>> + for line in rf:
>> + m = None
>> + for cs in ['md5sum', 'sha256sum']:
>> + m = re.match("^SRC_URI\[%s\].*=.*\"(.*)\"" %
>> cs, line)
>> + if m:
>> + if cs in checksums:
>> + oldcheck = m.group(1)
>> + newcheck = checksums[cs]
>> + line = line.replace(oldcheck, newcheck)
>> + break
>> + tmprf.write(line)
>> +
>> + os.rename(recipefile + ".tmp", recipefile)
>> +
>> + def _rename_patch_dirs(recipefolder, oldpv, newpv):
>> + for root, dirs, files in os.walk(recipefolder):
>> + for olddir in dirs:
>> + if olddir.find(oldpv) != -1:
>> + newdir = olddir.replace(oldpv, newpv)
>> + bb.process.run('mv %s %s' % (olddir, newdir))
>> +
>> + def _remove_patch_dirs(recipefolder):
>> + for root, dirs, files in os.walk(recipefolder):
>> + for d in dirs:
>> + shutil.rmtree(os.path.join(root,d))
>> +
>> + def _recipe_contains(recipefile, var):
>> + import re
>> + found = False
>> + with open(recipefile) as rf:
>> + for line in rf:
>> + if re.match("^%s.*=.*" % var, line):
>> + found = True
>> + break
>> + return found
>> +
>> + if not os.path.exists(recipetmp):
>> + raise DevtoolError("Temporal recipe %s does not exist" %
>> recipetmp)
>> +
>> + # Copy current recipe into workspace
>> + pn = d.getVar('PN', True)
>> + recipepath = os.path.join(config.workspace_path, 'recipes', pn)
>> + oe.recipeutils.copy_recipe_files(d, recipepath)
>> +
>> + # Rename recipe
>> + pv = d.getVar('PV', True)
>> + recipename = "%s_%s.bb" % (pn, pv)
>> + newrecipename = "%s_%s.bb" % (pn, args.version)
>> + if os.path.isfile(os.path.join(recipepath, recipename)):
>> + bb.process.run('mv %s %s' % (recipename, newrecipename),
>> cwd=recipepath)
>> + else:
>> + # Check if it is a git recipe
>> + recipename = newrecipename = "%s_git.bb" % pn
>> + if not os.path.isfile(os.path.join(recipepath, recipename)):
>> + raise DevtoolError("Original recipe not found on workspace")
>> +
>> + # Rename folders
>> + _rename_patch_dirs(recipepath, pv, args.version)
>> +
>> + # Update PV, just in case it is present
>> + if _recipe_contains(os.path.join(recipepath, newrecipename), 'PV'):
>> + oe.recipeutils.patch_recipe(d, os.path.join(recipepath,
>> newrecipename), {'PV':args.version})
>> +
>> + # Update checksums
>> + checksums = _get_checksums(os.path.join(recipepath, recipetmp))
>> + _replace_checksums(os.path.join(recipepath, newrecipename),
>> checksums)
>> +
>> + # Remove recipe created by the recipe-tool
>> + bb.process.run('rm %s' % recipetmp)
>> +
>> + return os.path.join(recipepath,newrecipename)
>> +
>> +def upgrade(args, config, basepath, workspace):
>> + """Entry point for the devtool 'upgrade' subcommand"""
>> + import bb
>> + import oe.recipeutils
>> +
>> + if args.recipename in workspace:
>> + raise DevtoolError("recipe %s is already in your workspace" %
>> + args.recipename)
>> + if not args.version:
>> + raise DevtoolError("Provide a version through the parameter
>> --version/-V")
>> +
>> + reason = oe.recipeutils.validate_pn(args.recipename)
>> + if reason:
>> + raise DevtoolError(reason)
>> +
>> + tinfoil = setup_tinfoil()
>> +
>> + rd = standard._parse_recipe(config, tinfoil, args.recipename, True)
>> + if not rd:
>> + return 1
>> +
>> + pn = rd.getVar('PV', True)
>> + if pn == args.version:
>> + raise DevtoolError("Current and upgrade versions are the same
>> %s" % pn)
>> +
>> + srctree = os.path.abspath(args.srctree)
>> +
>> + try:
>> + # Extract source from current recipe
>> + initial_rev_base = standard._extract_source(srctree, False,
>> args.branch, rd)
>> +
>> + # We need to shutdown tinfoil temporally because recipetool
>> will be used
>> + tinfoil.shutdown()
>> +
>> + # Extract new source code
>> + branch_upgrade = "%s_%s" % (args.branch, args.version)
>> + initial_rev_upgrade, recipe_file_tmp =
>> _extract_upgrade_source(args, branch_upgrade, config, basepath, rd)
>> + # Start again tinfoil
>> + tinfoil = setup_tinfoil()
>> +
>> + recipe_file = _create_upgrade_recipe(args, config,
>> recipe_file_tmp, tinfoil.config_data, rd)
>> +
>> + except DevtoolError as e:
>> + logger.error(e)
>> + # remove workspace recipe and srctree
>> + pn = rd.getVar('PN', True)
>> + recipespath = os.path.join(config.workspace_path, 'recipes')
>> + recipepath = os.path.join(recipespath, pn)
>> + if os.path.exists(recipepath):
>> + shutil.rmtree(recipepath)
>> + if not len(os.listdir(recipespath)):
>> + os.rmdir(recipespath)
>> + # remove srctree
>> + if os.path.exists(srctree):
>> + shutil.rmtree(srctree)
>
> May be will be better to put this inside a clean function?
>
>> + raise DevtoolError(e)
>> +
>> + appendpath = os.path.join(config.workspace_path, 'appends')
>> + if not os.path.exists(appendpath):
>> + os.makedirs(appendpath)
>> +
>> + recipe_file_base =
>> os.path.basename(os.path.splitext(recipe_file)[0])
>> + appendfile = os.path.join(appendpath, '%s.bbappend' %
>> recipe_file_base)
>> + with open(appendfile, 'w') as f:
>> + f.write('inherit externalsrc\n')
>> + f.write('EXTERNALSRC = "%s"\n' % srctree)
>> + f.write('EXTERNALSRC_BUILD = "%s"\n' % srctree)
>> + f.write('\n# initial_rev: %s\n' % initial_rev_upgrade)
>> +
>> + standard._add_md5(config, args.recipename, appendfile)
>> + logger.info('Source tree extracted to %s' % srctree)
>> + return 0
>> +
>> +def register_commands(subparsers, context):
>> + """Register devtool subcommands from this plugin"""
>> + parser_upgrade = subparsers.add_parser('upgrade', help='Upgrade
>> an existing recipe',
>> + description='Upgrades an
>> existing recipe',
>> +
>> formatter_class=argparse.ArgumentDefaultsHelpFormatter)
>> + parser_upgrade.add_argument('recipename', help='Name for recipe
>> to extract the source for')
>> + parser_upgrade.add_argument('srctree', help='Path to where to
>> extract the source tree')
>> + parser_upgrade.add_argument('--version', '-V', help='Version to
>> upgrade (PV)')
>> + parser_upgrade.add_argument('--branch', '-b', default="devtool",
>> help='Name for development branch to checkout')
>> + parser_upgrade.add_argument('--keep-temp', action="store_true",
>> help='Keep temporary directory (for debugging)')
>> + parser_upgrade.add_argument('--no-patch', action="store_true",
>> help='Do not patch the new source code')
>> + parser_upgrade.set_defaults(func=upgrade)
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 2/2] oeqa/selftest: new tests for devtool upgrage feature
2015-08-26 16:15 ` Aníbal Limón
@ 2015-08-26 16:29 ` Leonardo Sandoval
2015-08-26 16:47 ` Paul Eggleton
0 siblings, 1 reply; 13+ messages in thread
From: Leonardo Sandoval @ 2015-08-26 16:29 UTC (permalink / raw)
To: Aníbal Limón, openembedded-core; +Cc: paul.eggleton
On 08/26/2015 11:15 AM, Aníbal Limón wrote:
> Comments below.
>
> On 26/08/15 02:43, leonardo.sandoval.gonzalez@linux.intel.com wrote:
>> From: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
>>
>> Basic tests for the devtool's upgrade feature, including:
>> * Parameter check
>> * Upgrading a real recipe (e2fsprogrs) without patching and
>> checing its output
>> * Devtool status after upgrade
>>
>> Signed-off-by: Leonardo Sandoval
>> <leonardo.sandoval.gonzalez@linux.intel.com>
>> ---
>> meta/lib/oeqa/selftest/devtool.py | 36
>> ++++++++++++++++++++++++++++++++++++
>> 1 file changed, 36 insertions(+)
>>
>> diff --git a/meta/lib/oeqa/selftest/devtool.py
>> b/meta/lib/oeqa/selftest/devtool.py
>> index b59db15..f72e010 100644
>> --- a/meta/lib/oeqa/selftest/devtool.py
>> +++ b/meta/lib/oeqa/selftest/devtool.py
>> @@ -857,3 +857,39 @@ class DevtoolTests(DevtoolBase):
>> result = runCmd('devtool undeploy-target -c %s root@%s'
>> % (testrecipe, qemu.ip))
>> result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip,
>> testcommand), ignore_status=True)
>> self.assertNotEqual(result, 0, 'undeploy-target did not
>> remove command as it should have')
>> +
>> + def test_devtool_upgrade(self):
>> + # Check preconditions
>> + workspacedir = os.path.join(self.builddir, 'workspace')
>> + self.assertTrue(not os.path.exists(workspacedir), 'This test
>> cannot be run with a workspace directory under the build directory')
>> + # Check parameters
>> + result = runCmd('devtool upgrade -h')
>> + for param in 'recipename srctree --version -V --branch -b
>> --keep-temp --no-patch'.split():
>> + self.assertIn(param, result.output)
>> + # For the moment, we are using a real recipe.
>> + recipe='e2fsprogs'
>> + version='1.42.13'
>
> Isn't a guarantee that this version will be newer ever (i.e. if someone
> upgrade e2fsprogs to 1.42.13) what happen in this case?
Good question. In fact, the code will fail just in case current and
upgrade version are the same. The tool can also do downgrades, so at the
end it does not matter what the version is. I will correct this point
and send a v3.
As mentioned by Paul in a previous message, we need 'upgradable' recipes
which whenever the test is done, it can be upgrade. The only point here
is that we need a tarball somewhere, so the URL is always live. Not sure
if poky repository is a good place for the latter, any suggestion?
>> + tempdir = tempfile.mkdtemp(prefix='devtoolqa')
>> + # Check that recipe is not already under devtool control
>> + result = runCmd('devtool status')
>> + self.assertNotIn(recipe, result.output)
>> + # Check upgrade. Code does not check if new PV is older or
>> newer that current PV, so, it may be that
>> + # we are downgrading instead of upgrading.
>> + result = runCmd('devtool upgrade %s %s -V %s --no-patch' %
>> (recipe, tempdir, version))
>> + # Check if srctree at least is populated
>> + self.assertTrue(len(os.listdir(tempdir)) > 0, 'scrtree (%s)
>> should be populated with new (%s) source code' % (tempdir, version))
>> + # Check new recipe folder is present
>> +
>> self.assertTrue(os.path.exists(os.path.join(workspacedir,'recipes',recipe)),
>> 'Recipe folder should exist')
>> + # Check new recipe file is present
>> +
>> self.assertTrue(os.path.exists(os.path.join(workspacedir,'recipes',recipe,"%s_%s.bb"
>> % (recipe,version))), 'Recipe folder should exist')
>> + # Check devtool status and make sure recipe is present
>> + result = runCmd('devtool status')
>> + self.assertIn(recipe, result.output)
>> + self.assertIn(tempdir, result.output)
>> + # Check devtool reset recipe
>> + result = runCmd('devtool reset %s -n' % recipe)
>> + result = runCmd('devtool status')
>> + self.assertNotIn(recipe, result.output)
>> + self.track_for_cleanup(tempdir)
>> + self.track_for_cleanup(workspacedir)
>> + self.add_command_to_tearDown('bitbake-layers remove-layer
>> */workspace')
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 2/2] oeqa/selftest: new tests for devtool upgrage feature
2015-08-26 16:29 ` Leonardo Sandoval
@ 2015-08-26 16:47 ` Paul Eggleton
2015-08-26 16:51 ` Leonardo Sandoval
0 siblings, 1 reply; 13+ messages in thread
From: Paul Eggleton @ 2015-08-26 16:47 UTC (permalink / raw)
To: Leonardo Sandoval; +Cc: openembedded-core
On Wednesday 26 August 2015 11:29:37 Leonardo Sandoval wrote:
> On 08/26/2015 11:15 AM, Aníbal Limón wrote:
> > Comments below.
> >
> > On 26/08/15 02:43, leonardo.sandoval.gonzalez@linux.intel.com wrote:
> >> From: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
> >>
> >> Basic tests for the devtool's upgrade feature, including:
> >> * Parameter check
> >> * Upgrading a real recipe (e2fsprogrs) without patching and
> >>
> >> checing its output
> >>
> >> * Devtool status after upgrade
> >>
> >> Signed-off-by: Leonardo Sandoval
> >> <leonardo.sandoval.gonzalez@linux.intel.com>
> >> ---
> >>
> >> meta/lib/oeqa/selftest/devtool.py | 36
> >>
> >> ++++++++++++++++++++++++++++++++++++
> >>
> >> 1 file changed, 36 insertions(+)
> >>
> >> diff --git a/meta/lib/oeqa/selftest/devtool.py
> >> b/meta/lib/oeqa/selftest/devtool.py
> >> index b59db15..f72e010 100644
> >> --- a/meta/lib/oeqa/selftest/devtool.py
> >> +++ b/meta/lib/oeqa/selftest/devtool.py
> >>
> >> @@ -857,3 +857,39 @@ class DevtoolTests(DevtoolBase):
> >> result = runCmd('devtool undeploy-target -c %s root@%s'
> >>
> >> % (testrecipe, qemu.ip))
> >>
> >> result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip,
> >>
> >> testcommand), ignore_status=True)
> >>
> >> self.assertNotEqual(result, 0, 'undeploy-target did not
> >>
> >> remove command as it should have')
> >> +
> >> + def test_devtool_upgrade(self):
> >> + # Check preconditions
> >> + workspacedir = os.path.join(self.builddir, 'workspace')
> >> + self.assertTrue(not os.path.exists(workspacedir), 'This test
> >> cannot be run with a workspace directory under the build directory')
> >> + # Check parameters
> >> + result = runCmd('devtool upgrade -h')
> >> + for param in 'recipename srctree --version -V --branch -b
> >> --keep-temp --no-patch'.split():
> >> + self.assertIn(param, result.output)
> >> + # For the moment, we are using a real recipe.
> >> + recipe='e2fsprogs'
> >> + version='1.42.13'
> >
> > Isn't a guarantee that this version will be newer ever (i.e. if someone
> > upgrade e2fsprogs to 1.42.13) what happen in this case?
>
> Good question. In fact, the code will fail just in case current and
> upgrade version are the same. The tool can also do downgrades, so at the
> end it does not matter what the version is. I will correct this point
> and send a v3.
>
> As mentioned by Paul in a previous message, we need 'upgradable' recipes
> which whenever the test is done, it can be upgrade. The only point here
> is that we need a tarball somewhere, so the URL is always live. Not sure
> if poky repository is a good place for the latter, any suggestion?
This is what we have meta-selftest for. We can put a recipe (or multiple
recipes) in there that's named specially so as not to overlap with one in OE-
Core and is a known old version.
Cheers,
Paul
--
Paul Eggleton
Intel Open Source Technology Centre
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 2/2] oeqa/selftest: new tests for devtool upgrage feature
2015-08-26 16:47 ` Paul Eggleton
@ 2015-08-26 16:51 ` Leonardo Sandoval
0 siblings, 0 replies; 13+ messages in thread
From: Leonardo Sandoval @ 2015-08-26 16:51 UTC (permalink / raw)
To: Paul Eggleton; +Cc: openembedded-core
On 08/26/2015 11:47 AM, Paul Eggleton wrote:
> On Wednesday 26 August 2015 11:29:37 Leonardo Sandoval wrote:
>> On 08/26/2015 11:15 AM, Aníbal Limón wrote:
>>> Comments below.
>>>
>>> On 26/08/15 02:43, leonardo.sandoval.gonzalez@linux.intel.com wrote:
>>>> From: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
>>>>
>>>> Basic tests for the devtool's upgrade feature, including:
>>>> * Parameter check
>>>> * Upgrading a real recipe (e2fsprogrs) without patching and
>>>>
>>>> checing its output
>>>>
>>>> * Devtool status after upgrade
>>>>
>>>> Signed-off-by: Leonardo Sandoval
>>>> <leonardo.sandoval.gonzalez@linux.intel.com>
>>>> ---
>>>>
>>>> meta/lib/oeqa/selftest/devtool.py | 36
>>>>
>>>> ++++++++++++++++++++++++++++++++++++
>>>>
>>>> 1 file changed, 36 insertions(+)
>>>>
>>>> diff --git a/meta/lib/oeqa/selftest/devtool.py
>>>> b/meta/lib/oeqa/selftest/devtool.py
>>>> index b59db15..f72e010 100644
>>>> --- a/meta/lib/oeqa/selftest/devtool.py
>>>> +++ b/meta/lib/oeqa/selftest/devtool.py
>>>>
>>>> @@ -857,3 +857,39 @@ class DevtoolTests(DevtoolBase):
>>>> result = runCmd('devtool undeploy-target -c %s root@%s'
>>>>
>>>> % (testrecipe, qemu.ip))
>>>>
>>>> result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip,
>>>>
>>>> testcommand), ignore_status=True)
>>>>
>>>> self.assertNotEqual(result, 0, 'undeploy-target did not
>>>>
>>>> remove command as it should have')
>>>> +
>>>> + def test_devtool_upgrade(self):
>>>> + # Check preconditions
>>>> + workspacedir = os.path.join(self.builddir, 'workspace')
>>>> + self.assertTrue(not os.path.exists(workspacedir), 'This test
>>>> cannot be run with a workspace directory under the build directory')
>>>> + # Check parameters
>>>> + result = runCmd('devtool upgrade -h')
>>>> + for param in 'recipename srctree --version -V --branch -b
>>>> --keep-temp --no-patch'.split():
>>>> + self.assertIn(param, result.output)
>>>> + # For the moment, we are using a real recipe.
>>>> + recipe='e2fsprogs'
>>>> + version='1.42.13'
>>>
>>> Isn't a guarantee that this version will be newer ever (i.e. if someone
>>> upgrade e2fsprogs to 1.42.13) what happen in this case?
>>
>> Good question. In fact, the code will fail just in case current and
>> upgrade version are the same. The tool can also do downgrades, so at the
>> end it does not matter what the version is. I will correct this point
>> and send a v3.
>>
>> As mentioned by Paul in a previous message, we need 'upgradable' recipes
>> which whenever the test is done, it can be upgrade. The only point here
>> is that we need a tarball somewhere, so the URL is always live. Not sure
>> if poky repository is a good place for the latter, any suggestion?
>
> This is what we have meta-selftest for. We can put a recipe (or multiple
> recipes) in there that's named specially so as not to overlap with one in OE-
> Core and is a known old version.
Good. I will add a new recipe there and a tarball. Sending a V3 today.
>
> Cheers,
> Paul
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/2] devtool: upgrade feature
2015-08-26 7:43 ` [PATCH v2 1/2] " leonardo.sandoval.gonzalez
2015-08-26 16:09 ` Aníbal Limón
@ 2015-08-27 0:04 ` Paul Eggleton
2015-08-27 13:24 ` Leonardo Sandoval
1 sibling, 1 reply; 13+ messages in thread
From: Paul Eggleton @ 2015-08-27 0:04 UTC (permalink / raw)
To: leonardo.sandoval.gonzalez; +Cc: openembedded-core
On Wednesday 26 August 2015 07:43:23
leonardo.sandoval.gonzalez@linux.intel.com wrote:
> From: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
>
> Upgrades a recipe to a particular version and downloads the source code
> into srctree. User can avoid patching the source code. These are the
> general steps of the upgrade function:
> * Extract current recipe source code into srctree and create branch
> * Extract upgrade recipe source code into srctree and rebase with
> previous branch. This step also creates a temporal recipe (created
> using recipetool), containing the correct checksums.
> * Creates the new recipe under the workspace
OK, I haven't tested this yet but some comments just looking over the code
below.
> [YOCTO #7642]
>
> Signed-off-by: Leonardo Sandoval
> <leonardo.sandoval.gonzalez@linux.intel.com> ---
> scripts/lib/devtool/standard.py | 4 +-
> scripts/lib/devtool/upgrade.py | 314
> ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 317
> insertions(+), 1 deletion(-)
> create mode 100644 scripts/lib/devtool/upgrade.py
>
> diff --git a/scripts/lib/devtool/standard.py
> b/scripts/lib/devtool/standard.py index ea21877..cd5a3ed 100644
> --- a/scripts/lib/devtool/standard.py
> +++ b/scripts/lib/devtool/standard.py
> @@ -205,6 +205,8 @@ def extract(args, config, basepath, workspace):
>
> srctree = os.path.abspath(args.srctree)
> initial_rev = _extract_source(srctree, args.keep_temp, args.branch, rd)
> + logger.info('Source tree extracted to %s' % srctree)
> +
> if initial_rev:
> return 0
> else:
> @@ -360,7 +362,6 @@ def _extract_source(srctree, keep_temp, devbranch, d):
> bb.process.run('git checkout patches', cwd=srcsubdir)
>
> shutil.move(srcsubdir, srctree)
> - logger.info('Source tree extracted to %s' % srctree)
> finally:
> bb.logger.setLevel(origlevel)
>
> @@ -439,6 +440,7 @@ def modify(args, config, basepath, workspace):
> initial_rev = _extract_source(args.srctree, False, args.branch, rd)
> if not initial_rev:
> return 1
> + logger.info('Source tree extracted to %s' % srctree)
> # Get list of commits since this revision
> (stdout, _) = bb.process.run('git rev-list --reverse %s..HEAD' %
> initial_rev, cwd=args.srctree) commits = stdout.split()
> diff --git a/scripts/lib/devtool/upgrade.py b/scripts/lib/devtool/upgrade.py
> new file mode 100644
> index 0000000..9bef984
> --- /dev/null
> +++ b/scripts/lib/devtool/upgrade.py
> @@ -0,0 +1,314 @@
> +# Development tool - upgrade command plugin
> +#
> +# Copyright (C) 2014-2015 Intel Corporation
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License version 2 as
> +# published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License along
> +# with this program; if not, write to the Free Software Foundation, Inc.,
> +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> +#
> +# DESCRIPTION
> +# Created a new recipe under workspace/recipes/<recipename> and place the
> +# source code into <srctree>.
> +# The upgrade feature executes the following steps:
> +# * Extract current recipe source code into srctree and create branch
> +# * Extract upgrade recipe source code into srctree and rebase with
> +# previous branch. This step also creates a temporal recipe (created
> +# using recipetool), containing the correct checksums.
> +# * Creates the new recipe under the workspace
> +"""Devtool upgrade plugin"""
> +
> +import os
> +import sys
> +import re
> +import shutil
> +import tempfile
> +import logging
> +import argparse
> +import scriptutils
> +import errno
> +from devtool import standard
> +from devtool import exec_build_env_command, setup_tinfoil, DevtoolError
> +
> +logger = logging.getLogger('devtool')
> +
> +def plugin_init(pluginlist):
> + """Plugin initialization"""
> + pass
> +
> +def _extract_upgrade_source(args, devbranch, config, basepath, d,
> recipepostfix='tmp'): + """Extract sources of a recipe with PV given on
> args.version
> +
> + On the target source tree folder, a new branch
> (<devbranch>_<args.version>) + and tag (<devbranch>-base_<args.version>)
> will be created. In case patches + are applied, another tag is created
> (<devbranch>-patched_<args.version>). +
> + Returns: 1) The (git) initial revision ID
> + 2) The full path of a temporal recipe containing the correct
> checksums + """
> + import oe.recipeutils
> +
> + initial_rev = None
> + srctree = os.path.abspath(args.srctree)
> +
> + pn = d.getVar('PN', True)
> +
> + standard._check_compatible_recipe(pn, d)
> +
> + recipepath = os.path.join(config.workspace_path, 'recipes', pn)
> + bb.utils.mkdirhier(recipepath)
> + recipefile = os.path.join(recipepath, "%s-%s.bb" % (pn,recipepostfix))
> + tmpsrcbasetree = tempfile.mkdtemp(prefix='devtool')
> +
> + # Change PV and get URL
> + crd = d.createCopy()
> + pv = d.getVar('PV', True)
> + crd.setVar('PV', args.version)
> + src_uri = crd.getVar('SRC_URI', True)
> + if src_uri:
> + url = src_uri.split()[0]
> +
> + # Generate recipe and fetch new source
> + try:
> + cmdopts = "-o %s -x %s -V %s" % (recipefile, tmpsrcbasetree,
> args.version) + cmd = 'recipetool create "%s" %s' % (url, cmdopts)
> + stdout, _ = exec_build_env_command(config.init_path, basepath, cmd)
> + except bb.process.ExecutionError as e:
> + raise DevtoolError('Command \'%s\' failed:\n%s' % (e.command,
> e.stdout)) +
> + tmpsrctree = os.path.join(tmpsrcbasetree, pn + '-' + args.version)
> +
> + try:
> + # branch from devtool-base (original source code without patches)
> before copying new source code + bb.process.run('git checkout -b %s
> devtool-base' % devbranch, cwd=srctree) + bb.process.run('git tag -f
> devtool-base_%s' % args.version, cwd=srctree) +
> + # Copy tmpsrctree into srctree
> + src_files = standard._ls_tree(tmpsrctree)
> + for path in src_files:
> + tgt_dir = os.path.join(srctree, os.path.dirname(path))
> + bb.utils.mkdirhier(tgt_dir)
> + tgt_path = os.path.join(srctree, path)
> + os.rename(os.path.join(tmpsrctree, path), tgt_path)
> +
> + # Track modified and untracked files
> + (stdout,_) = bb.process.run('git ls-files --modified --others
> --exclude-standard', cwd=srctree) + add_files = stdout.splitlines()
> + for add_file in add_files:
> + bb.process.run('git add "%s"' % add_file, cwd=srctree)
> +
> + if len(add_files):
> + bb.process.run('git commit -q -m "Initial commit from upstream
> at version %s"' % args.version, cwd=srctree) + (stdout, _) =
> bb.process.run('git rev-parse HEAD', cwd=srctree) + initial_rev =
> stdout.rstrip()
> +
> + if args.no_patch:
> + patches = oe.recipeutils.get_recipe_patches(crd)
> + if len(patches):
> + logger.warn('By user choice, the following (%s_%s) patches
> will NOT be applied into' %(pn,pv)) + for patch in patches:
> + logger.warn("\t%s" % os.path.basename(patch))
> + else:
> + try:
> + bb.process.run('git rebase devtool-patched', cwd=srctree)
> + except bb.process.ExecutionError as e:
> + # this is the only case where we do not propagate the error
> + # user will have the change to correct merges
> + logger.error('Command \'%s\' failed:\n%s' % (e.command,
> e.stdout))
> + bb.process.run('git tag -f devtool-patched_%s' %
> args.version, cwd=srctree)
Could you use 'devtool-patched-%s' so we avoid mixing - and _ here ?
> + except bb.process.ExecutionError as e:
> + raise DevtoolError('Command \'%s\' failed:\n%s' % (e.command,
> e.stdout)) + finally:
> + if args.keep_temp:
> + logger.info('Preserving temporary directory %s' %
> tmpsrcbasetree) + else:
> + shutil.rmtree(tmpsrcbasetree)
> + return initial_rev, recipefile
> +
> +def _create_upgrade_recipe(args, config, recipetmp, config_data, d):
> + """Creates the new recipe under workspace
> +
> + Returns the full path on the new created recipe
> + """
> + import oe.recipeutils
> +
> + def _get_checksums(recipefile):
> + import re
> + checksums = {}
> + with open(recipefile) as rf:
> + for line in rf:
> + for cs in ['md5sum', 'sha256sum']:
> + m = re.match("^SRC_URI\[%s\].*=.*\"(.*)\"" % cs, line)
> + if m:
> + checksums[cs] = m.group(1)
> + return checksums
> +
> + def _replace_checksums(recipefile, checksums):
> + import re
> + with open(recipefile + ".tmp", "w+") as tmprf:
> + with open(recipefile) as rf:
> + for line in rf:
> + m = None
> + for cs in ['md5sum', 'sha256sum']:
> + m = re.match("^SRC_URI\[%s\].*=.*\"(.*)\"" % cs,
> line) + if m:
> + if cs in checksums:
> + oldcheck = m.group(1)
> + newcheck = checksums[cs]
> + line = line.replace(oldcheck, newcheck)
> + break
> + tmprf.write(line)
> +
> + os.rename(recipefile + ".tmp", recipefile)
We already have code for editing recipes like this in recipeutils (and in
lib/bb/utils as well) - in fact I see you're using patch_recipe() later on, so
why do it by hand here?
> + def _rename_patch_dirs(recipefolder, oldpv, newpv):
> + for root, dirs, files in os.walk(recipefolder):
> + for olddir in dirs:
> + if olddir.find(oldpv) != -1:
> + newdir = olddir.replace(oldpv, newpv)
> + bb.process.run('mv %s %s' % (olddir, newdir))
> +
> + def _remove_patch_dirs(recipefolder):
> + for root, dirs, files in os.walk(recipefolder):
> + for d in dirs:
> + shutil.rmtree(os.path.join(root,d))
> +
> + def _recipe_contains(recipefile, var):
> + import re
> + found = False
> + with open(recipefile) as rf:
> + for line in rf:
> + if re.match("^%s.*=.*" % var, line):
> + found = True
> + break
> + return found
> +
> + if not os.path.exists(recipetmp):
> + raise DevtoolError("Temporal recipe %s does not exist" % recipetmp)
> +
> + # Copy current recipe into workspace
> + pn = d.getVar('PN', True)
> + recipepath = os.path.join(config.workspace_path, 'recipes', pn)
> + oe.recipeutils.copy_recipe_files(d, recipepath)
> +
> + # Rename recipe
> + pv = d.getVar('PV', True)
> + recipename = "%s_%s.bb" % (pn, pv)
> + newrecipename = "%s_%s.bb" % (pn, args.version)
> + if os.path.isfile(os.path.join(recipepath, recipename)):
> + bb.process.run('mv %s %s' % (recipename, newrecipename),
> cwd=recipepath)
> + else:
> + # Check if it is a git recipe
> + recipename = newrecipename = "%s_git.bb" % pn
> + if not os.path.isfile(os.path.join(recipepath, recipename)):
> + raise DevtoolError("Original recipe not found on workspace")
> +
> + # Rename folders
> + _rename_patch_dirs(recipepath, pv, args.version)
> +
> + # Update PV, just in case it is present
> + if _recipe_contains(os.path.join(recipepath, newrecipename), 'PV'):
> + oe.recipeutils.patch_recipe(d, os.path.join(recipepath,
> newrecipename), {'PV':args.version}) +
> + # Update checksums
> + checksums = _get_checksums(os.path.join(recipepath, recipetmp))
> + _replace_checksums(os.path.join(recipepath, newrecipename), checksums)
> +
> + # Remove recipe created by the recipe-tool
> + bb.process.run('rm %s' % recipetmp)
> +
> + return os.path.join(recipepath,newrecipename)
> +
> +def upgrade(args, config, basepath, workspace):
> + """Entry point for the devtool 'upgrade' subcommand"""
> + import bb
> + import oe.recipeutils
> +
> + if args.recipename in workspace:
> + raise DevtoolError("recipe %s is already in your workspace" %
> + args.recipename)
> + if not args.version:
> + raise DevtoolError("Provide a version through the parameter
> --version/-V") +
> + reason = oe.recipeutils.validate_pn(args.recipename)
> + if reason:
> + raise DevtoolError(reason)
> +
> + tinfoil = setup_tinfoil()
> +
> + rd = standard._parse_recipe(config, tinfoil, args.recipename, True)
> + if not rd:
> + return 1
> +
> + pn = rd.getVar('PV', True)
> + if pn == args.version:
> + raise DevtoolError("Current and upgrade versions are the same %s" %
> pn) +
> + srctree = os.path.abspath(args.srctree)
> +
> + try:
> + # Extract source from current recipe
> + initial_rev_base = standard._extract_source(srctree, False,
> args.branch, rd) +
> + # We need to shutdown tinfoil temporally because recipetool will be
> used
> + tinfoil.shutdown()
> +
> + # Extract new source code
> + branch_upgrade = "%s_%s" % (args.branch, args.version)
> + initial_rev_upgrade, recipe_file_tmp =
> _extract_upgrade_source(args, branch_upgrade, config, basepath, rd) +
> # Start again tinfoil
> + tinfoil = setup_tinfoil()
> +
> + recipe_file = _create_upgrade_recipe(args, config, recipe_file_tmp,
> tinfoil.config_data, rd) +
> + except DevtoolError as e:
> + logger.error(e)
> + # remove workspace recipe and srctree
> + pn = rd.getVar('PN', True)
> + recipespath = os.path.join(config.workspace_path, 'recipes')
> + recipepath = os.path.join(recipespath, pn)
> + if os.path.exists(recipepath):
> + shutil.rmtree(recipepath)
> + if not len(os.listdir(recipespath)):
> + os.rmdir(recipespath)
> + # remove srctree
> + if os.path.exists(srctree):
> + shutil.rmtree(srctree)
> + raise DevtoolError(e)
> +
> + appendpath = os.path.join(config.workspace_path, 'appends')
> + if not os.path.exists(appendpath):
> + os.makedirs(appendpath)
> +
> + recipe_file_base = os.path.basename(os.path.splitext(recipe_file)[0])
> + appendfile = os.path.join(appendpath, '%s.bbappend' % recipe_file_base)
> + with open(appendfile, 'w') as f:
> + f.write('inherit externalsrc\n')
> + f.write('EXTERNALSRC = "%s"\n' % srctree)
> + f.write('EXTERNALSRC_BUILD = "%s"\n' % srctree)
Why is this unconditionally setting EXTERNALSRC_BUILD? Surely we should be
using the same logic as used elsewhere for this?
Cheers,
Paul
--
Paul Eggleton
Intel Open Source Technology Centre
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/2] devtool: upgrade feature
2015-08-27 0:04 ` Paul Eggleton
@ 2015-08-27 13:24 ` Leonardo Sandoval
2015-08-27 13:29 ` Otavio Salvador
0 siblings, 1 reply; 13+ messages in thread
From: Leonardo Sandoval @ 2015-08-27 13:24 UTC (permalink / raw)
To: Paul Eggleton; +Cc: openembedded-core
On 08/26/2015 07:04 PM, Paul Eggleton wrote:
> On Wednesday 26 August 2015 07:43:23
> leonardo.sandoval.gonzalez@linux.intel.com wrote:
>> From: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
>>
>> Upgrades a recipe to a particular version and downloads the source code
>> into srctree. User can avoid patching the source code. These are the
>> general steps of the upgrade function:
>> * Extract current recipe source code into srctree and create branch
>> * Extract upgrade recipe source code into srctree and rebase with
>> previous branch. This step also creates a temporal recipe (created
>> using recipetool), containing the correct checksums.
>> * Creates the new recipe under the workspace
>
> OK, I haven't tested this yet but some comments just looking over the code
> below.
Ok, let me know if you find something when testing.
>
>
>> [YOCTO #7642]
>>
>> Signed-off-by: Leonardo Sandoval
>> <leonardo.sandoval.gonzalez@linux.intel.com> ---
>> scripts/lib/devtool/standard.py | 4 +-
>> scripts/lib/devtool/upgrade.py | 314
>> ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 317
>> insertions(+), 1 deletion(-)
>> create mode 100644 scripts/lib/devtool/upgrade.py
>>
>> diff --git a/scripts/lib/devtool/standard.py
>> b/scripts/lib/devtool/standard.py index ea21877..cd5a3ed 100644
>> --- a/scripts/lib/devtool/standard.py
>> +++ b/scripts/lib/devtool/standard.py
>> @@ -205,6 +205,8 @@ def extract(args, config, basepath, workspace):
>>
>> srctree = os.path.abspath(args.srctree)
>> initial_rev = _extract_source(srctree, args.keep_temp, args.branch, rd)
>> + logger.info('Source tree extracted to %s' % srctree)
>> +
>> if initial_rev:
>> return 0
>> else:
>> @@ -360,7 +362,6 @@ def _extract_source(srctree, keep_temp, devbranch, d):
>> bb.process.run('git checkout patches', cwd=srcsubdir)
>>
>> shutil.move(srcsubdir, srctree)
>> - logger.info('Source tree extracted to %s' % srctree)
>> finally:
>> bb.logger.setLevel(origlevel)
>>
>> @@ -439,6 +440,7 @@ def modify(args, config, basepath, workspace):
>> initial_rev = _extract_source(args.srctree, False, args.branch, rd)
>> if not initial_rev:
>> return 1
>> + logger.info('Source tree extracted to %s' % srctree)
>> # Get list of commits since this revision
>> (stdout, _) = bb.process.run('git rev-list --reverse %s..HEAD' %
>> initial_rev, cwd=args.srctree) commits = stdout.split()
>> diff --git a/scripts/lib/devtool/upgrade.py b/scripts/lib/devtool/upgrade.py
>> new file mode 100644
>> index 0000000..9bef984
>> --- /dev/null
>> +++ b/scripts/lib/devtool/upgrade.py
>> @@ -0,0 +1,314 @@
>> +# Development tool - upgrade command plugin
>> +#
>> +# Copyright (C) 2014-2015 Intel Corporation
>> +#
>> +# This program is free software; you can redistribute it and/or modify
>> +# it under the terms of the GNU General Public License version 2 as
>> +# published by the Free Software Foundation.
>> +#
>> +# This program is distributed in the hope that it will be useful,
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> +# GNU General Public License for more details.
>> +#
>> +# You should have received a copy of the GNU General Public License along
>> +# with this program; if not, write to the Free Software Foundation, Inc.,
>> +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
>> +#
>> +# DESCRIPTION
>> +# Created a new recipe under workspace/recipes/<recipename> and place the
>> +# source code into <srctree>.
>> +# The upgrade feature executes the following steps:
>> +# * Extract current recipe source code into srctree and create branch
>> +# * Extract upgrade recipe source code into srctree and rebase with
>> +# previous branch. This step also creates a temporal recipe (created
>> +# using recipetool), containing the correct checksums.
>> +# * Creates the new recipe under the workspace
>> +"""Devtool upgrade plugin"""
>> +
>> +import os
>> +import sys
>> +import re
>> +import shutil
>> +import tempfile
>> +import logging
>> +import argparse
>> +import scriptutils
>> +import errno
>> +from devtool import standard
>> +from devtool import exec_build_env_command, setup_tinfoil, DevtoolError
>> +
>> +logger = logging.getLogger('devtool')
>> +
>> +def plugin_init(pluginlist):
>> + """Plugin initialization"""
>> + pass
>> +
>> +def _extract_upgrade_source(args, devbranch, config, basepath, d,
>> recipepostfix='tmp'): + """Extract sources of a recipe with PV given on
>> args.version
>> +
>> + On the target source tree folder, a new branch
>> (<devbranch>_<args.version>) + and tag (<devbranch>-base_<args.version>)
>> will be created. In case patches + are applied, another tag is created
>> (<devbranch>-patched_<args.version>). +
>> + Returns: 1) The (git) initial revision ID
>> + 2) The full path of a temporal recipe containing the correct
>> checksums + """
>> + import oe.recipeutils
>> +
>> + initial_rev = None
>> + srctree = os.path.abspath(args.srctree)
>> +
>> + pn = d.getVar('PN', True)
>> +
>> + standard._check_compatible_recipe(pn, d)
>> +
>> + recipepath = os.path.join(config.workspace_path, 'recipes', pn)
>> + bb.utils.mkdirhier(recipepath)
>> + recipefile = os.path.join(recipepath, "%s-%s.bb" % (pn,recipepostfix))
>> + tmpsrcbasetree = tempfile.mkdtemp(prefix='devtool')
>> +
>> + # Change PV and get URL
>> + crd = d.createCopy()
>> + pv = d.getVar('PV', True)
>> + crd.setVar('PV', args.version)
>> + src_uri = crd.getVar('SRC_URI', True)
>> + if src_uri:
>> + url = src_uri.split()[0]
>> +
>> + # Generate recipe and fetch new source
>> + try:
>> + cmdopts = "-o %s -x %s -V %s" % (recipefile, tmpsrcbasetree,
>> args.version) + cmd = 'recipetool create "%s" %s' % (url, cmdopts)
>> + stdout, _ = exec_build_env_command(config.init_path, basepath, cmd)
>> + except bb.process.ExecutionError as e:
>> + raise DevtoolError('Command \'%s\' failed:\n%s' % (e.command,
>> e.stdout)) +
>> + tmpsrctree = os.path.join(tmpsrcbasetree, pn + '-' + args.version)
>> +
>> + try:
>> + # branch from devtool-base (original source code without patches)
>> before copying new source code + bb.process.run('git checkout -b %s
>> devtool-base' % devbranch, cwd=srctree) + bb.process.run('git tag -f
>> devtool-base_%s' % args.version, cwd=srctree) +
>> + # Copy tmpsrctree into srctree
>> + src_files = standard._ls_tree(tmpsrctree)
>> + for path in src_files:
>> + tgt_dir = os.path.join(srctree, os.path.dirname(path))
>> + bb.utils.mkdirhier(tgt_dir)
>> + tgt_path = os.path.join(srctree, path)
>> + os.rename(os.path.join(tmpsrctree, path), tgt_path)
>> +
>> + # Track modified and untracked files
>> + (stdout,_) = bb.process.run('git ls-files --modified --others
>> --exclude-standard', cwd=srctree) + add_files = stdout.splitlines()
>> + for add_file in add_files:
>> + bb.process.run('git add "%s"' % add_file, cwd=srctree)
>> +
>> + if len(add_files):
>> + bb.process.run('git commit -q -m "Initial commit from upstream
>> at version %s"' % args.version, cwd=srctree) + (stdout, _) =
>> bb.process.run('git rev-parse HEAD', cwd=srctree) + initial_rev =
>> stdout.rstrip()
>> +
>> + if args.no_patch:
>> + patches = oe.recipeutils.get_recipe_patches(crd)
>> + if len(patches):
>> + logger.warn('By user choice, the following (%s_%s) patches
>> will NOT be applied into' %(pn,pv)) + for patch in patches:
>> + logger.warn("\t%s" % os.path.basename(patch))
>> + else:
>> + try:
>> + bb.process.run('git rebase devtool-patched', cwd=srctree)
>> + except bb.process.ExecutionError as e:
>> + # this is the only case where we do not propagate the error
>> + # user will have the change to correct merges
>> + logger.error('Command \'%s\' failed:\n%s' % (e.command,
>> e.stdout))
>> + bb.process.run('git tag -f devtool-patched_%s' %
>> args.version, cwd=srctree)
>
> Could you use 'devtool-patched-%s' so we avoid mixing - and _ here ?
>
Sounds good. The original intention was to have a similar syntax as
recipes, where PN and PV is divided by '_'.
>
>> + except bb.process.ExecutionError as e:
>> + raise DevtoolError('Command \'%s\' failed:\n%s' % (e.command,
>> e.stdout)) + finally:
>> + if args.keep_temp:
>> + logger.info('Preserving temporary directory %s' %
>> tmpsrcbasetree) + else:
>> + shutil.rmtree(tmpsrcbasetree)
>> + return initial_rev, recipefile
>> +
>> +def _create_upgrade_recipe(args, config, recipetmp, config_data, d):
>> + """Creates the new recipe under workspace
>> +
>> + Returns the full path on the new created recipe
>> + """
>> + import oe.recipeutils
>> +
>> + def _get_checksums(recipefile):
>> + import re
>> + checksums = {}
>> + with open(recipefile) as rf:
>> + for line in rf:
>> + for cs in ['md5sum', 'sha256sum']:
>> + m = re.match("^SRC_URI\[%s\].*=.*\"(.*)\"" % cs, line)
>> + if m:
>> + checksums[cs] = m.group(1)
>> + return checksums
>> +
>> + def _replace_checksums(recipefile, checksums):
>> + import re
>> + with open(recipefile + ".tmp", "w+") as tmprf:
>> + with open(recipefile) as rf:
>> + for line in rf:
>> + m = None
>> + for cs in ['md5sum', 'sha256sum']:
>> + m = re.match("^SRC_URI\[%s\].*=.*\"(.*)\"" % cs,
>> line) + if m:
>> + if cs in checksums:
>> + oldcheck = m.group(1)
>> + newcheck = checksums[cs]
>> + line = line.replace(oldcheck, newcheck)
>> + break
>> + tmprf.write(line)
>> +
>> + os.rename(recipefile + ".tmp", recipefile)
>
> We already have code for editing recipes like this in recipeutils (and in
> lib/bb/utils as well) - in fact I see you're using patch_recipe() later on, so
> why do it by hand here?
>
can 'oe.recipeutils.patch_recipe' patch variable's flags? I did not try
but looking at the function definition, seems like it does not.
>> + def _rename_patch_dirs(recipefolder, oldpv, newpv):
>> + for root, dirs, files in os.walk(recipefolder):
>> + for olddir in dirs:
>> + if olddir.find(oldpv) != -1:
>> + newdir = olddir.replace(oldpv, newpv)
>> + bb.process.run('mv %s %s' % (olddir, newdir))
>> +
>> + def _remove_patch_dirs(recipefolder):
>> + for root, dirs, files in os.walk(recipefolder):
>> + for d in dirs:
>> + shutil.rmtree(os.path.join(root,d))
>> +
>> + def _recipe_contains(recipefile, var):
>> + import re
>> + found = False
>> + with open(recipefile) as rf:
>> + for line in rf:
>> + if re.match("^%s.*=.*" % var, line):
>> + found = True
>> + break
>> + return found
>> +
>> + if not os.path.exists(recipetmp):
>> + raise DevtoolError("Temporal recipe %s does not exist" % recipetmp)
>> +
>> + # Copy current recipe into workspace
>> + pn = d.getVar('PN', True)
>> + recipepath = os.path.join(config.workspace_path, 'recipes', pn)
>> + oe.recipeutils.copy_recipe_files(d, recipepath)
>> +
>> + # Rename recipe
>> + pv = d.getVar('PV', True)
>> + recipename = "%s_%s.bb" % (pn, pv)
>> + newrecipename = "%s_%s.bb" % (pn, args.version)
>> + if os.path.isfile(os.path.join(recipepath, recipename)):
>> + bb.process.run('mv %s %s' % (recipename, newrecipename),
>> cwd=recipepath)
>> + else:
>> + # Check if it is a git recipe
>> + recipename = newrecipename = "%s_git.bb" % pn
>> + if not os.path.isfile(os.path.join(recipepath, recipename)):
>> + raise DevtoolError("Original recipe not found on workspace")
>> +
>> + # Rename folders
>> + _rename_patch_dirs(recipepath, pv, args.version)
>> +
>> + # Update PV, just in case it is present
>> + if _recipe_contains(os.path.join(recipepath, newrecipename), 'PV'):
>> + oe.recipeutils.patch_recipe(d, os.path.join(recipepath,
>> newrecipename), {'PV':args.version}) +
>> + # Update checksums
>> + checksums = _get_checksums(os.path.join(recipepath, recipetmp))
>> + _replace_checksums(os.path.join(recipepath, newrecipename), checksums)
>> +
>> + # Remove recipe created by the recipe-tool
>> + bb.process.run('rm %s' % recipetmp)
>> +
>> + return os.path.join(recipepath,newrecipename)
>> +
>> +def upgrade(args, config, basepath, workspace):
>> + """Entry point for the devtool 'upgrade' subcommand"""
>> + import bb
>> + import oe.recipeutils
>> +
>> + if args.recipename in workspace:
>> + raise DevtoolError("recipe %s is already in your workspace" %
>> + args.recipename)
>> + if not args.version:
>> + raise DevtoolError("Provide a version through the parameter
>> --version/-V") +
>> + reason = oe.recipeutils.validate_pn(args.recipename)
>> + if reason:
>> + raise DevtoolError(reason)
>> +
>> + tinfoil = setup_tinfoil()
>> +
>> + rd = standard._parse_recipe(config, tinfoil, args.recipename, True)
>> + if not rd:
>> + return 1
>> +
>> + pn = rd.getVar('PV', True)
>> + if pn == args.version:
>> + raise DevtoolError("Current and upgrade versions are the same %s" %
>> pn) +
>> + srctree = os.path.abspath(args.srctree)
>> +
>> + try:
>> + # Extract source from current recipe
>> + initial_rev_base = standard._extract_source(srctree, False,
>> args.branch, rd) +
>> + # We need to shutdown tinfoil temporally because recipetool will be
>> used
>> + tinfoil.shutdown()
>> +
>> + # Extract new source code
>> + branch_upgrade = "%s_%s" % (args.branch, args.version)
>> + initial_rev_upgrade, recipe_file_tmp =
>> _extract_upgrade_source(args, branch_upgrade, config, basepath, rd) +
>> # Start again tinfoil
>> + tinfoil = setup_tinfoil()
>> +
>> + recipe_file = _create_upgrade_recipe(args, config, recipe_file_tmp,
>> tinfoil.config_data, rd) +
>> + except DevtoolError as e:
>> + logger.error(e)
>> + # remove workspace recipe and srctree
>> + pn = rd.getVar('PN', True)
>> + recipespath = os.path.join(config.workspace_path, 'recipes')
>> + recipepath = os.path.join(recipespath, pn)
>> + if os.path.exists(recipepath):
>> + shutil.rmtree(recipepath)
>> + if not len(os.listdir(recipespath)):
>> + os.rmdir(recipespath)
>> + # remove srctree
>> + if os.path.exists(srctree):
>> + shutil.rmtree(srctree)
>> + raise DevtoolError(e)
>> +
>> + appendpath = os.path.join(config.workspace_path, 'appends')
>> + if not os.path.exists(appendpath):
>> + os.makedirs(appendpath)
>> +
>> + recipe_file_base = os.path.basename(os.path.splitext(recipe_file)[0])
>> + appendfile = os.path.join(appendpath, '%s.bbappend' % recipe_file_base)
>> + with open(appendfile, 'w') as f:
>> + f.write('inherit externalsrc\n')
>> + f.write('EXTERNALSRC = "%s"\n' % srctree)
>> + f.write('EXTERNALSRC_BUILD = "%s"\n' % srctree)
>
> Why is this unconditionally setting EXTERNALSRC_BUILD? Surely we should be
> using the same logic as used elsewhere for this?
Good point. I will add the required parameter ('--same-dir') and write
EXTERNALSRC_BUILD if present.
>
> Cheers,
> Paul
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/2] devtool: upgrade feature
2015-08-27 13:24 ` Leonardo Sandoval
@ 2015-08-27 13:29 ` Otavio Salvador
2015-08-27 20:08 ` Leonardo Sandoval
0 siblings, 1 reply; 13+ messages in thread
From: Otavio Salvador @ 2015-08-27 13:29 UTC (permalink / raw)
To: Leonardo Sandoval
Cc: Paul Eggleton, Patches and discussions about the oe-core layer
On Thu, Aug 27, 2015 at 10:24 AM, Leonardo Sandoval
<leonardo.sandoval.gonzalez@linux.intel.com> wrote:
> On 08/26/2015 07:04 PM, Paul Eggleton wrote:
>> We already have code for editing recipes like this in recipeutils (and in
>> lib/bb/utils as well) - in fact I see you're using patch_recipe() later
>> on, so
>> why do it by hand here?
>>
>
> can 'oe.recipeutils.patch_recipe' patch variable's flags? I did not try but
> looking at the function definition, seems like it does not.
So add this feature there if you need it.
--
Otavio Salvador O.S. Systems
http://www.ossystems.com.br http://code.ossystems.com.br
Mobile: +55 (53) 9981-7854 Mobile: +1 (347) 903-9750
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/2] devtool: upgrade feature
2015-08-27 13:29 ` Otavio Salvador
@ 2015-08-27 20:08 ` Leonardo Sandoval
0 siblings, 0 replies; 13+ messages in thread
From: Leonardo Sandoval @ 2015-08-27 20:08 UTC (permalink / raw)
To: Otavio Salvador
Cc: Paul Eggleton, Patches and discussions about the oe-core layer
On 08/27/2015 08:29 AM, Otavio Salvador wrote:
> On Thu, Aug 27, 2015 at 10:24 AM, Leonardo Sandoval
> <leonardo.sandoval.gonzalez@linux.intel.com> wrote:
>> On 08/26/2015 07:04 PM, Paul Eggleton wrote:
>>> We already have code for editing recipes like this in recipeutils (and in
>>> lib/bb/utils as well) - in fact I see you're using patch_recipe() later
>>> on, so
>>> why do it by hand here?
>>>
>>
>> can 'oe.recipeutils.patch_recipe' patch variable's flags? I did not try but
>> looking at the function definition, seems like it does not.
>
> So add this feature there if you need it.
Hi Otavio!
you are completely right. I gave it some hours today without much luck.
What I have right now is that it appends the new checksums at the end of
the recipe instead of replacing them. This works fine because the last
assignment is the one remaining after the parsing stage, but not really
elegant to have duplicate values in a single recipe.
Paul,
For this particular feature, I believe we should focus on bitbake's
edit_metadata function instead of recipeutils's patch_recipe. In
general, we should refactor many things on the devtool code, now that
many stuff is just in a single file (standard.py). This seems to me like
work for me targeting 2.1 release.
>
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2015-08-27 20:07 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-08-26 7:43 [PATCH v2 0/1] devtool: upgrade feature leonardo.sandoval.gonzalez
2015-08-26 7:43 ` [PATCH v2 1/2] " leonardo.sandoval.gonzalez
2015-08-26 16:09 ` Aníbal Limón
2015-08-26 16:23 ` Leonardo Sandoval
2015-08-27 0:04 ` Paul Eggleton
2015-08-27 13:24 ` Leonardo Sandoval
2015-08-27 13:29 ` Otavio Salvador
2015-08-27 20:08 ` Leonardo Sandoval
2015-08-26 7:43 ` [PATCH v2 2/2] oeqa/selftest: new tests for devtool upgrage feature leonardo.sandoval.gonzalez
2015-08-26 16:15 ` Aníbal Limón
2015-08-26 16:29 ` Leonardo Sandoval
2015-08-26 16:47 ` Paul Eggleton
2015-08-26 16:51 ` Leonardo Sandoval
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox