Skip to contentSkip to conversation

devtool: upgrade feature

PATCH Superseded [email protected] 4 participants 13 messages Aug 26, 2015 – Aug 27, 2015

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 cover devtool: upgrade feature

From: Leonardo Sandoval <[email protected]>

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
Signature · 1 line
1.8.4.5
#1 lore raw

PATCH v2 1/2 devtool: upgrade feature

From: Leonardo Sandoval <[email protected]>

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]

---
 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
scripts/lib/devtool/standard.py +3 -1
index ea21877..cd5a3ed 100644
@@ -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()
scripts/lib/devtool/upgrade.py new file +314 -0

314 lines not on this page · raw message

Signature · 1 line
1.8.4.5
#2 lore raw

Comments below,

Quoted 34 lines · On 26/08/15 02:43, [email protected] wrote:

From: Leonardo Sandoval <[email protected]>

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]

---
  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

scripts/lib/devtool/standard.py +2 -0
index ea21877..cd5a3ed 100644
@@ -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:

scripts/lib/devtool/standard.py def _extract_source(srctree, keep_temp, devbranch, d): · line 363

Quoted 4 lines
scripts/lib/devtool/standard.py +0 -1
@@ -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?

Quoted 116 lines
scripts/lib/devtool/standard.py +1 -0
      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()
scripts/lib/devtool/upgrade.py new file +99 -0
new file mode 100644
index 0000000..9bef984
@@ -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)

scripts/lib/devtool/upgrade.py line 109

Quoted 10 lines
scripts/lib/devtool/upgrade.py new file +10 -0
+            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?

Quoted 165 lines
scripts/lib/devtool/upgrade.py new file +165 -0
+        (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

scripts/lib/devtool/upgrade.py line 284

Quoted 10 lines
scripts/lib/devtool/upgrade.py new file +10 -0
+        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?

Quoted 30 lines
scripts/lib/devtool/upgrade.py new file +30 -0
+        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)
#3 lore raw
Quoted 37 lines · Aníbal Limón

Comments below,

Quoted 34 lines · On 26/08/15 02:43, [email protected] wrote:

From: Leonardo Sandoval <[email protected]>

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
<[email protected]>

---
  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

scripts/lib/devtool/standard.py +2 -0
index ea21877..cd5a3ed 100644
@@ -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:
Quoted 8 lines
Quoted 7 lines
scripts/lib/devtool/standard.py +0 -0
          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.

Quoted 142 lines
Quoted 142 lines

finally:
bb.logger.setLevel(origlevel)

scripts/lib/devtool/standard.py +1 -0
@@ -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()

scripts/lib/devtool/upgrade.py new file +14 -0
new file mode 100644
index 0000000..9bef984
@@ -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.,

scripts/lib/devtool/upgrade.py new file +4 -0
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# DESCRIPTION
+# Created a new recipe under workspace/recipes/<recipename> and place

the

scripts/lib/devtool/upgrade.py new file +3 -0
+# source code into <srctree>.
+# The upgrade feature executes the following steps:
+#     * Extract current recipe source code into srctree and create

branch

scripts/lib/devtool/upgrade.py new file +2 -0
+#     * Extract upgrade recipe source code into srctree and rebase with
+#       previous branch. This step also creates a temporal recipe

(created

scripts/lib/devtool/upgrade.py new file +23 -0
+#       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'):

scripts/lib/devtool/upgrade.py new file +3 -0
+    """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>).

scripts/lib/devtool/upgrade.py new file +3 -0
+
+    Returns: 1) The (git) initial revision ID
+             2) The full path of a temporal recipe containing the

correct checksums

scripts/lib/devtool/upgrade.py new file +13 -0
+    """
+    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))

scripts/lib/devtool/upgrade.py new file +13 -0
+    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)

scripts/lib/devtool/upgrade.py new file +2 -0
+        cmd = 'recipetool create "%s" %s' % (url, cmdopts)
+        stdout, _ = exec_build_env_command(config.init_path,

basepath, cmd)

scripts/lib/devtool/upgrade.py new file +2 -0
+    except bb.process.ExecutionError as e:
+        raise DevtoolError('Command \'%s\' failed:\n%s' % (e.command,

e.stdout))

