From 35a215b23d78843d2ccc194a6d353418fd406efc Mon Sep 17 00:00:00 2001
From: Andre Anjos <andre.dos.anjos@gmail.com>
Date: Tue, 12 Feb 2019 10:31:12 +0100
Subject: [PATCH] [build] Check specific version of python builds (if needed);
 Be more consistent when outputing package names

---
 bob/devtools/build.py | 118 +++++++++++++++++++++++++++---------------
 1 file changed, 77 insertions(+), 41 deletions(-)

diff --git a/bob/devtools/build.py b/bob/devtools/build.py
index da63fc44..4f475d26 100644
--- a/bob/devtools/build.py
+++ b/bob/devtools/build.py
@@ -154,7 +154,8 @@ def get_parsed_recipe(metadata):
   return yaml.load(output)
 
 
-def exists_on_channel(channel_url, name, version, build_number):
+def exists_on_channel(channel_url, name, version, build_number,
+    python_version):
   """Checks on the given channel if a package with the specs exist
 
   Args:
@@ -164,31 +165,48 @@ def exists_on_channel(channel_url, name, version, build_number):
     name: The name of the package
     version: The version of the package
     build_number: The build number of the package
+    python_version: The current version of python we're building for
 
-  Returns: ``True``, if the package already exists in the channel or ``False``
-  otherwise
+  Returns: A complete package name, version and build string, if the package
+  already exists in the channel or ``None`` otherwise.
 
   """
 
   from conda.exports import get_index
 
+  # no dot in py_ver
+  py_ver = python_version.replace('.', '')
+
   # get the channel index
   logger.debug('Downloading channel index from %s', channel_url)
   index = get_index(channel_urls=[channel_url], prepend=False)
 
-  logger.info('Checking for %s-%s_*_%s...', name, version, build_number)
+  logger.info('Checking for %s-%s-%s...', name, version, build_number)
+
   for dist in index:
 
     if dist.name == name and dist.version == version and \
         dist.build_string.endswith('_%s' % build_number):
-      match = re.match('py[2-9][0-9]+', dist.build_string)
-      logger.info('Found matching package (%s-%s_%s)', dist.name, dist.version,
-          dist.build_string)
-      return True
 
-  logger.info('No matches for %s-%s_%s found among %d packages',
-      name, version, build_number, len(index))
-  return False
+      # two possible options must be checked - (i) the package build_string
+      # starts with ``py``, which means it is a python specific package so we
+      # must also check for the matching python version.  (ii) the package is
+      # not a python-specific package and a simple match will do
+      if dist.build_string.startswith('py'):
+        match = re.match('py[2-9][0-9]+', dist.build_string)
+        if match and match.group() == 'py{}'.format(py_ver):
+          logger.debug('Found matching package (%s-%s-%s)', dist.name,
+              dist.version, dist.build_string)
+          return (dist.name, dist.version, dist.build_string)
+
+      else:
+        logger.debug('Found matching package (%s-%s-%s)', dist.name,
+            dist.version, dist.build_string)
+        return (dist.name, dist.version, dist.build_string)
+
+  logger.info('No matches for %s-%s-(py%s_?)%s found among %d packages',
+      name, version, py_ver, build_number, len(index))
+  return
 
 
 def remove_pins(deps):
@@ -444,7 +462,8 @@ def git_clean_build(runner, verbose):
       ['--exclude=%s' % k for k in exclude_from_cleanup])
 
 
-def base_build(server, intranet, recipe_dir, config):
+def base_build(server, intranet, recipe_dir, conda_build_config,
+    python_version, condarc_options):
   '''Builds a non-beat/bob software dependence that does not exist on defaults
 
   This function will build a software dependence that is required for our
@@ -460,33 +479,49 @@ def base_build(server, intranet, recipe_dir, config):
     intranet: Boolean indicating if we should add "private"/"public" prefixes
       on the returned paths
     recipe_dir: The directory containing the recipe's ``meta.yaml`` file
-    config: A dictionary containing the merged configuration, as produced by
-      conda-build API's ``get_or_merge_config()`` function
+    conda_build_config: Path to the ``conda_build_config.yaml`` file to use
+    python_version: String with the python version to build for, in the format
+      ``x.y`` (should be passed even if not building a python package)
+    condarc_options: Pre-parsed condarc options loaded from the respective YAML
+      file
 
   '''
 
   # if you get to this point, tries to build the package
