|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import os, time, sys, shutil, codecs, urllib.request, imp, subprocess, json, hashlib |
|
import platform as platform_module |
|
import types as types_module |
|
|
|
|
|
|
|
|
|
__all__ = ['execute', |
|
'_S_Values_', '_S_draft_', '_S_queued_', '_S_running_', '_S_passed_', '_S_failed_', '_S_build_failed_', '_S_script_failed_', |
|
'_StateKey_', '_ResultsKey_', '_LogKey_', '_DescriptionKey_', '_TestsKey_', |
|
'_multi_step_config_', '_multi_step_error_', '_multi_step_result_', |
|
'to_bytes', |
|
] |
|
|
|
_S_draft_ = 'draft' |
|
_S_queued_ = 'queued' |
|
_S_running_ = 'running' |
|
_S_passed_ = 'passed' |
|
_S_failed_ = 'failed' |
|
_S_build_failed_ = 'build failed' |
|
_S_script_failed_ = 'script failed' |
|
_S_queued_for_comparison_ = 'queued for comparison' |
|
|
|
_S_Values_ = [_S_draft_, _S_queued_, _S_running_, _S_passed_, _S_failed_, _S_build_failed_, _S_script_failed_, _S_queued_for_comparison_] |
|
|
|
_IgnoreKey_ = 'ignore' |
|
_StateKey_ = 'state' |
|
_ResultsKey_ = 'results' |
|
_LogKey_ = 'log' |
|
_DescriptionKey_ = 'description' |
|
_TestsKey_ = 'tests' |
|
_SummaryKey_ = 'summary' |
|
_FailedKey_ = 'failed' |
|
_TotalKey_ = 'total' |
|
_PlotsKey_ = 'plots' |
|
_FailedTestsKey_ = 'failed_tests' |
|
_HtmlKey_ = 'html' |
|
|
|
|
|
_multi_step_config_ = 'config.json' |
|
_multi_step_error_ = 'error.json' |
|
_multi_step_result_ = 'result.json' |
|
|
|
PyRosetta_unix_memory_requirement_per_cpu = 6 |
|
PyRosetta_unix_unit_test_memory_requirement_per_cpu = 3.0 |
|
|
|
|
|
PRE_COMPILE_SETUP_SCRIPTS = [ "./update_options.sh", "./update_submodules.sh", "./update_ResidueType_enum_files.sh", "python version.py" ] |
|
|
|
DEFAULT_PYTHON_VERSION='3.9' |
|
|
|
|
|
|
|
class BenchmarkError(Exception): |
|
def __init__(self, value): self.value = value |
|
def __repr__(self): return self.value |
|
def __str__(self): return self.value |
|
|
|
|
|
class NT: |
|
def __init__(self, **entries): self.__dict__.update(entries) |
|
def __repr__(self): |
|
r = 'NT: |' |
|
for i in dir(self): |
|
print(i) |
|
if not i.startswith('__') and i != '_as_dict' and not isinstance(getattr(self, i), types_module.MethodType): r += '%s --> %s, ' % (i, getattr(self, i)) |
|
return r[:-2]+'|' |
|
|
|
@property |
|
def _as_dict(self): |
|
return { a: getattr(self, a) for a in dir(self) if not a.startswith('__') and a != '_as_dict' and not isinstance(getattr(self, a), types_module.MethodType)} |
|
|
|
|
|
def Tracer(verbose=False): |
|
return print if verbose else lambda x: None |
|
|
|
|
|
def to_unicode(b): |
|
''' Conver bytes to string and handle the errors. If argument is already in string - do nothing |
|
''' |
|
|
|
return b if type(b) == str else str(b, 'utf-8', errors='backslashreplace') |
|
|
|
|
|
def to_bytes(u): |
|
''' Conver string to bytes and handle the errors. If argument is already of type bytes - do nothing |
|
''' |
|
return u if type(u) == bytes else u.encode('utf-8', errors='backslashreplace') |
|
|
|
|
|
''' Python-2 version |
|
def execute(message, commandline, return_=False, until_successes=False, terminate_on_failure=True, add_message_and_command_line_to_output=False): |
|
message, commandline = to_unicode(message), to_unicode(commandline) |
|
|
|
TR = Tracer() |
|
TR(message); TR(commandline) |
|
while True: |
|
(res, output) = commands.getstatusoutput(commandline) |
|
# Subprocess results will always be a bytes-string. |
|
# Probably ASCII, but may have some Unicode characters. |
|
# A UTF-8 decode will probably get decent results 99% of the time |
|
# and the replace option will gracefully handle the rest. |
|
output = to_unicode(output) |
|
|
|
TR(output) |
|
|
|
if res and until_successes: pass # Thats right - redability COUNT! |
|
else: break |
|
|
|
print( "Error while executing %s: %s\n" % (message, output) ) |
|
print( "Sleeping 60s... then I will retry..." ) |
|
time.sleep(60) |
|
|
|
if add_message_and_command_line_to_output: output = message + '\nCommand line: ' + commandline + '\n' + output |
|
|
|
if return_ == 'tuple': return(res, output) |
|
|
|
if res and terminate_on_failure: |
|
TR("\nEncounter error while executing: " + commandline) |
|
if return_==True: return res |
|
else: |
|
print("\nEncounter error while executing: " + commandline + '\n' + output) |
|
raise BenchmarkError("\nEncounter error while executing: " + commandline + '\n' + output) |
|
|
|
if return_ == 'output': return output |
|
else: return res |
|
''' |
|
|
|
def execute_through_subprocess(command_line): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
p = subprocess.Popen(command_line, bufsize=0, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
|
output, errors = p.communicate() |
|
|
|
output = output.decode(encoding='utf-8', errors='backslashreplace') |
|
exit_code = p.returncode |
|
|
|
return exit_code, output |
|
|
|
|
|
def execute_through_pexpect(command_line): |
|
import pexpect |
|
|
|
child = pexpect.spawn('/bin/bash', ['-c', command_line]) |
|
child.expect(pexpect.EOF) |
|
output = child.before.decode(encoding='utf-8', errors='backslashreplace') |
|
child.close() |
|
exit_code = child.signalstatus or child.exitstatus |
|
|
|
return exit_code, output |
|
|
|
|
|
def execute_through_pty(command_line): |
|
import pty, select |
|
|
|
if sys.platform == "darwin": |
|
|
|
master, slave = pty.openpty() |
|
p = subprocess.Popen(command_line, shell=True, stdout=slave, stdin=slave, |
|
stderr=subprocess.STDOUT, close_fds=True) |
|
|
|
buffer = [] |
|
while True: |
|
try: |
|
if select.select([master], [], [], 0.2)[0]: |
|
data = os.read(master, 1 << 22) |
|
if data: buffer.append(data) |
|
|
|
elif (p.poll() is not None) and (not select.select([master], [], [], 0.2)[0] ): break |
|
|
|
except OSError: break |
|
|
|
output = b''.join(buffer).decode(encoding='utf-8', errors='backslashreplace') |
|
|
|
os.close(master) |
|
os.close(slave) |
|
|
|
p.wait() |
|
exit_code = p.returncode |
|
|
|
''' |
|
buffer = [] |
|
while True: |
|
if select.select([master], [], [], 0.2)[0]: # has something to read |
|
data = os.read(master, 1 << 22) |
|
if data: buffer.append(data) |
|
# else: break # # EOF - well, technically process _should_ be finished here... |
|
|
|
# elif time.sleep(1) or (p.poll() is not None): # process is finished (sleep here is intentional to trigger race condition, see solution for this on the next few lines) |
|
# assert not select.select([master], [], [], 0.2)[0] # should be nothing left to read... |
|
# break |
|
|
|
elif (p.poll() is not None) and (not select.select([master], [], [], 0.2)[0] ): break # process is finished and output buffer if fully read |
|
|
|
assert not select.select([master], [], [], 0.2)[0] # should be nothing left to read... |
|
|
|
os.close(slave) |
|
os.close(master) |
|
|
|
output = b''.join(buffer).decode(encoding='utf-8', errors='backslashreplace') |
|
exit_code = p.returncode |
|
''' |
|
|
|
else: |
|
|
|
master, slave = pty.openpty() |
|
p = subprocess.Popen(command_line, shell=True, stdout=slave, stdin=slave, |
|
stderr=subprocess.STDOUT, close_fds=True) |
|
|
|
os.close(slave) |
|
|
|
buffer = [] |
|
while True: |
|
try: |
|
data = os.read(master, 1 << 22) |
|
if data: buffer.append(data) |
|
except OSError: break |
|
|
|
output = b''.join(buffer).decode(encoding='utf-8', errors='backslashreplace') |
|
|
|
os.close(master) |
|
|
|
p.wait() |
|
exit_code = p.returncode |
|
|
|
return exit_code, output |
|
|
|
|
|
|
|
def execute(message, command_line, return_='status', until_successes=False, terminate_on_failure=True, silent=False, silence_output=False, silence_output_on_errors=False, add_message_and_command_line_to_output=False): |
|
if not silent: print(message); print(command_line); sys.stdout.flush(); |
|
while True: |
|
|
|
|
|
|
|
exit_code, output = execute_through_pty(command_line) |
|
|
|
if (exit_code and not silence_output_on_errors) or not (silent or silence_output): print(output); sys.stdout.flush(); |
|
|
|
if exit_code and until_successes: pass |
|
else: break |
|
|
|
print( "Error while executing {}: {}\n".format(message, output) ) |
|
print("Sleeping 60s... then I will retry...") |
|
sys.stdout.flush(); |
|
time.sleep(60) |
|
|
|
if add_message_and_command_line_to_output: output = message + '\nCommand line: ' + command_line + '\n' + output |
|
|
|
if return_ == 'tuple' or return_ == tuple: return(exit_code, output) |
|
|
|
if exit_code and terminate_on_failure: |
|
print("\nEncounter error while executing: " + command_line) |
|
if return_==True: return True |
|
else: |
|
print('\nEncounter error while executing: ' + command_line + '\n' + output); |
|
raise BenchmarkError('\nEncounter error while executing: ' + command_line + '\n' + output) |
|
|
|
if return_ == 'output': return output |
|
else: return exit_code |
|
|
|
|
|
def parallel_execute(name, jobs, rosetta_dir, working_dir, cpu_count, time=16): |
|
''' Execute command line in parallel on local host |
|
time specifies the upper limit for cpu-usage runtime (in minutes) for any one process in the parallel execution. |
|
|
|
jobs should be dict with following structure: |
|
{ |
|
'job-string-id-1’: command_line-1, |
|
'job-string-id-2’: command_line-2, |
|
... |
|
} |
|
|
|
return: dict with jobs-id's as keys and value as dict with 'output' and 'result' keys: |
|
{ |
|
"job-string-id-1": { |
|
"output": "stdout + stdderr output of command_line-1", |
|
"result": <integer exit code for command_line-1> |
|
}, |
|
"c2": { |
|
"output": "stdout + stdderr output of command_line-2", |
|
"result": <integer exit code for command_line-2> |
|
}, |
|
... |
|
} |
|
''' |
|
job_file_name = working_dir + '/' + name |
|
with open(job_file_name + '.json', 'w') as f: json.dump(jobs, f, sort_keys=True, indent=2) |
|
if time is not None: |
|
allowed_time = int(time*60) |
|
ulimit_command = f'ulimit -t {allowed_time} && ' |
|
else: |
|
ulimit_command = '' |
|
command = f'cd {working_dir} && ' + ulimit_command + f'{rosetta_dir}/tests/benchmark/util/parallel.py -j{cpu_count} {job_file_name}.json' |
|
execute("Running {} in parallel with {} CPU's...".format(name, cpu_count), command ) |
|
|
|
with open(job_file_name+'.results.json') as f: return json.load(f) |
|
|
|
|
|
def calculate_unique_prefix_path(platform, config): |
|
''' calculate path for prefix location that is unique for this machine and OS |
|
''' |
|
hostname = os.uname()[1] |
|
return config['prefix'] + '/' + hostname + '/' + platform['os'] |
|
|
|
|
|
def get_python_include_and_lib(python): |
|
''' calculate python include dir and lib dir from given python executable path |
|
''' |
|
|
|
python_bin_dir = python.rpartition('/')[0] |
|
python_config = f'{python} {python}-config' if python.endswith('2.7') else f'{python}-config' |
|
|
|
|
|
|
|
info = execute('Getting python configuration info...', f'unset __PYVENV_LAUNCHER__ && cd {python_bin_dir} && PATH=.:$PATH && {python_config} --prefix --includes', return_='output').replace('\r', '').split('\n') |
|
python_prefix = info[0] |
|
python_include_dir = info[1].split()[0][len('-I'):] |
|
python_lib_dir = python_prefix + '/lib' |
|
|
|
|
|
|
|
return NT(python_include_dir=python_include_dir, python_lib_dir=python_lib_dir) |
|
|
|
|
|
def local_open_ssl_install(prefix, build_prefix, jobs): |
|
''' install OpenSSL at given prefix, return url of source archive |
|
''' |
|
|
|
|
|
url = 'https://www.openssl.org/source/openssl-1.1.1b.tar.gz' |
|
|
|
|
|
|
|
archive = build_prefix + '/' + url.split('/')[-1] |
|
build_dir = archive.rpartition('.tar.gz')[0] |
|
if os.path.isdir(build_dir): shutil.rmtree(build_dir) |
|
|
|
with open(archive, 'wb') as f: |
|
response = urllib.request.urlopen(url) |
|
f.write( response.read() ) |
|
|
|
execute('Unpacking {}'.format(archive), 'cd {build_prefix} && tar -xvzf {archive}'.format(**vars()) ) |
|
|
|
execute('Configuring...', f'cd {build_dir} && ./config --prefix={prefix}') |
|
execute('Building...', f'cd {build_dir} && make -j{jobs}') |
|
execute('Installing...', f'cd {build_dir} && make -j{jobs} install') |
|
|
|
return url |
|
|
|
|
|
def remove_pip_and_easy_install(prefix_root_path): |
|
''' remove `pip` and `easy_install` executable from given Python / virtual-environments install |
|
''' |
|
for f in os.listdir(prefix_root_path + '/bin'): |
|
for p in ['pip', 'easy_install']: |
|
if f.startswith(p): os.remove(prefix_root_path + '/bin/' + f) |
|
|
|
|
|
|
|
def local_python_install(platform, config): |
|
''' Perform local install of given Python version and return path-to-python-interpreter, python_include_dir, python_lib_dir |
|
If previous install is detected skip installiation. |
|
Provided Python install will _persistent_ and _immutable_ |
|
''' |
|
jobs = config['cpu_count'] |
|
compiler, cpp_compiler = ('clang', 'clang++') if platform['os'] == 'mac' else ('gcc', 'g++') |
|
|
|
python_version = platform.get('python', DEFAULT_PYTHON_VERSION) |
|
|
|
if python_version.endswith('.s'): |
|
assert python_version == f'{sys.version_info.major}.{sys.version_info.minor}.s' |
|
|
|
h = hashlib.md5(); h.update( (sys.executable + sys.version).encode('utf-8', errors='backslashreplace') ); hash = h.hexdigest() |
|
return NT( |
|
python = sys.executable, |
|
root = None, |
|
python_include_dir = None, |
|
python_lib_dir = None, |
|
version = python_version, |
|
url = None, |
|
platform = platform, |
|
config = config, |
|
hash = hash, |
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
python_sources = { |
|
'2.7' : 'https://www.python.org/ftp/python/2.7.18/Python-2.7.18.tgz', |
|
|
|
'3.5' : 'https://www.python.org/ftp/python/3.5.9/Python-3.5.9.tgz', |
|
'3.6' : 'https://www.python.org/ftp/python/3.6.15/Python-3.6.15.tgz', |
|
'3.7' : 'https://www.python.org/ftp/python/3.7.14/Python-3.7.14.tgz', |
|
'3.8' : 'https://www.python.org/ftp/python/3.8.14/Python-3.8.14.tgz', |
|
'3.9' : 'https://www.python.org/ftp/python/3.9.14/Python-3.9.14.tgz', |
|
'3.10' : 'https://www.python.org/ftp/python/3.10.10/Python-3.10.10.tgz', |
|
'3.11' : 'https://www.python.org/ftp/python/3.11.2/Python-3.11.2.tgz', |
|
} |
|
|
|
|
|
extras = { |
|
|
|
('mac',) : ('MACOSX_DEPLOYMENT_TARGET={}'.format(platform_module.mac_ver()[0]), ''), |
|
('linux', '2.7') : ('', '--enable-unicode=ucs4'), |
|
('ubuntu', '2.7') : ('', '--enable-unicode=ucs4'), |
|
} |
|
|
|
|
|
packages = 'setuptools' |
|
|
|
url = python_sources[python_version] |
|
|
|
extra = extras.get( (platform['os'],) , ('', '') ) |
|
extra = extras.get( (platform['os'], python_version) , extra) |
|
|
|
extra = ('unset __PYVENV_LAUNCHER__ && ' + extra[0], extra[1]) |
|
|
|
options = '--with-ensurepip' |
|
signature = f'v1.5.1 url: {url}\noptions: {options}\ncompiler: {compiler}\nextra: {extra}\npackages: {packages}\n' |
|
|
|
h = hashlib.md5(); h.update( signature.encode('utf-8', errors='backslashreplace') ); hash = h.hexdigest() |
|
|
|
root = calculate_unique_prefix_path(platform, config) + '/python-' + python_version + '.' + compiler + '/' + hash |
|
|
|
signature_file_name = root + '/.signature' |
|
|
|
|
|
executable = root + '/bin/python' + python_version |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if os.path.isfile(signature_file_name) and open(signature_file_name).read() == signature: |
|
|
|
pass |
|
|
|
else: |
|
print( 'Installing Python-{python_version}, using {url} with extra:{extra}...'.format( **vars() ) ) |
|
|
|
if os.path.isdir(root): shutil.rmtree(root) |
|
|
|
build_prefix = os.path.abspath(root + '/../build-python-{}'.format(python_version) ) |
|
|
|
if not os.path.isdir(root): os.makedirs(root) |
|
if not os.path.isdir(build_prefix): os.makedirs(build_prefix) |
|
|
|
platform_is_mac = True if platform['os'] in ['mac', 'm1'] else False |
|
platform_is_linux = not platform_is_mac |
|
|
|
|
|
if ( platform['os'] == 'mac' and python_version == '3.6' ) \ |
|
or ( platform_is_linux and python_version in ['3.10', '3.11'] ): |
|
open_ssl_url = local_open_ssl_install(root, build_prefix, jobs) |
|
options += f' --with-openssl={root} --with-openssl-rpath=auto' |
|
|
|
|
|
archive = build_prefix + '/' + url.split('/')[-1] |
|
build_dir = archive.rpartition('.tgz')[0] |
|
if os.path.isdir(build_dir): shutil.rmtree(build_dir) |
|
|
|
with open(archive, 'wb') as f: |
|
|
|
response = urllib.request.urlopen(url) |
|
f.write( response.read() ) |
|
|
|
|
|
|
|
execute('Unpacking {}'.format(archive), 'cd {build_prefix} && tar -xvzf {archive}'.format(**vars()) ) |
|
|
|
|
|
execute('Configuring...', 'cd {} && CC={compiler} CXX={cpp_compiler} {extra[0]} ./configure {options} {extra[1]} --prefix={root}'.format(build_dir, **locals()) ) |
|
execute('Building...', 'cd {} && {extra[0]} make -j{jobs}'.format(build_dir, **locals()) ) |
|
execute('Installing...', 'cd {} && {extra[0]} make -j{jobs} install'.format(build_dir, **locals()) ) |
|
|
|
shutil.rmtree(build_prefix) |
|
|
|
|
|
|
|
|
|
|
|
|
|
if packages: execute( f'Installing packages {packages}...', f'cd {root} && unset __PYVENV_LAUNCHER__ && {root}/bin/pip{python_version} install --upgrade {packages}' ) |
|
|
|
|
|
remove_pip_and_easy_install(root) |
|
|
|
with open(signature_file_name, 'w') as f: f.write(signature) |
|
|
|
print( 'Installing Python-{python_version}, using {url} with extra:{extra}... Done.'.format( **vars() ) ) |
|
|
|
il = get_python_include_and_lib(executable) |
|
|
|
return NT( |
|
python = executable, |
|
root = root, |
|
python_include_dir = il.python_include_dir, |
|
python_lib_dir = il.python_lib_dir, |
|
version = python_version, |
|
url = url, |
|
platform = platform, |
|
config = config, |
|
hash = hash, |
|
) |
|
|
|
|
|
|
|
def setup_python_virtual_environment(working_dir, python_environment, packages=''): |
|
''' Deploy Python virtual environment at working_dir |
|
''' |
|
|
|
python = python_environment.python |
|
|
|
execute('Setting up Python virtual environment...', 'unset __PYVENV_LAUNCHER__ && {python} -m venv --clear {working_dir}'.format(**vars()) ) |
|
|
|
activate = f'unset __PYVENV_LAUNCHER__ && . {working_dir}/bin/activate' |
|
|
|
bin=working_dir+'/bin' |
|
|
|
if packages: execute('Installing packages: {}...'.format(packages), 'unset __PYVENV_LAUNCHER__ && {bin}/python {bin}/pip install --upgrade pip setuptools && {bin}/python {bin}/pip install --progress-bar off {packages}'.format(**vars()) ) |
|
|
|
|
|
return NT(activate = activate, python = bin + '/python', root = working_dir, bin = bin) |
|
|
|
|
|
|
|
def setup_persistent_python_virtual_environment(python_environment, packages): |
|
''' Setup _persistent_ and _immutable_ Python virtual environment which will be saved between test runs |
|
''' |
|
|
|
if python_environment.version.startswith('2.'): |
|
assert not packages, f'ERROR: setup_persistent_python_virtual_environment does not support Python-2.* with non-empty package list!' |
|
return NT(activate = ':', python = python_environment.python, root = python_environment.root, bin = python_environment.root + '/bin') |
|
|
|
else: |
|
|
|
|
|
h = hashlib.md5() |
|
h.update(f'v1.0.0 platform: {python_environment.platform} python_source_url: {python_environment.url} python-hash: {python_environment.hash} packages: {packages}'.encode('utf-8', errors='backslashreplace') ) |
|
hash = h.hexdigest() |
|
|
|
prefix = calculate_unique_prefix_path(python_environment.platform, python_environment.config) |
|
|
|
root = os.path.abspath( prefix + '/python_virtual_environments/' + '/python-' + python_environment.version + '/' + hash ) |
|
signature_file_name = root + '/.signature' |
|
signature = f'setup_persistent_python_virtual_environment v1.0.0\npython: {python_environment.hash}\npackages: {packages}\n' |
|
|
|
activate = f'unset __PYVENV_LAUNCHER__ && . {root}/bin/activate' |
|
bin = f'{root}/bin' |
|
|
|
if os.path.isfile(signature_file_name) and open(signature_file_name).read() == signature: pass |
|
else: |
|
if os.path.isdir(root): shutil.rmtree(root) |
|
setup_python_virtual_environment(root, python_environment, packages=packages) |
|
remove_pip_and_easy_install(root) |
|
with open(signature_file_name, 'w') as f: f.write(signature) |
|
|
|
return NT(activate = activate, python = bin + '/python', root = root, bin = bin, hash = hash) |
|
|
|
|
|
|
|
def _get_path_to_conda_root(platform, config): |
|
''' Perform local (prefix) install of miniconda and return NT(activate, conda_root_dir, conda) |
|
this function is for inner use only, - to setup custom conda environment inside your test use `setup_conda_virtual_environment` defined below |
|
''' |
|
miniconda_sources = { |
|
'mac' : 'https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh', |
|
'linux' : 'https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh', |
|
'aarch64': 'https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh', |
|
'ubuntu' : 'https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh', |
|
'm1' : 'https://repo.anaconda.com/miniconda/Miniconda3-py38_4.10.1-MacOSX-arm64.sh', |
|
} |
|
|
|
conda_sources = { |
|
'mac' : 'https://repo.continuum.io/archive/Anaconda3-2018.12-MacOSX-x86_64.sh', |
|
'linux' : 'https://repo.continuum.io/archive/Anaconda3-2018.12-Linux-x86_64.sh', |
|
'ubuntu' : 'https://repo.continuum.io/archive/Anaconda3-2018.12-Linux-x86_64.sh', |
|
} |
|
|
|
|
|
|
|
|
|
platform_os = platform['os'] |
|
for o in 'alpine centos ubuntu'.split(): |
|
if platform_os.startswith(o): platform_os = 'linux' |
|
|
|
url = miniconda_sources[platform_os] |
|
|
|
version = '1' |
|
channels = '' |
|
|
|
|
|
|
|
packages = ['conda-build anaconda-client conda-verify',] |
|
|
|
signature = f'url: {url}\nversion: {version}\channels: {channels}\npackages: {packages}\n' |
|
|
|
root = calculate_unique_prefix_path(platform, config) + '/conda' |
|
|
|
signature_file_name = root + '/.signature' |
|
|
|
|
|
unset = 'unset __PYVENV_LAUNCHER__ && unset PYTHONHOME && unset PYTHONPATH' |
|
activate = unset + ' && . ' + root + '/bin/activate' |
|
|
|
executable = root + '/bin/conda' |
|
|
|
|
|
if os.path.isfile(signature_file_name) and open(signature_file_name).read() == signature: |
|
print( f'Install for MiniConda is detected, skipping installation procedure...' ) |
|
|
|
else: |
|
print( f'Installing MiniConda, using {url}...' ) |
|
|
|
if os.path.isdir(root): shutil.rmtree(root) |
|
|
|
build_prefix = os.path.abspath(root + f'/../build-conda' ) |
|
|
|
|
|
if not os.path.isdir(build_prefix): os.makedirs(build_prefix) |
|
|
|
archive = build_prefix + '/' + url.split('/')[-1] |
|
|
|
with open(archive, 'wb') as f: |
|
response = urllib.request.urlopen(url) |
|
f.write( response.read() ) |
|
|
|
execute('Installing conda...', f'cd {build_prefix} && {unset} && bash {archive} -b -p {root}' ) |
|
|
|
|
|
|
|
if channels: execute(f'Adding extra channles {channels}...', f'cd {build_prefix} && {activate} && conda config --add channels {channels}' ) |
|
|
|
for p in packages: execute(f'Installing conda packages: {p}...', f'cd {build_prefix} && {activate} && conda install --quiet --yes {p}' ) |
|
|
|
shutil.rmtree(build_prefix) |
|
|
|
with open(signature_file_name, 'w') as f: f.write(signature) |
|
|
|
print( f'Installing MiniConda, using {url}... Done.' ) |
|
|
|
execute(f'Updating conda base...', f'{activate} && conda update --all --yes' ) |
|
return NT(conda=executable, root=root, activate=activate, url=url) |
|
|
|
|
|
|
|
def setup_conda_virtual_environment(working_dir, platform, config, packages=''): |
|
''' Deploy Conda virtual environment at working_dir |
|
''' |
|
conda_root_env = _get_path_to_conda_root(platform, config) |
|
activate = conda_root_env.activate |
|
|
|
python_version = platform.get('python', DEFAULT_PYTHON_VERSION) |
|
|
|
prefix = os.path.abspath( working_dir + '/.conda-python-' + python_version ) |
|
|
|
command_line = f'conda create --quiet --yes --prefix {prefix} python={python_version}' |
|
|
|
execute( f'Setting up Conda for Python-{python_version} virtual environment...', f'cd {working_dir} && {activate} && ( {command_line} || ( conda clean --yes && {command_line} ) )' ) |
|
|
|
activate = f'{activate} && conda activate {prefix}' |
|
|
|
if packages: execute( f'Setting up extra packages {packages}...', f'cd {working_dir} && {activate} && conda install --quiet --yes {packages}' ) |
|
|
|
python = prefix + '/bin/python' + python_version |
|
|
|
il = get_python_include_and_lib(python) |
|
|
|
return NT( |
|
activate = activate, |
|
root = prefix, |
|
python = python, |
|
python_include_dir = il.python_include_dir, |
|
python_lib_dir = il.python_lib_dir, |
|
version = python_version, |
|
activate_base = conda_root_env.activate, |
|
url = prefix, |
|
platform=platform, |
|
config=config, |
|
) |
|
|
|
|
|
|
|
class FileLock(): |
|
''' Implementation of file-lock object that could be use with Python `with` statement |
|
''' |
|
|
|
def __init__(self, file_name): |
|
self.locked = False |
|
self.file_name = file_name |
|
|
|
|
|
def __enter__(self): |
|
if not self.locked: self.acquire() |
|
return self |
|
|
|
|
|
def __exit__(self, exc_type, exc_value, traceback): |
|
if self.locked: self.release() |
|
|
|
|
|
def __del__(self): |
|
self.release() |
|
|
|
|
|
def acquire(self): |
|
while True: |
|
try: |
|
os.close( os.open(self.file_name, os.O_CREAT | os.O_EXCL, mode=0o600) ) |
|
self.locked = True |
|
break |
|
|
|
except FileExistsError as e: |
|
time.sleep(60) |
|
|
|
|
|
def release(self): |
|
if self.locked: |
|
os.remove(self.file_name) |
|
self.locked = False |
|
|
|
|
|
|
|
def convert_submodule_urls_from_ssh_to_https(repository_root): |
|
''' switching submodules URL to HTTPS so we can clone without SSH key |
|
''' |
|
with open(f'{repository_root}/.gitmodules') as f: m = f.read() |
|
with open(f'{repository_root}/.gitmodules', 'w') as f: |
|
f.write( |
|
m |
|
.replace('url = git@github.com:', 'url = https://github.com/') |
|
.replace('url = ../../../', 'url = https://github.com/RosettaCommons/') |
|
.replace('url = ../../', 'url = https://github.com/RosettaCommons/') |
|
.replace('url = ../', 'url = https://github.com/RosettaCommons/') |
|
) |
|
|