scripts/lib/devtool/upgrade.py new file +5 -0
+
+    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)

scripts/lib/devtool/upgrade.py new file +13 -1
+
+        # 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:
Quoted 8 lines
Quoted 5 lines
scripts/lib/devtool/upgrade.py new file +4 -0
+            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.

Quoted 239 lines
Quoted 184 lines
scripts/lib/devtool/upgrade.py new file +7 -0
+        (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))

scripts/lib/devtool/upgrade.py new file +5 -0
+                for patch in patches:
+                    logger.warn("\t%s" % os.path.basename(patch))
+        else:
+            try:
+                bb.process.run('git rebase devtool-patched',

cwd=srctree)

scripts/lib/devtool/upgrade.py new file +2 -0
+            except bb.process.ExecutionError as e:
+                # this is the only case where we do not propagate the

error

scripts/lib/devtool/upgrade.py new file +2 -0
+                # 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)

scripts/lib/devtool/upgrade.py new file +2 -0
+    except bb.process.ExecutionError as e:
+        raise DevtoolError('Command \'%s\' failed:\n%s' % (e.command,

e.stdout))

scripts/lib/devtool/upgrade.py new file +3 -0
+    finally:
+        if args.keep_temp:
+            logger.info('Preserving temporary directory %s' %

tmpsrcbasetree)

scripts/lib/devtool/upgrade.py new file +18 -0
+        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)

scripts/lib/devtool/upgrade.py new file +12 -0
+                    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)

scripts/lib/devtool/upgrade.py new file +34 -0
+                        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)

scripts/lib/devtool/upgrade.py new file +12 -0
+
+    # 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)

scripts/lib/devtool/upgrade.py new file +12 -0
+    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})

scripts/lib/devtool/upgrade.py new file +4 -0
+
+    # Update checksums
+    checksums = _get_checksums(os.path.join(recipepath, recipetmp))
+    _replace_checksums(os.path.join(recipepath, newrecipename),

checksums)

scripts/lib/devtool/upgrade.py new file +30 -1
+
+    # 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)

scripts/lib/devtool/upgrade.py new file +6 -0
+
+    srctree = os.path.abspath(args.srctree)
+
+    try:
+        # Extract source from current recipe
+        initial_rev_base = standard._extract_source(srctree, False,

args.branch, rd)

scripts/lib/devtool/upgrade.py new file +2 -0
+
+        # We need to shutdown tinfoil temporally because recipetool

will be used

scripts/lib/devtool/upgrade.py new file +5 -0
+        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)

scripts/lib/devtool/upgrade.py new file +4 -0
+        # Start again tinfoil
+        tinfoil = setup_tinfoil()
+
+        recipe_file = _create_upgrade_recipe(args, config,

recipe_file_tmp, tinfoil.config_data, rd)

scripts/lib/devtool/upgrade.py new file +4 -0
+
+    except DevtoolError as e:
+        logger.error(e)
+        # remove workspace recipe and srctree

scripts/lib/devtool/upgrade.py line 284

Quoted 10 lines
scripts/lib/devtool/upgrade.py new file +10 -0
+        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?

Quoted 41 lines
scripts/lib/devtool/upgrade.py new file +7 -0
+        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)

scripts/lib/devtool/upgrade.py new file +13 -0
+    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)

#4 lore raw
In reply to [email protected]
Quoted 10 lines · On Wednesday 26 August 2015 07:43:23 [email protected]…

From: Leonardo Sandoval <[email protected]>

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.

Quoted 172 lines

[YOCTO #7642]

Signed-off-by: Leonardo Sandoval
<[email protected]> ---

 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
(quoted diff) +0 -0

b/scripts/lib/devtool/standard.py index ea21877..cd5a3ed 100644

scripts/lib/devtool/standard.py +2 -1
@@ -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()

scripts/lib/devtool/upgrade.py new file +47 -0
new file mode 100644
index 0000000..9bef984
@@ -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

scripts/lib/devtool/upgrade.py new file +2 -0
+
+    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>). +

