diff --git a/MANIFEST.in b/MANIFEST.in index 09fc130a0450115612e5063b6a063347d2b9092a..c99bfdf4c4f9d4a01d05a514702d8027d1adc417 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,3 @@ include LICENSE README.rst buildout.cfg version.txt recursive-include doc conf.py *.rst -recursive-include bob/devtools/data *.md +recursive-include bob/devtools/data *.md *.yaml buid-condarc diff --git a/bob/devtools/bootstrap.py b/bob/devtools/bootstrap.py new file mode 100755 index 0000000000000000000000000000000000000000..90700df89be871be60e9f4ac7630ebff305fa671 --- /dev/null +++ b/bob/devtools/bootstrap.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# vim: set fileencoding=utf-8 : + +import os +import json +import shutil +import subprocess + +import logging +logger = logging.getLogger(__name__) + +import yaml +from conda_build.api import get_or_merge_config, render, output_yaml + + +def get_rendered_recipe(conda, recipe_dir, python, config): + '''Renders the recipe and returns the interpreted YAML file''' + + # equivalent command execute - in here we use the conda API + cmd = [ + conda, 'render', + '--variant-config-files', config, + '--python', python, + recipe_dir, + ] + logger.debug('$ ' + ' '.join(cmd)) + + # do the real job + config = get_or_merge_config(None, variant_config_files=config, + python=python) + metadata = render(recipe_dir, config=config) + output = output_yaml(metadata[0][0]) + return yaml.load(output) + + +def remove_pins(deps): + return [l.split()[0] for l in deps] + + +def parse_dependencies(conda, recipe_dir, python, config): + + recipe = get_rendered_recipe(conda, recipe_dir, python, config) + return remove_pins(recipe['requirements'].get('build', [])) + \ + remove_pins(recipe['requirements'].get('host', [])) + \ + recipe['requirements'].get('run', []) + \ + recipe.get('test', {}).get('requires', []) + \ + ['bob.buildout', 'mr.developer', 'ipdb'] + # by last, packages required for local dev + + +def get_env_directory(conda, name): + + cmd = [conda, 'env', 'list', '--json'] + output = subprocess.check_output(cmd) + data = json.loads(output) + retval = [k for k in data.get('envs', []) if k.endswith(os.sep + name)] + if retval: + return retval[0] + return None + + +def conda_create(conda, name, overwrite, condarc, packages, dry_run): + + specs = [] + for k in packages: + k = ' '.join(k.split()[:2]) # remove eventual build string + if any(elem in k for elem in '><|'): + specs.append(k.replace(' ', '')) + else: + specs.append(k.replace(' ', '=')) + + # if the current environment exists, delete it first + envdir = get_env_directory(conda, name) + if envdir is not None: + if overwrite: + cmd = [conda, 'env', 'remove', '--yes', '--name', name] + logger.debug('$ ' + ' '.join(cmd)) + if not dry_run: + status = subprocess.call(cmd) + if status != 0: + return status + else: + raise RuntimeError('environment `%s\' exists in `%s\' - use ' + '--overwrite to overwrite' % (name, envdir)) + + cmd = [conda, 'create', '--yes', '--name', name] + sorted(specs) + if dry_run: + cmd.insert(2, '--dry-run') + logger.debug('$ ' + ' '.join(cmd)) + status = subprocess.call(cmd) + if status != 0: + return status + + # copy the used condarc file to the just created environment + if not dry_run: + # get envdir again - it may just be created! + envdir = get_env_directory(conda, name) + destrc = os.path.join(envdir, '.condarc') + logger.debug('$ cp %s -> %s' % (condarc, destrc)) + shutil.copy2(condarc, destrc) + + return status diff --git a/bob/devtools/data/build-condarc b/bob/devtools/data/build-condarc new file mode 100644 index 0000000000000000000000000000000000000000..aacfae4cbafcce6f1e0de3a84d0e826cdfe7a1cb --- /dev/null +++ b/bob/devtools/data/build-condarc @@ -0,0 +1,14 @@ +default_channels: + - https://repo.anaconda.com/pkgs/main + - https://repo.anaconda.com/pkgs/free + - https://repo.anaconda.com/pkgs/r + - https://repo.anaconda.com/pkgs/pro +add_pip_as_python_dependency: false +show_channel_urls: true +anaconda_upload: false +ssl_verify: false +quiet: true +channels: + - https://www.idiap.ch/software/bob/conda/label/beta + - https://www.idiap.ch/software/bob/conda + - defaults diff --git a/bob/devtools/data/conda_build_config.yaml b/bob/devtools/data/conda_build_config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7c9e4ffd87a73e6d49982fb5f7e14ebf47786de5 --- /dev/null +++ b/bob/devtools/data/conda_build_config.yaml @@ -0,0 +1,223 @@ +macos_min_version: + - 10.9 +macos_machine: + - x86_64-apple-darwin13.4.0 +MACOSX_DEPLOYMENT_TARGET: + - 10.9 +CONDA_BUILD_SYSROOT: # [osx] + - /opt/MacOSX10.9.sdk # [osx] +# This helps CMAKE find the sysroot. See +# https://cmake.org/cmake/help/v3.11/variable/CMAKE_OSX_SYSROOT.html +SDKROOT: # [osx] + - /opt/MacOSX10.9.sdk # [osx] +# makes autotools verbose +VERBOSE_AT: + - V=1 +# makes cmake verbose +VERBOSE_CM: + - VERBOSE=1 + +# the blas implementations that we build against +blas_impl: + - mkl + +pin_run_as_build: + libboost: + max_pin: x.x.x + py_boost: + max_pin: x.x.x + boost: + max_pin: x.x.x + +## the dependencies that we build against multiple versions +python: + - 3.6 + +zip_keys: + - # [win] + - vc # [win] + - c_compiler # [win] + - cxx_compiler # [win] + - fortran_compiler_version # [win] + - python # [win] + + +# Here is the version of dependencies are used when building packages (build +# and host requirements). We keep a list of **all of them** here to make sure +# everything goes as expected in our conda build process. For the version of +# packages that are used for testing packages, see the recipe of bob-devel. +# The version here do not necessarily match the versions in bob-devel. + +# This version of bob-devel will be used at test time of packages: +bob_devel: + - 2018.12.29 + +# This version of beat-devel will be used at test time of packages. Notice it +# uses bob-devel and should have a version that is greater or equal its value +beat_devel: + - 2018.12.29 + +# The build time only dependencies (build requirements). +# Updating these to the latest version all the time is OK and a good idea. +# These versions should match the versions inside bob-devel as well (if they +# overlap) so update them in both places. +cmake: + - 3.12.2 +pkg_config: + - 0.29.2 +cython: + - 0.28.1 + +# The dependencies that we link against (host requirements). +# Ideally we want to build against the oldest possible version of packages when +# we are linking against them. It is best to keep this in sync with: +# https://github.com/AnacondaRecipes/aggregate/blob/master/conda_build_config.yaml +numpy: + - 1.14.5 +boost: + - 1.65.1 +cyvlfeat: + - 0.4.6 +ffmpeg: + - 3.4 +freetype: + - 2.8 +giflib: + - 5.1.4 +hdf5: + - 1.10.1 +jpeg: + - 9b +libblitz: + - 1.0.1 +libmatio: + - 1.5.11 +libogg: + - 1.3.2 +libpng: + - 1.6.35 +libsvm: + - 3.22 +libtiff: + - 4.0.9 +mkl: + - 2018.0.3 +openfst: + - 1.6.1 +sox: + - 14.4.2 +speex: + - 1.2.0 +speexdsp: + - 1.2rc3 +sqlite: + - 3.20.1 +vlfeat: + - 0.9.21 +xz: + - 5.2.3 +zlib: + - 1.2.11 + +# The dependencies that are needed for runtime only (run requirements). +# These versions **should** match the versions inside bob-devel recipe. +caffe: + - 1.0 +click: + - 6.7 +click_plugins: + - 1.0.3 +coverage: + - 4.5.1 +dlib: + - 19.7 +docopt: + - 0.6.2 +jinja2: + - 2.10 +kaldi: + - 2017.03.13 +matplotlib: + - 2.2.3 +menpo: + - 0.8.1 +menpofit: + - 0.5.0 +mne: + - 0.15.2 +mr_developer: + - 1.38 +nose: + - 1.3.7 +opencv: + - 3.3.1 +pillow: + - 5.2.0 +pyedflib: + - 0.1.11 +pytorch: + - 0.4.1 +pyyaml: + - 3.13 +requests: + - 2.19.1 +schema: + - 0.6.7 +scikit_image: + - 0.14 +scikit_learn: + - 0.19.2 +scipy: + - 1.1.0 +setuptools: + - 40.2.0 +six: + - 1.11.0 +sphinx: + - 1.8.1 +sphinx_rtd_theme: + - 0.4.1 +sqlalchemy: + - 1.2.11 +tabulate: + - 0.8.2 +tensorflow: + - 1.9.0 +torchvision: + - 0.2.1 +zc_buildout: + - 2.12.2 +zc_recipe_egg: + - 2.0.7 + +# The dependencies that are needed for runtime only (run requirements) of BEAT +# packages. These versions **should** match the versions inside beat-devel +# recipe. +docker_py: + - 3.6.0 +jsonschema: + - 2.6.0 +oset: + - 0.1.3 +python_graphviz: + - 0.8.4 +pyzmq: + - 17.1.2 +simplejson: + - 3.16.0 +termcolor: + - 1.1.0 +sphinxcontrib_programoutput: + - 0.11 +sphinxcontrib_httpdomain: + - 1.7.0 +nodejs: + - 8.9.3 +flask: + - 1.0.2 +flask_cors: + - 3.0.7 +flask_restful: + - 0.3.6 +psycopg2: + - 2.7.6.1 diff --git a/bob/devtools/scripts/bootstrap.py b/bob/devtools/scripts/bootstrap.py new file mode 100644 index 0000000000000000000000000000000000000000..d01cb3f5b9f71b55d3ea792173df62ea0e9a115d --- /dev/null +++ b/bob/devtools/scripts/bootstrap.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import sys +import logging +logger = logging.getLogger(__name__) + +import pkg_resources +import click + +from . import bdt +from ..log import verbosity_option +from ..bootstrap import parse_dependencies, conda_create + + +DEFAULT_CONDARC = pkg_resources.resource_filename(__name__, + os.path.join('..', 'data', 'build-condarc')) +DEFAULT_VARIANT = pkg_resources.resource_filename(__name__, + os.path.join('..', 'data', 'conda_build_config.yaml')) + + +@click.command(epilog=''' +Examples: + + 1. Creates an environment called `myenv' for developing the currently checked-out package (N.B.: first activate the base environment): + +\b + $ cd bob.package.foo + $ bdt bootstrap -vv myenv + + The above command assumes the directory `conda' exists on the current directory and that it contains a file called `meta.yaml' containing the recipe for the package you want to create a development environment for. + + If you get things right by default, the above form is the typical usage scenario of this app. Read-on to tweak special flags and settings. + + + 2. By default, we use the native python version of your conda installation as the default python version to use for the newly created environment. You may select a different one with `--python=X.Y': + + $ bdt bootstrap -vv --python=3.6 myenv + + + 3. By default, we use our own condarc and `conda_build_config.yaml` files that are used in creating packages for our CI/CD system. If you wish to use your own, specify them on the command line: + + $ bdt bootstrap -vv --python=3.6 --config=config.yaml --condarc=~/.condarc myenv + + Notice the condarc file **must** end in `condarc', or conda will complain. + + + 4. You can use the option `--dry-run' to simulate what would be installed + instead of actually creating a new environment. Combine with `-vvv` to + enable debug printing. Equivalent conda commands you can execute on the + shell will be printed: + + + $ bdt bootstrap -vvv --dry-run myenv +''') +@click.argument('name') +@click.argument('recipe-dir', required=False, type=click.Path(file_okay=False, + dir_okay=True, exists=True)) +@click.option('-p', '--python', default=('%d.%d' % sys.version_info[:2]), + show_default=True, help='Version of python to build the ' \ + 'environment for [default: %(default)s]') +@click.option('-o', '--overwrite/--no-overwrite', default=False, + help='If set and an environment with the same name exists, ' \ + 'deletes it first before creating the new environment', + show_default=True) +@click.option('-r', '--condarc', default=DEFAULT_CONDARC, show_default=True, + help='overwrites the path leading to the condarc file to use',) +@click.option('-m', '--config', '--variant-config-files', show_default=True, + default=DEFAULT_VARIANT, help='overwrites the path leading to ' \ + 'variant configuration file to use') +@click.option('-d', '--dry-run/--no-dry-run', default=False, + help='Only goes through the actions, but does not execute them ' \ + '(combine with the verbosity flags - e.g. ``-vvv``) to enable ' \ + 'printing to help you understand what will be done') +@verbosity_option() +@bdt.raise_on_error +def bootstrap(name, recipe_dir, python, overwrite, condarc, config, dry_run): + """This program uses conda to build a development environment for a recipe + + It uses the conda render API to render a recipe and install an environment + containing all build/host, run and test dependencies of a package. It does + **not** build the package itself, just install dependencies so you can build + the package by hand, possibly using buildout or similar. If you'd like to + conda-build your package, just use `conda build` instead. + + Once the environment is created, a copy of the used `condarc' file is placed + on the root of the environment. Installing or updating packages on the newly + created environment should be possible without further configuration. Notice + that beta packages quickly get outdated and upgrading may no longer be + possible for aging development environments. You're advised to always re-use + this app and use the flag `--overwrite` to re-create from scratch the + development environment. + """ + + recipe_dir = recipe_dir or os.path.join(os.path.realpath('.'), 'conda') + + if not os.path.exists(recipe_dir): + raise RuntimeError("The directory %s does not exist" % recipe_dir) + + conda = os.environ.get('CONDA_EXE') + if conda is None: + raise RuntimeError("Cannot find `conda' executable (${CONDA_EXEC}) - " \ + "have you activated the build environment containing bob.devtools " \ + "properly?") + + # set condarc before continuing + logger.debug('$ export CONDARC=%s', condarc) + os.environ['CONDARC'] = condarc + + deps = parse_dependencies(conda, recipe_dir, python, config) + status = conda_create(conda, name, overwrite, condarc, deps, dry_run) + click.echo('Execute on your shell: "conda activate %s"' % name) diff --git a/conda/conda_build_config.yaml b/conda/conda_build_config.yaml deleted file mode 100644 index 7c9e4ffd87a73e6d49982fb5f7e14ebf47786de5..0000000000000000000000000000000000000000 --- a/conda/conda_build_config.yaml +++ /dev/null @@ -1,223 +0,0 @@ -macos_min_version: - - 10.9 -macos_machine: - - x86_64-apple-darwin13.4.0 -MACOSX_DEPLOYMENT_TARGET: - - 10.9 -CONDA_BUILD_SYSROOT: # [osx] - - /opt/MacOSX10.9.sdk # [osx] -# This helps CMAKE find the sysroot. See -# https://cmake.org/cmake/help/v3.11/variable/CMAKE_OSX_SYSROOT.html -SDKROOT: # [osx] - - /opt/MacOSX10.9.sdk # [osx] -# makes autotools verbose -VERBOSE_AT: - - V=1 -# makes cmake verbose -VERBOSE_CM: - - VERBOSE=1 - -# the blas implementations that we build against -blas_impl: - - mkl - -pin_run_as_build: - libboost: - max_pin: x.x.x - py_boost: - max_pin: x.x.x - boost: - max_pin: x.x.x - -## the dependencies that we build against multiple versions -python: - - 3.6 - -zip_keys: - - # [win] - - vc # [win] - - c_compiler # [win] - - cxx_compiler # [win] - - fortran_compiler_version # [win] - - python # [win] - - -# Here is the version of dependencies are used when building packages (build -# and host requirements). We keep a list of **all of them** here to make sure -# everything goes as expected in our conda build process. For the version of -# packages that are used for testing packages, see the recipe of bob-devel. -# The version here do not necessarily match the versions in bob-devel. - -# This version of bob-devel will be used at test time of packages: -bob_devel: - - 2018.12.29 - -# This version of beat-devel will be used at test time of packages. Notice it -# uses bob-devel and should have a version that is greater or equal its value -beat_devel: - - 2018.12.29 - -# The build time only dependencies (build requirements). -# Updating these to the latest version all the time is OK and a good idea. -# These versions should match the versions inside bob-devel as well (if they -# overlap) so update them in both places. -cmake: - - 3.12.2 -pkg_config: - - 0.29.2 -cython: - - 0.28.1 - -# The dependencies that we link against (host requirements). -# Ideally we want to build against the oldest possible version of packages when -# we are linking against them. It is best to keep this in sync with: -# https://github.com/AnacondaRecipes/aggregate/blob/master/conda_build_config.yaml -numpy: - - 1.14.5 -boost: - - 1.65.1 -cyvlfeat: - - 0.4.6 -ffmpeg: - - 3.4 -freetype: - - 2.8 -giflib: - - 5.1.4 -hdf5: - - 1.10.1 -jpeg: - - 9b -libblitz: - - 1.0.1 -libmatio: - - 1.5.11 -libogg: - - 1.3.2 -libpng: - - 1.6.35 -libsvm: - - 3.22 -libtiff: - - 4.0.9 -mkl: - - 2018.0.3 -openfst: - - 1.6.1 -sox: - - 14.4.2 -speex: - - 1.2.0 -speexdsp: - - 1.2rc3 -sqlite: - - 3.20.1 -vlfeat: - - 0.9.21 -xz: - - 5.2.3 -zlib: - - 1.2.11 - -# The dependencies that are needed for runtime only (run requirements). -# These versions **should** match the versions inside bob-devel recipe. -caffe: - - 1.0 -click: - - 6.7 -click_plugins: - - 1.0.3 -coverage: - - 4.5.1 -dlib: - - 19.7 -docopt: - - 0.6.2 -jinja2: - - 2.10 -kaldi: - - 2017.03.13 -matplotlib: - - 2.2.3 -menpo: - - 0.8.1 -menpofit: - - 0.5.0 -mne: - - 0.15.2 -mr_developer: - - 1.38 -nose: - - 1.3.7 -opencv: - - 3.3.1 -pillow: - - 5.2.0 -pyedflib: - - 0.1.11 -pytorch: - - 0.4.1 -pyyaml: - - 3.13 -requests: - - 2.19.1 -schema: - - 0.6.7 -scikit_image: - - 0.14 -scikit_learn: - - 0.19.2 -scipy: - - 1.1.0 -setuptools: - - 40.2.0 -six: - - 1.11.0 -sphinx: - - 1.8.1 -sphinx_rtd_theme: - - 0.4.1 -sqlalchemy: - - 1.2.11 -tabulate: - - 0.8.2 -tensorflow: - - 1.9.0 -torchvision: - - 0.2.1 -zc_buildout: - - 2.12.2 -zc_recipe_egg: - - 2.0.7 - -# The dependencies that are needed for runtime only (run requirements) of BEAT -# packages. These versions **should** match the versions inside beat-devel -# recipe. -docker_py: - - 3.6.0 -jsonschema: - - 2.6.0 -oset: - - 0.1.3 -python_graphviz: - - 0.8.4 -pyzmq: - - 17.1.2 -simplejson: - - 3.16.0 -termcolor: - - 1.1.0 -sphinxcontrib_programoutput: - - 0.11 -sphinxcontrib_httpdomain: - - 1.7.0 -nodejs: - - 8.9.3 -flask: - - 1.0.2 -flask_cors: - - 3.0.7 -flask_restful: - - 0.3.6 -psycopg2: - - 2.7.6.1 diff --git a/conda/conda_build_config.yaml b/conda/conda_build_config.yaml new file mode 120000 index 0000000000000000000000000000000000000000..dbdbc0f110d96e2e6a1cdf7c1c1ae3c2bc9b0445 --- /dev/null +++ b/conda/conda_build_config.yaml @@ -0,0 +1 @@ +../bob/devtools/data/conda_build_config.yaml \ No newline at end of file diff --git a/conda/meta.yaml b/conda/meta.yaml index 21ddbc1150e197cf94089cd35f486253a8608ce3..b46bad8903b09fc87d32f66d4cf06fd3ad28f36a 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -40,6 +40,7 @@ requirements: - python-gitlab - requests - sphinx + - pyyaml test: requires: @@ -55,6 +56,7 @@ test: - bdt release --help - bdt visibility --help - bdt dumpsphinx --help + - bdt bootstrap --help - sphinx-build -aEW ${PREFIX}/share/doc/{{ name }}/doc {{ project_dir }}/sphinx about: diff --git a/setup.py b/setup.py index 103b99b713b4fd9b28a3af938305c0db41b4db39..d5a8e803dc9f10e924f8f392a909a842a68c31ee 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,7 @@ requires = [ 'gitpython', 'python-gitlab', 'sphinx', + 'pyyaml', ] setup( @@ -44,6 +45,7 @@ setup( 'lasttag = bob.devtools.scripts.lasttag:lasttag', 'visibility = bob.devtools.scripts.visibility:visibility', 'dumpsphinx = bob.devtools.scripts.dumpsphinx:dumpsphinx', + 'bootstrap = bob.devtools.scripts.bootstrap:bootstrap', ], }, classifiers=[