diff options
Diffstat (limited to 'build_scripts/utils.py')
-rw-r--r-- | build_scripts/utils.py | 203 |
1 files changed, 110 insertions, 93 deletions
diff --git a/build_scripts/utils.py b/build_scripts/utils.py index 7160630d1..165366e26 100644 --- a/build_scripts/utils.py +++ b/build_scripts/utils.py @@ -39,17 +39,13 @@ import sys import os -import stat import re import stat import errno -import time import shutil import subprocess import fnmatch -import glob import itertools -import popenasync import glob # There is no urllib.request in Python2 @@ -58,11 +54,9 @@ try: except ImportError: import urllib -from distutils import log +import distutils.log as log from distutils.errors import DistutilsOptionError from distutils.errors import DistutilsSetupError -from distutils.spawn import spawn -from distutils.spawn import DistutilsExecError try: WindowsError @@ -70,32 +64,6 @@ except NameError: WindowsError = None -def has_option(name): - try: - sys.argv.remove("--{}".format(name)) - return True - except ValueError: - pass - return False - - -def option_value(name): - for index, option in enumerate(sys.argv): - if option == '--' + name: - if index+1 >= len(sys.argv): - raise DistutilsOptionError("The option {} requires a " - "value".format(option)) - value = sys.argv[index+1] - sys.argv[index:index+2] = [] - return value - if option.startswith('--' + name + '='): - value = option[len(name)+3:] - sys.argv[index:index+1] = [] - return value - env_val = os.getenv(name.upper().replace('-', '_')) - return env_val - - def filter_match(name, patterns): for pattern in patterns: if pattern is None: @@ -182,7 +150,6 @@ def find_vcdir(version): """ from distutils.msvc9compiler import VS_BASE from distutils.msvc9compiler import Reg - from distutils import log vsbase = VS_BASE % version try: productdir = Reg.get_value(r"{}\Setup\VC".format(vsbase), "productdir") @@ -416,13 +383,15 @@ def rmtree(dirname, ignore=False): os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777 func(path) else: - raise + raise IOError shutil.rmtree(dirname, ignore_errors=ignore, onerror=handle_remove_readonly) def run_process_output(args, initial_env=None): if initial_env is None: initial_env = os.environ - std_out = subprocess.Popen(args, env = initial_env, universal_newlines = 1, + std_out = subprocess.Popen(args, + env = initial_env, + universal_newlines = 1, stdout=subprocess.PIPE).stdout result = [] for raw_line in std_out.readlines(): @@ -431,53 +400,21 @@ def run_process_output(args, initial_env=None): return result def run_process(args, initial_env=None): - def _log(buffer, check_new_line=False): - ends_with_new_line = False - if buffer.endswith('\n'): - ends_with_new_line = True - if check_new_line and buffer.find('\n') == -1: - return buffer - lines = buffer.splitlines() - buffer = '' - if check_new_line and not ends_with_new_line: - buffer = lines[-1] - lines = lines[:-1] - for line in lines: - log.info(line.rstrip('\r')) - return buffer - _log("Running process in {0}: {1}".format(os.getcwd(), - " ".join([(" " in x and '"{0}"'.format(x) or x) for x in args]))) - - if sys.platform != "win32": - try: - spawn(args) - return 0 - except DistutilsExecError: - return -1 - - shell = False - if sys.platform == "win32": - shell = True + """ + Run process until completion and return the process exit code. + Prints both stdout and stderr to the console. + No output is captured. + """ + log.info("Running process in directory {0}: command {1}".format( + os.getcwd(), + " ".join([(" " in x and '"{0}"'.format(x) or x) for x in args])) + ) if initial_env is None: initial_env = os.environ - proc = popenasync.Popen(args, - stdin = subprocess.PIPE, - stdout = subprocess.PIPE, - stderr = subprocess.STDOUT, - universal_newlines = 1, - shell = shell, - env = initial_env) - - log_buffer = None; - while proc.poll() is None: - log_buffer = _log(proc.read_async(wait=0.1, e=0)) - if log_buffer: - _log(log_buffer) - - proc.wait() - return proc.returncode + exit_code = subprocess.call(args, stderr=subprocess.STDOUT, env=initial_env) + return exit_code def get_environment_from_batch_command(env_cmd, initial=None): @@ -665,6 +602,9 @@ def macos_get_rpaths(libpath): ctr += 3 return rpaths +def macos_add_rpath(rpath, library_path): + back_tick('install_name_tool -add_rpath {rpath} {library_path}'.format( + rpath=rpath, library_path=library_path)) def macos_fix_rpaths_for_library(library_path, qt_lib_dir): """ Adds required rpath load commands to given library. @@ -703,8 +643,7 @@ def macos_fix_rpaths_for_library(library_path, qt_lib_dir): break if needs_loader_path and "@loader_path" not in existing_rpath_commands: - back_tick('install_name_tool -add_rpath {rpath} {library_path}'.format( - rpath="@loader_path", library_path=library_path)) + macos_add_rpath("@loader_path", library_path) # If the library depends on a Qt library, add an rpath load comment # pointing to the Qt lib directory. @@ -738,8 +677,7 @@ def macos_add_qt_rpath(library_path, qt_lib_dir, break if needs_qt_rpath: - back_tick('install_name_tool -add_rpath {rpath} {library_path}'.format( - rpath=qt_lib_dir, library_path=library_path)) + macos_add_rpath(qt_lib_dir, library_path) # Find an executable specified by a glob pattern ('foo*') in the OS path def find_glob_in_path(pattern): @@ -996,6 +934,17 @@ def copy_icu_libs(patchelf, destination_lib_dir): new_rpaths_string = ":".join(rpaths) linux_set_rpaths(patchelf, qt_core_library_path, new_rpaths_string) + +def linux_run_read_elf(executable_path): + cmd = "readelf -d {}".format(executable_path) + (out, err, code) = back_tick(cmd, True) + if code != 0: + raise RuntimeError("Running `readelf -d {}` failed with error " + "output:\n {}. ".format(executable_path, err)) + lines = split_and_strip(out) + return lines + + def linux_set_rpaths(patchelf, executable_path, rpath_string): """ Patches the `executable_path` with a new rpath string. """ @@ -1005,18 +954,32 @@ def linux_set_rpaths(patchelf, executable_path, rpath_string): raise RuntimeError("Error patching rpath in {}".format( executable_path)) + +def linux_get_dependent_libraries(executable_path): + """ + Returns a list of libraries that executable_path depends on. + """ + + lines = linux_run_read_elf(executable_path) + pattern = re.compile(r"^.+?\(NEEDED\).+?\[(.+?)\]$") + + library_lines = [] + for line in lines: + match = pattern.search(line) + if match: + library_line = match.group(1) + library_lines.append(library_line) + + return library_lines + + def linux_get_rpaths(executable_path): """ Returns a list of run path values embedded in the executable or just an empty list. """ - cmd = "readelf -d {}".format(executable_path) - (out, err, code) = back_tick(cmd, True) - if code != 0: - raise RuntimeError("Running `readelf -d {}` failed with error " - "output:\n {}. ".format(executable_path, err)) - lines = split_and_strip(out) + lines = linux_run_read_elf(executable_path) pattern = re.compile(r"^.+?\(RUNPATH\).+?\[(.+?)\]$") rpath_line = None @@ -1033,6 +996,7 @@ def linux_get_rpaths(executable_path): return rpaths + def rpaths_has_origin(rpaths): """ Return True if the specified list of rpaths has an "$ORIGIN" value @@ -1048,6 +1012,39 @@ def rpaths_has_origin(rpaths): return True return False + +def linux_needs_qt_rpath(executable_path): + """ + Returns true if library_path depends on Qt libraries. + """ + + dependencies = linux_get_dependent_libraries(executable_path) + + # Check if any library dependencies are Qt libraries (hacky). + needs_qt_rpath = False + for dep in dependencies: + if 'Qt' in dep: + needs_qt_rpath = True + break + return needs_qt_rpath + + +def linux_fix_rpaths_for_library(patchelf, executable_path, qt_rpath, override=False): + """ + Adds or overrides required rpaths in given executable / library. + """ + rpaths = ['$ORIGIN/'] + existing_rpaths = [] + if not override: + existing_rpaths = linux_get_rpaths(executable_path) + rpaths.extend(existing_rpaths) + + if linux_needs_qt_rpath(executable_path) and qt_rpath not in existing_rpaths: + rpaths.append(qt_rpath) + + rpaths_string = ':'.join(rpaths) + linux_set_rpaths(patchelf, executable_path, rpaths_string) + def memoize(function): """ Decorator to wrap a function with a memoizing callable. @@ -1076,9 +1073,27 @@ def get_python_dict(python_script_path): "file: {}.".format(python_script_path)) raise -def install_pip_dependencies(env_pip, packages): +def install_pip_wheel_package(env_pip): + # Need to install an unreleased wheel version, due to a bug that + # will generate a wheel which will not be installable. + # See https://github.com/pypa/wheel/issues/263 + wheel_url = "git+https://github.com/pypa/wheel.git@fbf3e3ada64d36ca7bb9c1422f5a1ccdba7e4dcf" + install_pip_package_from_url_specifier(env_pip, wheel_url) + +def install_pip_package_from_url_specifier(env_pip, url, upgrade=True): + args = [env_pip, "install", url] + if upgrade: + args.append("--upgrade") + args.append(url) + run_instruction(args, "Failed to install {}".format(url)) + +def install_pip_dependencies(env_pip, packages, upgrade=True): for p in packages: - run_instruction([env_pip, "install", p], "Failed to install " + p) + args = [env_pip, "install"] + if upgrade: + args.append("--upgrade") + args.append(p) + run_instruction(args, "Failed to install " + p) def get_qtci_virtualEnv(python_ver, host, hostArch, targetArch): _pExe = "python" @@ -1105,9 +1120,11 @@ def get_qtci_virtualEnv(python_ver, host, hostArch, targetArch): _pExe = "python3" return(_pExe, _env, env_pip, env_python) -def run_instruction(instruction, error): +def run_instruction(instruction, error, initial_env=None): + if initial_env is None: + initial_env = os.environ print("Running Coin instruction: " + ' '.join(str(e) for e in instruction)) - result = subprocess.call(instruction) + result = subprocess.call(instruction, env=initial_env) if result != 0: print("ERROR : " + error) exit(result) |