scripts/lib/devtool/upgrade.py new file +2 -0
+    Returns: 1) The (git) initial revision ID
+             2) The full path of a temporal recipe containing the correct

checksums + """

scripts/lib/devtool/upgrade.py new file +25 -0
+    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)

scripts/lib/devtool/upgrade.py new file +3 -0
+        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)) +

scripts/lib/devtool/upgrade.py new file +4 -0
+    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) +

scripts/lib/devtool/upgrade.py new file +15 -1
+        # 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()

scripts/lib/devtool/upgrade.py new file +5 -0
+
+        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:

scripts/lib/devtool/upgrade.py new file +8 -0
+                    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,
Quoted 3 lines

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 ?

Quoted 36 lines
scripts/lib/devtool/upgrade.py new file +2 -0
+    except bb.process.ExecutionError as e:
+        raise DevtoolError('Command \'%s\' failed:\n%s' % (e.command,

e.stdout)) + finally:

scripts/lib/devtool/upgrade.py new file +2 -0
+        if args.keep_temp:
+            logger.info('Preserving temporary directory %s' %

tmpsrcbasetree) + else:

scripts/lib/devtool/upgrade.py new file +29 -0
+            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:

scripts/lib/devtool/upgrade.py line 170

Quoted 8 lines
scripts/lib/devtool/upgrade.py new file +8 -0
+                            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?

Quoted 118 lines
scripts/lib/devtool/upgrade.py new file +36 -0
+    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)

scripts/lib/devtool/upgrade.py new file +12 -0
+    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}) +

scripts/lib/devtool/upgrade.py new file +32 -1
+    # 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) +

scripts/lib/devtool/upgrade.py new file +5 -0
+    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

scripts/lib/devtool/upgrade.py new file +5 -0
+        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

scripts/lib/devtool/upgrade.py new file +3 -0
+        tinfoil = setup_tinfoil()
+
+        recipe_file = _create_upgrade_recipe(args, config, recipe_file_tmp,

tinfoil.config_data, rd) +

scripts/lib/devtool/upgrade.py new file +15 -0
+    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)
+

scripts/lib/devtool/upgrade.py line 296

Quoted 10 lines
scripts/lib/devtool/upgrade.py new file +10 -0
+    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

Signature · 2 lines
Paul Eggleton
Intel Open Source Technology Centre
#5 lore raw
Quoted 13 lines · Paul Eggleton
Quoted 10 lines · On Wednesday 26 August 2015 07:43:23 [email protected]…

From: Leonardo Sandoval <[email protected]>

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

Quoted 2 lines

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.

Quoted 171 lines
Quoted 171 lines

[YOCTO #7642]

Signed-off-by: Leonardo Sandoval
<[email protected]> ---

  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

(quoted diff) +0 -0

b/scripts/lib/devtool/standard.py index ea21877..cd5a3ed 100644

scripts/lib/devtool/standard.py +2 -1
@@ -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()

scripts/lib/devtool/upgrade.py new file +47 -0
new file mode 100644
index 0000000..9bef984
@@ -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

scripts/lib/devtool/upgrade.py new file +2 -0
+
+    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>). +

scripts/lib/devtool/upgrade.py new file +2 -0
+    Returns: 1) The (git) initial revision ID
+             2) The full path of a temporal recipe containing the correct

checksums + """

scripts/lib/devtool/upgrade.py new file +25 -0
+    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)

scripts/lib/devtool/upgrade.py new file +3 -0
+        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)) +

scripts/lib/devtool/upgrade.py new file +4 -0
+    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) +

scripts/lib/devtool/upgrade.py new file +15 -1
+        # 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()

scripts/lib/devtool/upgrade.py new file +5 -0
+
+        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:

scripts/lib/devtool/upgrade.py new file +5 -0
+                    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:
Quoted 9 lines
Quoted 6 lines
scripts/lib/devtool/upgrade.py new file +3 -0
+                # 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 '_'.

Quoted 41 lines
Quoted 41 lines
scripts/lib/devtool/upgrade.py new file +2 -0
+    except bb.process.ExecutionError as e:
+        raise DevtoolError('Command \'%s\' failed:\n%s' % (e.command,

e.stdout)) + finally:

