Skip to contentSkip to conversation

Several fixes for layerindex

PATCH In review Robert Yang 2 participants 13 messages Jun 23, 2017 – Jul 21, 2017

lore
Share & tools
lore
#0 lore raw

v5 cover Several fixes for layerindex

* V5
- Fix Paul's comments
- Add REMOVE_LAYER_DEPENDENCIES = False to settings, the _add_dependency() will check and remove dependencies which is not in conf/layer.conf when True (WARN when False)..

* V4
- Add an option -i to update_layer.py to get initial layer value (dependencies, versions and so on), update.py doesn't use tinfoil any more since tinfoil before morty can't be shutdown totally if the process is not exited, this was why splitted update_layer.py from update.py.
- Squash 11 patches (v3) into 10, since two of them have overlap.

* V3:
- Better error messages when required collections not found
- Delete layerbranch for non-existed branch
- update_layer.py: remove --update-dependencies

* V2:
- Remove "import bb" from update.py to avoid causing confusions when switch branches.

* V1:
- The "-a ACTUAL_BRANCH" is a litle different from what we had talked, now it respects "-l" and "-b", we can use -l to specify layers or default to all, and it requires "-b" to work, only one branch is supported in a run, for example:

$ update.py -b master -a branch_20170526
All the layers which have branch master and actual_branch branch_20170526 will be updated to branch_20170526.

$ update.py -b master -l meta-oe -a branch_20170526 Only meta-oe layer will be updated.

$ update.py -b master -l bitbake -a branch_20170526 The bitbake's bitbake_branch will be updated.

// Robert

The following changes since commit b958a991caa947fde2ccb073e3cb7924e162562d:

layerindex/views: support querying by layer name (2017-06-15 16:04:14 +0200)

are available in the git repository at:

git://git.pokylinux.org/poky-contrib rbt/layerindex-10fixes
http://git.pokylinux.org/cgit.cgi/poky-contrib/log/?h=rbt/layerindex-10fixes

Robert Yang (10):
update.py: update actual branch for layer and bitbake layerconfparse.py: remove unused layerbranch from parse_layer() utils.py: add REMOVE_LAYER_DEPENDENCIES to remove dependencies update_layer.py: set layerbranch's collection before add_dependencies recipeparse.py: restore cwd when the parsing is done update.py: update layers orderly
update_layer.py: remove --update-dependencies update_layer.py: move the location of transaction.atomic() update.py: add -p to git fetch
update_layer.py: delete layerbranch for non-existed branch

 layerindex/layerconfparse.py     |   2 +-
 layerindex/recipeparse.py        |   3 +
 layerindex/tools/import_layer.py |   2 +-
 layerindex/update.py             | 186 ++++++++++++++++++++++++++++++---------
 layerindex/update_layer.py       |  99 +++++++++------------
 layerindex/utils.py              |  71 ++++++++++++++-
 settings.py                      |   3 +
 7 files changed, 263 insertions(+), 103 deletions(-)
Signature · 1 line
2.10.2
#1 lore raw

PATCH v5 1/10 update.py: update actual branch for layer and bitbake

Add an option "-a" to update actual branch for layer and bitbake, it is useful when there are many layers and need update actual branches frequently. We only can update them via website without this patch, which is not fun and easy to make mistakes.

* It works with "-l", and "-l bitbake" means update bitbake branch.
* It requires "-b" to work, and only one branch is supported in a run.

For example:

$ update.py -b master -a branch_20170526
All the layers which have branch master and actual_branch branch_20170526
will be updated to branch_20170526.
$ update.py -b master -l meta-oe -a branch_20170526
Only meta-oe layer will be updated.
$ update.py -b master -l bitbake -a branch_20170526
The bitbake's bitbake_branch will be updated.
---
 layerindex/update.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++----
 layerindex/utils.py  | 11 ++++++++
 2 files changed, 77 insertions(+), 5 deletions(-)
layerindex/update.py +66 -5
index d5c56cd..1a0bbd0 100755
@@ -85,6 +85,43 @@ def prepare_update_layer_command(options, branch, layer, updatedeps=False):
         cmd += ' -q'
     return cmd
 
+def update_actual_branch(layerquery, fetchdir, branch, options, update_bitbake, bitbakepath):
+    """Update actual branch for layers and bitbake in database"""
+    to_save = set()
+    actual_branch = options.actual_branch
+    if update_bitbake:
+        branchobj = utils.get_branch(branch)
+        if actual_branch != branchobj.bitbake_branch:
+            if utils.is_branch_valid(bitbakepath, actual_branch):
+                logger.info("bitbake: %s.bitbake_branch: %s -> %s" % (branch, branchobj.bitbake_branch, actual_branch))
+                branchobj.bitbake_branch = actual_branch
+                to_save.add(branchobj)
+            else:
+                logger.info("Skipping update bitbake_branch for bitbake - branch %s doesn't exist" % actual_branch)
+        else:
+            logger.info("bitbake: %s.bitbake_branch is already %s, so no change" % (branch, actual_branch))
+
+    for layer in layerquery:
+        urldir = layer.get_fetch_dir()
+        repodir = os.path.join(fetchdir, urldir)
+        if not utils.is_branch_valid(repodir, actual_branch):
+            logger.info("Skipping update actual_branch for %s - branch %s doesn't exist" % (layer.name, actual_branch))
+            continue
+        layerbranch = layer.get_layerbranch(branch)
+        if not layerbranch:
+            logger.info("Skipping update actual_branch for %s - layerbranch %s doesn't exist" % (layer.name, branch))
+            continue
+        if actual_branch != layerbranch.actual_branch:
+            logger.info("%s: %s.actual_branch: %s -> %s" % (layer.name, branch, layerbranch.actual_branch, actual_branch))
+            layerbranch.actual_branch = actual_branch
+            to_save.add(layerbranch)
+        else:
+            logger.info("%s: %s.actual_branch is already %s, so no change" % (layer.name, branch, actual_branch))
+
+    # At last, do the save
+    if not options.dryrun:
+        for s in to_save:
+            s.save()
 
 def main():
     if LooseVersion(git.__version__) < '0.3.1':
