build.py 6.09 KB
Newer Older
1
2
3
4
5
6
7
8
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import logging
logger = logging.getLogger(__name__)

9
import yaml
André Anjos's avatar
André Anjos committed
10
11
12
import click
import pkg_resources
import conda_build.api
13
14
15

from . import bdt
from ..log import verbosity_option
16
from ..build import next_build_number, conda_arch, should_skip_build, \
17
18
    get_rendered_metadata, get_parsed_recipe, make_conda_config, \
    get_docserver_setup
19
20
21
from ..constants import CONDA_BUILD_CONFIG, CONDA_RECIPE_APPEND, \
    SERVER, MATPLOTLIB_RCDIR, BASE_CONDARC
from ..bootstrap import set_environment, get_channels
22
23
24
25
26
27
28


@click.command(epilog='''
Examples:

  1. Builds recipe from one of our build dependencies (inside bob.conda):

29
\b
30
31
32
33
     $ cd bob.conda
     $ bdt build -vv conda/libblitz


34
  2. Builds recipe from one of our packages, for Python 3.6 (if that is not already the default for you):
35
36
37
38
39
40

     $ bdt build --python=3.6 -vv path/to/conda/dir


  3. To build multiple recipes, just pass the paths to them:

41
     $ bdt build --python=3.6 -vv path/to/recipe-dir1 path/to/recipe-dir2
42
43
44
45
46
47
''')
@click.argument('recipe-dir', required=False, type=click.Path(file_okay=False,
  dir_okay=True, exists=True), nargs=-1)
@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]')
48
49
@click.option('-r', '--condarc',
    help='Use custom conda configuration file instead of our own',)
50
@click.option('-m', '--config', '--variant-config-files', show_default=True,
51
52
    default=CONDA_BUILD_CONFIG, help='overwrites the path leading to ' \
        'variant configuration file to use')
53
54
55
@click.option('-n', '--no-test', is_flag=True,
    help='Do not test the package, only builds it')
@click.option('-a', '--append-file', show_default=True,
56
57
58
59
60
61
62
63
64
65
66
67
68
    default=CONDA_RECIPE_APPEND, help='overwrites the path leading to ' \
        'appended configuration file to use')
@click.option('-S', '--server', show_default=True,
    default='https://www.idiap.ch/software/bob', help='Server used for ' \
    'downloading conda packages and documentation indexes of required packages')
@click.option('-P', '--private/--no-private', default=False,
    help='Set this to **include** private channels on your build - ' \
        'you **must** be at Idiap to execute this build in this case - ' \
        'you **must** also use the correct server name through --server - ' \
        'notice this option has no effect if you also pass --condarc')
@click.option('-X', '--stable/--no-stable', default=False,
    help='Set this to **exclude** beta channels from your build - ' \
        'notice this option has no effect if you also pass --condarc')
69
70
71
72
73
74
@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
75
76
def build(recipe_dir, python, condarc, config, no_test, append_file,
    server, private, stable, dry_run):
André Anjos's avatar
André Anjos committed
77
  """Builds package through conda-build with stock configuration
78
79

  This command wraps the execution of conda-build so that you use the same
80
81
  conda configuration we use for our CI.  It always set
  ``--no-anaconda-upload``.
82
83
84
85
86
87
88
89
90
91
92
93

  Note that both files are embedded within bob.devtools - you may need to
  update your environment before trying this.
  """

  # if we are in a dry-run mode, let's let it be known
  if dry_run:
      logger.warn('!!!! DRY RUN MODE !!!!')
      logger.warn('Nothing will be really built')

  recipe_dir = recipe_dir or [os.path.join(os.path.realpath('.'), 'conda')]

94
95
96
  # get potential channel upload and other auxiliary channels
  channels = get_channels(public=(not private), stable=stable, server=server,
      intranet=private)
97

98
99
100
101
102
103
104
  if condarc is not None:
    logger.info('Loading CONDARC file from %s...', condarc)
    with open(condarc, 'rb') as f:
      condarc_options = yaml.load(f)
  else:
    # use default and add channels
    condarc_options = yaml.load(BASE_CONDARC)  #n.b.: no channels
André Anjos's avatar
André Anjos committed
105
106
    logger.info('Using the following channels during build:\n  - %s',
        '\n  - '.join(channels + ['defaults']))
107
108
109
    condarc_options['channels'] = channels + ['defaults']

  conda_config = make_conda_config(config, python, append_file, condarc_options)
110

André Anjos's avatar
André Anjos committed
111
  set_environment('MATPLOTLIBRC', MATPLOTLIB_RCDIR, verbose=True)
112

113
114
  # setup BOB_DOCUMENTATION_SERVER environment variable (used for bob.extension
  # and derived documentation building via Sphinx)
André Anjos's avatar
André Anjos committed
115
  set_environment('DOCSERVER', server, verbose=True)
116
117
  doc_urls = get_docserver_setup(public=(not private), stable=stable,
      server=server, intranet=private)
André Anjos's avatar
André Anjos committed
118
  set_environment('BOB_DOCUMENTATION_SERVER', doc_urls, verbose=True)
119

120
121
122
123
124
125
126
127
  for d in recipe_dir:

    if not os.path.exists(d):
      raise RuntimeError("The directory %s does not exist" % recipe_dir)

    version_candidate = os.path.join(d, '..', 'version.txt')
    if os.path.exists(version_candidate):
      version = open(version_candidate).read().rstrip()
André Anjos's avatar
André Anjos committed
128
      set_environment('BOB_PACKAGE_VERSION', version, verbose=True)
129
130
131
132
133

    # pre-renders the recipe - figures out package name and version
    metadata = get_rendered_metadata(d, conda_config)

    # checks we should actually build this recipe
134
    arch = conda_arch()
135
136
    if should_skip_build(metadata):
      logger.warn('Skipping UNSUPPORTED build of "%s" for py%s on %s',
137
          d, python.replace('.',''), arch)
138
139
140
141
142
143
      return 0

    # converts the metadata output into parsed yaml and continues the process
    rendered_recipe = get_parsed_recipe(metadata)

    # if a channel URL was passed, set the build number
André Anjos's avatar
André Anjos committed
144
145
146
    build_number, _ = next_build_number(channels[0],
        rendered_recipe['package']['name'],
        rendered_recipe['package']['version'], python)
147

André Anjos's avatar
André Anjos committed
148
    set_environment('BOB_BUILD_NUMBER', str(build_number), verbose=True)
149
150
151
152

    logger.info('Building %s-%s-py%s (build: %d) for %s',
        rendered_recipe['package']['name'],
        rendered_recipe['package']['version'], python.replace('.',''),
153
        build_number, arch)
154
    if not dry_run:
André Anjos's avatar
André Anjos committed
155
      conda_build.api.build(d, config=conda_config, notest=no_test)