scripts/lib/devtool/upgrade.py new file +2 -0
+        if args.keep_temp:
+            logger.info('Preserving temporary directory %s' %

tmpsrcbasetree) + else:

scripts/lib/devtool/upgrade.py new file +29 -0
+            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:

scripts/lib/devtool/upgrade.py new file +4 -0
+                            if cs in checksums:
+                                oldcheck = m.group(1)
+                                newcheck = checksums[cs]
+                                line = line.replace(oldcheck, newcheck)
Quoted 9 lines

scripts/lib/devtool/upgrade.py line 170

Quoted 4 lines
scripts/lib/devtool/upgrade.py new file +4 -0
+                            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.

Quoted 123 lines
Quoted 123 lines
scripts/lib/devtool/upgrade.py new file +36 -0
+    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)

scripts/lib/devtool/upgrade.py new file +12 -0
+    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}) +

scripts/lib/devtool/upgrade.py new file +32 -1
+    # 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) +

scripts/lib/devtool/upgrade.py new file +5 -0
+    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

scripts/lib/devtool/upgrade.py new file +5 -0
+        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

scripts/lib/devtool/upgrade.py new file +3 -0
+        tinfoil = setup_tinfoil()
+
+        recipe_file = _create_upgrade_recipe(args, config, recipe_file_tmp,

tinfoil.config_data, rd) +

scripts/lib/devtool/upgrade.py new file +20 -0
+    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])
Quoted 8 lines

scripts/lib/devtool/upgrade.py line 296

Quoted 5 lines
scripts/lib/devtool/upgrade.py new file +5 -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.

Quoted 4 lines

Cheers,
Paul

#6 lore raw
Quoted 9 lines · Leonardo Sandoval
Quoted 5 lines · Paul Eggleton

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.

Signature · 3 lines
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
#7 lore raw
Quoted 9 lines · Otavio Salvador
Quoted 7 lines · Leonardo Sandoval
Quoted 5 lines · Paul Eggleton

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?

Quoted 4 lines
Quoted 2 lines

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.

Quoted 1 line
#8 lore raw

PATCH v2 2/2 oeqa/selftest: new tests for devtool upgrage feature

In reply to [email protected]

From: Leonardo Sandoval <[email protected]>

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

---
 meta/lib/oeqa/selftest/devtool.py | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
meta/lib/oeqa/selftest/devtool.py +36 -0
index b59db15..f72e010 100644
@@ -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')
Signature · 1 line
1.8.4.5
#9 lore raw

Re: [PATCH v2 2/2] oeqa/selftest: new tests for devtool upgrage feature

Comments below.

Quoted 23 lines · On 26/08/15 02:43, [email protected] wrote:

From: Leonardo Sandoval <[email protected]>

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

---
  meta/lib/oeqa/selftest/devtool.py | 36 ++++++++++++++++++++++++++++++++++++
  1 file changed, 36 insertions(+)
meta/lib/oeqa/selftest/devtool.py +2 -0
index b59db15..f72e010 100644
@@ -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):

meta/lib/oeqa/selftest/devtool.py class DevtoolTests(DevtoolBase): · line 871

Quoted 10 lines
meta/lib/oeqa/selftest/devtool.py +10 -0
+        # 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?

Quoted 24 lines
meta/lib/oeqa/selftest/devtool.py +24 -0
+        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')
#10 lore raw

Re: [PATCH v2 2/2] oeqa/selftest: new tests for devtool upgrage feature

Quoted 39 lines · Aníbal Limón

Comments below.

Quoted 36 lines · On 26/08/15 02:43, [email protected] wrote:

From: Leonardo Sandoval <[email protected]>

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
<[email protected]>

---
  meta/lib/oeqa/selftest/devtool.py | 36
++++++++++++++++++++++++++++++++++++
  1 file changed, 36 insertions(+)
meta/lib/oeqa/selftest/devtool.py +0 -0
index b59db15..f72e010 100644
@@ -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')

meta/lib/oeqa/selftest/devtool.py +5 -0
+
+    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')

