Openembedded Core Discussions
 help / color / mirror / Atom feed
* [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