@@ -117,6 +154,9 @@ def main():
     parser.add_option("", "--nocheckout",
             help = "Don't check out branches",
             action="store_true", dest="nocheckout")
+    parser.add_option("-a", "--actual-branch",
+            help = "Update actual branch for layer and bitbake",
+            action="store", dest="actual_branch", default='')
     parser.add_option("-d", "--debug",
             help = "Enable debug output",
             action="store_const", const=logging.DEBUG, dest="loglevel", default=logging.INFO)
@@ -151,17 +191,34 @@ def main():
         logger.error("Please set LAYER_FETCH_DIR in settings.py")
         sys.exit(1)
 
+    # For -a option to update bitbake branch
+    update_bitbake = False
     if options.layers:
-        layerquery = LayerItem.objects.filter(classic=False).filter(name__in=options.layers.split(','))
-        if layerquery.count() == 0:
-            logger.error('No layers matching specified query "%s"' % options.layers)
-            sys.exit(1)
+        layers = options.layers.split(',')
+        if 'bitbake' in layers:
+            update_bitbake = True
+            layers.remove('bitbake')
+        for layer in layers:
+            layerquery = LayerItem.objects.filter(classic=False).filter(name=layer)
+            if layerquery.count() == 0:
+                logger.error('No layers matching specified query "%s"' % layer)
+                sys.exit(1)
+        layerquery = LayerItem.objects.filter(classic=False).filter(name__in=layers)
     else:
         # We deliberately exclude status == 'X' ("no update") here
         layerquery = LayerItem.objects.filter(classic=False).filter(status='P')
         if layerquery.count() == 0:
             logger.info("No published layers to update")
             sys.exit(1)
+        update_bitbake = True
+
+    if options.actual_branch:
+        if not options.branch:
+            logger.error("-a option requires -b")
+            sys.exit(1)
+        elif len(branches) != 1:
+            logger.error("Only one branch should be used with -a")
+            sys.exit(1)
 
     if not os.path.exists(fetchdir):
         os.makedirs(fetchdir)
@@ -209,7 +266,7 @@ def main():
                             continue
                         fetchedrepos.append(layer.vcs_url)
 
-                if not fetchedrepos:
+                if not (fetchedrepos or update_bitbake):
                     logger.error("No repositories could be fetched, exiting")
                     sys.exit(1)
 
@@ -219,6 +276,10 @@ def main():
                 else:
                     out = utils.runcmd("git fetch", bitbakepath, logger=logger)
 
+            if options.actual_branch:
+                update_actual_branch(layerquery, fetchdir, branches[0], options, update_bitbake, bitbakepath)
+                return
+
             # Process and extract data from each layer
             # We now do this by calling out to a separate script; doing otherwise turned out to be
             # unreliable due to leaking memory (we're using bitbake internals in a manner in which
layerindex/utils.py +11 -0
index 3b45b07..186d342 100644
@@ -160,6 +160,17 @@ def is_layer_valid(layerdir):
         return False
     return True
 
+def is_branch_valid(layerdir, branch):
+    import git
+
+    g = git.cmd.Git(layerdir)
+    assert g.rev_parse('--is-bare-repository') == 'false'
+    try:
+        g.rev_parse('--verify', 'origin/%s' % branch)
+    except git.exc.GitCommandError:
+        return False
+    return True
+
 def parse_conf(conf_file, d):
     if hasattr(bb.parse, "handle"):
         # Newer BitBake
Signature · 1 line
2.10.2
#2 lore raw

PATCH v5 2/10 layerconfparse.py: remove unused layerbranch from parse_layer()

In reply to Robert Yang

The layerbranch is not used in parse_layer(), so remove it.

---
 layerindex/layerconfparse.py     | 2 +-
 layerindex/tools/import_layer.py | 2 +-
 layerindex/update_layer.py       | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)
layerindex/layerconfparse.py +1 -1
index d599ed5..526d2c2 100644
@@ -32,7 +32,7 @@ class LayerConfParse:
 
         self.config_data_copy = bb.data.createCopy(self.tinfoil.config_data)
 
-    def parse_layer(self, layerbranch, layerdir):
+    def parse_layer(self, layerdir):
 
         # This is not a valid layer, parsing will cause exception.
         if not utils.is_layer_valid(layerdir):
layerindex/tools/import_layer.py +1 -1
index ad16e40..a806dd1 100755
@@ -377,7 +377,7 @@ def main():
                         layerdep.save()
                     layerconfparser = LayerConfParse(logger=logger)
                     try:
-                        config_data = layerconfparser.parse_layer(layerbranch, layerdir)
+                        config_data = layerconfparser.parse_layer(layerdir)
                         if config_data:
                             utils.add_dependencies(layerbranch, config_data, logger=logger)
                             utils.add_recommends(layerbranch, config_data, logger=logger)
layerindex/update_layer.py +2 -2
index 9fe96fb..05ab3e7 100644
@@ -267,7 +267,7 @@ def main():
                     sys.exit(0)
 
                 layerconfparser = layerconfparse.LayerConfParse(logger=logger, bitbakepath=bitbakepath, tinfoil=tinfoil)
-                config_data = layerconfparser.parse_layer(layerbranch, layerdir)
+                config_data = layerconfparser.parse_layer(layerdir)
                 if not config_data:
                     logger.debug("Layer %s does not appear to be valid for branch %s" % (layer.name, branchdesc))
                     sys.exit(0)
@@ -355,7 +355,7 @@ def main():
                 logger.info("Collecting data for layer %s on branch %s" % (layer.name, branchdesc))
 
                 layerconfparser = layerconfparse.LayerConfParse(logger=logger, tinfoil=tinfoil)
