#!/usr/bin/env python # -*- coding: utf-8 -*- # :noTabs=true: # (c) Copyright Rosetta Commons Member Institutions. # (c) This file is part of the Rosetta software suite and is made available under license. # (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. # (c) For more information, see http://www.rosettacommons.org. Questions about this can be # (c) addressed to University of Washington CoMotion, email: license@uw.edu. ## @file tests/__init__.py ## @brief Common constats and types for all test types ## @author Sergey Lyskov import os, time, sys, shutil, codecs, urllib.request, imp, subprocess, json, hashlib # urllib.error, urllib.parse, import platform as platform_module import types as types_module # ⚔ do not change wording below, it have to stay in sync with upstream (up to benchmark-model). # Copied from benchmark-model, standard state code's for tests results. __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' # file names for multi-step test files _multi_step_config_ = 'config.json' _multi_step_error_ = 'error.json' _multi_step_result_ = 'result.json' PyRosetta_unix_memory_requirement_per_cpu = 6 # Memory per sub-process in Gb's PyRosetta_unix_unit_test_memory_requirement_per_cpu = 3.0 # Memory per sub-process in Gb's for running PyRosetta unit tests # Commands to run all the scripts needed for setting up Rosetta compiles. (Run from main/source directory) PRE_COMPILE_SETUP_SCRIPTS = [ "./update_options.sh", "./update_submodules.sh", "./update_ResidueType_enum_files.sh", "python version.py" ] DEFAULT_PYTHON_VERSION='3.9' # Standard funtions and classes below --------------------------------------------------------------------------------- class BenchmarkError(Exception): def __init__(self, value): self.value = value def __repr__(self): return self.value def __str__(self): return self.value class NT: # named tuple 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) == unicode else unicode(b, 'utf-8', errors='replace') 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): # exit_code, output = subprocess.getstatusoutput(command_line) # p = subprocess.Popen(command_line, bufsize=0, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # output, errors = p.communicate() # output = (output + errors).decode(encoding='utf-8', errors='backslashreplace') # exit_code = p.returncode # previous 'main' version based on subprocess module. Main issue that output of segfaults will not be captured since they generated by shell p = subprocess.Popen(command_line, bufsize=0, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output, errors = p.communicate() # output = output + errors # ← we redirected stderr into same pipe as stdcout so errors is None, - no need to concatenate 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]: # has something to read 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 # process is finished and output buffer if fully read except OSError: break # OSError will be raised when child process close PTY descriptior 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 # OSError will be raised when child process close PTY descriptior 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_subprocess(command_line) #exit_code, output = execute_through_pexpect(command_line) 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 # Thats right - redability COUNT! 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": }, "c2": { "output": "stdout + stdderr output of command_line-2", "result": }, ... } ''' job_file_name = working_dir + '/' + name with open(job_file_name + '.json', 'w') as f: json.dump(jobs, f, sort_keys=True, indent=2) # JSON handles unicode internally 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 = os.path.realpath(python) python_bin_dir = python.rpartition('/')[0] python_config = f'{python} {python}-config' if python.endswith('2.7') else f'{python}-config' #if not os.path.isfile(python_config): python_config = python_bin_dir + '/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-3 only: --abiflags python_prefix = info[0] python_include_dir = info[1].split()[0][len('-I'):] python_lib_dir = python_prefix + '/lib' #python_abi_suffix = info[2] #print(python_include_dir, python_lib_dir) 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 ''' #with tempfile.TemporaryDirectory('open_ssl_build', dir=prefix) as build_prefix: url = 'https://www.openssl.org/source/openssl-1.1.1b.tar.gz' #url = 'https://www.openssl.org/source/openssl-3.0.0.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'): # removing all pip's and easy_install's to make sure that environment is immutable 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++') # disregarding platform compiler setting and instead use default compiler for platform 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' #root = executable.rpartition('/bin/python')[0] 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, ) # deprecated, no longer needed # python_version = {'python2' : '2.7', # 'python2.7' : '2.7', # 'python3' : '3.5', # }.get(python_version, python_version) # for security reasons we only allow installs for version listed here with hand-coded URL's 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', } # map of env -> ('shell-code-before ./configure', 'extra-arguments-for-configure') extras = { #('mac',) : ('__PYVENV_LAUNCHER__="" MACOSX_DEPLOYMENT_TARGET={}'.format(platform_module.mac_ver()[0]), ''), # __PYVENV_LAUNCHER__ now used by-default for all platform installs ('mac',) : ('MACOSX_DEPLOYMENT_TARGET={}'.format(platform_module.mac_ver()[0]), ''), ('linux', '2.7') : ('', '--enable-unicode=ucs4'), ('ubuntu', '2.7') : ('', '--enable-unicode=ucs4'), } #packages = '' if (python_version[0] == '2' or python_version == '3.5' ) and platform['os'] == 'mac' else 'pip setuptools wheel' # 2.7 is now deprecated on Mac so some packages could not be installed 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' #'--without-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' #activate = root + '/bin/activate' executable = root + '/bin/python' + python_version # if os.path.isfile(executable) and (not execute('Getting python configuration info...', '{executable}-config --prefix --includes'.format(**vars()), terminate_on_failure=False) ): # print('found executable!') # _, executable_version = execute('Checking Python interpreter version...', '{executable} --version'.format(**vars()), return_='tuple') # executable_version = executable_version.split()[-1] # else: executable_version = '' # print('executable_version: {}'.format(executable_version)) #if executable_version != url.rpartition('Python-')[2][:-len('.tgz')]: if os.path.isfile(signature_file_name) and open(signature_file_name).read() == signature: #print('Install for Python-{} is detected, skipping installation procedure...'.format(python_version)) 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 False and platform['os'] == 'mac' and platform_module.machine() == 'arm64' and tuple( map(int, python_version.split('.') ) ) >= (3, 9): 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' #signature += 'OpenSSL install: ' + open_ssl_url + '\n' 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 = urllib2.urlopen(url) response = urllib.request.urlopen(url) f.write( response.read() ) #execute('Execution environment:', 'env'.format(**vars()) ) execute('Unpacking {}'.format(archive), 'cd {build_prefix} && tar -xvzf {archive}'.format(**vars()) ) #execute('Building and installing...', 'cd {} && CC={compiler} CXX={cpp_compiler} {extra[0]} ./configure {extra[1]} --prefix={root} && {extra[0]} make -j{jobs} && {extra[0]} make install'.format(build_dir, **locals()) ) 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) #execute('Updating setuptools...', f'cd {root} && {root}/bin/pip{python_version} install --upgrade setuptools wheel' ) # if 'certifi' not in packages: # packages += ' certifi' if packages: execute( f'Installing packages {packages}...', f'cd {root} && unset __PYVENV_LAUNCHER__ && {root}/bin/pip{python_version} install --upgrade {packages}' ) #if packages: execute( f'Installing packages {packages}...', f'cd {root} && unset __PYVENV_LAUNCHER__ && {executable} -m pip install --upgrade {packages}' ) remove_pip_and_easy_install(root) # removing all pip's and easy_install's to make sure that environment is immutable 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()) ) #if packages: execute('Installing packages: {}...'.format(packages), '{bin}/pip{python_environment.version} install {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: #if 'certifi' not in packages: packages += ' certifi' 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) # removing all pip's and easy_install's to make sure that environment is immutable 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 = 'm1' if platform_module.machine() == 'arm64' else platform['os'] #url = miniconda_sources[ platform_os ] 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 = '' # conda-forge #packages = ['conda-build gcc libgcc', 'libgcc=5.2.0'] # libgcc installs is workaround for "Anaconda libstdc++.so.6: version `GLIBCXX_3.4.20' not found", see: https://stackoverflow.com/questions/48453497/anaconda-libstdc-so-6-version-glibcxx-3-4-20-not-found #packages = ['conda-build gcc'] # libgcc installs is workaround for "Anaconda libstdc++.so.6: version `GLIBCXX_3.4.20' not found", see: https://stackoverflow.com/questions/48453497/anaconda-libstdc-so-6-version-glibcxx-3-4-20-not-found 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' # presense of __PYVENV_LAUNCHER__,PYTHONHOME, PYTHONPATH sometimes confuse Python so we have to unset them 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(root): os.makedirs(root) 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}' ) # conda update --yes --quiet -n base -c defaults conda 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, # conda_root_env.url, 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/') )