diff options
Diffstat (limited to 'third_party/libwebrtc/build/toolchain/apple/linker_driver.py')
-rwxr-xr-x | third_party/libwebrtc/build/toolchain/apple/linker_driver.py | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/third_party/libwebrtc/build/toolchain/apple/linker_driver.py b/third_party/libwebrtc/build/toolchain/apple/linker_driver.py new file mode 100755 index 0000000000..c21e18a0fb --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/apple/linker_driver.py @@ -0,0 +1,306 @@ +#!/usr/bin/env python + +# Copyright 2016 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. + +import os +import os.path +import shutil +import subprocess +import sys + +# On mac, the values of these globals are modified when parsing -Wcrl, flags. On +# ios, the script uses the defaults. +DSYMUTIL_INVOKE = ['xcrun', 'dsymutil'] +STRIP_INVOKE = ['xcrun', 'strip'] + +# Setting this flag will emit a deterministic binary by stripping dates from the +# N_OSO field. +DETERMINISTIC_FLAG = '--deterministic' + +# The linker_driver.py is responsible for forwarding a linker invocation to +# the compiler driver, while processing special arguments itself. +# +# Usage: linker_driver.py clang++ main.o -L. -llib -o prog -Wcrl,dsym,out +# +# On Mac, the logical step of linking is handled by three discrete tools to +# perform the image link, debug info link, and strip. The linker_driver.py +# combines these three steps into a single tool. +# +# The command passed to the linker_driver.py should be the compiler driver +# invocation for the linker. It is first invoked unaltered (except for the +# removal of the special driver arguments, described below). Then the driver +# performs additional actions, based on these arguments: +# +# -Wcrl,dsym,<dsym_path_prefix> +# After invoking the linker, this will run `dsymutil` on the linker's +# output, producing a dSYM bundle, stored at dsym_path_prefix. As an +# example, if the linker driver were invoked with: +# "... -o out/gn/obj/foo/libbar.dylib ... -Wcrl,dsym,out/gn ..." +# The resulting dSYM would be out/gn/libbar.dylib.dSYM/. +# +# -Wcrl,dsymutilpath,<dsymutil_path> +# Sets the path to the dsymutil to run with -Wcrl,dsym, in which case +# `xcrun` is not used to invoke it. +# +# -Wcrl,unstripped,<unstripped_path_prefix> +# After invoking the linker, and before strip, this will save a copy of +# the unstripped linker output in the directory unstripped_path_prefix. +# +# -Wcrl,strip,<strip_arguments> +# After invoking the linker, and optionally dsymutil, this will run +# the strip command on the linker's output. strip_arguments are +# comma-separated arguments to be passed to the strip command. +# +# -Wcrl,strippath,<strip_path> +# Sets the path to the strip to run with -Wcrl,strip, in which case +# `xcrun` is not used to invoke it. + + +def Main(args): + """Main function for the linker driver. Separates out the arguments for + the main compiler driver and the linker driver, then invokes all the + required tools. + + Args: + args: list of string, Arguments to the script. + """ + + if len(args) < 2: + raise RuntimeError("Usage: linker_driver.py [linker-invocation]") + + # Collect arguments to the linker driver (this script) and remove them from + # the arguments being passed to the compiler driver. + linker_driver_actions = {} + compiler_driver_args = [] + deterministic = False + for arg in args[1:]: + if arg.startswith(_LINKER_DRIVER_ARG_PREFIX): + # Convert driver actions into a map of name => lambda to invoke. + driver_action = ProcessLinkerDriverArg(arg) + assert driver_action[0] not in linker_driver_actions + linker_driver_actions[driver_action[0]] = driver_action[1] + elif arg == DETERMINISTIC_FLAG: + deterministic = True + else: + compiler_driver_args.append(arg) + + linker_driver_outputs = [_FindLinkerOutput(compiler_driver_args)] + + try: + # Zero the mtime in OSO fields for deterministic builds. + # https://crbug.com/330262. + env = os.environ.copy() + if deterministic: + env['ZERO_AR_DATE'] = '1' + # Run the linker by invoking the compiler driver. + subprocess.check_call(compiler_driver_args, env=env) + + # Run the linker driver actions, in the order specified by the actions list. + for action in _LINKER_DRIVER_ACTIONS: + name = action[0] + if name in linker_driver_actions: + linker_driver_outputs += linker_driver_actions[name](args) + except: + # If a linker driver action failed, remove all the outputs to make the + # build step atomic. + map(_RemovePath, linker_driver_outputs) + + # Re-report the original failure. + raise + + +def ProcessLinkerDriverArg(arg): + """Processes a linker driver argument and returns a tuple containing the + name and unary lambda to invoke for that linker driver action. + + Args: + arg: string, The linker driver argument. + + Returns: + A 2-tuple: + 0: The driver action name, as in _LINKER_DRIVER_ACTIONS. + 1: An 1-ary lambda that takes the full list of arguments passed to + Main(). The lambda should call the linker driver action that + corresponds to the argument and return a list of outputs from the + action. + """ + if not arg.startswith(_LINKER_DRIVER_ARG_PREFIX): + raise ValueError('%s is not a linker driver argument' % (arg, )) + + sub_arg = arg[len(_LINKER_DRIVER_ARG_PREFIX):] + + for driver_action in _LINKER_DRIVER_ACTIONS: + (name, action) = driver_action + if sub_arg.startswith(name): + return (name, lambda full_args: action(sub_arg[len(name):], full_args)) + + raise ValueError('Unknown linker driver argument: %s' % (arg, )) + + +def RunDsymUtil(dsym_path_prefix, full_args): + """Linker driver action for -Wcrl,dsym,<dsym-path-prefix>. Invokes dsymutil + on the linker's output and produces a dsym file at |dsym_file| path. + + Args: + dsym_path_prefix: string, The path at which the dsymutil output should be + located. + full_args: list of string, Full argument list for the linker driver. + + Returns: + list of string, Build step outputs. + """ + if not len(dsym_path_prefix): + raise ValueError('Unspecified dSYM output file') + + linker_out = _FindLinkerOutput(full_args) + base = os.path.basename(linker_out) + dsym_out = os.path.join(dsym_path_prefix, base + '.dSYM') + + # Remove old dSYMs before invoking dsymutil. + _RemovePath(dsym_out) + + tools_paths = _FindToolsPaths(full_args) + if os.environ.get('PATH'): + tools_paths.append(os.environ['PATH']) + dsymutil_env = os.environ.copy() + dsymutil_env['PATH'] = ':'.join(tools_paths) + subprocess.check_call(DSYMUTIL_INVOKE + ['-o', dsym_out, linker_out], + env=dsymutil_env) + return [dsym_out] + + +def SetDsymutilPath(dsymutil_path, full_args): + """Linker driver action for -Wcrl,dsymutilpath,<dsymutil_path>. + + Sets the invocation command for dsymutil, which allows the caller to specify + an alternate dsymutil. This action is always processed before the RunDsymUtil + action. + + Args: + dsymutil_path: string, The path to the dsymutil binary to run + full_args: list of string, Full argument list for the linker driver. + + Returns: + No output - this step is run purely for its side-effect. + """ + global DSYMUTIL_INVOKE + DSYMUTIL_INVOKE = [dsymutil_path] + return [] + + +def RunSaveUnstripped(unstripped_path_prefix, full_args): + """Linker driver action for -Wcrl,unstripped,<unstripped_path_prefix>. Copies + the linker output to |unstripped_path_prefix| before stripping. + + Args: + unstripped_path_prefix: string, The path at which the unstripped output + should be located. + full_args: list of string, Full argument list for the linker driver. + + Returns: + list of string, Build step outputs. + """ + if not len(unstripped_path_prefix): + raise ValueError('Unspecified unstripped output file') + + linker_out = _FindLinkerOutput(full_args) + base = os.path.basename(linker_out) + unstripped_out = os.path.join(unstripped_path_prefix, base + '.unstripped') + + shutil.copyfile(linker_out, unstripped_out) + return [unstripped_out] + + +def RunStrip(strip_args_string, full_args): + """Linker driver action for -Wcrl,strip,<strip_arguments>. + + Args: + strip_args_string: string, Comma-separated arguments for `strip`. + full_args: list of string, Full arguments for the linker driver. + + Returns: + list of string, Build step outputs. + """ + strip_command = list(STRIP_INVOKE) + if len(strip_args_string) > 0: + strip_command += strip_args_string.split(',') + strip_command.append(_FindLinkerOutput(full_args)) + subprocess.check_call(strip_command) + return [] + + +def SetStripPath(strip_path, full_args): + """Linker driver action for -Wcrl,strippath,<strip_path>. + + Sets the invocation command for strip, which allows the caller to specify + an alternate strip. This action is always processed before the RunStrip + action. + + Args: + strip_path: string, The path to the strip binary to run + full_args: list of string, Full argument list for the linker driver. + + Returns: + No output - this step is run purely for its side-effect. + """ + global STRIP_INVOKE + STRIP_INVOKE = [strip_path] + return [] + + +def _FindLinkerOutput(full_args): + """Finds the output of the linker by looking for the output flag in its + argument list. As this is a required linker argument, raises an error if it + cannot be found. + """ + # The linker_driver.py script may be used to wrap either the compiler linker + # (uses -o to configure the output) or lipo (uses -output to configure the + # output). Since wrapping the compiler linker is the most likely possibility + # use try/except and fallback to checking for -output if -o is not found. + try: + output_flag_index = full_args.index('-o') + except ValueError: + output_flag_index = full_args.index('-output') + return full_args[output_flag_index + 1] + + +def _FindToolsPaths(full_args): + """Finds all paths where the script should look for additional tools.""" + paths = [] + for idx, arg in enumerate(full_args): + if arg in ['-B', '--prefix']: + paths.append(full_args[idx + 1]) + elif arg.startswith('-B'): + paths.append(arg[2:]) + elif arg.startswith('--prefix='): + paths.append(arg[9:]) + return paths + + +def _RemovePath(path): + """Removes the file or directory at |path| if it exists.""" + if os.path.exists(path): + if os.path.isdir(path): + shutil.rmtree(path) + else: + os.unlink(path) + + +_LINKER_DRIVER_ARG_PREFIX = '-Wcrl,' +"""List of linker driver actions. The sort order of this list affects the +order in which the actions are invoked. The first item in the tuple is the +argument's -Wcrl,<sub_argument> and the second is the function to invoke. +""" +_LINKER_DRIVER_ACTIONS = [ + ('dsymutilpath,', SetDsymutilPath), + ('dsym,', RunDsymUtil), + ('unstripped,', RunSaveUnstripped), + ('strippath,', SetStripPath), + ('strip,', RunStrip), +] + +if __name__ == '__main__': + Main(sys.argv) + sys.exit(0) |