-                layer_config_data = layerconfparser.parse_layer(layerbranch, layerdir)
+                layer_config_data = layerconfparser.parse_layer(layerdir)
                 if not layer_config_data:
                     logger.info("Skipping update of layer %s for branch %s - conf/layer.conf may have parse issues" % (layer.name, branchdesc))
                     layerconfparser.shutdown()
Signature · 1 line
2.10.2
#3 lore raw

PATCH v5 3/10 utils.py: add REMOVE_LAYER_DEPENDENCIES to remove dependencies

In reply to Robert Yang

Fixed:
- set LAYERDEPENDS_openembedded-layer = "core"
- $ "update.py -l meta-oe -b master"
Check from web, its dependency is "openembedded-core"
- Change LAYERDEPENDS_openembedded-layer = "foo"
- Run "update.py -l meta-oe -b master"
Check from web, its dependency is "openembedded-core and foo", this might be incorrect, now if set REMOVE_LAYER_DEPENDENCIES to true, the old dependency openembedded-core will be removed, the default is False which prints warnings to notify users.

And also the existing checking should filter(required=required), otherwise it can't work well when a layer is in both depends and recommends, this can't happen in a normal case, but it would surprise the user when this happens.

---
 layerindex/utils.py | 19 ++++++++++++++++++-
 settings.py         |  3 +++
 2 files changed, 21 insertions(+), 1 deletion(-)