-  public_channel = bootstrap.get_channels(public=True, stable=True,
-    server=server, intranet=intranet)[0]
-  metadata = get_rendered_metadata(recipe_dir, config)
+  public_channels = bootstrap.get_channels(public=True, stable=True,
+    server=server, intranet=intranet)
+
+  logger.info('Using the following channels during (potential) build:\n  - %s',
+      '\n  - '.join(public_channels + ['defaults']))
+  condarc_options['channels'] = public_channels + ['defaults']
+
+  logger.info('Merging conda configuration files...')
+  conda_config = make_conda_config(conda_build_config, python_version,
+      None, condarc_options)
+
+  metadata = get_rendered_metadata(recipe_dir, conda_config)
   recipe = get_parsed_recipe(metadata)
 
   if recipe is None:
-    logger.warn('Skipping build for %s - rendering returned None', recipe_dir)
+    logger.info('Skipping build for %s - rendering returned None', recipe_dir)
     return
 
-  if exists_on_channel(public_channel, recipe['package']['name'],
-      recipe['package']['version'], recipe['build']['number']):
-    logger.warn('Skipping build for %s-%s_%s - exists on channel already',
-        recipe['package']['name'], recipe['package']['version'],
-        recipe['build']['number'])
+  # no dot in py_ver
+  py_ver = python_version.replace('.', '')
+  arch = conda_arch()
+
+  candidate = exists_on_channel(public_channels[0], recipe['package']['name'],
+      recipe['package']['version'], recipe['build']['number'],
+      python_version)
+  if candidate is not None:
+    logger.info('Skipping build for %s-%s-(py%s_?)%s for %s - exists ' \
+        'on channel', candidate[0], candidate[1], candidate[2], py_ver)
     return
 
   # if you get to this point, just builds the package
-  arch = conda_arch()
-  logger.info('Building %s-%s (build: %d) for %s',
+    logger.info('Building %s-%s-(py%s_?)%s for %s',
       recipe['package']['name'], recipe['package']['version'],
-      recipe['build']['number'], arch)
+      recipe['build']['number'], py_ver, arch)
   conda_build.api.build(recipe_dir, config=conda_config)
 
 
@@ -559,23 +594,9 @@ if __name__ == '__main__':
   with open(condarc, 'rb') as f:
     condarc_options = yaml.load(f)
 
-  # notice this condarc typically will only contain the defaults channel - we
-  # need to boost this up with more channels to get it right for this package's
-  # build
-  public = ( args.visibility == 'public' )
-  channels = bootstrap.get_channels(public=public, stable=(not is_prerelease),
-      server=server, intranet=(not args.internet))
-  logger.info('Using the following channels during build:\n  - %s',
-      '\n  - '.join(channels + ['defaults']))
-  condarc_options['channels'] = channels + ['defaults']
-
   # dump packages at conda_root
   condarc_options['croot'] = os.path.join(args.conda_root, 'conda-bld')
 
-  logger.info('Merging conda configuration files...')
-  conda_config = make_conda_config(conda_build_config, args.python_version,
-      recipe_append, condarc_options)
-
   # builds all dependencies in the 'deps' subdirectory - or at least checks
   # these dependencies are already available; these dependencies go directly to
   # the public channel once built
@@ -583,13 +604,28 @@ if __name__ == '__main__':
     if not os.path.exists(os.path.join(recipe, 'meta.yaml')):
       # ignore - not a conda package
       continue
-    base_build(server, not args.internet, recipe, conda_config)
+    base_build(server, not args.internet, recipe, conda_build_config,
+        args.python_version, condarc_options)
+
+  # notice this condarc typically will only contain the defaults channel - we
+  # need to boost this up with more channels to get it right for this package's
+  # build
+  public = ( args.visibility == 'public' )
+  channels = bootstrap.get_channels(public=public, stable=(not is_prerelease),
+      server=server, intranet=(not args.internet))
+  logger.info('Using the following channels during build:\n  - %s',
+      '\n  - '.join(channels + ['defaults']))
+  condarc_options['channels'] = channels + ['defaults']
 
   # retrieve the current build number for this build
   build_number, _ = next_build_number(channels[0], args.name, version,
       args.python_version)
   bootstrap.set_environment('BOB_BUILD_NUMBER', str(build_number))
 
+  logger.info('Merging conda configuration files...')
+  conda_config = make_conda_config(conda_build_config, args.python_version,
+      recipe_append, condarc_options)
+
   # runs the build using the conda-build API
   arch = conda_arch()
   logger.info('Building %s-%s-py%s (build: %d) for %s',
-- 
GitLab