meta/lib/oeqa/selftest/devtool.py +3 -0
+        # Check parameters
+        result = runCmd('devtool upgrade -h')
+        for param in 'recipename srctree --version -V --branch -b
Quoted 8 lines

meta/lib/oeqa/selftest/devtool.py class DevtoolTests(DevtoolBase): · line 871

Quoted 5 lines

--keep-temp --no-patch'.split():

meta/lib/oeqa/selftest/devtool.py +4 -0
+            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?

Quoted 33 lines
Quoted 32 lines
meta/lib/oeqa/selftest/devtool.py +5 -0
+        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

meta/lib/oeqa/selftest/devtool.py +2 -0
+        # we are downgrading instead of upgrading.
+        result = runCmd('devtool upgrade %s %s -V %s --no-patch' %

(recipe, tempdir, version))

meta/lib/oeqa/selftest/devtool.py +2 -0
+        # 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))

meta/lib/oeqa/selftest/devtool.py +2 -0
+        # Check new recipe folder is present
+

self.assertTrue(os.path.exists(os.path.join(workspacedir,'recipes',recipe)), 'Recipe folder should exist')

meta/lib/oeqa/selftest/devtool.py +2 -0
+        # 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')

meta/lib/oeqa/selftest/devtool.py +11 -0
+        # 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')

#11 lore raw

Re: [PATCH v2 2/2] oeqa/selftest: new tests for devtool upgrage feature

Quoted 65 lines · On Wednesday 26 August 2015 11:29:37 Leonardo Sandoval wrote:
Quoted 50 lines · Aníbal Limón

Comments below.

Quoted 47 lines · On 26/08/15 02:43, [email protected] wrote:

From: Leonardo Sandoval <[email protected]>

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
<[email protected]>
---

  meta/lib/oeqa/selftest/devtool.py | 36

++++++++++++++++++++++++++++++++++++

  1 file changed, 36 insertions(+)
meta/lib/oeqa/selftest/devtool.py +0 -0
index b59db15..f72e010 100644
@@ -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')

meta/lib/oeqa/selftest/devtool.py +5 -0
+
+    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')

meta/lib/oeqa/selftest/devtool.py +3 -0
+        # Check parameters
+        result = runCmd('devtool upgrade -h')
+        for param in 'recipename srctree --version -V --branch -b
Quoted 8 lines

meta/lib/oeqa/selftest/devtool.py class DevtoolTests(DevtoolBase): · line 871

Quoted 5 lines

--keep-temp --no-patch'.split():

meta/lib/oeqa/selftest/devtool.py +4 -0
+            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.

Quoted 4 lines

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

Signature · 2 lines
Paul Eggleton
Intel Open Source Technology Centre
#12 lore raw

Re: [PATCH v2 2/2] oeqa/selftest: new tests for devtool upgrage feature

Quoted 66 lines · Paul Eggleton
Quoted 65 lines · On Wednesday 26 August 2015 11:29:37 Leonardo Sandoval wrote:
Quoted 50 lines · Aníbal Limón

Comments below.

Quoted 47 lines · On 26/08/15 02:43, [email protected] wrote:

From: Leonardo Sandoval <[email protected]>

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
<[email protected]>
---

   meta/lib/oeqa/selftest/devtool.py | 36

++++++++++++++++++++++++++++++++++++

   1 file changed, 36 insertions(+)
meta/lib/oeqa/selftest/devtool.py +0 -0
index b59db15..f72e010 100644
@@ -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')

meta/lib/oeqa/selftest/devtool.py +5 -0
+
+    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')

meta/lib/oeqa/selftest/devtool.py +3 -0
+        # Check parameters
+        result = runCmd('devtool upgrade -h')
+        for param in 'recipename srctree --version -V --branch -b
Quoted 8 lines

meta/lib/oeqa/selftest/devtool.py class DevtoolTests(DevtoolBase): · line 871

Quoted 5 lines

--keep-temp --no-patch'.split():

meta/lib/oeqa/selftest/devtool.py +4 -0
+            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.

Quoted 8 lines
Quoted 4 lines

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.

Quoted 4 lines

Cheers,
Paul