layerindex/utils.py +18 -1
index 186d342..5acf35f 100644
@@ -88,6 +88,7 @@ def _add_dependency(var, name, layerbranch, config_data, logger=None, required=T
         logger.debug('Error parsing %s_%s for %s\n%s' % (var, var_name, layer_name, str(vse)))
         return
 
+    need_remove = None
     for dep, ver_list in list(dep_dict.items()):
         ver_str = None
         if ver_list:
@@ -106,8 +107,14 @@ def _add_dependency(var, name, layerbranch, config_data, logger=None, required=T
                 logger.error('Cannot resolve %s %s (version %s) for %s' % (name, dep, ver_str, layer_name))
                 continue
 
+        # Preparing to remove obsolete ones
+        if not need_remove:
+            need_remove = LayerDependency.objects.filter(layerbranch=layerbranch).filter(required=required).exclude(dependency=dep_layer)
+        else:
+            need_remove = need_remove.exclude(dependency=dep_layer)
+
         # Skip existing entries.
-        existing = list(LayerDependency.objects.filter(layerbranch=layerbranch).filter(dependency=dep_layer))
+        existing = list(LayerDependency.objects.filter(layerbranch=layerbranch).filter(required=required).filter(dependency=dep_layer))
         if existing:
             logger.debug('Skipping %s - already a dependency for %s' % (dep, layer_name))
             continue
@@ -121,6 +128,16 @@ def _add_dependency(var, name, layerbranch, config_data, logger=None, required=T
         layerdep.required = required
         layerdep.save()
 
+    if need_remove:
+        import settings
+        remove_layer_dependencies = getattr(settings, 'REMOVE_LAYER_DEPENDENCIES', False)
+        if remove_layer_dependencies:
+            logger.info('Removing obsolete dependencies "%s" for layer %s' % (need_remove, layer_name))
+            need_remove.delete()
+        else:
+            logger.warn('Dependencies "%s" is not in %s\'s conf/layer.conf' % (need_remove, layer_name))
+            logger.warn('Either set REMOVE_LAYER_DEPENDENCIES to remove it from database or fix conf/layer.conf')
+
 def set_layerbranch_collection_version(layerbranch, config_data, logger=None):
     layerbranch.collection = config_data.getVar('BBFILE_COLLECTIONS', True)
     ver_str = "LAYERVERSION_"
settings.py +3 -0
index 0ecf90b..3498e7a 100644
@@ -214,6 +214,9 @@ CORE_LAYER_NAME = "openembedded-core"
 # Update records older than this number of days will be deleted every update
 UPDATE_PURGE_DAYS = 30
 
+# Remove layer dependencies if it is not in conf/layer.conf
+REMOVE_LAYER_DEPENDENCIES = False
+
 # Settings for layer submission feature
 SUBMIT_EMAIL_FROM = '[email protected]'
 SUBMIT_EMAIL_SUBJECT = 'OE Layerindex layer submission'
Signature · 1 line
2.10.2
#4 lore raw

PATCH v5 4/10 update_layer.py: set layerbranch's collection before add_dependencies

In reply to Robert Yang

The _add_dependency() uses:
if layerbranch.collection:
var_name = layerbranch.collection

The layerbranch.collection is none if it is newly created, thus it can't get LAYERDEPENDS, because what defined in layer.conf is LAYERDEPENDS_<collection>, but what it would get is LAYERDEPENDS_<layer_name>, this patch can fix the problem.

Reproducer:

$ python3 update_layer.py -l mete-xfce -b <newbranch> --fullreload -d

It would get None LAYERDEPENDS.

---
 layerindex/update_layer.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
layerindex/update_layer.py +1 -1
index 05ab3e7..bcf7056 100644
@@ -360,9 +360,9 @@ def main():
                     logger.info("Skipping update of layer %s for branch %s - conf/layer.conf may have parse issues" % (layer.name, branchdesc))
                     layerconfparser.shutdown()
                     sys.exit(1)
+                utils.set_layerbranch_collection_version(layerbranch, layer_config_data, logger=logger)
                 utils.add_dependencies(layerbranch, layer_config_data, logger=logger)
                 utils.add_recommends(layerbranch, layer_config_data, logger=logger)
-                utils.set_layerbranch_collection_version(layerbranch, layer_config_data, logger=logger)
                 layerbranch.save()
 
                 try:
Signature · 1 line
2.10.2
#5 lore raw

PATCH v5 5/10 recipeparse.py: restore cwd when the parsing is done

In reply to Robert Yang

Othewise it may cause troubles to the function who calls it.

---
 layerindex/recipeparse.py | 3 +++
 1 file changed, 3 insertions(+)
layerindex/recipeparse.py +3 -0
index dd85bc3..f2a5235 100644
@@ -70,10 +70,13 @@ def init_parser(settings, branch, bitbakepath, enable_tracking=False, nocheckout
     if not os.path.exists(settings.TEMP_BASE_DIR):
         os.makedirs(settings.TEMP_BASE_DIR)
     tempdir = tempfile.mkdtemp(dir=settings.TEMP_BASE_DIR)
+    saved_cwd = os.getcwd()
     os.chdir(tempdir)
 
     tinfoil = utils.setup_tinfoil(bitbakepath, enable_tracking)
 
+    os.chdir(saved_cwd)
+
     # Ensure TMPDIR exists (or insane.bbclass will blow up trying to write to the QA log)
     oe_tmpdir = tinfoil.config_data.getVar('TMPDIR', True)
     if not os.path.exists(oe_tmpdir):
Signature · 1 line
2.10.2
#6 lore raw

PATCH v5 6/10 update.py: update layers orderly

In reply to Robert Yang

* Problems
The update.py couldn't handle new (not only new branch, in fact, but also existing branches, see below for more info) branch well, for example, there are 3 layers: layer_A, layer_B and layer_C, and create new branch "branch_1" for them, and they have depends:

layer_A -> layer_B -> layer_C

The "->" means depends on.

Then run "update.py -b branch_1", there would be errors like:

ERROR: Dependency layer_B of layer_A does not have branch record for branch branch_1

Though update.py runs "update_layer.py" twice, but it didn't help since layerbranch was None when it was failed to create in the first run.

The reason is if update.py updates layer_A firstly, it would fail since it can't find layer_B:branch_1 in database (not added to database yet), similarly, if add layer_B before layer_C, it would also fail. Only layer_C can be added (assume it has no dependencies). So we have to re-run update.py again and again to make it work, here we may have to run update.py 3 times, and more runs are needed if the dependency chain is longer.

* Solutions:
Make update.py pass layers orderly to update_layer.py according to dependencies can fix the problem, we can get intial dependencies from tinfoil, add an option "-i, --initial" to update_layer.py to get them.

Not only new branch, but also existing branches may have the problem, because collections and dependencies maybe changed in the coming update, so we can't trust database when the layer is going to be updated, for example, if there are 10 layers in database, and 3 of them will be updated (-l layer1,layer2,layer3), then we can not use the 3 layers' collections data from database, we need get them from tinfoil.

* Performance improvement:
It should be the same as before in theory, I have tested it with 97 layers:
- Before: 11m6.472s, but only 75 layers were added, 22 ones were failed, I have to re-run update.py again and again (maybe 4 times to make all of them added). So:
(11 * 60 + 6)/75*97/60 = 14m35s

- Now 12m10.350s, all the layers are added in the first run.

So about 2 minutes are saved.

---
 layerindex/update.py       | 113 ++++++++++++++++++++++++++++++---------------
 layerindex/update_layer.py |  10 +++-
 layerindex/utils.py        |  41 ++++++++++++++++
 3 files changed, 127 insertions(+), 37 deletions(-)
layerindex/update.py +77 -36
index 1a0bbd0..f8e5a37 100755
@@ -18,6 +18,8 @@ import signal
 from datetime import datetime, timedelta
 from distutils.version import LooseVersion
 import utils
+import operator
+import re
 
 import warnings
 warnings.filterwarnings("ignore", category=DeprecationWarning)
@@ -62,15 +64,13 @@ def run_command_interruptible(cmd):
     return process.returncode, buf
 
 
-def prepare_update_layer_command(options, branch, layer, updatedeps=False):
+def prepare_update_layer_command(options, branch, layer, initial=False):
     """Prepare the update_layer.py command line"""
     if branch.update_environment:
         cmdprefix = branch.update_environment.get_command()
     else:
         cmdprefix = 'python3'
     cmd = '%s update_layer.py -l %s -b %s' % (cmdprefix, layer.name, branch.name)
-    if updatedeps:
-        cmd += ' --update-dependencies'
     if options.reload:
         cmd += ' --reload'
     if options.fullreload:
@@ -79,6 +79,8 @@ def prepare_update_layer_command(options, branch, layer, updatedeps=False):
         cmd += ' --nocheckout'
     if options.dryrun:
         cmd += ' -n'
+    if initial:
+        cmd += ' -i'
     if options.loglevel == logging.DEBUG:
         cmd += ' -d'
     elif options.loglevel == logging.ERROR:
@@ -191,6 +193,13 @@ def main():
         logger.error("Please set LAYER_FETCH_DIR in settings.py")
         sys.exit(1)
 
+
+    # We deliberately exclude status == 'X' ("no update") here
+    layerquery_all = LayerItem.objects.filter(classic=False).filter(status='P')
+    if layerquery_all.count() == 0:
+        logger.info("No published layers to update")
+        sys.exit(1)
+
     # For -a option to update bitbake branch
     update_bitbake = False
     if options.layers:
@@ -205,11 +214,7 @@ def main():
                 sys.exit(1)
         layerquery = LayerItem.objects.filter(classic=False).filter(name__in=layers)
     else:
-        # We deliberately exclude status == 'X' ("no update") here
-        layerquery = LayerItem.objects.filter(classic=False).filter(status='P')
-        if layerquery.count() == 0:
-            logger.info("No published layers to update")
-            sys.exit(1)
+        layerquery = layerquery_all
         update_bitbake = True
 
     if options.actual_branch:
@@ -286,8 +291,72 @@ def main():
             # they never get used during normal operation).
             last_rev = {}
             for branch in branches:
+                # If layer_A depends(or recommends) on layer_B, add layer_B before layer_A
+                deps_dict_all = {}
+                layerquery_sorted = []
+                collections_done = set()
                 branchobj = utils.get_branch(branch)
+                for layer in layerquery_all:
+                    # Get all collections from database, but we can't trust the
+                    # one which will be updated since its collections maybe
+                    # changed (different from database).
+                    if layer in layerquery:
+                        continue
+                    layerbranch = layer.get_layerbranch(branch)
+                    if layerbranch:
+                        collections_done.add((layerbranch.collection, layerbranch.version))
+
                 for layer in layerquery:
+                    cmd = prepare_update_layer_command(options, branchobj, layer, initial=True)
+                    logger.debug('Running layer update command: %s' % cmd)
+                    ret, output = run_command_interruptible(cmd)
+                    logger.debug('output: %s' % output)
+                    if ret != 0:
+                        continue
+                    col = re.search("^BBFILE_COLLECTIONS = \"(.*)\"", output, re.M).group(1) or ''
+                    ver = re.search("^LAYERVERSION = \"(.*)\"", output, re.M).group(1) or ''
+                    deps = re.search("^LAYERDEPENDS = \"(.*)\"", output, re.M).group(1) or ''
+                    recs = re.search("^LAYERRECOMMENDS = \"(.*)\"", output, re.M).group(1) or ''
+
+                    deps_dict = utils.explode_dep_versions2(bitbakepath, deps + ' ' + recs)
+                    if len(deps_dict) == 0:
+                        # No depends, add it firstly
+                        layerquery_sorted.append(layer)
+                        collections_done.add((col, ver))
+                        continue
+                    deps_dict_all[layer] = {'requires': deps_dict, 'collection': col, 'version': ver}
+
+                # Move deps_dict_all to layerquery_sorted orderly
+                logger.info("Sorting layers for branch %s" % branch)
+                while True:
+                    deps_dict_all_copy = deps_dict_all.copy()
+                    for layer, value in deps_dict_all_copy.items():
+                        for req_col, req_ver_list in value['requires'].copy().items():
+                            matched = False
+                            if req_ver_list:
+                                req_ver = req_ver_list[0]
+                            else:
+                                req_ver = None
+                            if utils.is_deps_satisfied(req_col, req_ver, collections_done):
+                                del(value['requires'][req_col])
+                        if not value['requires']:
+                            # All the depends are in collections_done:
+                            del(deps_dict_all[layer])
+                            layerquery_sorted.append(layer)
+                            collections_done.add((value['collection'], value['version']))
+
+                    if not len(deps_dict_all):
+                        break
+
+                    # Something is wrong if nothing changed after a run
+                    if operator.eq(deps_dict_all_copy, deps_dict_all):
+                        logger.error("Cannot find required collections on branch %s:" % branch)
+                        for layer, value in deps_dict_all.items():
+                            logger.error('%s: %s' % (layer.name, value['requires']))
+                        logger.error("Known collections: %s" % collections_done)
+                        sys.exit(1)
+
+                for layer in layerquery_sorted:
                     layerupdate = LayerUpdate()
                     layerupdate.update = update
 
@@ -304,9 +373,6 @@ def main():
                                 layerupdate.save()
                         continue
 
-                    urldir = layer.get_fetch_dir()
-                    repodir = os.path.join(fetchdir, urldir)
-
                     cmd = prepare_update_layer_command(options, branchobj, layer)
                     logger.debug('Running layer update command: %s' % cmd)
                     layerupdate.started = datetime.now()
@@ -327,31 +393,6 @@ def main():
                     if ret == 254:
                         # Interrupted by user, break out of loop
                         break
-
-            # Since update_layer may not be called in the correct order to have the
-            # dependencies created before trying to link them, we now have to loop
-            # back through all the branches and layers and try to link in the
-            # dependencies that may have been missed.  Note that creating the
-            # dependencies is a best-effort and continues if they are not found.
-            for branch in branches:
-                branchobj = utils.get_branch(branch)
-                for layer in layerquery:
-                    layerbranch = layer.get_layerbranch(branch)
-                    if layerbranch:
-                        if not (options.reload or options.fullreload):
-                            # Skip layers that did not change.
-                            layer_last_rev = last_rev.get(layerbranch, None)
-                            if layer_last_rev is None or layer_last_rev == layerbranch.vcs_last_rev:
-                                continue
-
-                        logger.info('Updating layer dependencies for %s on branch %s' % (layer.name, branch))
-                        cmd = prepare_update_layer_command(options, branchobj, layer, updatedeps=True)
-                        logger.debug('Running update dependencies command: %s' % cmd)
-                        ret, output = run_command_interruptible(cmd)
-                        if ret == 254:
-                            # Interrupted by user, break out of loop
-                            break
-
         finally:
             utils.unlock_file(lockfile)
 
layerindex/update_layer.py +9 -1
index bcf7056..7b945e7 100644
@@ -188,6 +188,9 @@ def main():
     parser.add_option("", "--nocheckout",
             help = "Don't check out branches",
             action="store_true", dest="nocheckout")
+    parser.add_option("-i", "--initial",
+            help = "Print initial values parsed from layer.conf only",
+            action="store_true")
     parser.add_option("-d", "--debug",
             help = "Enable debug output",
             action="store_const", const=logging.DEBUG, dest="loglevel", default=logging.INFO)
@@ -336,7 +339,7 @@ def main():
             layerdistros = Distro.objects.filter(layerbranch=layerbranch)
             layerappends = BBAppend.objects.filter(layerbranch=layerbranch)
             layerclasses = BBClass.objects.filter(layerbranch=layerbranch)
-            if layerbranch.vcs_last_rev != topcommit.hexsha or options.reload:
+            if layerbranch.vcs_last_rev != topcommit.hexsha or options.reload or options.initial:
                 # Check out appropriate branch
                 if not options.nocheckout:
                     utils.checkout_layer_branch(layerbranch, repodir, logger=logger)
@@ -361,6 +364,11 @@ def main():
                     layerconfparser.shutdown()
                     sys.exit(1)
                 utils.set_layerbranch_collection_version(layerbranch, layer_config_data, logger=logger)
+                if options.initial:
+                    # Use print() rather than logger.info() since "-q" makes it print nothing.
+                    for i in ["BBFILE_COLLECTIONS", "LAYERVERSION", "LAYERDEPENDS", "LAYERRECOMMENDS"]:
+                        print('%s = "%s"' % (i, utils.get_layer_var(layer_config_data, i, logger)))
+                    sys.exit(0)
                 utils.add_dependencies(layerbranch, layer_config_data, logger=logger)
                 utils.add_recommends(layerbranch, layer_config_data, logger=logger)
                 layerbranch.save()
layerindex/utils.py +41 -0

53 lines not on this page · raw message

Signature · 1 line
2.10.2
#7 lore raw

PATCH v5 7/10 update_layer.py: remove --update-dependencies

In reply to Robert Yang

It never works since it is in the middle of transaction.atomic() block, and update.py doesn't need it any more, so remove it.

---
 layerindex/update_layer.py | 27 ---------------------------
 1 file changed, 27 deletions(-)
layerindex/update_layer.py +0 -27
index 7b945e7..1c33c78 100644
@@ -182,9 +182,6 @@ def main():
     parser.add_option("-n", "--dry-run",
             help = "Don't write any data back to the database",
             action="store_true", dest="dryrun")
-    parser.add_option("", "--update-dependencies",
-            help = "Update layer dependencies only",
-            action="store_true", dest="updatedeps")
     parser.add_option("", "--nocheckout",
             help = "Don't check out branches",
             action="store_true", dest="nocheckout")
@@ -256,30 +253,6 @@ def main():
                     branchname = layerbranch.actual_branch
                     branchdesc = "%s (%s)" % (options.branch, branchname)
 
-            if options.updatedeps:
-                # Update layer dependencies only
-                if not layerbranch:
-                    logger.debug('Skipping dependency update for layer %s on branch %s - no layerbranch record' % (layer, branchdesc))
-                    sys.exit(0)
-                if not options.nocheckout:
-                    utils.checkout_layer_branch(layerbranch, repodir, logger=logger)
-                layerdir = os.path.join(repodir, layerbranch.vcs_subdir)
-                if not os.path.exists(layerdir):
-                    # If this happens it was already flagged during the main update, so ignore it
-                    logger.debug('Skipping dependency update for layer %s on branch %s - layer directory not found' % (layer, branchdesc))
-                    sys.exit(0)
-
-                layerconfparser = layerconfparse.LayerConfParse(logger=logger, bitbakepath=bitbakepath, tinfoil=tinfoil)
-                config_data = layerconfparser.parse_layer(layerdir)
-                if not config_data:
-                    logger.debug("Layer %s does not appear to be valid for branch %s" % (layer.name, branchdesc))
-                    sys.exit(0)
-
-                utils.add_dependencies(layerbranch, config_data, logger=logger)
-                utils.add_recommends(layerbranch, config_data, logger=logger)
-
-                sys.exit(0)
-
             # Collect repo info
             repo = git.Repo(repodir)
             assert repo.bare == False
Signature · 1 line
2.10.2
#8 lore raw

PATCH v5 8/10 update_layer.py: move the location of transaction.atomic()

In reply to Robert Yang

It doesn't need to be so ahead since we only need it when writing database, and a following patch will remove layerbranch from database when the branch had been removed from the repo, it's not easy to do the work in transaction.atomic() block.

---
 layerindex/update_layer.py | 56 +++++++++++++++++++++++-----------------------
 1 file changed, 28 insertions(+), 28 deletions(-)
layerindex/update_layer.py +28 -28
index 1c33c78..b6c2f4a 100644
@@ -238,36 +238,36 @@ def main():
     # why won't they just fix that?!)
     tinfoil.config_data.setVar('LICENSE', '')
 
+    layer = utils.get_layer(options.layer)
+    urldir = layer.get_fetch_dir()
+    repodir = os.path.join(fetchdir, urldir)
+
+    layerbranch = layer.get_layerbranch(options.branch)
+
+    branchname = options.branch
+    branchdesc = options.branch
+    if layerbranch:
+        if layerbranch.actual_branch:
+            branchname = layerbranch.actual_branch
+            branchdesc = "%s (%s)" % (options.branch, branchname)
+
+    # Collect repo info
+    repo = git.Repo(repodir)
+    assert repo.bare == False
     try:
-        with transaction.atomic():
-            layer = utils.get_layer(options.layer)
-            urldir = layer.get_fetch_dir()
-            repodir = os.path.join(fetchdir, urldir)
-
-            layerbranch = layer.get_layerbranch(options.branch)
-
-            branchname = options.branch
-            branchdesc = options.branch
-            if layerbranch:
-                if layerbranch.actual_branch:
-                    branchname = layerbranch.actual_branch
-                    branchdesc = "%s (%s)" % (options.branch, branchname)
-
-            # Collect repo info
-            repo = git.Repo(repodir)
-            assert repo.bare == False
-            try:
-                if options.nocheckout:
-                    topcommit = repo.commit('HEAD')
-                else:
-                    topcommit = repo.commit('origin/%s' % branchname)
-            except:
-                if layerbranch:
-                    logger.error("Failed update of layer %s - branch %s no longer exists" % (layer.name, branchdesc))
-                else:
-                    logger.info("Skipping update of layer %s - branch %s doesn't exist" % (layer.name, branchdesc))
-                sys.exit(1)
+        if options.nocheckout:
+            topcommit = repo.commit('HEAD')
+        else:
+            topcommit = repo.commit('origin/%s' % branchname)
+    except:
+        if layerbranch:
+            logger.error("Failed update of layer %s - branch %s no longer exists" % (layer.name, branchdesc))
+        else:
+            logger.info("Skipping update of layer %s - branch %s doesn't exist" % (layer.name, branchdesc))
+        sys.exit(1)
 
+    try:
+        with transaction.atomic():
             newbranch = False
             if not layerbranch:
                 # LayerBranch doesn't exist for this branch, create it
Signature · 1 line
2.10.2
#9 lore raw

PATCH v5 9/10 update.py: add -p to git fetch

In reply to Robert Yang

-p, --prune
Before fetching, remove any remote-tracking references that no longer exist on the remote.

Fixed:

$ git push origin :test_branch
$ ./update.py

The test_branch was still in fetched local repo which was incorrect, it should be gone since it has been removed by upstream.

---
 layerindex/update.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
layerindex/update.py +2 -2
index f8e5a37..2d578df 100755
@@ -264,7 +264,7 @@ def main():
                             if not os.path.exists(repodir):
                                 out = utils.runcmd("git clone %s %s" % (layer.vcs_url, urldir), fetchdir, logger=logger, printerr=False)
                             else:
-                                out = utils.runcmd("git fetch", repodir, logger=logger, printerr=False)
+                                out = utils.runcmd("git fetch -p", repodir, logger=logger, printerr=False)
                         except subprocess.CalledProcessError as e:
                             logger.error("Fetch of layer %s failed: %s" % (layer.name, e.output))
                             failedrepos[layer.vcs_url] = e.output
@@ -279,7 +279,7 @@ def main():
                 if not os.path.exists(bitbakepath):
                     out = utils.runcmd("git clone %s %s" % (settings.BITBAKE_REPO_URL, 'bitbake'), fetchdir, logger=logger)
                 else:
-                    out = utils.runcmd("git fetch", bitbakepath, logger=logger)
+                    out = utils.runcmd("git fetch -p", bitbakepath, logger=logger)
 
             if options.actual_branch:
                 update_actual_branch(layerquery, fetchdir, branches[0], options, update_bitbake, bitbakepath)
Signature · 1 line
2.10.2
#10 lore raw

PATCH v5 10/10 update_layer.py: delete layerbranch for non-existed branch

In reply to Robert Yang

The branch is not needed any more when it has been removed from the repo, so we also need remove its layerbranch, otherwise it still can be got from the web, which causes confusions.

Note, we have to move the location of tinfiol's code to avoid creating tinfoil when not needed, otherewise, if tinfoil is created (but not needed), and the program exists earlier before "try ... finally" block, then tinfoil.shutdown() doesn't run so that it is not shutdown. Move the code back, right before where it is needed can fix the problem.

---
 layerindex/update_layer.py | 34 ++++++++++++++++++----------------
 1 file changed, 18 insertions(+), 16 deletions(-)
layerindex/update_layer.py +18 -16
index b6c2f4a..7f0c08f 100644
@@ -223,21 +223,6 @@ def main():
 
     bitbakepath = os.path.join(fetchdir, 'bitbake')
 
-    try:
-        (tinfoil, tempdir) = recipeparse.init_parser(settings, branch, bitbakepath, nocheckout=options.nocheckout, logger=logger)
-    except recipeparse.RecipeParseError as e:
-        logger.error(str(e))
-        sys.exit(1)
-    # Clear the default value of SUMMARY so that we can use DESCRIPTION instead if it hasn't been set
-    tinfoil.config_data.setVar('SUMMARY', '')
-    # Clear the default value of DESCRIPTION so that we can see where it's not set
-    tinfoil.config_data.setVar('DESCRIPTION', '')
-    # Clear the default value of HOMEPAGE ('unknown')
-    tinfoil.config_data.setVar('HOMEPAGE', '')
-    # Set a blank value for LICENSE so that it doesn't cause the parser to die (e.g. with meta-ti -
-    # why won't they just fix that?!)
-    tinfoil.config_data.setVar('LICENSE', '')
-
     layer = utils.get_layer(options.layer)
     urldir = layer.get_fetch_dir()
     repodir = os.path.join(fetchdir, urldir)
@@ -261,12 +246,29 @@ def main():
             topcommit = repo.commit('origin/%s' % branchname)
     except:
         if layerbranch:
-            logger.error("Failed update of layer %s - branch %s no longer exists" % (layer.name, branchdesc))
+            logger.info("layer %s - branch %s no longer exists, removing it from database" % (layer.name, branchdesc))
+            if not options.dryrun:
+                layerbranch.delete()
         else:
             logger.info("Skipping update of layer %s - branch %s doesn't exist" % (layer.name, branchdesc))
         sys.exit(1)
 
     try:
+        (tinfoil, tempdir) = recipeparse.init_parser(settings, branch, bitbakepath, nocheckout=options.nocheckout, logger=logger)
+    except recipeparse.RecipeParseError as e:
+        logger.error(str(e))
+        sys.exit(1)
+    # Clear the default value of SUMMARY so that we can use DESCRIPTION instead if it hasn't been set
+    tinfoil.config_data.setVar('SUMMARY', '')
+    # Clear the default value of DESCRIPTION so that we can see where it's not set
+    tinfoil.config_data.setVar('DESCRIPTION', '')
+    # Clear the default value of HOMEPAGE ('unknown')
+    tinfoil.config_data.setVar('HOMEPAGE', '')
+    # Set a blank value for LICENSE so that it doesn't cause the parser to die (e.g. with meta-ti -
+    # why won't they just fix that?!)
+    tinfoil.config_data.setVar('LICENSE', '')
+
+    try:
         with transaction.atomic():
             newbranch = False
             if not layerbranch:
Signature · 1 line
2.10.2
#11 lore raw
In reply to Robert Yang

Hi Robert / all,

This patchset finally got merged. I tweaked some of the commit messages, messages and comments but otherwise they went in unchanged. Thanks for sorting out these issues!

Cheers,
Paul

Quoted 8 lines · CEST Robert Yang

* V5
- Fix Paul's comments
- Add REMOVE_LAYER_DEPENDENCIES = False to settings, the _add_dependency() will check and remove dependencies which is not in conf/layer.conf when True (WARN when False)..

* V4
- Add an option -i to update_layer.py to get initial layer value

(dependencies,

Quoted 1 line

versions and so on), update.py doesn't use tinfoil any more since

tinfoil

Quoted 1 line

before morty can't be shutdown totally if the process is not exited,

this was

Quoted 10 lines

why splitted update_layer.py from update.py.
- Squash 11 patches (v3) into 10, since two of them have overlap.

* V3:
- Better error messages when required collections not found
- Delete layerbranch for non-existed branch
- update_layer.py: remove --update-dependencies

* V2:
- Remove "import bb" from update.py to avoid causing confusions when

switch branches.

Quoted 3 lines

* V1:
- The "-a ACTUAL_BRANCH" is a litle different from what we had talked, now

it

Quoted 1 line

respects "-l" and "-b", we can use -l to specify layers or default to

all,

Quoted 1 line

and it requires "-b" to work, only one branch is supported in a run, for

example:

Quoted 3 lines
    $ update.py -b master -a branch_20170526
    All the layers which have branch master and actual_branch 

branch_20170526

Quoted 8 lines
    will be updated to branch_20170526.

    $ update.py -b master -l meta-oe -a branch_20170526
    Only meta-oe layer will be updated.

    $ update.py -b master -l bitbake -a branch_20170526
    The bitbake's bitbake_branch will be updated.
Quoted 5 lines

// Robert

The following changes since commit b958a991caa947fde2ccb073e3cb7924e162562d:

layerindex/views: support querying by layer name (2017-06-15 16:04:14

+0200)

Quoted 5 lines

are available in the git repository at:

git://git.pokylinux.org/poky-contrib rbt/layerindex-10fixes
http://git.pokylinux.org/cgit.cgi/poky-contrib/log/?h=rbt/

layerindex-10fixes

Quoted 13 lines

Robert Yang (10):
update.py: update actual branch for layer and bitbake layerconfparse.py: remove unused layerbranch from parse_layer() utils.py: add REMOVE_LAYER_DEPENDENCIES to remove dependencies update_layer.py: set layerbranch's collection before add_dependencies recipeparse.py: restore cwd when the parsing is done update.py: update layers orderly
update_layer.py: remove --update-dependencies update_layer.py: move the location of transaction.atomic() update.py: add -p to git fetch
update_layer.py: delete layerbranch for non-existed branch

Quoted 4 lines
 layerindex/layerconfparse.py     |   2 +-
 layerindex/recipeparse.py        |   3 +
 layerindex/tools/import_layer.py |   2 +-
 layerindex/update.py             | 186 +++++++++++++++++++++++++++++
+---------
>  layerindex/update_layer.py       |  99 +++++++++------------
>  layerindex/utils.py              |  71 ++++++++++++++-
>  settings.py                      |   3 +
>  7 files changed, 263 insertions(+), 103 deletions(-)
Quoted 2 lines
Signature · 2 lines
Paul Eggleton
Intel Open Source Technology Centre
#12 lore raw

Hi Paul,

Thank you very much.

// Robert

Quoted 92 lines · Paul Eggleton

Hi Robert / all,

This patchset finally got merged. I tweaked some of the commit messages, messages and comments but otherwise they went in unchanged. Thanks for sorting out these issues!

Cheers,
Paul

Quoted 8 lines · CEST Robert Yang

* V5
- Fix Paul's comments
- Add REMOVE_LAYER_DEPENDENCIES = False to settings, the _add_dependency() will check and remove dependencies which is not in conf/layer.conf when True (WARN when False)..

* V4
- Add an option -i to update_layer.py to get initial layer value

(dependencies,

Quoted 1 line

versions and so on), update.py doesn't use tinfoil any more since

tinfoil

Quoted 1 line

before morty can't be shutdown totally if the process is not exited,

this was

Quoted 10 lines

why splitted update_layer.py from update.py.
- Squash 11 patches (v3) into 10, since two of them have overlap.

* V3:
- Better error messages when required collections not found
- Delete layerbranch for non-existed branch
- update_layer.py: remove --update-dependencies

* V2:
- Remove "import bb" from update.py to avoid causing confusions when

switch branches.

Quoted 3 lines

* V1:
- The "-a ACTUAL_BRANCH" is a litle different from what we had talked, now

it

Quoted 1 line

respects "-l" and "-b", we can use -l to specify layers or default to

all,

Quoted 1 line

and it requires "-b" to work, only one branch is supported in a run, for

example:

Quoted 3 lines
    $ update.py -b master -a branch_20170526
    All the layers which have branch master and actual_branch

branch_20170526

Quoted 8 lines
    will be updated to branch_20170526.

    $ update.py -b master -l meta-oe -a branch_20170526
    Only meta-oe layer will be updated.

    $ update.py -b master -l bitbake -a branch_20170526
    The bitbake's bitbake_branch will be updated.
Quoted 5 lines

// Robert

The following changes since commit b958a991caa947fde2ccb073e3cb7924e162562d:

layerindex/views: support querying by layer name (2017-06-15 16:04:14

+0200)

Quoted 5 lines

are available in the git repository at:

git://git.pokylinux.org/poky-contrib rbt/layerindex-10fixes
http://git.pokylinux.org/cgit.cgi/poky-contrib/log/?h=rbt/

layerindex-10fixes

Quoted 13 lines

Robert Yang (10):
update.py: update actual branch for layer and bitbake layerconfparse.py: remove unused layerbranch from parse_layer() utils.py: add REMOVE_LAYER_DEPENDENCIES to remove dependencies update_layer.py: set layerbranch's collection before add_dependencies recipeparse.py: restore cwd when the parsing is done update.py: update layers orderly
update_layer.py: remove --update-dependencies update_layer.py: move the location of transaction.atomic() update.py: add -p to git fetch
update_layer.py: delete layerbranch for non-existed branch

Quoted 4 lines
 layerindex/layerconfparse.py     |   2 +-
 layerindex/recipeparse.py        |   3 +
 layerindex/tools/import_layer.py |   2 +-
 layerindex/update.py             | 186 +++++++++++++++++++++++++++++
+---------
>  layerindex/update_layer.py       |  99 +++++++++------------
>  layerindex/utils.py              |  71 ++++++++++++++-
>  settings.py                      |   3 +
>  7 files changed, 263 insertions(+), 103 deletions(-)
Quoted 2 lines