blob: 485ba1af26ad6c2dac729407c1fbd06607454fd8 [file]
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Functions to work with Miniconda python environment.
See http://conda.pydata.org/miniconda.html
"""
from recipe_engine import recipe_api
class CondaEnv(object):
def __init__(self, module_api, version, path):
self._module_api = module_api
self.version = version
self.path = path
self._wiped = False
def __enter__(self, *_args):
return self
def __exit__(self, *_args):
self.wipe()
@property
def conda_exe(self):
"""Path to 'conda' executable."""
if self._module_api.m.platform.is_win:
return self.path.join('Scripts', 'conda.exe')
return self.path.join('bin', 'conda')
def install(self, pkg):
"""Installs a conda package into the environment."""
return self._call(['install', pkg])
def convert_to_cipd_package(self, package_name, output_file):
"""Packages Conda environment as CIPD package.
It also breaks it in the process (by irreversibly mutating it to be
prefix independent, as much as possible). It is not possible to install
new packages into the environment once it has been mutated.
Args:
package_name: name of the CIPD package, 'infra/conda_python/linux-amd64'.
output_file: path to put *.cipd package to.
"""
self._call(['clean', '--tarballs', '--index-cache', '--packages'])
self._module_api.m.python(
'make conda env location independent',
self._module_api.resource('butcher_conda.py'),
args=[self.path])
self._module_api.m.cipd.build(
input_dir=self.path,
output_package=output_file,
package_name=package_name,
install_mode='copy')
def wipe(self):
"""Wipes the directory with Conda installation."""
if not self._wiped:
self._wiped = True
self._module_api.m.file.rmtree('removing conda', self.path)
def _call(self, cmd):
with self._module_api.m.context(env={'PYTHONPATH': None}):
return self._module_api.m.step(
' '.join(['conda'] + cmd),
[self.conda_exe] + cmd + ['--yes'])
class CondaApi(recipe_api.RecipeApi):
def install(self, version, path):
"""Downloads Miniconda installer for given version and executes it.
Args:
version: version of Miniconda to install, e.g. 'Miniconda2-3.18.3'.
path: prefix to install Miniconda into.
Returns:
Instance of CondaEnv, that also optionally acts as context manager that
deletes the environment on exit.
"""
# Construct URL to installer. See https://repo.continuum.io/miniconda/.
os = {
'linux': 'Linux',
'mac': 'MacOSX',
'win': 'Windows',
}[self.m.platform.name]
arch = {
32: 'x86',
64: 'x86_64',
}[self.m.platform.bits]
ext = '.exe' if self.m.platform.is_win else '.sh'
url = (
'https://repo.continuum.io/miniconda/%s-%s-%s%s' %
(version, os, arch, ext))
# Fetch installer into temp directory and install Conda to 'path'.
# We acknowledge the license agreement.
tmp = self.m.path.mkdtemp('conda')
installer = tmp.join(url[url.rfind('/')+1:])
try:
self.m.url.get_file(
url,
installer,
step_name='fetch miniconda installer')
# See http://conda.pydata.org/docs/help/silent.html
if self.m.platform.is_win:
install_cmd = [
installer, '/InstallationType=JustMe', '/AddToPath=0',
'/RegisterPython=0', '/S', '/D=' + str(path),
]
else:
install_cmd = ['/bin/bash', installer, '-b', '-p', path]
with self.m.context(env={'PYTHONPATH': ''}):
self.m.step('install miniconda', install_cmd)
return CondaEnv(self, version, path)
finally:
self.m.file.rmtree('remove miniconda installer', tmp)