diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/libwebrtc/build/toolchain | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/build/toolchain')
54 files changed, 7445 insertions, 0 deletions
diff --git a/third_party/libwebrtc/build/toolchain/BUILD.gn b/third_party/libwebrtc/build/toolchain/BUILD.gn new file mode 100644 index 0000000000..6cf8f1b403 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/BUILD.gn @@ -0,0 +1,26 @@ +# 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("//build/toolchain/concurrent_links.gni") +import("//build/toolchain/goma.gni") +import("//build/toolchain/rbe.gni") + +declare_args() { + # Pool for non goma tasks. + action_pool_depth = -1 +} + +if (current_toolchain == default_toolchain) { + if (action_pool_depth == -1 || (use_goma || use_rbe)) { + action_pool_depth = exec_script("get_cpu_count.py", [], "value") + } + + pool("link_pool") { + depth = concurrent_links + } + + pool("action_pool") { + depth = action_pool_depth + } +} diff --git a/third_party/libwebrtc/build/toolchain/OWNERS b/third_party/libwebrtc/build/toolchain/OWNERS new file mode 100644 index 0000000000..d7012d39ad --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/OWNERS @@ -0,0 +1,5 @@ +dpranke@google.com +scottmg@chromium.org + +# Code Coverage. +per-file *code_coverage*=liaoyuke@chromium.org diff --git a/third_party/libwebrtc/build/toolchain/aix/BUILD.gn b/third_party/libwebrtc/build/toolchain/aix/BUILD.gn new file mode 100644 index 0000000000..0390a0c42c --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/aix/BUILD.gn @@ -0,0 +1,24 @@ +# Copyright 2017 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("//build/toolchain/gcc_toolchain.gni") + +gcc_toolchain("ppc64") { + cc = "gcc" + cxx = "g++" + + readelf = "readelf" + nm = "nm" + ar = "ar" + ld = cxx + + toolchain_args = { + target_cpu = "ppc64" + current_os = "aix" + + # reclient does not support gcc. + use_rbe = false + is_clang = false + } +} diff --git a/third_party/libwebrtc/build/toolchain/android/BUILD.gn b/third_party/libwebrtc/build/toolchain/android/BUILD.gn new file mode 100644 index 0000000000..a611a09f46 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/android/BUILD.gn @@ -0,0 +1,156 @@ +# Copyright 2013 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("//build/config/android/config.gni") +import("//build/config/clang/clang.gni") +import("//build/config/compiler/compiler.gni") +import("//build/config/ozone.gni") +import("//build/config/sysroot.gni") # Imports android/config.gni. +import("//build/toolchain/gcc_toolchain.gni") + +declare_args() { + # Whether unstripped binaries, i.e. compiled with debug symbols, should be + # considered runtime_deps rather than stripped ones. + android_unstripped_runtime_outputs = true +} + +# The Android clang toolchains share most of the same parameters, so we have this +# wrapper around gcc_toolchain to avoid duplication of logic. +# +# Parameters: +# - binary_prefix +# Prefix of compiler executables. +template("android_clang_toolchain") { + gcc_toolchain(target_name) { + assert(defined(invoker.toolchain_args), + "toolchain_args must be defined for android_clang_toolchain()") + + # Android toolchains need to declare .dwp files as outputs, so need to know + # the value of "use_debug_fission" when defining them. + # The derived value of "use_debug_fission" varies based on current_os, but + # toolchain definitions are evaluated under the default toolchain. + # Rather than computing the value under current_os="android", just disable + # it if target_os != "android". + _use_debug_fission = use_debug_fission && target_os == "android" + + toolchain_args = { + forward_variables_from(invoker.toolchain_args, "*") + current_os = "android" + use_debug_fission = _use_debug_fission + } + + # Output linker map files for binary size analysis. + enable_linker_map = true + + _android_tool_prefix = + "$android_toolchain_root/bin/${invoker.binary_prefix}-" + + # The tools should be run relative to the build dir. + _tool_prefix = rebase_path("$_android_tool_prefix", root_build_dir) + + _prefix = rebase_path("$clang_base_path/bin", root_build_dir) + cc = "$_prefix/clang" + cxx = "$_prefix/clang++" + ar = "$_prefix/llvm-ar" + ld = cxx + readelf = _tool_prefix + "readelf" + nm = "$_prefix/llvm-nm" + strip = "$_prefix/llvm-strip" + if (_use_debug_fission) { + dwp = _tool_prefix + "dwp" + } + use_unstripped_as_runtime_outputs = android_unstripped_runtime_outputs + + # Don't use .cr.so for loadable_modules since they are always loaded via + # absolute path. + loadable_module_extension = ".so" + } +} + +android_clang_toolchain("android_clang_x86") { + binary_prefix = "i686-linux-android" + toolchain_args = { + target_cpu = "x86" + + # We lack the libclang_rt.profile library for x86 and x86_64, so we cannot + # link any binaries that are generated with coverage instrumentation. + # Therefore we need to turn off 'use_clang_coverage' for this toolchain. + # TODO(crbug.com/865376) + use_clang_coverage = false + + # This turns off all of the LaCrOS-specific flags. A LaCrOS related build + # may use |ash_clang_x64| or |lacros_clang_x64| toolchain, which are + # chromeos toolchains, to build Ash-Chrome or Lacros-Chrome in a + # subdirectory, and because chromeos toolchain uses android toolchain, which + # eventually resulted in that android toolchains being used inside a LaCrOS + # build. + also_build_ash_chrome = false + also_build_lacros_chrome = false + chromeos_is_browser_only = false + ozone_platform = "" + ozone_platform_wayland = false + } +} + +android_clang_toolchain("android_clang_arm") { + binary_prefix = "arm-linux-androideabi" + toolchain_args = { + target_cpu = "arm" + } +} + +android_clang_toolchain("android_clang_mipsel") { + binary_prefix = "mipsel-linux-android" + toolchain_args = { + target_cpu = "mipsel" + } +} + +android_clang_toolchain("android_clang_x64") { + binary_prefix = "x86_64-linux-android" + toolchain_args = { + target_cpu = "x64" + + # We lack the libclang_rt.profile library for x86 and x86_64, so we cannot + # link any binaries that are generated with coverage instrumentation. + # Therefore we need to turn off 'use_clang_coverage' for this toolchain. + # TODO(crbug.com/865376) + use_clang_coverage = false + + # This turns off all of the LaCrOS-specific flags. A LaCrOS related build + # may use |ash_clang_x64| or |lacros_clang_x64| toolchain, which are + # chromeos toolchains, to build Ash-Chrome or Lacros-Chrome in a + # subdirectory, and because chromeos toolchain uses android toolchain, which + # eventually resulted in that android toolchains being used inside a LaCrOS + # build. + also_build_ash_chrome = false + also_build_lacros_chrome = false + chromeos_is_browser_only = false + ozone_platform = "" + ozone_platform_wayland = false + } +} + +android_clang_toolchain("android_clang_arm64") { + binary_prefix = "aarch64-linux-android" + toolchain_args = { + target_cpu = "arm64" + } +} + +android_clang_toolchain("android_clang_arm64_hwasan") { + binary_prefix = "aarch64-linux-android" + toolchain_args = { + target_cpu = "arm64" + is_hwasan = true + android64_ndk_api_level = 29 + } +} + +android_clang_toolchain("android_clang_mips64el") { + binary_prefix = "mips64el-linux-android" + toolchain_args = { + target_cpu = "mips64el" + } +} diff --git a/third_party/libwebrtc/build/toolchain/android/OWNERS b/third_party/libwebrtc/build/toolchain/android/OWNERS new file mode 100644 index 0000000000..a74cfbe228 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/android/OWNERS @@ -0,0 +1 @@ +file://build/android/OWNERS diff --git a/third_party/libwebrtc/build/toolchain/apple/BUILD.gn b/third_party/libwebrtc/build/toolchain/apple/BUILD.gn new file mode 100644 index 0000000000..6f074fd2d8 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/apple/BUILD.gn @@ -0,0 +1,22 @@ +# Copyright 2021 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("//build/toolchain/concurrent_links.gni") + +declare_args() { + # Reduce the number of tasks using the copy_bundle_data and compile_xcassets + # tools as they can cause lots of I/O contention when invoking ninja with a + # large number of parallel jobs (e.g. when using distributed build like goma). + bundle_pool_depth = -1 +} + +if (current_toolchain == default_toolchain) { + pool("bundle_pool") { + if (bundle_pool_depth == -1) { + depth = concurrent_links + } else { + depth = bundle_pool_depth + } + } +} diff --git a/third_party/libwebrtc/build/toolchain/apple/OWNERS b/third_party/libwebrtc/build/toolchain/apple/OWNERS new file mode 100644 index 0000000000..6f3324f07c --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/apple/OWNERS @@ -0,0 +1 @@ +file://build/apple/OWNERS diff --git a/third_party/libwebrtc/build/toolchain/apple/filter_libtool.py b/third_party/libwebrtc/build/toolchain/apple/filter_libtool.py new file mode 100644 index 0000000000..44c1c4400d --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/apple/filter_libtool.py @@ -0,0 +1,52 @@ +# 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. + +from __future__ import print_function + +import os +import re +import subprocess +import sys + +# This script executes libool and filters out logspam lines like: +# '/path/to/libtool: file: foo.o has no symbols' + +SUPPRESSED_PATTERNS = [ + re.compile(v) for v in [ + r'^.*libtool: (?:for architecture: \S* )?file: .* has no symbols$', + # Xcode 11 spelling of the "empty archive" warning. + # TODO(thakis): Remove once we require Xcode 12. + r'^.*libtool: warning for library: .* the table of contents is empty ' \ + r'\(no object file members in the library define global symbols\)$', + # Xcode 12 spelling of the "empty archive" warning. + r'^warning: .*libtool: archive library: .* ' \ + r'the table of contents is empty ', + r'\(no object file members in the library define global symbols\)$', + r'^.*libtool: warning same member name \(\S*\) in output file used ' \ + r'for input files: \S* and: \S* \(due to use of basename, ' \ + r'truncation, blank padding or duplicate input files\)$', + ] +] + + +def ShouldSuppressLine(line): + """Returns whether the line should be filtered out.""" + for pattern in SUPPRESSED_PATTERNS: + if pattern.match(line): + return True + return False + + +def Main(cmd_list): + env = os.environ.copy() + libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env) + _, err = libtoolout.communicate() + for line in err.decode('UTF-8').splitlines(): + if not ShouldSuppressLine(line): + print(line, file=sys.stderr) + return libtoolout.returncode + + +if __name__ == '__main__': + sys.exit(Main(sys.argv[1:])) diff --git a/third_party/libwebrtc/build/toolchain/apple/get_tool_mtime.py b/third_party/libwebrtc/build/toolchain/apple/get_tool_mtime.py new file mode 100644 index 0000000000..ff0254c635 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/apple/get_tool_mtime.py @@ -0,0 +1,19 @@ +# 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. + +from __future__ import print_function + +import os +import sys + +# Usage: python get_tool_mtime.py path/to/file1.py path/to/file2.py +# +# Prints a GN scope with the variable name being the basename sans-extension +# and the value being the file modification time. A variable is emitted for +# each file argument on the command line. + +if __name__ == '__main__': + for f in sys.argv[1:]: + variable = os.path.splitext(os.path.basename(f))[0] + print('%s = %d' % (variable, os.path.getmtime(f))) 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) diff --git a/third_party/libwebrtc/build/toolchain/apple/toolchain.gni b/third_party/libwebrtc/build/toolchain/apple/toolchain.gni new file mode 100644 index 0000000000..f914d8d400 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/apple/toolchain.gni @@ -0,0 +1,611 @@ +# Copyright (c) 2013 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. + +# TODO(brettw) Use "gcc_toolchain.gni" like the Linux toolchains. This requires +# some enhancements since the commands on Mac are slightly different than on +# Linux. + +import("//build/config/apple/symbols.gni") +import("//build/config/clang/clang.gni") +import("//build/config/compiler/compiler.gni") +import("//build/config/coverage/coverage.gni") +import("//build/config/rust.gni") +import("//build/toolchain/cc_wrapper.gni") +import("//build/toolchain/goma.gni") +import("//build/toolchain/rbe.gni") +import("//build/toolchain/toolchain.gni") + +assert((target_os == "ios" && host_os == "mac") || host_os != "win") + +declare_args() { + # This controls whether whole module optimization is enabled when building + # Swift modules. If enabled, the compiler will compile the module as one + # unit, generating just one single object file. Otherwise, it will generate + # one object file per .swift file. If unspecified, will default to "true" + # for official builds, and "false" for all other builds. + swift_whole_module_optimization = -1 +} + +if (swift_whole_module_optimization == -1) { + swift_whole_module_optimization = is_official_build +} + +# When implementing tools using Python scripts, a TOOL_VERSION=N env +# variable is placed in front of the command. The N should be incremented +# whenever the script is changed, so that the build system rebuilds all +# edges that utilize the script. Ideally this should be changed to use +# proper input-dirty checking, but that could be expensive. Instead, use a +# script to get the tool scripts' modification time to use as the version. +# This won't cause a re-generation of GN files when the tool script changes +# but it will cause edges to be marked as dirty if the ninja files are +# regenerated. See https://crbug.com/619083 for details. A proper fix +# would be to have inputs to tools (https://crbug.com/621119). +tool_versions = + exec_script("get_tool_mtime.py", + rebase_path([ + "//build/toolchain/apple/filter_libtool.py", + "//build/toolchain/apple/linker_driver.py", + "//build/toolchain/ios/compile_xcassets.py", + "//build/toolchain/ios/swiftc.py", + ], + root_build_dir), + "trim scope") + +# Shared toolchain definition. Invocations should set current_os to set the +# build args in this definition. +template("apple_toolchain") { + toolchain(target_name) { + # When invoking this toolchain not as the default one, these args will be + # passed to the build. They are ignored when this is the default toolchain. + assert(defined(invoker.toolchain_args), + "Toolchains must declare toolchain_args") + toolchain_args = { + # Populate toolchain args from the invoker. + forward_variables_from(invoker.toolchain_args, "*") + + # The host toolchain value computed by the default toolchain's setup + # needs to be passed through unchanged to all secondary toolchains to + # ensure that it's always the same, regardless of the values that may be + # set on those toolchains. + host_toolchain = host_toolchain + } + + # When the invoker has explicitly overridden use_goma or cc_wrapper in the + # toolchain args, use those values, otherwise default to the global one. + # This works because the only reasonable override that toolchains might + # supply for these values are to force-disable them. + if (defined(toolchain_args.use_remoteexec)) { + toolchain_uses_remoteexec = toolchain_args.use_remoteexec + } else { + toolchain_uses_remoteexec = use_remoteexec + } + if (defined(toolchain_args.use_goma)) { + toolchain_uses_goma = toolchain_args.use_goma + } else { + toolchain_uses_goma = use_goma + } + if (defined(toolchain_args.cc_wrapper)) { + toolchain_cc_wrapper = toolchain_args.cc_wrapper + } else { + toolchain_cc_wrapper = cc_wrapper + } + assert(!(toolchain_uses_remoteexec && toolchain_uses_goma), + "Goma and re-client can't be used together.") + assert(!(toolchain_cc_wrapper != "" && toolchain_uses_remoteexec), + "re-client and cc_wrapper can't be used together.") + assert(!(toolchain_cc_wrapper != "" && toolchain_uses_goma), + "Goma and cc_wrapper can't be used together.") + + if (defined(toolchain_args.use_lld)) { + toolchain_uses_lld = toolchain_args.use_lld + } else { + toolchain_uses_lld = use_lld + } + if (defined(toolchain_args.use_xcode_clang)) { + toolchain_uses_xcode_clang = toolchain_args.use_xcode_clang + } else { + toolchain_uses_xcode_clang = use_xcode_clang + } + + # Supports building with the version of clang shipped with Xcode when + # targeting iOS by not respecting clang_base_path. + if (toolchain_uses_xcode_clang) { + prefix = invoker.bin_path + } else { + prefix = rebase_path("$clang_base_path/bin/", root_build_dir) + } + + _cc = "${prefix}clang" + _cxx = "${prefix}clang++" + + swiftmodule_switch = "-Wl,-add_ast_path," + + # Compute the compiler prefix. + if (toolchain_uses_remoteexec) { + if (defined(toolchain_args.rbe_cc_cfg_file)) { + toolchain_rbe_cc_cfg_file = toolchain_args.rbe_cc_cfg_file + } else { + toolchain_rbe_cc_cfg_file = rbe_cc_cfg_file + } + + # C/C++ (clang) rewrapper prefix to use when use_remoteexec is true. + compiler_prefix = "${rbe_bin_dir}/rewrapper -cfg=${toolchain_rbe_cc_cfg_file} -exec_root=${rbe_exec_root} " + } else if (toolchain_uses_goma) { + assert(toolchain_cc_wrapper == "", + "Goma and cc_wrapper can't be used together.") + compiler_prefix = "$goma_dir/gomacc " + if (use_goma_rust) { + rust_compiler_prefix = compiler_prefix + } + } else if (toolchain_cc_wrapper != "") { + compiler_prefix = toolchain_cc_wrapper + " " + } else { + compiler_prefix = "" + } + + cc = compiler_prefix + _cc + cxx = compiler_prefix + _cxx + ld = _cxx + + # Set the explicit search path for clang++ so it uses the right linker + # binary. + if (!toolchain_uses_lld) { + ld += " -B " + invoker.bin_path + } + + if (defined(toolchain_args.coverage_instrumentation_input_file)) { + toolchain_coverage_instrumentation_input_file = + toolchain_args.coverage_instrumentation_input_file + } else { + toolchain_coverage_instrumentation_input_file = + coverage_instrumentation_input_file + } + _use_clang_coverage_wrapper = + toolchain_coverage_instrumentation_input_file != "" + if (_use_clang_coverage_wrapper) { + _coverage_wrapper = + rebase_path("//build/toolchain/clang_code_coverage_wrapper.py", + root_build_dir) + " --files-to-instrument=" + + rebase_path(toolchain_coverage_instrumentation_input_file, + root_build_dir) + " --target-os=" + target_os + cc = "$python_path $_coverage_wrapper ${cc}" + cxx = "$python_path $_coverage_wrapper ${cxx}" + } + + linker_driver = + "TOOL_VERSION=${tool_versions.linker_driver} " + + rebase_path("//build/toolchain/apple/linker_driver.py", root_build_dir) + + # Specify an explicit path for the strip binary. + _strippath = invoker.bin_path + "strip" + linker_driver += " -Wcrl,strippath," + _strippath + + # This makes the linker set timestamps in Mach-O files to 0. + linker_driver += " --deterministic" + + # On iOS, the final applications are assembled using lipo (to support fat + # builds). The correct flags are passed to the linker_driver.py script + # directly during the lipo call. The test is against the target_os because + # there is no need to create .dSYMs for targets compiled for the host. + if (defined(invoker.strip_with_lipo) && invoker.strip_with_lipo) { + _enable_dsyms = false + _save_unstripped_output = false + } else { + _enable_dsyms = enable_dsyms + _save_unstripped_output = save_unstripped_output + } + + # Make these apply to all tools below. + lib_switch = "-l" + lib_dir_switch = "-L" + + # Object files go in this directory. Use label_name instead of + # target_output_name since labels will generally have no spaces and will be + # unique in the directory. + object_subdir = "{{target_out_dir}}/{{label_name}}" + + # If dSYMs are enabled, this flag will be added to the link tools. + if (_enable_dsyms) { + dsym_switch = " -Wcrl,dsym,{{root_out_dir}} " + dsym_switch += "-Wcrl,dsymutilpath," + + rebase_path("//tools/clang/dsymutil/bin/dsymutil", + root_build_dir) + " " + + dsym_output_dir = + "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.dSYM" + dsym_output = [ + "$dsym_output_dir/Contents/Info.plist", + "$dsym_output_dir/Contents/Resources/DWARF/" + + "{{target_output_name}}{{output_extension}}", + ] + } else { + dsym_switch = "" + } + + if (_save_unstripped_output) { + _unstripped_output = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.unstripped" + } + + if (toolchain_has_rust) { + if (!defined(rust_compiler_prefix)) { + rust_compiler_prefix = "" + } + rustc = "$rust_compiler_prefix${rust_prefix}rustc" + + # Ideally, we'd add -Clink-args=\"{{ldflags}}\" to each of the Rust + # tools below which may link (i.e. rust_bin, rust_cdylib, rust_macro). + # However, it seems -fuse-ld=lld causes difficulties. + + tool("rust_staticlib") { + rust_outfile = "{{target_out_dir}}/{{crate_name}}.a" + depfile = "{{output}}.d" + command = "$rustc $rustc_common_args --emit=dep-info=$depfile,link -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + + tool("rust_rlib") { + rust_outfile = "{{target_out_dir}}/lib{{crate_name}}.rlib" + depfile = "{{output}}.d" + command = "$rustc $rustc_common_args --emit=dep-info=$depfile,link -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + + if (rustc_can_link) { + tool("rust_bin") { + rust_outfile = "{{root_out_dir}}/{{crate_name}}" + depfile = "{{output}}.d" + command = "$rustc $rustc_common_args --emit=dep-info=$depfile,link -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + + tool("rust_cdylib") { + rust_outfile = "{{target_out_dir}}/lib{{crate_name}}.dylib" + depfile = "{{output}}.d" + command = "$rustc $rustc_common_args --emit=dep-info=$depfile,link -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + + tool("rust_macro") { + rust_outfile = "{{target_out_dir}}/lib{{crate_name}}.dylib" + depfile = "{{output}}.d" + command = "$rustc $rustc_common_args --emit=dep-info=$depfile,link -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + } + } + + tool("cc") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" + command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "CC {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.o" ] + } + + tool("cxx") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" + command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "CXX {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.o" ] + } + + tool("asm") { + # For GCC we can just use the C compiler to compile assembly. + depfile = "{{output}}.d" + command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "ASM {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.o" ] + } + + tool("objc") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" + command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objc}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "OBJC {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.o" ] + } + + tool("objcxx") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" + command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objcc}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "OBJCXX {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.o" ] + } + + tool("alink") { + rspfile = "{{output}}.rsp" + + if (!toolchain_uses_lld) { + # Note about -filelist: Apple's linker reads the file list file and + # interprets each newline-separated chunk of text as a file name. It + # doesn't do the things one would expect from the shell like unescaping + # or handling quotes. In contrast, when Ninja finds a file name with + # spaces, it single-quotes them in $inputs_newline as it would normally + # do for command-line arguments. Thus any source names with spaces, or + # label names with spaces (which GN bases the output paths on) will be + # corrupted by this process. Don't use spaces for source files or + # labels. + rspfile_content = "{{inputs_newline}}" + + script = rebase_path("//build/toolchain/apple/filter_libtool.py", + root_build_dir) + + # Specify explicit path for libtool. + libtool = invoker.bin_path + "libtool" + command = "rm -f {{output}} && TOOL_VERSION=${tool_versions.filter_libtool} $python_path $script $libtool -static -D {{arflags}} -o {{output}} -filelist $rspfile" + description = "LIBTOOL-STATIC {{output}}" + } else { + rspfile_content = "{{inputs}}" + ar = "${prefix}llvm-ar" + command = "\"$ar\" {{arflags}} -r -c -s -D {{output}} \"@$rspfile\"" + + # Remove the output file first so that ar doesn't try to modify the + # existing file. + command = "rm -f {{output}} && $command" + description = "AR {{output}}" + } + outputs = [ "{{output_dir}}/{{target_output_name}}{{output_extension}}" ] + default_output_dir = "{{target_out_dir}}" + default_output_extension = ".a" + output_prefix = "lib" + } + + tool("solink") { + # E.g. "./libfoo.dylib": + dylib = "{{output_dir}}/{{target_output_name}}{{output_extension}}" + rspfile = dylib + ".rsp" + pool = "//build/toolchain:link_pool($default_toolchain)" + + # These variables are not built into GN but are helpers that implement + # (1) linking to produce a .dylib, (2) extracting the symbols from that + # file to a temporary file, (3) if the temporary file has differences from + # the existing .TOC file, overwrite it, otherwise, don't change it. + # + # As a special case, if the library reexports symbols from other dynamic + # libraries, we always update the .TOC and skip the temporary file and + # diffing steps, since that library always needs to be re-linked. + tocname = dylib + ".TOC" + temporary_tocname = dylib + ".tmp" + + # Use explicit paths to binaries. The binaries present on the default + # search path in /usr/bin are thin wrappers around xcrun, which requires a + # full CommandLineTools or Xcode install, and still may not choose the + # appropriate binary if there are multiple installs. + if (host_os == "mac") { + nm = invoker.bin_path + "nm" + otool = invoker.bin_path + "otool" + } else { + nm = "${prefix}llvm-nm" + otool = "${prefix}llvm-otool" + } + + does_reexport_command = "[ ! -e \"$dylib\" -o ! -e \"$tocname\" ] || $otool -l \"$dylib\" | grep -q LC_REEXPORT_DYLIB" + + link_command = "$linker_driver $ld -shared " + if (is_component_build) { + link_command += " -Wl,-install_name,@rpath/\"{{target_output_name}}{{output_extension}}\" " + } + link_command += dsym_switch + link_command += "{{ldflags}} -o \"$dylib\" -Wl,-filelist,\"$rspfile\" {{frameworks}} {{swiftmodules}} {{solibs}} {{libs}} {{rlibs}}" + + replace_command = "if ! cmp -s \"$temporary_tocname\" \"$tocname\"; then mv \"$temporary_tocname\" \"$tocname\"" + extract_toc_command = "{ $otool -l \"$dylib\" | grep LC_ID_DYLIB -A 5; $nm -gPp \"$dylib\" | cut -f1-2 -d' ' | grep -v U\$\$; true; }" + + command = "if $does_reexport_command ; then $link_command && $extract_toc_command > \"$tocname\"; else $link_command && $extract_toc_command > \"$temporary_tocname\" && $replace_command ; fi; fi" + + rspfile_content = "{{inputs_newline}}" + + description = "SOLINK {{output}}" + + # Use this for {{output_extension}} expansions unless a target manually + # overrides it (in which case {{output_extension}} will be what the target + # specifies). + default_output_dir = "{{root_out_dir}}" + default_output_extension = ".dylib" + + output_prefix = "lib" + + # Since the above commands only updates the .TOC file when it changes, ask + # Ninja to check if the timestamp actually changed to know if downstream + # dependencies should be recompiled. + restat = true + + # Tell GN about the output files. It will link to the dylib but use the + # tocname for dependency management. + outputs = [ + dylib, + tocname, + ] + link_output = dylib + depend_output = tocname + + if (_enable_dsyms) { + outputs += dsym_output + } + if (_save_unstripped_output) { + outputs += [ _unstripped_output ] + } + } + + tool("solink_module") { + # E.g. "./libfoo.so": + sofile = "{{output_dir}}/{{target_output_name}}{{output_extension}}" + rspfile = sofile + ".rsp" + pool = "//build/toolchain:link_pool($default_toolchain)" + + link_command = "$linker_driver $ld -bundle {{ldflags}} -o \"$sofile\" -Wl,-filelist,\"$rspfile\"" + link_command += dsym_switch + link_command += + " {{frameworks}} {{swiftmodules}} {{solibs}} {{libs}} {{rlibs}}" + command = link_command + + rspfile_content = "{{inputs_newline}}" + + description = "SOLINK_MODULE {{output}}" + + # Use this for {{output_extension}} expansions unless a target manually + # overrides it (in which case {{output_extension}} will be what the target + # specifies). + default_output_dir = "{{root_out_dir}}" + default_output_extension = ".so" + + outputs = [ sofile ] + + if (_enable_dsyms) { + outputs += dsym_output + } + if (_save_unstripped_output) { + outputs += [ _unstripped_output ] + } + } + + tool("link") { + outfile = "{{output_dir}}/{{target_output_name}}{{output_extension}}" + rspfile = "$outfile.rsp" + pool = "//build/toolchain:link_pool($default_toolchain)" + + # Note about -filelist: Apple's linker reads the file list file and + # interprets each newline-separated chunk of text as a file name. It + # doesn't do the things one would expect from the shell like unescaping + # or handling quotes. In contrast, when Ninja finds a file name with + # spaces, it single-quotes them in $inputs_newline as it would normally + # do for command-line arguments. Thus any source names with spaces, or + # label names with spaces (which GN bases the output paths on) will be + # corrupted by this process. Don't use spaces for source files or labels. + command = "$linker_driver $ld $dsym_switch {{ldflags}} -o \"$outfile\" -Wl,-filelist,\"$rspfile\" {{frameworks}} {{swiftmodules}} {{solibs}} {{libs}} {{rlibs}}" + description = "LINK $outfile" + rspfile_content = "{{inputs_newline}}" + outputs = [ outfile ] + + if (_enable_dsyms) { + outputs += dsym_output + } + if (_save_unstripped_output) { + outputs += [ _unstripped_output ] + } + + default_output_dir = "{{root_out_dir}}" + } + + # These two are really entirely generic, but have to be repeated in + # each toolchain because GN doesn't allow a template to be used here. + # See //build/toolchain/toolchain.gni for details. + tool("stamp") { + command = stamp_command + description = stamp_description + } + tool("copy") { + command = copy_command + description = copy_description + } + + tool("copy_bundle_data") { + # copy_command use hardlink if possible but this does not work with + # directories. Also when running EG2 tests from Xcode, Xcode tries to + # copy some files into the application bundle which fails if source + # and destination are hardlinked together. + # + # Instead use clonefile to copy the files which is as efficient as + # hardlink but ensure the file have distinct metadata (thus avoid the + # error with ditto, see https://crbug.com/1042182). + if (host_os == "mac") { + command = "rm -rf {{output}} && /bin/cp -Rc {{source}} {{output}}" + } else { + command = "rm -rf {{output}} && /bin/cp -Rl {{source}} {{output}}" + } + description = "COPY_BUNDLE_DATA {{source}} {{output}}" + pool = "//build/toolchain/apple:bundle_pool($default_toolchain)" + } + + # Swift is only used on iOS, not macOS. We want to minimize the number + # of Xcode-based tools used by the macOS toolchain, so we intentionally + # disallow future uses of Swift on macOS. https://crbug.com/965663. + if (toolchain_args.current_os == "ios") { + tool("swift") { + _tool = rebase_path("//build/toolchain/ios/swiftc.py", root_build_dir) + + depfile = "{{target_out_dir}}/{{module_name}}.d" + depsformat = "gcc" + + outputs = [ + # The module needs to be the first output listed. The blank line after + # the module is required to prevent `gn format` from changing the file + # order. + "{{target_gen_dir}}/{{module_name}}.swiftmodule", + + "{{target_gen_dir}}/{{module_name}}.h", + "{{target_gen_dir}}/{{module_name}}.swiftdoc", + "{{target_gen_dir}}/{{module_name}}.swiftsourceinfo", + ] + + if (swift_whole_module_optimization) { + _extra_flags = "-whole-module-optimization" + _objects_dir = "{{target_out_dir}}" + + outputs += [ "$_objects_dir/{{module_name}}.o" ] + } else { + _extra_flags = "" + _objects_dir = "{{target_out_dir}}/{{label_name}}" + + partial_outputs = [ "$_objects_dir/{{source_name_part}}.o" ] + } + + _env_vars = "TOOL_VERSION=${tool_versions.swiftc}" + if (invoker.sdk_developer_dir != "") { + _env_vars += " DEVELOPER_DIR=${toolchain_args.sdk_developer_dir}" + } + + command = + "$_env_vars $python_path $_tool -module-name {{module_name}} " + + "-object-dir $_objects_dir " + + "-module-path {{target_gen_dir}}/{{module_name}}.swiftmodule " + + "-header-path {{target_gen_dir}}/{{module_name}}.h " + + "-depfile {{target_out_dir}}/{{module_name}}.d " + + "-depfile-filter {{target_gen_dir}}/{{module_name}}.swiftmodule " + + "-bridge-header {{bridge_header}} $_extra_flags " + + "{{swiftflags}} {{include_dirs}} {{module_dirs}} {{inputs}}" + } + } + + # xcassets are only used on iOS, not macOS. We want to minimize the number + # of Xcode-based tools used by the macOS toolchain, so we intentionally + # disallow future uses of xcassets on macOS. https://crbug.com/965663. + if (toolchain_args.current_os == "ios") { + tool("compile_xcassets") { + _tool = rebase_path("//build/toolchain/ios/compile_xcassets.py", + root_build_dir) + + _env_vars = "TOOL_VERSION=${tool_versions.compile_xcassets}" + if (invoker.sdk_developer_dir != "") { + _env_vars += " DEVELOPER_DIR=${toolchain_args.sdk_developer_dir}" + } + + command = + "$_env_vars $python_path $_tool -p \"${invoker.sdk_name}\" " + + "-t \"${invoker.deployment_target}\" " + + "-T \"{{bundle_product_type}}\" " + + "-P \"{{bundle_partial_info_plist}}\" " + "-o {{output}} {{inputs}}" + + description = "COMPILE_XCASSETS {{output}}" + pool = "//build/toolchain/apple:bundle_pool($default_toolchain)" + } + } + + tool("action") { + pool = "//build/toolchain:action_pool($default_toolchain)" + } + } +} diff --git a/third_party/libwebrtc/build/toolchain/cc_wrapper.gni b/third_party/libwebrtc/build/toolchain/cc_wrapper.gni new file mode 100644 index 0000000000..6186070e3d --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/cc_wrapper.gni @@ -0,0 +1,43 @@ +# Copyright (c) 2014 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("//build/toolchain/goma.gni") +import("//build/toolchain/rbe.gni") + +# Defines the configuration of cc wrapper +# ccache: a c/c++ compiler cache which can greatly reduce recompilation times. +# icecc, distcc: it takes compile jobs from a build and distributes them among +# remote machines allowing a parallel build. +# +# TIPS +# +# 1) ccache +# Set clang_use_chrome_plugins=false if using ccache 3.1.9 or earlier, since +# these versions don't support -Xclang. (3.1.10 and later will silently +# ignore -Xclang, so it doesn't matter if you disable clang_use_chrome_plugins +# or not). +# +# Use ccache 3.2 or later to avoid clang unused argument warnings: +# https://bugzilla.samba.org/show_bug.cgi?id=8118 +# +# To avoid -Wparentheses-equality clang warnings, at some cost in terms of +# speed, you can do: +# export CCACHE_CPP2=yes +# +# 2) icecc +# Set clang_use_chrome_plugins=false because icecc cannot distribute custom +# clang libraries. +# +# To use icecc and ccache together, set cc_wrapper = "ccache" with +# export CCACHE_PREFIX=icecc + +declare_args() { + # Set to "ccache", "icecc" or "distcc". Probably doesn't work on windows. + cc_wrapper = "" +} + +assert(!use_goma || cc_wrapper == "", + "use_goma and cc_wrapper can not be used together.") +assert(!use_remoteexec || cc_wrapper == "", + "use_remoteexec and cc_wrapper can not be used together.") diff --git a/third_party/libwebrtc/build/toolchain/clang_code_coverage_wrapper.py b/third_party/libwebrtc/build/toolchain/clang_code_coverage_wrapper.py new file mode 100755 index 0000000000..38d4155213 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/clang_code_coverage_wrapper.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python +# Copyright 2018 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. +"""Removes code coverage flags from invocations of the Clang C/C++ compiler. + +If the GN arg `use_clang_coverage=true`, this script will be invoked by default. +GN will add coverage instrumentation flags to almost all source files. + +This script is used to remove instrumentation flags from a subset of the source +files. By default, it will not remove flags from any files. If the option +--files-to-instrument is passed, this script will remove flags from all files +except the ones listed in --files-to-instrument. + +This script also contains hard-coded exclusion lists of files to never +instrument, indexed by target operating system. Files in these lists have their +flags removed in both modes. The OS can be selected with --target-os. + +This script also contains hard-coded force lists of files to always instrument, +indexed by target operating system. Files in these lists never have their flags +removed in either mode. The OS can be selected with --target-os. + +The order of precedence is: force list, exclusion list, --files-to-instrument. + +The path to the coverage instrumentation input file should be relative to the +root build directory, and the file consists of multiple lines where each line +represents a path to a source file, and the specified paths must be relative to +the root build directory. e.g. ../../base/task/post_task.cc for build +directory 'out/Release'. The paths should be written using OS-native path +separators for the current platform. + +One caveat with this compiler wrapper is that it may introduce unexpected +behaviors in incremental builds when the file path to the coverage +instrumentation input file changes between consecutive runs, so callers of this +script are strongly advised to always use the same path such as +"${root_build_dir}/coverage_instrumentation_input.txt". + +It's worth noting on try job builders, if the contents of the instrumentation +file changes so that a file doesn't need to be instrumented any longer, it will +be recompiled automatically because if try job B runs after try job A, the files +that were instrumented in A will be updated (i.e., reverted to the checked in +version) in B, and so they'll be considered out of date by ninja and recompiled. + +Example usage: + clang_code_coverage_wrapper.py \\ + --files-to-instrument=coverage_instrumentation_input.txt +""" + +from __future__ import print_function + +import argparse +import os +import subprocess +import sys + +# Flags used to enable coverage instrumentation. +# Flags should be listed in the same order that they are added in +# build/config/coverage/BUILD.gn +_COVERAGE_FLAGS = [ + '-fprofile-instr-generate', + '-fcoverage-mapping', + # Following experimental flags remove unused header functions from the + # coverage mapping data embedded in the test binaries, and the reduction + # of binary size enables building Chrome's large unit test targets on + # MacOS. Please refer to crbug.com/796290 for more details. + '-mllvm', + '-limited-coverage-experimental=true', +] + +# Files that should not be built with coverage flags by default. +_DEFAULT_COVERAGE_EXCLUSION_LIST = [ + # TODO(crbug.com/1051561): angle_unittests affected by coverage. + '../../base/message_loop/message_pump_default.cc', + '../../base/message_loop/message_pump_libevent.cc', + '../../base/message_loop/message_pump_win.cc', + '../../base/task/sequence_manager/thread_controller_with_message_pump_impl.cc', #pylint: disable=line-too-long +] + +# Map of exclusion lists indexed by target OS. +# If no target OS is defined, or one is defined that doesn't have a specific +# entry, use _DEFAULT_COVERAGE_EXCLUSION_LIST. +_COVERAGE_EXCLUSION_LIST_MAP = { + 'android': [ + # This file caused webview native library failed on arm64. + '../../device/gamepad/dualshock4_controller.cc', + ], + 'fuchsia': [ + # TODO(crbug.com/1174725): These files caused clang to crash while + # compiling them. + '../../base/allocator/partition_allocator/pcscan.cc', + '../../third_party/skia/src/core/SkOpts.cpp', + '../../third_party/skia/src/opts/SkOpts_hsw.cpp', + '../../third_party/skia/third_party/skcms/skcms.cc', + ], + 'linux': [ + # These files caused a static initializer to be generated, which + # shouldn't. + # TODO(crbug.com/990948): Remove when the bug is fixed. + '../../chrome/browser/media/router/providers/cast/cast_internal_message_util.cc', #pylint: disable=line-too-long + '../../components/cast_channel/cast_channel_enum.cc', + '../../components/cast_channel/cast_message_util.cc', + '../../components/media_router/common/providers/cast/cast_media_source.cc', #pylint: disable=line-too-long + '../../ui/events/keycodes/dom/keycode_converter.cc', + # TODO(crbug.com/1051561): angle_unittests affected by coverage. + '../../base/message_loop/message_pump_default.cc', + '../../base/message_loop/message_pump_libevent.cc', + '../../base/message_loop/message_pump_win.cc', + '../../base/task/sequence_manager/thread_controller_with_message_pump_impl.cc', #pylint: disable=line-too-long + ], + 'chromeos': [ + # These files caused clang to crash while compiling them. They are + # excluded pending an investigation into the underlying compiler bug. + '../../third_party/webrtc/p2p/base/p2p_transport_channel.cc', + '../../third_party/icu/source/common/uts46.cpp', + '../../third_party/icu/source/common/ucnvmbcs.cpp', + '../../base/android/android_image_reader_compat.cc', + # TODO(crbug.com/1051561): angle_unittests affected by coverage. + '../../base/message_loop/message_pump_default.cc', + '../../base/message_loop/message_pump_libevent.cc', + '../../base/message_loop/message_pump_win.cc', + '../../base/task/sequence_manager/thread_controller_with_message_pump_impl.cc', #pylint: disable=line-too-long + ], + 'win': [ + # TODO(crbug.com/1051561): angle_unittests affected by coverage. + '../../base/message_loop/message_pump_default.cc', + '../../base/message_loop/message_pump_libevent.cc', + '../../base/message_loop/message_pump_win.cc', + '../../base/task/sequence_manager/thread_controller_with_message_pump_impl.cc', #pylint: disable=line-too-long + ], +} + +# Map of force lists indexed by target OS. +_COVERAGE_FORCE_LIST_MAP = { + # clang_profiling.cc refers to the symbol `__llvm_profile_dump` from the + # profiling runtime. In a partial coverage build, it is possible for a + # binary to include clang_profiling.cc but have no instrumented files, thus + # causing an unresolved symbol error because the profiling runtime will not + # be linked in. Therefore we force coverage for this file to ensure that + # any target that includes it will also get the profiling runtime. + 'win': [r'..\..\base\test\clang_profiling.cc'], + # TODO(crbug.com/1141727) We're seeing runtime LLVM errors in mac-rel when + # no files are changed, so we suspect that this is similar to the other + # problem with clang_profiling.cc on Windows. The TODO here is to force + # coverage for this specific file on ALL platforms, if it turns out to fix + # this issue on Mac as well. It's the only file that directly calls + # `__llvm_profile_dump` so it warrants some special treatment. + 'mac': ['../../base/test/clang_profiling.cc'], +} + + +def _remove_flags_from_command(command): + # We need to remove the coverage flags for this file, but we only want to + # remove them if we see the exact sequence defined in _COVERAGE_FLAGS. + # That ensures that we only remove the flags added by GN when + # "use_clang_coverage" is true. Otherwise, we would remove flags set by + # other parts of the build system. + start_flag = _COVERAGE_FLAGS[0] + num_flags = len(_COVERAGE_FLAGS) + start_idx = 0 + try: + while True: + idx = command.index(start_flag, start_idx) + if command[idx:idx + num_flags] == _COVERAGE_FLAGS: + del command[idx:idx + num_flags] + # There can be multiple sets of _COVERAGE_FLAGS. All of these need to be + # removed. + start_idx = idx + else: + start_idx = idx + 1 + except ValueError: + pass + + +def main(): + arg_parser = argparse.ArgumentParser() + arg_parser.usage = __doc__ + arg_parser.add_argument( + '--files-to-instrument', + type=str, + help='Path to a file that contains a list of file names to instrument.') + arg_parser.add_argument( + '--target-os', required=False, help='The OS to compile for.') + arg_parser.add_argument('args', nargs=argparse.REMAINDER) + parsed_args = arg_parser.parse_args() + + if (parsed_args.files_to_instrument and + not os.path.isfile(parsed_args.files_to_instrument)): + raise Exception('Path to the coverage instrumentation file: "%s" doesn\'t ' + 'exist.' % parsed_args.files_to_instrument) + + compile_command = parsed_args.args + if not any('clang' in s for s in compile_command): + return subprocess.call(compile_command) + + target_os = parsed_args.target_os + + try: + # The command is assumed to use Clang as the compiler, and the path to the + # source file is behind the -c argument, and the path to the source path is + # relative to the root build directory. For example: + # clang++ -fvisibility=hidden -c ../../base/files/file_path.cc -o \ + # obj/base/base/file_path.o + # On Windows, clang-cl.exe uses /c instead of -c. + source_flag = '/c' if target_os == 'win' else '-c' + source_flag_index = compile_command.index(source_flag) + except ValueError: + print('%s argument is not found in the compile command.' % source_flag) + raise + + if source_flag_index + 1 >= len(compile_command): + raise Exception('Source file to be compiled is missing from the command.') + + # On Windows, filesystem paths should use '\', but GN creates build commands + # that use '/'. We invoke os.path.normpath to ensure that the path uses the + # correct separator for the current platform (i.e. '\' on Windows and '/' + # otherwise). + compile_source_file = os.path.normpath(compile_command[source_flag_index + 1]) + extension = os.path.splitext(compile_source_file)[1] + if not extension in ['.c', '.cc', '.cpp', '.cxx', '.m', '.mm', '.S']: + raise Exception('Invalid source file %s found' % compile_source_file) + exclusion_list = _COVERAGE_EXCLUSION_LIST_MAP.get( + target_os, _DEFAULT_COVERAGE_EXCLUSION_LIST) + force_list = _COVERAGE_FORCE_LIST_MAP.get(target_os, []) + + should_remove_flags = False + if compile_source_file not in force_list: + if compile_source_file in exclusion_list: + should_remove_flags = True + elif parsed_args.files_to_instrument: + with open(parsed_args.files_to_instrument) as f: + if compile_source_file not in f.read(): + should_remove_flags = True + + if should_remove_flags: + _remove_flags_from_command(compile_command) + + return subprocess.call(compile_command) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/third_party/libwebrtc/build/toolchain/concurrent_links.gni b/third_party/libwebrtc/build/toolchain/concurrent_links.gni new file mode 100644 index 0000000000..c0342256a6 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/concurrent_links.gni @@ -0,0 +1,106 @@ +# 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. + +# This file should only be imported from files that define toolchains. +# There's no way to enforce this exactly, but all toolchains are processed +# in the context of the default_toolchain, so we can at least check for that. +assert(current_toolchain == default_toolchain) + +import("//build/config/android/config.gni") +import("//build/config/apple/symbols.gni") +import("//build/config/chromeos/ui_mode.gni") +import("//build/config/compiler/compiler.gni") +import("//build/config/coverage/coverage.gni") +import("//build/config/sanitizers/sanitizers.gni") +import("//build/toolchain/toolchain.gni") + +declare_args() { + # Limit the number of concurrent links; we often want to run fewer + # links at once than we do compiles, because linking is memory-intensive. + # The default to use varies by platform and by the amount of memory + # available, so we call out to a script to get the right value. + concurrent_links = -1 +} + +if (concurrent_links == -1) { + if (use_thin_lto) { + _args = [ "--reserve_mem_gb=10" ] + if (use_goma_thin_lto) { + _args += [ "--thin-lto=goma" ] + } else { + _args += [ "--thin-lto=local" ] + } + if (is_win) { + # Based on measurements of linking chrome.dll and chrome_child.dll, plus + # a little padding to account for future growth. + _args += [ "--mem_per_link_gb=45" ] + } else { + _args += [ "--mem_per_link_gb=16" ] + } + } else if ((use_clang_coverage && + # When coverage_instrumentation_input_file is not empty it means + # we're only instrumenting changed files and not using a lot of + # memory. Likewise, when it's empty we're building everything with + # coverage, which requires more memory. + coverage_instrumentation_input_file == "") || + use_sanitizer_coverage || use_fuzzing_engine) { + # Full sanitizer coverage instrumentation increases linker memory consumption + # significantly. + _args = [ "--mem_per_link_gb=16" ] + } else if (is_win && symbol_level == 1 && !is_debug && is_component_build) { + _args = [ "--mem_per_link_gb=3" ] + } else if (is_win) { + _args = [ "--mem_per_link_gb=6" ] + } else if (is_mac) { + if (enable_dsyms) { + _args = [ "--mem_per_link_gb=12" ] + } else { + _args = [ "--mem_per_link_gb=4" ] + } + } else if (is_android && !is_component_build && symbol_level == 2) { + # Full debug symbols require large memory for link. + _args = [ "--mem_per_link_gb=25" ] + } else if (is_android && !is_debug && !using_sanitizer && is_java_debug && + disable_android_lint && symbol_level < 2) { + if (symbol_level == 1) { + _args = [ "--mem_per_link_gb=6" ] + } else { + _args = [ "--mem_per_link_gb=4" ] + } + } else if ((is_linux || is_chromeos_lacros) && symbol_level == 0) { + # Memory consumption on link without debug symbols is low on linux. + _args = [ "--mem_per_link_gb=3" ] + } else { + _args = [] + } + + # For Android builds, we also need to be wary of: + # * ProGuard / R8 + # * Android Lint + # These both have a peak usage of < 2GB, but that is still large enough for + # them to need to use a pool since they both typically happen at the + # same time as linking. + if (is_android) { + _args += [ "--secondary_mem_per_link=2" ] + } + + # TODO(crbug.com/617429) Pass more build configuration info to the script + # so that we can compute better values. + _command_dict = exec_script("get_concurrent_links.py", _args, "scope") + + concurrent_links = _command_dict.primary_pool_size + concurrent_links_logs = _command_dict.explanation + + if (_command_dict.secondary_pool_size >= concurrent_links) { + # Have R8 / Lint share the link pool unless we would safely get more + # concurrency out of using a separate one. + # On low-RAM machines, this allows an apk's native library to link at the + # same time as its java is optimized with R8. + java_cmd_pool_size = _command_dict.secondary_pool_size + } +} else { + assert(!use_thin_lto, "can't explicitly set concurrent_links with thinlto") + concurrent_links_logs = + [ "concurrent_links set by GN arg (value=$concurrent_links)" ] +} diff --git a/third_party/libwebrtc/build/toolchain/cros/BUILD.gn b/third_party/libwebrtc/build/toolchain/cros/BUILD.gn new file mode 100644 index 0000000000..7140f87d22 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/cros/BUILD.gn @@ -0,0 +1,176 @@ +# Copyright 2014 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("//build/config/compiler/compiler.gni") +import("//build/config/sysroot.gni") +import("//build/toolchain/cros_toolchain.gni") +import("//build/toolchain/gcc_toolchain.gni") + +# This is mostly identical to gcc_toolchain, but handles relativizing toolchain +# paths. This is needed for CrOS since these paths often change based on the +# environment. For example, cxx is a relative path picked up on $PATH in the +# chroot. But in Simple Chrome, cxx is a system-absolute path. +template("cros_toolchain") { + gcc_toolchain(target_name) { + forward_variables_from(invoker, "*") + + # CrOS's target toolchain wrapper prefers to invoke gomacc itself, so pass + # it the gomacc path via cmd-line arg. Otherwise, for both CrOS's host + # wrapper (used in the ebuild) and Chrome's clang (used in Simple Chrome), + # prepend gomacc like normal. + if (use_goma && toolchain_args.needs_gomacc_path_arg) { + extra_cppflags += " --gomacc-path $goma_dir/gomacc" + } + if (use_rbe && toolchain_args.needs_gomacc_path_arg) { + extra_cppflags += " --gomacc-path $rbe_cros_cc_wrapper" + } + + # Relativize path if compiler is specified such that not to lookup from $PATH + # and cc/cxx does not contain additional flags. + if (cc != get_path_info(cc, "file") && string_replace(cc, " ", "") == cc) { + cc = rebase_path(cc, root_build_dir) + } + if (cxx != get_path_info(cxx, "file") && + string_replace(cxx, " ", "") == cxx) { + cxx = rebase_path(cxx, root_build_dir) + } + if (ar != get_path_info(ar, "file") && string_replace(ar, " ", "") == ar) { + ar = rebase_path(ar, root_build_dir) + } + if (ld != get_path_info(ld, "file") && string_replace(ld, " ", "") == ld) { + ld = rebase_path(ld, root_build_dir) + } + } +} + +# This is the normal toolchain for most targets. +cros_toolchain("target") { + ar = cros_target_ar + cc = cros_target_cc + cxx = cros_target_cxx + ld = cros_target_ld + + if (cros_target_nm != "") { + nm = cros_target_nm + } + if (cros_target_readelf != "") { + readelf = cros_target_readelf + } + extra_cflags = cros_target_extra_cflags + extra_cppflags = cros_target_extra_cppflags + extra_cxxflags = cros_target_extra_cxxflags + extra_ldflags = cros_target_extra_ldflags + + toolchain_args = { + cc_wrapper = "" + needs_gomacc_path_arg = true + clang_use_chrome_plugins = false + target_cpu = target_cpu + current_os = "chromeos" + is_clang = is_clang + use_debug_fission = use_debug_fission + use_gold = use_gold + use_sysroot = use_sysroot + sysroot = target_sysroot + } +} + +# This is a special toolchain needed just for the nacl_bootstrap target in +# //native_client/src/trusted/service_runtime/linux. It is identical +# to ":target" except that it forces use_debug_fission, use_gold, and +# use_sysroot off, and allows the user to set different sets of extra flags. +cros_toolchain("nacl_bootstrap") { + ar = cros_target_ar + cc = cros_target_cc + cxx = cros_target_cxx + ld = cros_target_ld + + if (cros_target_nm != "") { + nm = cros_target_nm + } + if (cros_target_readelf != "") { + readelf = cros_target_readelf + } + extra_cflags = cros_nacl_bootstrap_extra_cflags + extra_cppflags = cros_nacl_bootstrap_extra_cppflags + extra_cxxflags = cros_nacl_bootstrap_extra_cxxflags + extra_ldflags = cros_nacl_bootstrap_extra_ldflags + + toolchain_args = { + cc_wrapper = "" + needs_gomacc_path_arg = true + clang_use_chrome_plugins = false + target_cpu = target_cpu + current_os = "chromeos" + is_clang = is_clang + use_debug_fission = false + use_gold = false + use_sysroot = false + } +} + +cros_toolchain("host") { + # These are args for the template. + ar = cros_host_ar + cc = cros_host_cc + cxx = cros_host_cxx + ld = cros_host_ld + + if (cros_host_nm != "") { + nm = cros_host_nm + } + if (cros_host_readelf != "") { + readelf = cros_host_readelf + } + extra_cflags = cros_host_extra_cflags + extra_cppflags = cros_host_extra_cppflags + extra_cxxflags = cros_host_extra_cxxflags + extra_ldflags = cros_host_extra_ldflags + + toolchain_args = { + cc_wrapper = "" + needs_gomacc_path_arg = false + clang_use_chrome_plugins = false + is_clang = cros_host_is_clang + target_cpu = host_cpu + current_os = "linux" + use_sysroot = use_sysroot + sysroot = cros_host_sysroot + } +} + +cros_toolchain("v8_snapshot") { + # These are args for the template. + ar = cros_v8_snapshot_ar + cc = cros_v8_snapshot_cc + cxx = cros_v8_snapshot_cxx + ld = cros_v8_snapshot_ld + + if (cros_v8_snapshot_nm != "") { + nm = cros_v8_snapshot_nm + } + if (cros_v8_snapshot_readelf != "") { + readelf = cros_v8_snapshot_readelf + } + extra_cflags = cros_v8_snapshot_extra_cflags + extra_cppflags = cros_v8_snapshot_extra_cppflags + extra_cxxflags = cros_v8_snapshot_extra_cxxflags + extra_ldflags = cros_v8_snapshot_extra_ldflags + + toolchain_args = { + cc_wrapper = "" + needs_gomacc_path_arg = false + clang_use_chrome_plugins = false + is_clang = cros_v8_snapshot_is_clang + if (target_cpu == "x86" || target_cpu == "arm" || target_cpu == "mipsel") { + target_cpu = "x86" + } else { + target_cpu = "x64" + } + v8_target_cpu = v8_target_cpu + current_os = "linux" + use_sysroot = use_sysroot + sysroot = cros_v8_snapshot_sysroot + } +} diff --git a/third_party/libwebrtc/build/toolchain/cros_toolchain.gni b/third_party/libwebrtc/build/toolchain/cros_toolchain.gni new file mode 100644 index 0000000000..ccc4db293c --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/cros_toolchain.gni @@ -0,0 +1,88 @@ +# 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. + +# CrOS builds must cross-compile on a Linux host for the actual CrOS +# device target. There are many different CrOS devices so the build +# system provides configuration variables that permit a CrOS build to +# control the cross-compilation tool chain. However, requiring such +# fine-grain specification is tedious for build-bots and developers. +# Consequently, the CrOS build system defaults to a convenience +# compilation mode where the compilation host is also the build target. +# +# Chrome can be compiled in this way with the gn variable: +# +# target_os = "chromeos" +# +# To perform a board-specific build, first obtain the correct system +# root (http://goo.gl/aFB4XH) for the board. Then configure GN to use it +# by setting appropriate cross-compilation variables. +# +# For example, to compile a Chrome source tree in /g/src for an +# auron_paine CrOS device with the system root cached in /g/.cros_cache, +# the following GN arguments must be provided to configure +# cross-compilation with Goma acceleration. (NB: additional variables +# will be necessary to successfully compile a working CrOS Chrome. See +# the definition of GYP_DEFINES inside a sysroot shell.) +# +# goma_dir = "/g/.cros_cache/common/goma+2" +# target_sysroot= /g/.cros_cache/chrome-sdk/tarballs/auron_paine+7644.0.0+sysroot_chromeos-base_chromeos-chrome.tar.xz" +# cros_target_cc = "x86_64-cros-linux-gnu-gcc -B/g/.cros_cache/chrome-sdk/tarballs/auron_paine+7657.0.0+target_toolchain/usr/x86_64-pc-linux-gnu/x86_64-cros-linux-gnu/binutils-bin/2.25.51-gold" +# cros_target_cxx = "x86_64-cros-linux-gnu-g++ -B/g/.cros_cache/chrome-sdk/tarballs/auron_paine+7657.0.0+target_toolchain/usr/x86_64-pc-linux-gnu/x86_64-cros-linux-gnu/binutils-bin/2.25.51-gold" +# cros_target_ar = "x86_64-cros-linux-gnu-gcc-ar" +# target_cpu = "x64" + +import("//build/config/clang/clang.gni") +import("//build/config/compiler/compiler.gni") + +declare_args() { + # These must be specified for a board-specific build. + cros_target_ar = "ar" + cros_target_cc = "gcc" + cros_target_cxx = "g++" + cros_target_nm = "" + cros_target_readelf = "" + + # These can be optionally set. The "_cppflags" will be applied to *both* + # C and C++ files; use "_cxxflags" for C++-only flags. + cros_target_extra_cflags = "" + cros_target_extra_cppflags = "" + cros_target_extra_cxxflags = "" + cros_target_extra_ldflags = "" + + # is_clang is used instead of cros_target_is_clang + cros_host_ar = "${clang_base_path}/bin/llvm-ar" + cros_host_cc = "${clang_base_path}/bin/clang" + cros_host_cxx = "${clang_base_path}/bin/clang++" + cros_host_is_clang = true + cros_host_nm = "" + cros_host_readelf = "" + cros_host_extra_cflags = "" + cros_host_extra_cppflags = "" + cros_host_extra_cxxflags = "" + cros_host_extra_ldflags = "" + cros_host_sysroot = "" + + cros_v8_snapshot_ar = "${clang_base_path}/bin/llvm-ar" + cros_v8_snapshot_cc = "${clang_base_path}/bin/clang" + cros_v8_snapshot_cxx = "${clang_base_path}/bin/clang++" + cros_v8_snapshot_is_clang = true + cros_v8_snapshot_nm = "" + cros_v8_snapshot_readelf = "" + cros_v8_snapshot_extra_cflags = "" + cros_v8_snapshot_extra_cppflags = "" + cros_v8_snapshot_extra_cxxflags = "" + cros_v8_snapshot_extra_ldflags = "" + cros_v8_snapshot_sysroot = "" + + cros_nacl_bootstrap_extra_cflags = "" + cros_nacl_bootstrap_extra_cppflags = "" + cros_nacl_bootstrap_extra_cxxflags = "" + cros_nacl_bootstrap_extra_ldflags = "" +} + +declare_args() { + cros_target_ld = cros_target_cxx + cros_host_ld = cros_host_cxx + cros_v8_snapshot_ld = cros_v8_snapshot_cxx +} diff --git a/third_party/libwebrtc/build/toolchain/fuchsia/BUILD.gn b/third_party/libwebrtc/build/toolchain/fuchsia/BUILD.gn new file mode 100644 index 0000000000..dbfcc9802f --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/fuchsia/BUILD.gn @@ -0,0 +1,37 @@ +# Copyright 2017 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("//build/toolchain/gcc_toolchain.gni") + +# Fuchsia builds using the Clang toolchain, with most parameters common across +# the different target architectures. +template("fuchsia_clang_toolchain") { + clang_toolchain(target_name) { + assert(host_os == "linux" || host_os == "mac") + assert(defined(invoker.toolchain_args), + "toolchain_args must be defined for fuchsia_clang_toolchain()") + + # We want to build and strip binaries, but retain the unstripped binaries + # in runtime_deps to make them available for isolates. + strip = rebase_path("${clang_base_path}/bin/llvm-strip", root_build_dir) + use_unstripped_as_runtime_outputs = true + + default_shlib_subdir = "/lib" + + toolchain_args = invoker.toolchain_args + toolchain_args.current_os = "fuchsia" + } +} + +fuchsia_clang_toolchain("x64") { + toolchain_args = { + target_cpu = "x64" + } +} + +fuchsia_clang_toolchain("arm64") { + toolchain_args = { + target_cpu = "arm64" + } +} diff --git a/third_party/libwebrtc/build/toolchain/fuchsia/DIR_METADATA b/third_party/libwebrtc/build/toolchain/fuchsia/DIR_METADATA new file mode 100644 index 0000000000..6d8f079aa5 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/fuchsia/DIR_METADATA @@ -0,0 +1,7 @@ +monorail { + component: "Fuchsia" +} + +team_email: "cr-fuchsia@chromium.org" + +os: FUCHSIA diff --git a/third_party/libwebrtc/build/toolchain/fuchsia/OWNERS b/third_party/libwebrtc/build/toolchain/fuchsia/OWNERS new file mode 100644 index 0000000000..3f809e82b1 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/fuchsia/OWNERS @@ -0,0 +1 @@ +scottmg@chromium.org diff --git a/third_party/libwebrtc/build/toolchain/gcc_link_wrapper.py b/third_party/libwebrtc/build/toolchain/gcc_link_wrapper.py new file mode 100755 index 0000000000..b70de8b31c --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/gcc_link_wrapper.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# 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. + +"""Runs a linking command and optionally a strip command. + +This script exists to avoid using complex shell commands in +gcc_toolchain.gni's tool("link"), in case the host running the compiler +does not have a POSIX-like shell (e.g. Windows). +""" + +import argparse +import os +import subprocess +import sys + +import wrapper_utils + + +# When running on a Windows host and using a toolchain whose tools are +# actually wrapper scripts (i.e. .bat files on Windows) rather than binary +# executables, the "command" to run has to be prefixed with this magic. +# The GN toolchain definitions take care of that for when GN/Ninja is +# running the tool directly. When that command is passed in to this +# script, it appears as a unitary string but needs to be split up so that +# just 'cmd' is the actual command given to Python's subprocess module. +BAT_PREFIX = 'cmd /c call ' + +def CommandToRun(command): + if command[0].startswith(BAT_PREFIX): + command = command[0].split(None, 3) + command[1:] + return command + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--strip', + help='The strip binary to run', + metavar='PATH') + parser.add_argument('--unstripped-file', + help='Executable file produced by linking command', + metavar='FILE') + parser.add_argument('--map-file', + help=('Use --Wl,-Map to generate a map file. Will be ' + 'gzipped if extension ends with .gz'), + metavar='FILE') + parser.add_argument('--dwp', help=('The dwp binary to run'), metavar='FILE') + parser.add_argument('--output', + required=True, + help='Final output executable file', + metavar='FILE') + parser.add_argument('command', nargs='+', + help='Linking command') + args = parser.parse_args() + + # Work-around for gold being slow-by-default. http://crbug.com/632230 + fast_env = dict(os.environ) + fast_env['LC_ALL'] = 'C' + result = wrapper_utils.RunLinkWithOptionalMapFile(args.command, env=fast_env, + map_file=args.map_file) + if result != 0: + return result + + # If dwp is set, then package debug info for this exe. + dwp_proc = None + if args.dwp: + exe_file = args.output + if args.unstripped_file: + exe_file = args.unstripped_file + # Suppress output here because it doesn't seem to be useful. The most + # common error is a segfault, which will happen if files are missing. + with open(os.devnull, "w") as devnull: + dwp_proc = subprocess.Popen(wrapper_utils.CommandToRun( + [args.dwp, '-e', exe_file, '-o', exe_file + '.dwp']), + stdout=devnull, + stderr=subprocess.STDOUT) + + # Finally, strip the linked executable (if desired). + if args.strip: + result = subprocess.call( + CommandToRun([args.strip, '-o', args.output, args.unstripped_file])) + + if dwp_proc: + dwp_result = dwp_proc.wait() + if dwp_result != 0: + sys.stderr.write('dwp failed with error code {}\n'.format(dwp_result)) + return dwp_result + + return result + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/third_party/libwebrtc/build/toolchain/gcc_solink_wrapper.py b/third_party/libwebrtc/build/toolchain/gcc_solink_wrapper.py new file mode 100755 index 0000000000..39aef4d1e9 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/gcc_solink_wrapper.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python +# 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. + +"""Runs 'ld -shared' and generates a .TOC file that's untouched when unchanged. + +This script exists to avoid using complex shell commands in +gcc_toolchain.gni's tool("solink"), in case the host running the compiler +does not have a POSIX-like shell (e.g. Windows). +""" + +import argparse +import os +import shlex +import subprocess +import sys + +import wrapper_utils + + +def CollectSONAME(args): + """Replaces: readelf -d $sofile | grep SONAME""" + toc = '' + readelf = subprocess.Popen(wrapper_utils.CommandToRun( + [args.readelf, '-d', args.sofile]), + stdout=subprocess.PIPE, + bufsize=-1, + universal_newlines=True) + for line in readelf.stdout: + if 'SONAME' in line: + toc += line + return readelf.wait(), toc + + +def CollectDynSym(args): + """Replaces: nm --format=posix -g -D -p $sofile | cut -f1-2 -d' '""" + toc = '' + nm = subprocess.Popen(wrapper_utils.CommandToRun( + [args.nm, '--format=posix', '-g', '-D', '-p', args.sofile]), + stdout=subprocess.PIPE, + bufsize=-1, + universal_newlines=True) + for line in nm.stdout: + toc += ' '.join(line.split(' ', 2)[:2]) + '\n' + return nm.wait(), toc + + +def CollectTOC(args): + result, toc = CollectSONAME(args) + if result == 0: + result, dynsym = CollectDynSym(args) + toc += dynsym + return result, toc + + +def UpdateTOC(tocfile, toc): + if os.path.exists(tocfile): + old_toc = open(tocfile, 'r').read() + else: + old_toc = None + if toc != old_toc: + open(tocfile, 'w').write(toc) + + +def CollectInputs(out, args): + for x in args: + if x.startswith('@'): + with open(x[1:]) as rsp: + CollectInputs(out, shlex.split(rsp.read())) + elif not x.startswith('-') and (x.endswith('.o') or x.endswith('.a')): + out.write(x) + out.write('\n') + + +def InterceptFlag(flag, command): + ret = flag in command + if ret: + command.remove(flag) + return ret + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--readelf', + required=True, + help='The readelf binary to run', + metavar='PATH') + parser.add_argument('--nm', + required=True, + help='The nm binary to run', + metavar='PATH') + parser.add_argument('--strip', + help='The strip binary to run', + metavar='PATH') + parser.add_argument('--dwp', help='The dwp binary to run', metavar='PATH') + parser.add_argument('--sofile', + required=True, + help='Shared object file produced by linking command', + metavar='FILE') + parser.add_argument('--tocfile', + required=True, + help='Output table-of-contents file', + metavar='FILE') + parser.add_argument('--map-file', + help=('Use --Wl,-Map to generate a map file. Will be ' + 'gzipped if extension ends with .gz'), + metavar='FILE') + parser.add_argument('--output', + required=True, + help='Final output shared object file', + metavar='FILE') + parser.add_argument('command', nargs='+', + help='Linking command') + args = parser.parse_args() + + # Work-around for gold being slow-by-default. http://crbug.com/632230 + fast_env = dict(os.environ) + fast_env['LC_ALL'] = 'C' + + # Extract flags passed through ldflags but meant for this script. + # https://crbug.com/954311 tracks finding a better way to plumb these. + link_only = InterceptFlag('--link-only', args.command) + collect_inputs_only = InterceptFlag('--collect-inputs-only', args.command) + + # If only linking, we are likely generating a partitioned .so that will be + # split apart later. In that case: + # + # - The TOC file optimization isn't useful, because the partition libraries + # must always be re-extracted if the combined library changes (and nothing + # should be depending on the combined library's dynamic symbol table). + # - Stripping isn't necessary, because the combined library is not used in + # production or published. + # + # Both of these operations could still be done, they're needless work, and + # tools would need to be updated to handle and/or not complain about + # partitioned libraries. Instead, to keep Ninja happy, simply create dummy + # files for the TOC and stripped lib. + if link_only or collect_inputs_only: + open(args.output, 'w').close() + open(args.tocfile, 'w').close() + if args.dwp: + open(args.sofile + '.dwp', 'w').close() + + # Instead of linking, records all inputs to a file. This is used by + # enable_resource_allowlist_generation in order to avoid needing to + # link (which is slow) to build the resources allowlist. + if collect_inputs_only: + with open(args.sofile, 'w') as f: + CollectInputs(f, args.command) + if args.map_file: + open(args.map_file, 'w').close() + return 0 + + # First, run the actual link. + command = wrapper_utils.CommandToRun(args.command) + result = wrapper_utils.RunLinkWithOptionalMapFile(command, + env=fast_env, + map_file=args.map_file) + + if result != 0 or link_only: + return result + + # If dwp is set, then package debug info for this SO. + dwp_proc = None + if args.dwp: + # Suppress output here because it doesn't seem to be useful. The most + # common error is a segfault, which will happen if files are missing. + with open(os.devnull, "w") as devnull: + dwp_proc = subprocess.Popen(wrapper_utils.CommandToRun( + [args.dwp, '-e', args.sofile, '-o', args.sofile + '.dwp']), + stdout=devnull, + stderr=subprocess.STDOUT) + + # Next, generate the contents of the TOC file. + result, toc = CollectTOC(args) + if result != 0: + return result + + # If there is an existing TOC file with identical contents, leave it alone. + # Otherwise, write out the TOC file. + UpdateTOC(args.tocfile, toc) + + # Finally, strip the linked shared object file (if desired). + if args.strip: + result = subprocess.call(wrapper_utils.CommandToRun( + [args.strip, '-o', args.output, args.sofile])) + + if dwp_proc: + dwp_result = dwp_proc.wait() + if dwp_result != 0: + return dwp_result + + return result + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/third_party/libwebrtc/build/toolchain/gcc_toolchain.gni b/third_party/libwebrtc/build/toolchain/gcc_toolchain.gni new file mode 100644 index 0000000000..2b547c3435 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/gcc_toolchain.gni @@ -0,0 +1,762 @@ +# Copyright (c) 2013 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("//build/config/clang/clang.gni") +import("//build/config/compiler/compiler.gni") +import("//build/config/coverage/coverage.gni") +import("//build/config/rust.gni") +import("//build/config/sanitizers/sanitizers.gni") +import("//build/config/v8_target_cpu.gni") +import("//build/toolchain/cc_wrapper.gni") +import("//build/toolchain/goma.gni") +import("//build/toolchain/rbe.gni") +import("//build/toolchain/toolchain.gni") + +if (is_nacl) { + # To keep NaCl variables out of builds that don't include NaCl, all + # variables defined in nacl/config.gni referenced here should be protected by + # is_nacl conditions. + import("//build/config/nacl/config.gni") +} + +declare_args() { + # Enables allowlist generation for IDR_ grit defines seen by the compiler. + # Currently works only on some platforms and enabled by default for official + # builds. Requires debug info. + enable_resource_allowlist_generation = + is_official_build && + # Don't enable for Android-on-Chrome OS. + (target_os == "android" || target_os == "win") +} + +# When the arg is set via args.gn, it applies to all toolchains. In order to not +# hit the assert in grit_rule.gni, explicitly disable for host toolchains. +if ((is_linux || is_chromeos) && target_os == "android") { + enable_resource_allowlist_generation = false +} + +# Ensure enable_resource_allowlist_generation is enabled only when it will work. +if (enable_resource_allowlist_generation) { + assert( + !strip_debug_info, + "enable_resource_allowlist_generation=true requires strip_debug_info=false") + assert( + !is_component_build, + "enable_resource_allowlist_generation=true requires is_component_build=false") + assert( + target_os == "android" || target_os == "win", + "enable_resource_allowlist_generation=true does not work for target_os=$target_os") +} + +# This template defines a toolchain for something that works like gcc +# (including clang). +# +# It requires the following variables specifying the executables to run: +# - ar +# - cc +# - cxx +# - ld +# +# Optional parameters that control the tools: +# +# - extra_cflags +# Extra flags to be appended when compiling C files (but not C++ files). +# - extra_cppflags +# Extra flags to be appended when compiling both C and C++ files. "CPP" +# stands for "C PreProcessor" in this context, although it can be +# used for non-preprocessor flags as well. Not to be confused with +# "CXX" (which follows). +# - extra_cxxflags +# Extra flags to be appended when compiling C++ files (but not C files). +# - extra_asmflags +# Extra flags to be appended when compiling assembly. +# - extra_ldflags +# Extra flags to be appended when linking +# +# - link_outputs +# The content of this array, if specified, will be added to the list of +# outputs from the link command. This can be useful in conjunction with +# the post_link parameter. +# - use_unstripped_as_runtime_outputs +# When |strip| is set, mark unstripped executables as runtime deps rather +# than stripped ones. +# - post_link +# The content of this string, if specified, will be run as a separate +# command following the the link command. +# - deps +# Just forwarded to the toolchain definition. +# - executable_extension +# If this string is specified it will be used for the file extension +# for an executable, rather than using no extension; targets will +# still be able to override the extension using the output_extension +# variable. +# - rebuild_define +# The contents of this string, if specified, will be passed as a #define +# to the toolchain. It can be used to force recompiles whenever a +# toolchain is updated. +# - shlib_extension +# If this string is specified it will be used for the file extension +# for a shared library, rather than default value specified in +# toolchain.gni +# - strip +# Location of the strip executable. When specified, strip will be run on +# all shared libraries and executables as they are built. The pre-stripped +# artifacts will be put in lib.unstripped/ and exe.unstripped/. +template("gcc_toolchain") { + toolchain(target_name) { + assert(defined(invoker.ar), "gcc_toolchain() must specify a \"ar\" value") + assert(defined(invoker.cc), "gcc_toolchain() must specify a \"cc\" value") + assert(defined(invoker.cxx), "gcc_toolchain() must specify a \"cxx\" value") + assert(defined(invoker.ld), "gcc_toolchain() must specify a \"ld\" value") + + # This define changes when the toolchain changes, forcing a rebuild. + # Nothing should ever use this define. + if (defined(invoker.rebuild_define)) { + rebuild_string = "-D" + invoker.rebuild_define + " " + } else { + rebuild_string = "" + } + + # GN's syntax can't handle more than one scope dereference at once, like + # "invoker.toolchain_args.foo", so make a temporary to hold the toolchain + # args so we can do "invoker_toolchain_args.foo". + assert(defined(invoker.toolchain_args), + "Toolchains must specify toolchain_args") + invoker_toolchain_args = invoker.toolchain_args + assert(defined(invoker_toolchain_args.target_cpu), + "toolchain_args must specify a target_cpu") + assert(defined(invoker_toolchain_args.current_os), + "toolchain_args must specify a current_os") + + # When invoking this toolchain not as the default one, these args will be + # passed to the build. They are ignored when this is the default toolchain. + toolchain_args = { + # Populate toolchain args from the invoker. + forward_variables_from(invoker_toolchain_args, "*") + + # The host toolchain value computed by the default toolchain's setup + # needs to be passed through unchanged to all secondary toolchains to + # ensure that it's always the same, regardless of the values that may be + # set on those toolchains. + host_toolchain = host_toolchain + + if (!defined(invoker_toolchain_args.v8_target_cpu)) { + v8_target_cpu = invoker_toolchain_args.target_cpu + } + } + + # When the invoker has explicitly overridden use_remoteexec, use_goma or + # cc_wrapper in the toolchain args, use those values, otherwise default + # to the global one. This works because the only reasonable override + # that toolchains might supply for these values are to force-disable them. + if (defined(toolchain_args.use_remoteexec)) { + toolchain_uses_remoteexec = toolchain_args.use_remoteexec + } else { + toolchain_uses_remoteexec = use_remoteexec + } + if (defined(toolchain_args.use_goma)) { + toolchain_uses_goma = toolchain_args.use_goma + } else { + toolchain_uses_goma = use_goma + } + + # x86_64-nacl-* is ELF-32 and Goma/RBE won't support ELF-32. + if (toolchain_uses_goma && + get_path_info(invoker.cc, "name") == "x86_64-nacl-gcc") { + # it will also disable x86_64-nacl-g++ since these are in + # the same toolchain. + toolchain_uses_goma = false + } + if (defined(toolchain_args.cc_wrapper)) { + toolchain_cc_wrapper = toolchain_args.cc_wrapper + } else { + toolchain_cc_wrapper = cc_wrapper + } + assert(!(toolchain_uses_remoteexec && toolchain_uses_goma), + "Goma and re-client can't be used together.") + assert(!(toolchain_cc_wrapper != "" && toolchain_uses_remoteexec), + "re-client and cc_wrapper can't be used together.") + assert(!(toolchain_cc_wrapper != "" && toolchain_uses_goma), + "Goma and cc_wrapper can't be used together.") + + # When the invoker has explicitly overridden use_goma or cc_wrapper in the + # toolchain args, use those values, otherwise default to the global one. + # This works because the only reasonable override that toolchains might + # supply for these values are to force-disable them. + # But if needs_gomacc_path_arg is set in a Chrome OS build, the toolchain + # wrapper will have picked up gomacc via cmd-line arg. So need to prepend + # gomacc in that case. + goma_path = "$goma_dir/gomacc" + if (toolchain_uses_remoteexec && + (!defined(invoker_toolchain_args.needs_gomacc_path_arg) || + !invoker_toolchain_args.needs_gomacc_path_arg)) { + if (defined(toolchain_args.rbe_cc_cfg_file)) { + toolchain_rbe_cc_cfg_file = toolchain_args.rbe_cc_cfg_file + } else { + toolchain_rbe_cc_cfg_file = rbe_cc_cfg_file + } + + # C/C++ (clang) rewrapper prefix to use when use_remoteexec is true. + compiler_prefix = "${rbe_bin_dir}/rewrapper -cfg=${toolchain_rbe_cc_cfg_file} -exec_root=${rbe_exec_root} " + } else if (toolchain_uses_goma && + (!defined(invoker_toolchain_args.needs_gomacc_path_arg) || + !invoker_toolchain_args.needs_gomacc_path_arg)) { + compiler_prefix = "${goma_path} " + if (use_goma_rust) { + rust_compiler_prefix = compiler_prefix + } + } else { + compiler_prefix = "${toolchain_cc_wrapper} " + } + + if (use_goma_thin_lto && toolchain_uses_goma && use_thin_lto) { + # goma_ld.py uses autoninja in an attempt to set a reasonable + # number of jobs, but this results in too low a value on + # Chrome OS builders. So we pass in an explicit value. + link_prefix = + "$python_path " + + rebase_path("//tools/clang/scripts/goma_ld.py", root_build_dir) + + " --gomacc ${goma_path} --jobs 200 -- " + } else { + link_prefix = "" + not_needed([ "goma_path" ]) + } + + # Create a distinct variable for "asm", since coverage runs pass a bunch of + # flags to clang/clang++ that are nonsensical on assembler runs. + asm_prefix = compiler_prefix + + # A specific toolchain may wish to avoid coverage instrumentation, so we + # allow the global "use_clang_coverage" arg to be overridden. + if (defined(toolchain_args.use_clang_coverage)) { + toolchain_use_clang_coverage = toolchain_args.use_clang_coverage + } else { + toolchain_use_clang_coverage = use_clang_coverage + } + + # For a coverage build, we use the wrapper script globally so that it can + # remove coverage cflags from files that should not have them. + if (toolchain_use_clang_coverage) { + # "coverage_instrumentation_input_file" is set in args.gn, but it can be + # overridden by a toolchain config. + if (defined(toolchain_args.coverage_instrumentation_input_file)) { + toolchain_coverage_instrumentation_input_file = + toolchain_args.coverage_instrumentation_input_file + } else { + toolchain_coverage_instrumentation_input_file = + coverage_instrumentation_input_file + } + + _coverage_wrapper = + rebase_path("//build/toolchain/clang_code_coverage_wrapper.py", + root_build_dir) + + # The wrapper needs to know what OS we target because it uses that to + # select a list of files that should not be instrumented. + _coverage_wrapper = _coverage_wrapper + " --target-os=" + + invoker_toolchain_args.current_os + + # We want to instrument everything if there is no input file set. + # If there is a file we need to give it to the wrapper script so it can + # instrument only those files. + if (toolchain_coverage_instrumentation_input_file != "") { + _coverage_wrapper = + _coverage_wrapper + " --files-to-instrument=" + + rebase_path(toolchain_coverage_instrumentation_input_file, + root_build_dir) + } + compiler_prefix = "$python_path ${_coverage_wrapper} " + compiler_prefix + } + + cc = compiler_prefix + invoker.cc + cxx = compiler_prefix + invoker.cxx + asm = asm_prefix + invoker.cc + ar = invoker.ar + ld = link_prefix + invoker.ld + if (defined(invoker.readelf)) { + readelf = invoker.readelf + } else { + readelf = "readelf" + } + if (defined(invoker.nm)) { + nm = invoker.nm + } else { + nm = "nm" + } + if (defined(invoker.dwp)) { + dwp_switch = " --dwp=\"${invoker.dwp}\"" + } else { + dwp_switch = "" + } + + if (defined(invoker.shlib_extension)) { + default_shlib_extension = invoker.shlib_extension + } else { + default_shlib_extension = shlib_extension + } + + if (defined(invoker.default_shlib_subdir)) { + default_shlib_subdir = invoker.default_shlib_subdir + } else { + default_shlib_subdir = "" + } + + if (defined(invoker.executable_extension)) { + default_executable_extension = invoker.executable_extension + } else { + default_executable_extension = "" + } + + # Bring these into our scope for string interpolation with default values. + if (defined(invoker.extra_cflags) && invoker.extra_cflags != "") { + extra_cflags = " " + invoker.extra_cflags + } else { + extra_cflags = "" + } + + if (defined(invoker.extra_cppflags) && invoker.extra_cppflags != "") { + extra_cppflags = " " + invoker.extra_cppflags + } else { + extra_cppflags = "" + } + + if (defined(invoker.extra_cxxflags) && invoker.extra_cxxflags != "") { + extra_cxxflags = " " + invoker.extra_cxxflags + } else { + extra_cxxflags = "" + } + + if (defined(invoker.extra_asmflags) && invoker.extra_asmflags != "") { + extra_asmflags = " " + invoker.extra_asmflags + } else { + extra_asmflags = "" + } + + if (defined(invoker.extra_ldflags) && invoker.extra_ldflags != "") { + extra_ldflags = " " + invoker.extra_ldflags + } else { + extra_ldflags = "" + } + + enable_linker_map = defined(invoker.enable_linker_map) && + invoker.enable_linker_map && generate_linker_map + + # These library switches can apply to all tools below. + lib_switch = "-l" + lib_dir_switch = "-L" + + # Object files go in this directory. + object_subdir = "{{target_out_dir}}/{{label_name}}" + + tool("cc") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" + command = "$cc -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}}${extra_cppflags}${extra_cflags} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "CC {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.o" ] + } + + tool("cxx") { + depfile = "{{output}}.d" + precompiled_header_type = "gcc" + command = "$cxx -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}}${extra_cppflags}${extra_cxxflags} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "CXX {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.o" ] + } + + tool("asm") { + # For GCC we can just use the C compiler to compile assembly. + depfile = "{{output}}.d" + command = "$asm -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{asmflags}}${extra_asmflags} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "ASM {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.o" ] + } + + tool("alink") { + if (current_os == "aix") { + # AIX does not support either -D (deterministic output) or response + # files. + command = "$ar -X64 {{arflags}} -r -c -s {{output}} {{inputs}}" + } else { + rspfile = "{{output}}.rsp" + rspfile_content = "{{inputs}}" + command = "\"$ar\" {{arflags}} -r -c -s -D {{output}} @\"$rspfile\"" + } + + # Remove the output file first so that ar doesn't try to modify the + # existing file. + if (host_os == "win") { + tool_wrapper_path = + rebase_path("//build/toolchain/win/tool_wrapper.py", root_build_dir) + command = "cmd /c $python_path $tool_wrapper_path delete-file {{output}} && $command" + } else { + command = "rm -f {{output}} && $command" + } + + # Almost all targets build with //build/config/compiler:thin_archive which + # adds -T to arflags. + description = "AR {{output}}" + outputs = [ "{{output_dir}}/{{target_output_name}}{{output_extension}}" ] + + # Shared libraries go in the target out directory by default so we can + # generate different targets with the same name and not have them collide. + default_output_dir = "{{target_out_dir}}" + default_output_extension = ".a" + output_prefix = "lib" + } + + tool("solink") { + soname = "{{target_output_name}}{{output_extension}}" # e.g. "libfoo.so". + sofile = "{{output_dir}}/$soname" # Possibly including toolchain dir. + rspfile = sofile + ".rsp" + + pool = "//build/toolchain:link_pool($default_toolchain)" + + if (defined(invoker.strip)) { + unstripped_sofile = "{{root_out_dir}}/lib.unstripped/$soname" + } else { + unstripped_sofile = sofile + } + + # These variables are not built into GN but are helpers that + # implement (1) linking to produce a .so, (2) extracting the symbols + # from that file (3) if the extracted list differs from the existing + # .TOC file, overwrite it, otherwise, don't change it. + tocfile = sofile + ".TOC" + + soname_flag = "" + if (current_os != "aix") { + # -soname flag is not available on aix ld + soname_flag = "-Wl,-soname=\"$soname\"" + } + link_command = "$ld -shared $soname_flag {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" @\"$rspfile\"" + + # Generate a map file to be used for binary size analysis. + # Map file adds ~10% to the link time on a z620. + # With target_os="android", libchrome.so.map.gz is ~20MB. + map_switch = "" + if (enable_linker_map) { + map_file = "$unstripped_sofile.map.gz" + map_switch = " --map-file \"$map_file\"" + } + + assert(defined(readelf), "to solink you must have a readelf") + assert(defined(nm), "to solink you must have an nm") + strip_switch = "" + if (defined(invoker.strip)) { + strip_switch = "--strip=${invoker.strip} " + } + + # This needs a Python script to avoid using a complex shell command + # requiring sh control structures, pipelines, and POSIX utilities. + # The host might not have a POSIX shell and utilities (e.g. Windows). + solink_wrapper = + rebase_path("//build/toolchain/gcc_solink_wrapper.py", root_build_dir) + solink_extra_flags = "" + if (current_os == "aix") { + # to be intercepted by solink_wrapper + solink_extra_flags = "--link-only" + } + command = "$python_path \"$solink_wrapper\" --readelf=\"$readelf\" --nm=\"$nm\" $strip_switch$dwp_switch --sofile=\"$unstripped_sofile\" --tocfile=\"$tocfile\"$map_switch --output=\"$sofile\" -- $link_command $solink_extra_flags" + + if (target_cpu == "mipsel" && is_component_build && is_android) { + rspfile_content = "-Wl,--start-group -Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}} -Wl,--end-group {{rlibs}}" + } else if (current_os == "aix") { + # --whole-archive, --no-whole-archive flags are not available on the aix ld. + rspfile_content = "{{inputs}} {{solibs}} {{libs}} {{rlibs}}" + } else { + rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}} {{rlibs}}" + } + + description = "SOLINK $sofile" + + # Use this for {{output_extension}} expansions unless a target manually + # overrides it (in which case {{output_extension}} will be what the target + # specifies). + default_output_extension = default_shlib_extension + + default_output_dir = "{{root_out_dir}}${default_shlib_subdir}" + + output_prefix = "lib" + + # Since the above commands only updates the .TOC file when it changes, ask + # Ninja to check if the timestamp actually changed to know if downstream + # dependencies should be recompiled. + restat = true + + # Tell GN about the output files. It will link to the sofile but use the + # tocfile for dependency management. + outputs = [ + sofile, + tocfile, + ] + if (sofile != unstripped_sofile) { + outputs += [ unstripped_sofile ] + if (defined(invoker.use_unstripped_as_runtime_outputs) && + invoker.use_unstripped_as_runtime_outputs) { + runtime_outputs = [ unstripped_sofile ] + } + } + + # Clank build will generate DWP files when Fission is used. + # Other builds generate DWP files outside of the gn link targets, if at + # all. + if (defined(invoker.dwp)) { + outputs += [ unstripped_sofile + ".dwp" ] + if (defined(invoker.use_unstripped_as_runtime_outputs) && + invoker.use_unstripped_as_runtime_outputs) { + runtime_outputs += [ unstripped_sofile + ".dwp" ] + } + } + if (defined(map_file)) { + outputs += [ map_file ] + } + link_output = sofile + depend_output = tocfile + } + + tool("solink_module") { + soname = "{{target_output_name}}{{output_extension}}" # e.g. "libfoo.so". + sofile = "{{output_dir}}/$soname" + rspfile = sofile + ".rsp" + + pool = "//build/toolchain:link_pool($default_toolchain)" + + if (defined(invoker.strip)) { + unstripped_sofile = "{{root_out_dir}}/lib.unstripped/$soname" + } else { + unstripped_sofile = sofile + } + + soname_flag = "" + whole_archive_flag = "" + no_whole_archive_flag = "" + if (current_os != "aix") { + # -soname, --whole-archive, --no-whole-archive flags are not available on aix ld + soname_flag = "-Wl,-soname=\"$soname\"" + whole_archive_flag = "-Wl,--whole-archive" + no_whole_archive_flag = "-Wl,--no-whole-archive" + } + command = "$ld -shared {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" $soname_flag @\"$rspfile\"" + + if (defined(invoker.strip)) { + strip_command = "${invoker.strip} -o \"$sofile\" \"$unstripped_sofile\"" + command += " && " + strip_command + } + rspfile_content = "$whole_archive_flag {{inputs}} {{solibs}} $no_whole_archive_flag {{libs}} {{rlibs}}" + + description = "SOLINK_MODULE $sofile" + + # Use this for {{output_extension}} expansions unless a target manually + # overrides it (in which case {{output_extension}} will be what the target + # specifies). + if (defined(invoker.loadable_module_extension)) { + default_output_extension = invoker.loadable_module_extension + } else { + default_output_extension = default_shlib_extension + } + + default_output_dir = "{{root_out_dir}}${default_shlib_subdir}" + + output_prefix = "lib" + + outputs = [ sofile ] + if (sofile != unstripped_sofile) { + outputs += [ unstripped_sofile ] + if (defined(invoker.use_unstripped_as_runtime_outputs) && + invoker.use_unstripped_as_runtime_outputs) { + runtime_outputs = [ unstripped_sofile ] + } + } + } + + tool("link") { + exename = "{{target_output_name}}{{output_extension}}" + outfile = "{{output_dir}}/$exename" + rspfile = "$outfile.rsp" + unstripped_outfile = outfile + + pool = "//build/toolchain:link_pool($default_toolchain)" + + # Use this for {{output_extension}} expansions unless a target manually + # overrides it (in which case {{output_extension}} will be what the target + # specifies). + default_output_extension = default_executable_extension + + default_output_dir = "{{root_out_dir}}" + + if (defined(invoker.strip)) { + unstripped_outfile = "{{root_out_dir}}/exe.unstripped/$exename" + } + + start_group_flag = "" + end_group_flag = "" + if (current_os != "aix") { + # the "--start-group .. --end-group" feature isn't available on the aix ld. + start_group_flag = "-Wl,--start-group" + end_group_flag = "-Wl,--end-group " + } + link_command = "$ld {{ldflags}}${extra_ldflags} -o \"$unstripped_outfile\" $start_group_flag @\"$rspfile\" {{solibs}} $end_group_flag {{libs}}" + + # Generate a map file to be used for binary size analysis. + # Map file adds ~10% to the link time on a z620. + # With target_os="android", libchrome.so.map.gz is ~20MB. + map_switch = "" + if (enable_linker_map) { + map_file = "$unstripped_outfile.map.gz" + map_switch = " --map-file \"$map_file\"" + } + + strip_switch = "" + if (defined(invoker.strip)) { + strip_switch = " --strip=\"${invoker.strip}\" --unstripped-file=\"$unstripped_outfile\"" + } + + link_wrapper = + rebase_path("//build/toolchain/gcc_link_wrapper.py", root_build_dir) + command = "$python_path \"$link_wrapper\" --output=\"$outfile\"$strip_switch$map_switch$dwp_switch -- $link_command" + + description = "LINK $outfile" + rspfile_content = "{{inputs}} {{rlibs}}" + outputs = [ outfile ] + if (outfile != unstripped_outfile) { + outputs += [ unstripped_outfile ] + if (defined(invoker.use_unstripped_as_runtime_outputs) && + invoker.use_unstripped_as_runtime_outputs) { + runtime_outputs = [ unstripped_outfile ] + } + } + + # Clank build will generate DWP files when Fission is used. + # Other builds generate DWP files outside of the gn link targets, if at + # all. + if (defined(invoker.dwp)) { + outputs += [ unstripped_outfile + ".dwp" ] + if (defined(invoker.use_unstripped_as_runtime_outputs) && + invoker.use_unstripped_as_runtime_outputs) { + runtime_outputs += [ unstripped_outfile + ".dwp" ] + } + } + if (defined(invoker.link_outputs)) { + outputs += invoker.link_outputs + } + if (defined(map_file)) { + outputs += [ map_file ] + } + } + + # These two are really entirely generic, but have to be repeated in + # each toolchain because GN doesn't allow a template to be used here. + # See //build/toolchain/toolchain.gni for details. + tool("stamp") { + command = stamp_command + description = stamp_description + } + tool("copy") { + command = copy_command + description = copy_description + } + + tool("action") { + pool = "//build/toolchain:action_pool($default_toolchain)" + } + + if (toolchain_has_rust) { + rustc = "" + if (defined(rust_compiler_prefix)) { + rustc += rust_compiler_prefix + } + rustc += rust_prefix + rustc += "rustc" + + # RSP files not used due to https://bugs.chromium.org/p/gn/issues/detail?id=249 + tool("rust_staticlib") { + rust_outfile = "{{target_out_dir}}/{{crate_name}}.a" + depfile = "{{output}}.d" + command = "$rustc $rustc_common_args --emit=dep-info=$depfile,link -Clinker=$ld -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + + tool("rust_rlib") { + rust_outfile = "{{target_out_dir}}/lib{{crate_name}}.rlib" + depfile = "{{output}}.d" + command = "$rustc $rustc_common_args --emit=dep-info=$depfile,link -Clinker=$ld -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + + if (rustc_can_link) { + tool("rust_bin") { + rust_outfile = "{{root_out_dir}}/{{crate_name}}" + depfile = "{{output}}.d" + command = "$rustc $rustc_common_args --emit=dep-info=$depfile,link -Clinker=$ld -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + + tool("rust_cdylib") { + rust_outfile = "{{target_out_dir}}/lib{{crate_name}}.so" + depfile = "{{output}}.d" + command = "$rustc $rustc_common_args --emit=dep-info=$depfile,link -Clinker=$ld -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + + tool("rust_macro") { + rust_outfile = "{{target_out_dir}}/lib{{crate_name}}.so" + depfile = "{{output}}.d" + command = "$rustc $rustc_common_args --emit=dep-info=$depfile,link -Clinker=$ld -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + } + } + + forward_variables_from(invoker, [ "deps" ]) + } +} + +# This is a shorthand for gcc_toolchain instances based on the Chromium-built +# version of Clang. Only the toolchain_cpu and toolchain_os variables need to +# be specified by the invoker, and optionally toolprefix if it's a +# cross-compile case. Note that for a cross-compile case this toolchain +# requires a config to pass the appropriate -target option, or else it will +# actually just be doing a native compile. The invoker can optionally override +# use_gold too. +template("clang_toolchain") { + if (defined(invoker.toolprefix)) { + toolprefix = invoker.toolprefix + } else { + toolprefix = "" + } + + gcc_toolchain(target_name) { + prefix = rebase_path("$clang_base_path/bin", root_build_dir) + cc = "$prefix/clang" + cxx = "$prefix/clang++" + ld = cxx + readelf = "${toolprefix}readelf" + ar = "${prefix}/llvm-ar" + nm = "${prefix}/llvm-nm" + + forward_variables_from(invoker, + [ + "strip", + "default_shlib_subdir", + "enable_linker_map", + "use_unstripped_as_runtime_outputs", + ]) + + toolchain_args = { + if (defined(invoker.toolchain_args)) { + forward_variables_from(invoker.toolchain_args, "*") + } + is_clang = true + } + } +} diff --git a/third_party/libwebrtc/build/toolchain/get_concurrent_links.py b/third_party/libwebrtc/build/toolchain/get_concurrent_links.py new file mode 100755 index 0000000000..d77bb4375e --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/get_concurrent_links.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python +# Copyright 2014 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. + +# This script computs the number of concurrent links we want to run in the build +# as a function of machine spec. It's based on GetDefaultConcurrentLinks in GYP. + +from __future__ import print_function + +import argparse +import multiprocessing +import os +import re +import subprocess +import sys + +sys.path.insert(1, os.path.join(os.path.dirname(__file__), '..')) +import gn_helpers + + +def _GetTotalMemoryInBytes(): + if sys.platform in ('win32', 'cygwin'): + import ctypes + + class MEMORYSTATUSEX(ctypes.Structure): + _fields_ = [ + ("dwLength", ctypes.c_ulong), + ("dwMemoryLoad", ctypes.c_ulong), + ("ullTotalPhys", ctypes.c_ulonglong), + ("ullAvailPhys", ctypes.c_ulonglong), + ("ullTotalPageFile", ctypes.c_ulonglong), + ("ullAvailPageFile", ctypes.c_ulonglong), + ("ullTotalVirtual", ctypes.c_ulonglong), + ("ullAvailVirtual", ctypes.c_ulonglong), + ("sullAvailExtendedVirtual", ctypes.c_ulonglong), + ] + + stat = MEMORYSTATUSEX(dwLength=ctypes.sizeof(MEMORYSTATUSEX)) + ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat)) + return stat.ullTotalPhys + elif sys.platform.startswith('linux'): + if os.path.exists("/proc/meminfo"): + with open("/proc/meminfo") as meminfo: + memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB') + for line in meminfo: + match = memtotal_re.match(line) + if not match: + continue + return float(match.group(1)) * 2**10 + elif sys.platform == 'darwin': + try: + return int(subprocess.check_output(['sysctl', '-n', 'hw.memsize'])) + except Exception: + return 0 + # TODO(scottmg): Implement this for other platforms. + return 0 + + +def _GetDefaultConcurrentLinks(per_link_gb, reserve_gb, thin_lto_type, + secondary_per_link_gb): + explanation = [] + explanation.append( + 'per_link_gb={} reserve_gb={} secondary_per_link_gb={}'.format( + per_link_gb, reserve_gb, secondary_per_link_gb)) + mem_total_gb = float(_GetTotalMemoryInBytes()) / 2**30 + mem_total_gb = max(0, mem_total_gb - reserve_gb) + mem_cap = int(max(1, mem_total_gb / per_link_gb)) + + try: + cpu_count = multiprocessing.cpu_count() + except: + cpu_count = 1 + + # A local LTO links saturate all cores, but only for some amount of the link. + # Goma LTO runs LTO codegen on goma, only run one of these tasks at once. + cpu_cap = cpu_count + if thin_lto_type is not None: + if thin_lto_type == 'goma': + cpu_cap = 1 + else: + assert thin_lto_type == 'local' + cpu_cap = min(cpu_count, 6) + + explanation.append('cpu_count={} cpu_cap={} mem_total_gb={:.1f}GiB'.format( + cpu_count, cpu_cap, mem_total_gb)) + + num_links = min(mem_cap, cpu_cap) + if num_links == cpu_cap: + if cpu_cap == cpu_count: + reason = 'cpu_count' + else: + reason = 'cpu_cap (thinlto)' + else: + reason = 'RAM' + + explanation.append('concurrent_links={} (reason: {})'.format( + num_links, reason)) + + # See if there is RAM leftover for a secondary pool. + if secondary_per_link_gb and num_links == mem_cap: + mem_remaining = mem_total_gb - mem_cap * per_link_gb + secondary_size = int(max(0, mem_remaining / secondary_per_link_gb)) + explanation.append('secondary_size={} (mem_remaining={:.1f}GiB)'.format( + secondary_size, mem_remaining)) + else: + secondary_size = 0 + + return num_links, secondary_size, explanation + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--mem_per_link_gb', type=int, default=8) + parser.add_argument('--reserve_mem_gb', type=int, default=0) + parser.add_argument('--secondary_mem_per_link', type=int, default=0) + parser.add_argument('--thin-lto') + options = parser.parse_args() + + primary_pool_size, secondary_pool_size, explanation = ( + _GetDefaultConcurrentLinks(options.mem_per_link_gb, + options.reserve_mem_gb, options.thin_lto, + options.secondary_mem_per_link)) + sys.stdout.write( + gn_helpers.ToGNString({ + 'primary_pool_size': primary_pool_size, + 'secondary_pool_size': secondary_pool_size, + 'explanation': explanation, + })) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/third_party/libwebrtc/build/toolchain/get_cpu_count.py b/third_party/libwebrtc/build/toolchain/get_cpu_count.py new file mode 100644 index 0000000000..765c7c78f6 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/get_cpu_count.py @@ -0,0 +1,23 @@ +# Copyright 2018 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. + +# This script shows cpu count to specify capacity of action pool. + +from __future__ import print_function + +import multiprocessing +import sys + +def main(): + try: + cpu_count = multiprocessing.cpu_count() + except: + cpu_count = 1 + + print(cpu_count) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/third_party/libwebrtc/build/toolchain/get_goma_dir.py b/third_party/libwebrtc/build/toolchain/get_goma_dir.py new file mode 100644 index 0000000000..114da6c0ea --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/get_goma_dir.py @@ -0,0 +1,42 @@ +# Copyright 2020 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. + +# This script gets default goma_dir for depot_tools goma. + +import os +import sys + + +def main(): + gomacc = 'gomacc' + candidates = [] + if sys.platform in ['win32', 'cygwin']: + gomacc = 'gomacc.exe' + + for path in os.environ.get('PATH', '').split(os.pathsep): + # normpath() required to strip trailing slash when present. + if os.path.basename(os.path.normpath(path)) == 'depot_tools': + candidates.append(os.path.join(path, '.cipd_bin')) + + for d in candidates: + if os.path.isfile(os.path.join(d, gomacc)): + sys.stdout.write(d) + return 0 + # mb analyze step set use_goma=true, but goma_dir="", + # and bot doesn't have goma in default locataion above. + # to mitigate this, just use initial depot_tools path + # or default path as before (if depot_tools doesn't exist + # in PATH). + # TODO(ukai): crbug.com/1073276: fix mb analyze step and make it hard error? + if sys.platform in ['win32', 'cygwin']: + sys.stdout.write('C:\\src\\goma\\goma-win64') + elif 'GOMA_DIR' in os.environ: + sys.stdout.write(os.environ.get('GOMA_DIR')) + else: + sys.stdout.write(os.path.join(os.environ.get('HOME', ''), 'goma')) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/third_party/libwebrtc/build/toolchain/goma.gni b/third_party/libwebrtc/build/toolchain/goma.gni new file mode 100644 index 0000000000..56787f1b9b --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/goma.gni @@ -0,0 +1,35 @@ +# Copyright (c) 2013 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. + +# Defines the configuration of Goma. + +declare_args() { + # Set to true to enable distributed compilation using Goma. + use_goma = false + + # This flag is for ChromeOS compiler wrapper. + # By passing gomacc path via cmd-line arg, ChromeOS' compiler wrapper + # invokes gomacc inside it. + needs_gomacc_path_arg = false + + # Absolute directory containing the gomacc binary. + goma_dir = "" +} + +if (use_goma && goma_dir == "") { + goma_dir = exec_script("get_goma_dir.py", [], "string") +} + +declare_args() { + # TODO(crbug.com/726475): true if use_goma = true in the future. + use_java_goma = false + + # Deprecated and ignored as Goma RBE is now the default. Still exists + # to avoid breaking the build on the bots. Will be removed when all + # bots have been configured to not set this variable. + ios_use_goma_rbe = -1 +} + +assert(!is_win || !use_goma || is_clang, + "cl.exe does not work on goma, use clang") diff --git a/third_party/libwebrtc/build/toolchain/ios/BUILD.gn b/third_party/libwebrtc/build/toolchain/ios/BUILD.gn new file mode 100644 index 0000000000..4c9cc586d2 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/ios/BUILD.gn @@ -0,0 +1,113 @@ +# Copyright 2021 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("//build/config/ios/ios_sdk.gni") +import("//build/toolchain/apple/toolchain.gni") + +# Specialisation of the apple_toolchain template to declare the toolchain +# and its tools to build target for iOS platform. +template("ios_toolchain") { + assert(defined(invoker.toolchain_args), + "Toolchains must declare toolchain_args") + + apple_toolchain(target_name) { + forward_variables_from(invoker, "*", [ "toolchain_args" ]) + + sdk_developer_dir = ios_sdk_developer_dir + deployment_target = ios_deployment_target + sdk_name = ios_sdk_name + bin_path = ios_bin_path + strip_with_lipo = true + + toolchain_args = { + forward_variables_from(invoker.toolchain_args, "*") + current_os = "ios" + } + } +} + +ios_toolchain("ios_clang_arm64") { + toolchain_args = { + target_cpu = "arm64" + } +} + +ios_toolchain("ios_clang_arm64_13_0") { + toolchain_args = { + target_cpu = "arm64" + ios_deployment_target = "13.0" + } +} + +ios_toolchain("ios_clang_arm64_14_0") { + toolchain_args = { + target_cpu = "arm64" + ios_deployment_target = "14.0" + } +} + +ios_toolchain("ios_clang_arm64_fat_arm") { + toolchain_args = { + target_cpu = "arm" + is_fat_secondary_toolchain = true + primary_fat_toolchain_name = "//build/toolchain/ios:ios_clang_arm64" + } +} + +ios_toolchain("ios_clang_arm") { + toolchain_args = { + target_cpu = "arm" + } +} + +ios_toolchain("ios_clang_x64") { + toolchain_args = { + target_cpu = "x64" + } +} + +ios_toolchain("ios_clang_x64_fat_arm64") { + toolchain_args = { + target_cpu = "arm64" + is_fat_secondary_toolchain = true + primary_fat_toolchain_name = "//build/toolchain/ios:ios_clang_x64" + } +} + +ios_toolchain("ios_clang_x64_13_0") { + toolchain_args = { + target_cpu = "x64" + ios_deployment_target = "13.0" + } +} + +ios_toolchain("ios_clang_x64_14_0") { + toolchain_args = { + target_cpu = "x64" + ios_deployment_target = "14.0" + } +} + +ios_toolchain("ios_clang_x64_14_0_fat_arm64") { + toolchain_args = { + target_cpu = "arm64" + ios_deployment_target = "14.0" + is_fat_secondary_toolchain = true + primary_fat_toolchain_name = "//build/toolchain/ios:ios_clang_x64_14_0" + } +} + +ios_toolchain("ios_clang_x64_fat_x86") { + toolchain_args = { + target_cpu = "x86" + is_fat_secondary_toolchain = true + primary_fat_toolchain_name = "//build/toolchain/ios:ios_clang_x64" + } +} + +ios_toolchain("ios_clang_x86") { + toolchain_args = { + target_cpu = "x86" + } +} diff --git a/third_party/libwebrtc/build/toolchain/ios/OWNERS b/third_party/libwebrtc/build/toolchain/ios/OWNERS new file mode 100644 index 0000000000..6f3324f07c --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/ios/OWNERS @@ -0,0 +1 @@ +file://build/apple/OWNERS diff --git a/third_party/libwebrtc/build/toolchain/ios/compile_xcassets.py b/third_party/libwebrtc/build/toolchain/ios/compile_xcassets.py new file mode 100644 index 0000000000..8b86713d14 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/ios/compile_xcassets.py @@ -0,0 +1,258 @@ +# 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. + +"""Wrapper around actool to compile assets catalog. + +The script compile_xcassets.py is a wrapper around actool to compile +assets catalog to Assets.car that turns warning into errors. It also +fixes some quirks of actool to make it work from ninja (mostly that +actool seems to require absolute path but gn generates command-line +with relative paths). + +The wrapper filter out any message that is not a section header and +not a warning or error message, and fails if filtered output is not +empty. This should to treat all warnings as error until actool has +an option to fail with non-zero error code when there are warnings. +""" + +import argparse +import os +import re +import shutil +import subprocess +import sys +import tempfile + +# Pattern matching a section header in the output of actool. +SECTION_HEADER = re.compile('^/\\* ([^ ]*) \\*/$') + +# Name of the section containing informational messages that can be ignored. +NOTICE_SECTION = 'com.apple.actool.compilation-results' + +# Map special type of asset catalog to the corresponding command-line +# parameter that need to be passed to actool. +ACTOOL_FLAG_FOR_ASSET_TYPE = { + '.appiconset': '--app-icon', + '.launchimage': '--launch-image', +} + +def FixAbsolutePathInLine(line, relative_paths): + """Fix absolute paths present in |line| to relative paths.""" + absolute_path = line.split(':')[0] + relative_path = relative_paths.get(absolute_path, absolute_path) + if absolute_path == relative_path: + return line + return relative_path + line[len(absolute_path):] + + +def FilterCompilerOutput(compiler_output, relative_paths): + """Filers actool compilation output. + + The compiler output is composed of multiple sections for each different + level of output (error, warning, notices, ...). Each section starts with + the section name on a single line, followed by all the messages from the + section. + + The function filter any lines that are not in com.apple.actool.errors or + com.apple.actool.document.warnings sections (as spurious messages comes + before any section of the output). + + See crbug.com/730054, crbug.com/739163 and crbug.com/770634 for some example + messages that pollute the output of actool and cause flaky builds. + + Args: + compiler_output: string containing the output generated by the + compiler (contains both stdout and stderr) + relative_paths: mapping from absolute to relative paths used to + convert paths in the warning and error messages (unknown paths + will be left unaltered) + + Returns: + The filtered output of the compiler. If the compilation was a + success, then the output will be empty, otherwise it will use + relative path and omit any irrelevant output. + """ + + filtered_output = [] + current_section = None + data_in_section = False + for line in compiler_output.splitlines(): + match = SECTION_HEADER.search(line) + if match is not None: + data_in_section = False + current_section = match.group(1) + continue + if current_section and current_section != NOTICE_SECTION: + if not data_in_section: + data_in_section = True + filtered_output.append('/* %s */\n' % current_section) + + fixed_line = FixAbsolutePathInLine(line, relative_paths) + filtered_output.append(fixed_line + '\n') + + return ''.join(filtered_output) + + +def CompileAssetCatalog(output, platform, product_type, min_deployment_target, + inputs, compress_pngs, partial_info_plist): + """Compile the .xcassets bundles to an asset catalog using actool. + + Args: + output: absolute path to the containing bundle + platform: the targeted platform + product_type: the bundle type + min_deployment_target: minimum deployment target + inputs: list of absolute paths to .xcassets bundles + compress_pngs: whether to enable compression of pngs + partial_info_plist: path to partial Info.plist to generate + """ + command = [ + 'xcrun', + 'actool', + '--output-format=human-readable-text', + '--notices', + '--warnings', + '--errors', + '--platform', + platform, + '--minimum-deployment-target', + min_deployment_target, + ] + + if compress_pngs: + command.extend(['--compress-pngs']) + + if product_type != '': + command.extend(['--product-type', product_type]) + + if platform == 'macosx': + command.extend(['--target-device', 'mac']) + else: + command.extend(['--target-device', 'iphone', '--target-device', 'ipad']) + + # Scan the input directories for the presence of asset catalog types that + # require special treatment, and if so, add them to the actool command-line. + for relative_path in inputs: + + if not os.path.isdir(relative_path): + continue + + for file_or_dir_name in os.listdir(relative_path): + if not os.path.isdir(os.path.join(relative_path, file_or_dir_name)): + continue + + asset_name, asset_type = os.path.splitext(file_or_dir_name) + if asset_type not in ACTOOL_FLAG_FOR_ASSET_TYPE: + continue + + command.extend([ACTOOL_FLAG_FOR_ASSET_TYPE[asset_type], asset_name]) + + # Always ask actool to generate a partial Info.plist file. If no path + # has been given by the caller, use a temporary file name. + temporary_file = None + if not partial_info_plist: + temporary_file = tempfile.NamedTemporaryFile(suffix='.plist') + partial_info_plist = temporary_file.name + + command.extend(['--output-partial-info-plist', partial_info_plist]) + + # Dictionary used to convert absolute paths back to their relative form + # in the output of actool. + relative_paths = {} + + # actool crashes if paths are relative, so convert input and output paths + # to absolute paths, and record the relative paths to fix them back when + # filtering the output. + absolute_output = os.path.abspath(output) + relative_paths[output] = absolute_output + relative_paths[os.path.dirname(output)] = os.path.dirname(absolute_output) + command.extend(['--compile', os.path.dirname(os.path.abspath(output))]) + + for relative_path in inputs: + absolute_path = os.path.abspath(relative_path) + relative_paths[absolute_path] = relative_path + command.append(absolute_path) + + try: + # Run actool and redirect stdout and stderr to the same pipe (as actool + # is confused about what should go to stderr/stdout). + process = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + stdout = process.communicate()[0].decode('utf-8') + + # If the invocation of `actool` failed, copy all the compiler output to + # the standard error stream and exit. See https://crbug.com/1205775 for + # example of compilation that failed with no error message due to filter. + if process.returncode: + for line in stdout.splitlines(): + fixed_line = FixAbsolutePathInLine(line, relative_paths) + sys.stderr.write(fixed_line + '\n') + sys.exit(1) + + # Filter the output to remove all garbage and to fix the paths. If the + # output is not empty after filtering, then report the compilation as a + # failure (as some version of `actool` report error to stdout, yet exit + # with an return code of zero). + stdout = FilterCompilerOutput(stdout, relative_paths) + if stdout: + sys.stderr.write(stdout) + sys.exit(1) + + finally: + if temporary_file: + temporary_file.close() + + +def Main(): + parser = argparse.ArgumentParser( + description='compile assets catalog for a bundle') + parser.add_argument('--platform', + '-p', + required=True, + choices=('macosx', 'iphoneos', 'iphonesimulator'), + help='target platform for the compiled assets catalog') + parser.add_argument( + '--minimum-deployment-target', + '-t', + required=True, + help='minimum deployment target for the compiled assets catalog') + parser.add_argument('--output', + '-o', + required=True, + help='path to the compiled assets catalog') + parser.add_argument('--compress-pngs', + '-c', + action='store_true', + default=False, + help='recompress PNGs while compiling assets catalog') + parser.add_argument('--product-type', + '-T', + help='type of the containing bundle') + parser.add_argument('--partial-info-plist', + '-P', + help='path to partial info plist to create') + parser.add_argument('inputs', + nargs='+', + help='path to input assets catalog sources') + args = parser.parse_args() + + if os.path.basename(args.output) != 'Assets.car': + sys.stderr.write('output should be path to compiled asset catalog, not ' + 'to the containing bundle: %s\n' % (args.output, )) + sys.exit(1) + + if os.path.exists(args.output): + if os.path.isfile(args.output): + os.unlink(args.output) + else: + shutil.rmtree(args.output) + + CompileAssetCatalog(args.output, args.platform, args.product_type, + args.minimum_deployment_target, args.inputs, + args.compress_pngs, args.partial_info_plist) + + +if __name__ == '__main__': + sys.exit(Main()) diff --git a/third_party/libwebrtc/build/toolchain/ios/swiftc.py b/third_party/libwebrtc/build/toolchain/ios/swiftc.py new file mode 100644 index 0000000000..a08dc8d129 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/ios/swiftc.py @@ -0,0 +1,174 @@ +# Copyright 2020 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 argparse +import collections +import json +import os +import subprocess +import sys +import tempfile + + +class OrderedSet(collections.OrderedDict): + def add(self, value): + self[value] = True + + +def compile_module(module, sources, settings, extras, tmpdir): + output_file_map = {} + if settings.whole_module_optimization: + output_file_map[''] = { + 'object': os.path.join(settings.object_dir, module + '.o'), + 'dependencies': os.path.join(tmpdir, module + '.d'), + } + else: + for source in sources: + name, _ = os.path.splitext(os.path.basename(source)) + output_file_map[source] = { + 'object': os.path.join(settings.object_dir, name + '.o'), + 'dependencies': os.path.join(tmpdir, name + '.d'), + } + + for key in ('module_path', 'header_path', 'depfile'): + path = getattr(settings, key) + if os.path.exists(path): + os.unlink(path) + if key == 'module_path': + for ext in '.swiftdoc', '.swiftsourceinfo': + path = os.path.splitext(getattr(settings, key))[0] + ext + if os.path.exists(path): + os.unlink(path) + directory = os.path.dirname(path) + if not os.path.exists(directory): + os.makedirs(directory) + + if not os.path.exists(settings.object_dir): + os.makedirs(settings.object_dir) + + for key in output_file_map: + path = output_file_map[key]['object'] + if os.path.exists(path): + os.unlink(path) + + output_file_map_path = os.path.join(tmpdir, module + '.json') + with open(output_file_map_path, 'w') as output_file_map_file: + output_file_map_file.write(json.dumps(output_file_map)) + output_file_map_file.flush() + + extra_args = [] + if settings.bridge_header: + extra_args.extend([ + '-import-objc-header', + os.path.abspath(settings.bridge_header), + ]) + + if settings.whole_module_optimization: + extra_args.append('-whole-module-optimization') + + if settings.target: + extra_args.extend([ + '-target', + settings.target, + ]) + + if settings.sdk: + extra_args.extend([ + '-sdk', + os.path.abspath(settings.sdk), + ]) + + if settings.swift_version: + extra_args.extend([ + '-swift-version', + settings.swift_version, + ]) + + if settings.include_dirs: + for include_dir in settings.include_dirs: + extra_args.append('-I' + include_dir) + + process = subprocess.Popen([ + 'swiftc', + '-parse-as-library', + '-module-name', + module, + '-emit-object', + '-emit-dependencies', + '-emit-module', + '-emit-module-path', + settings.module_path, + '-emit-objc-header', + '-emit-objc-header-path', + settings.header_path, + '-output-file-map', + output_file_map_path, + ] + extra_args + extras + sources, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + + stdout, stderr = process.communicate() + if process.returncode: + sys.stdout.write(stdout) + sys.stderr.write(stderr) + sys.exit(process.returncode) + + depfile_content = collections.OrderedDict() + for key in output_file_map: + for line in open(output_file_map[key]['dependencies']): + output, inputs = line.split(' : ', 2) + _, ext = os.path.splitext(output) + if ext == '.o': + key = output + else: + key = os.path.splitext(settings.module_path)[0] + ext + if key not in depfile_content: + depfile_content[key] = OrderedSet() + for path in inputs.split(): + depfile_content[key].add(path) + + with open(settings.depfile, 'w') as depfile: + for key in depfile_content: + if not settings.depfile_filter or key in settings.depfile_filter: + inputs = depfile_content[key] + depfile.write('%s : %s\n' % (key, ' '.join(inputs))) + + +def main(args): + parser = argparse.ArgumentParser(add_help=False) + parser.add_argument('-module-name', help='name of the Swift module') + parser.add_argument('-include', + '-I', + action='append', + dest='include_dirs', + help='add directory to header search path') + parser.add_argument('sources', nargs='+', help='Swift source file to compile') + parser.add_argument('-whole-module-optimization', + action='store_true', + help='enable whole module optimization') + parser.add_argument('-object-dir', + help='path to the generated object files directory') + parser.add_argument('-module-path', help='path to the generated module file') + parser.add_argument('-header-path', help='path to the generated header file') + parser.add_argument('-bridge-header', + help='path to the Objective-C bridge header') + parser.add_argument('-depfile', help='path to the generated depfile') + parser.add_argument('-swift-version', + help='version of Swift language to support') + parser.add_argument('-depfile-filter', + action='append', + help='limit depfile to those files') + parser.add_argument('-target', + action='store', + help='generate code for the given target <triple>') + parser.add_argument('-sdk', action='store', help='compile against sdk') + + parsed, extras = parser.parse_known_args(args) + with tempfile.TemporaryDirectory() as tmpdir: + compile_module(parsed.module_name, parsed.sources, parsed, extras, tmpdir) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/third_party/libwebrtc/build/toolchain/kythe.gni b/third_party/libwebrtc/build/toolchain/kythe.gni new file mode 100644 index 0000000000..7486a4f15b --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/kythe.gni @@ -0,0 +1,11 @@ +# Copyright 2020 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. + +# This file defines configuration for Kythe, an indexer and cross-referencer +# that powers codesearch. + +declare_args() { + # Enables Kythe annotations necessary to build cross references. + enable_kythe_annotations = false +} diff --git a/third_party/libwebrtc/build/toolchain/linux/BUILD.gn b/third_party/libwebrtc/build/toolchain/linux/BUILD.gn new file mode 100644 index 0000000000..697247abcd --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/linux/BUILD.gn @@ -0,0 +1,445 @@ +# Copyright 2013 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("//build/config/ozone.gni") +import("//build/config/sysroot.gni") +import("//build/toolchain/gcc_toolchain.gni") + +clang_toolchain("clang_ppc64") { + enable_linker_map = true + toolchain_args = { + target_cpu = "ppc64" + current_os = "linux" + } +} + +clang_toolchain("clang_arm") { + toolprefix = "arm-linux-gnueabihf-" + toolchain_args = { + target_cpu = "arm" + current_os = "linux" + } +} + +clang_toolchain("clang_arm64") { + toolprefix = "aarch64-linux-gnu-" + toolchain_args = { + target_cpu = "arm64" + current_os = "linux" + } +} + +gcc_toolchain("arm64") { + toolprefix = "aarch64-linux-gnu-" + + cc = "${toolprefix}gcc" + cxx = "${toolprefix}g++" + + ar = "${toolprefix}ar" + ld = cxx + readelf = "${toolprefix}readelf" + nm = "${toolprefix}nm" + + toolchain_args = { + target_cpu = "arm64" + current_os = "linux" + + # reclient does not support gcc. + use_rbe = false + is_clang = false + } +} + +gcc_toolchain("arm") { + toolprefix = "arm-linux-gnueabihf-" + + cc = "${toolprefix}gcc" + cxx = "${toolprefix}g++" + + ar = "${toolprefix}ar" + ld = cxx + readelf = "${toolprefix}readelf" + nm = "${toolprefix}nm" + + toolchain_args = { + target_cpu = "arm" + current_os = "linux" + + # reclient does not support gcc. + use_rbe = false + is_clang = false + } +} + +clang_toolchain("clang_x86") { + # Output linker map files for binary size analysis. + enable_linker_map = true + + toolchain_args = { + target_cpu = "x86" + current_os = "linux" + } +} + +clang_toolchain("clang_x86_v8_arm") { + toolchain_args = { + target_cpu = "x86" + v8_target_cpu = "arm" + current_os = "linux" + } +} + +clang_toolchain("clang_x86_v8_mipsel") { + toolchain_args = { + target_cpu = "x86" + v8_target_cpu = "mipsel" + current_os = "linux" + } +} + +clang_toolchain("clang_x86_v8_mips") { + toolchain_args = { + target_cpu = "x86" + v8_target_cpu = "mips" + current_os = "linux" + } +} + +gcc_toolchain("x86") { + cc = "gcc" + cxx = "g++" + + readelf = "readelf" + nm = "nm" + ar = "ar" + ld = cxx + + # Output linker map files for binary size analysis. + enable_linker_map = true + + toolchain_args = { + target_cpu = "x86" + current_os = "linux" + + # reclient does not support gcc. + use_rbe = false + is_clang = false + } +} + +clang_toolchain("clang_x64") { + # Output linker map files for binary size analysis. + enable_linker_map = true + + toolchain_args = { + target_cpu = "x64" + current_os = "linux" + } +} + +clang_toolchain("clang_x64_v8_arm64") { + toolchain_args = { + target_cpu = "x64" + v8_target_cpu = "arm64" + current_os = "linux" + } +} + +clang_toolchain("clang_x64_v8_mips64el") { + toolchain_args = { + target_cpu = "x64" + v8_target_cpu = "mips64el" + current_os = "linux" + } +} + +clang_toolchain("clang_x64_v8_mips64") { + toolchain_args = { + target_cpu = "x64" + v8_target_cpu = "mips64" + current_os = "linux" + } +} + +clang_toolchain("clang_x64_v8_riscv64") { + toolchain_args = { + target_cpu = "x64" + v8_target_cpu = "riscv64" + current_os = "linux" + } +} + +clang_toolchain("clang_x64_v8_loong64") { + toolchain_args = { + target_cpu = "x64" + v8_target_cpu = "loong64" + current_os = "linux" + } +} + +# In a LaCrOS build, this toolchain is intended to be used as an alternate +# toolchain to build Ash-Chrome in a subdirectory. +clang_toolchain("ash_clang_x64") { + toolchain_args = { + # This turns the toolchain into the "Linux ChromeOS" build + current_os = "chromeos" + target_os = "chromeos" + target_cpu = target_cpu + + # This turns off all of the LaCrOS-specific flags. + also_build_ash_chrome = false + chromeos_is_browser_only = false + use_clang_coverage = false + } +} + +# In an ash build, this toolchain is intended to be used as an alternate +# toolchain to build lacros-Chrome in a subdirectory. +clang_toolchain("lacros_clang_x64") { + toolchain_args = { + # This turns the toolchain into the "Lacros" build + current_os = "chromeos" + target_os = "chromeos" + target_cpu = target_cpu + + # This turns on the LaCrOS-specific flag. + also_build_lacros_chrome = false + chromeos_is_browser_only = true + use_clang_coverage = false + } +} + +gcc_toolchain("x64") { + cc = "gcc" + cxx = "g++" + + readelf = "readelf" + nm = "nm" + ar = "ar" + ld = cxx + + # Output linker map files for binary size analysis. + enable_linker_map = true + + toolchain_args = { + target_cpu = "x64" + current_os = "linux" + + # reclient does not support gcc. + use_rbe = false + is_clang = false + } +} + +clang_toolchain("clang_mipsel") { + toolchain_args = { + target_cpu = "mipsel" + current_os = "linux" + } +} + +clang_toolchain("clang_mips64el") { + toolchain_args = { + target_cpu = "mips64el" + current_os = "linux" + } +} + +clang_toolchain("clang_mips64") { + toolchain_args = { + target_cpu = "mips64" + current_os = "linux" + } +} + +gcc_toolchain("mipsel") { + toolprefix = "mipsel-linux-gnu-" + + cc = "${toolprefix}gcc" + cxx = " ${toolprefix}g++" + ar = "${toolprefix}ar" + ld = cxx + readelf = "${toolprefix}readelf" + nm = "${toolprefix}nm" + + toolchain_args = { + cc_wrapper = "" + target_cpu = "mipsel" + current_os = "linux" + + # reclient does not support gcc. + use_rbe = false + is_clang = false + use_goma = false + } +} + +gcc_toolchain("mips64el") { + toolprefix = "mips64el-linux-gnuabi64-" + + cc = "${toolprefix}gcc" + cxx = "${toolprefix}g++" + ar = "${toolprefix}ar" + ld = cxx + readelf = "${toolprefix}readelf" + nm = "${toolprefix}nm" + + toolchain_args = { + cc_wrapper = "" + target_cpu = "mips64el" + current_os = "linux" + + # reclient does not support gcc. + use_rbe = false + is_clang = false + use_goma = false + } +} + +gcc_toolchain("riscv64") { + toolprefix = "riscv64-linux-gnu" + + cc = "${toolprefix}-gcc" + cxx = "${toolprefix}-g++" + + readelf = "${toolprefix}-readelf" + nm = "${toolprefix}-nm" + ar = "${toolprefix}-ar" + ld = cxx + + toolchain_args = { + target_cpu = "riscv64" + current_os = "linux" + is_clang = false + } +} + +clang_toolchain("clang_s390x") { + toolchain_args = { + target_cpu = "s390x" + current_os = "linux" + is_clang = true + } +} + +gcc_toolchain("s390x") { + cc = "gcc" + cxx = "g++" + + readelf = "readelf" + nm = "nm" + ar = "ar" + ld = cxx + + toolchain_args = { + target_cpu = "s390x" + current_os = "linux" + + # reclient does not support gcc. + use_rbe = false + is_clang = false + } +} + +#gcc_toolchain("mips64") { +# cc = "gcc" +# cxx = "g++" +# +# readelf = "readelf" +# nm = "nm" +# ar = "ar" +# ld = cxx +# +# toolchain_args = { +# target_cpu = "mips64" +# current_os = "linux" +# is_clang = false +# } +#} + +gcc_toolchain("ppc64") { + cc = "gcc" + cxx = "g++" + + readelf = "readelf" + nm = "nm" + ar = "ar" + ld = cxx + + toolchain_args = { + target_cpu = "ppc64" + current_os = "linux" + + # reclient does not support gcc. + use_rbe = false + is_clang = false + } +} + +gcc_toolchain("mips") { + toolprefix = "mips-linux-gnu-" + + cc = "${toolprefix}gcc" + cxx = "${toolprefix}g++" + + readelf = "${toolprefix}readelf" + nm = "${toolprefix}nm" + ar = "${toolprefix}ar" + ld = cxx + + toolchain_args = { + target_cpu = "mips" + current_os = "linux" + + # reclient does not support gcc. + use_rbe = false + is_clang = false + } +} + +gcc_toolchain("mips64") { + toolprefix = "mips64-linux-gnuabi64-" + + cc = "${toolprefix}gcc" + cxx = "${toolprefix}g++" + + readelf = "${toolprefix}readelf" + nm = "${toolprefix}nm" + ar = "${toolprefix}ar" + ld = cxx + + toolchain_args = { + target_cpu = "mips64" + current_os = "linux" + + # reclient does not support gcc. + use_rbe = false + is_clang = false + } +} + +clang_toolchain("clang_loong64") { + toolchain_args = { + target_cpu = "loong64" + current_os = "linux" + } +} + +gcc_toolchain("loong64") { + toolprefix = "loongarch64-linux-gnu" + + cc = "${toolprefix}-gcc" + cxx = "${toolprefix}-g++" + + readelf = "${toolprefix}-readelf" + nm = "${toolprefix}-nm" + ar = "${toolprefix}-ar" + ld = cxx + + toolchain_args = { + target_cpu = "loong64" + current_os = "linux" + is_clang = false + } +} diff --git a/third_party/libwebrtc/build/toolchain/linux/unbundle/BUILD.gn b/third_party/libwebrtc/build/toolchain/linux/unbundle/BUILD.gn new file mode 100644 index 0000000000..0e20f271f0 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/linux/unbundle/BUILD.gn @@ -0,0 +1,41 @@ +# Copyright 2017 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("//build/toolchain/gcc_toolchain.gni") + +gcc_toolchain("default") { + cc = getenv("CC") + cxx = getenv("CXX") + ar = getenv("AR") + nm = getenv("NM") + ld = cxx + + extra_cflags = getenv("CFLAGS") + extra_cppflags = getenv("CPPFLAGS") + extra_cxxflags = getenv("CXXFLAGS") + extra_ldflags = getenv("LDFLAGS") + + toolchain_args = { + target_cpu = target_cpu + current_os = current_os + } +} + +gcc_toolchain("host") { + cc = getenv("BUILD_CC") + cxx = getenv("BUILD_CXX") + ar = getenv("BUILD_AR") + nm = getenv("BUILD_NM") + ld = cxx + + extra_cflags = getenv("BUILD_CFLAGS") + extra_cppflags = getenv("BUILD_CPPFLAGS") + extra_cxxflags = getenv("BUILD_CXXFLAGS") + extra_ldflags = getenv("BUILD_LDFLAGS") + + toolchain_args = { + target_cpu = target_cpu + current_os = current_os + } +} diff --git a/third_party/libwebrtc/build/toolchain/linux/unbundle/README.md b/third_party/libwebrtc/build/toolchain/linux/unbundle/README.md new file mode 100644 index 0000000000..17b93c9fde --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/linux/unbundle/README.md @@ -0,0 +1,41 @@ +# Overview + +This directory contains files that make it possible for Linux +distributions to build Chromium using system toolchain. + +For more info on established way such builds are configured, +please read the following: + + - https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html + +Why do distros want CFLAGS, LDFLAGS, etc? Please read the following +for some examples. This is not an exhaustive list. + + - https://wiki.debian.org/Hardening + - https://wiki.ubuntu.com/DistCompilerFlags + - https://fedoraproject.org/wiki/Changes/Harden_All_Packages + - https://fedoraproject.org/wiki/Changes/Modernise_GCC_Flags + - https://fedoraproject.org/wiki/Packaging:Guidelines#Compiler_flags + - https://blog.flameeyes.eu/2010/09/are-we-done-with-ldflags/ + - https://blog.flameeyes.eu/2008/08/flags-and-flags/ + +# Usage + +Add the following to GN args: + +``` +custom_toolchain="//build/toolchain/linux/unbundle:default" +host_toolchain="//build/toolchain/linux/unbundle:default" +``` + +See [more docs on GN](https://gn.googlesource.com/gn/+/master/docs/quick_start.md). + +To cross-compile (not fully tested), add the following: + +``` +host_toolchain="//build/toolchain/linux/unbundle:host" +v8_snapshot_toolchain="//build/toolchain/linux/unbundle:host" +``` + +Note: when cross-compiling for a 32-bit target, a matching 32-bit toolchain +may be needed. diff --git a/third_party/libwebrtc/build/toolchain/mac/BUILD.gn b/third_party/libwebrtc/build/toolchain/mac/BUILD.gn new file mode 100644 index 0000000000..4809b4ba0a --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/mac/BUILD.gn @@ -0,0 +1,102 @@ +# Copyright 2021 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("//build/config/mac/mac_sdk.gni") +import("//build/toolchain/apple/toolchain.gni") + +# Specialisation of the apple_toolchain template to declare the toolchain +# and its tools to build target for macOS platform. +template("mac_toolchain") { + assert(defined(invoker.toolchain_args), + "Toolchains must declare toolchain_args") + + apple_toolchain(target_name) { + forward_variables_from(invoker, "*", [ "toolchain_args" ]) + + bin_path = mac_bin_path + + toolchain_args = { + forward_variables_from(invoker.toolchain_args, "*") + current_os = "mac" + + if (target_os == "ios") { + # Use LLD for the host part of a chrome/ios build. + use_lld = true + + # TODO(crbug.com/753445): the use_sanitizer_coverage arg is currently + # not supported by the Chromium mac_clang_x64 toolchain on iOS + # distribution. + use_sanitizer_coverage = false + + # Do not use Xcode version of clang when building macOS tools for the + # host even if this is the version used to build for the iOS target. + use_xcode_clang = false + } + } + } +} + +mac_toolchain("clang_arm") { + toolchain_args = { + target_cpu = "arm" + } +} + +mac_toolchain("clang_arm64") { + toolchain_args = { + target_cpu = "arm64" + } +} + +mac_toolchain("clang_x64") { + toolchain_args = { + target_cpu = "x64" + } +} + +mac_toolchain("clang_x86") { + toolchain_args = { + target_cpu = "x86" + } +} + +mac_toolchain("clang_x86_v8_arm") { + toolchain_args = { + target_cpu = "x86" + + if (defined(v8_target_cpu)) { + v8_target_cpu = "arm" + } + } +} + +mac_toolchain("clang_x86_v8_mipsel") { + toolchain_args = { + target_cpu = "x86" + + if (defined(v8_target_cpu)) { + v8_target_cpu = "mipsel" + } + } +} + +mac_toolchain("clang_x64_v8_arm64") { + toolchain_args = { + target_cpu = "x64" + + if (defined(v8_target_cpu)) { + v8_target_cpu = "arm64" + } + } +} + +mac_toolchain("clang_x64_v8_mips64el") { + toolchain_args = { + target_cpu = "x64" + + if (defined(v8_target_cpu)) { + v8_target_cpu = "mips64el" + } + } +} diff --git a/third_party/libwebrtc/build/toolchain/mac/OWNERS b/third_party/libwebrtc/build/toolchain/mac/OWNERS new file mode 100644 index 0000000000..6f3324f07c --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/mac/OWNERS @@ -0,0 +1 @@ +file://build/apple/OWNERS diff --git a/third_party/libwebrtc/build/toolchain/nacl/BUILD.gn b/third_party/libwebrtc/build/toolchain/nacl/BUILD.gn new file mode 100644 index 0000000000..259e6347ae --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/nacl/BUILD.gn @@ -0,0 +1,305 @@ +# Copyright (c) 2014 The Native Client 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("//build/config/nacl/config.gni") +import("//build/config/sysroot.gni") +import("//build/toolchain/nacl_toolchain.gni") + +# Add the toolchain revision as a preprocessor define so that sources are +# rebuilt when a toolchain is updated. +# Idea we could use the toolchain deps feature, but currently that feature is +# bugged and does not trigger a rebuild. +# https://code.google.com/p/chromium/issues/detail?id=431880 +# Calls to get the toolchain revision are relatively slow, so do them all in a +# single batch to amortize python startup, etc. +revisions = exec_script("//native_client/build/get_toolchain_revision.py", + [ + "nacl_x86_glibc", + "nacl_arm_glibc", + "pnacl_newlib", + "saigo_newlib", + ], + "trim list lines") +nacl_x86_glibc_rev = revisions[0] +nacl_arm_glibc_rev = revisions[1] + +pnacl_newlib_rev = revisions[2] +saigo_newlib_rev = revisions[3] + +use_saigo = true + +if (host_os == "win") { + toolsuffix = ".exe" +} else { + toolsuffix = "" +} + +# The PNaCl toolchain tools are all wrapper scripts rather than binary +# executables. On POSIX systems, nobody cares what kind of executable +# file you are. But on Windows, scripts (.bat files) cannot be run +# directly and need the Windows shell (cmd.exe) specified explicily. +if (host_os == "win") { + # NOTE! The //build/toolchain/gcc_*_wrapper.py scripts recognize + # this exact prefix string, so they must be updated if this string + # is changed in any way. + scriptprefix = "cmd /c call " + scriptsuffix = ".bat" +} else { + scriptprefix = "" + scriptsuffix = "" +} + +# When the compilers are run via goma, rbe or ccache rather than directly by +# GN/Ninja, the rbe/goma/ccache wrapper handles .bat files but gets confused +# by being given the scriptprefix. +if (host_os == "win" && !use_goma && !use_rbe && cc_wrapper == "") { + compiler_scriptprefix = scriptprefix +} else { + compiler_scriptprefix = "" +} + +template("pnacl_toolchain") { + assert(defined(invoker.executable_extension), + "Must define executable_extension") + + nacl_toolchain(target_name) { + toolchain_package = "pnacl_newlib" + toolchain_revision = pnacl_newlib_rev + toolprefix = + rebase_path("${nacl_toolchain_dir}/${toolchain_package}/bin/pnacl-", + root_build_dir) + + if (host_os == "win") { + # Flip the slashes so that copy/paste of the commands works. + # This is also done throughout build\toolchain\win\BUILD.gn + toolprefix = string_replace(toolprefix, "/", "\\") + } + + cc = compiler_scriptprefix + toolprefix + "clang" + scriptsuffix + cxx = compiler_scriptprefix + toolprefix + "clang++" + scriptsuffix + ar = toolprefix + "ar" + scriptsuffix + readelf = scriptprefix + toolprefix + "readelf" + scriptsuffix + nm = scriptprefix + toolprefix + "nm" + scriptsuffix + if (defined(invoker.strip)) { + strip = scriptprefix + toolprefix + invoker.strip + scriptsuffix + } + forward_variables_from(invoker, + [ + "executable_extension", + "is_clang_analysis_supported", + "extra_cppflags", + ]) + + # Note this is not the usual "ld = cxx" because "ld" uses are + # never run via goma, so this needs scriptprefix. + ld = scriptprefix + toolprefix + "clang++" + scriptsuffix + + toolchain_args = { + is_clang = true + target_cpu = "pnacl" + use_lld = false + } + } +} + +pnacl_toolchain("newlib_pnacl") { + executable_extension = ".pexe" + + # The pnacl-finalize tool turns a .pexe.debug file into a .pexe file. + # It's very similar in purpose to the traditional "strip" utility: it + # turns what comes out of the linker into what you actually want to + # distribute and run. PNaCl doesn't have a "strip"-like utility that + # you ever actually want to use other than pnacl-finalize, so just + # make pnacl-finalize the strip tool rather than adding an additional + # step like "postlink" to run pnacl-finalize. + strip = "finalize" +} + +pnacl_toolchain("newlib_pnacl_nonsfi") { + executable_extension = "" + strip = "strip" + + # This macro is embedded on nonsfi toolchains but reclient can't figure + # that out itself, so we make it explicit. + extra_cppflags = "-D__native_client_nonsfi__" +} + +template("nacl_glibc_toolchain") { + toolchain_cpu = target_name + assert(defined(invoker.toolchain_tuple), "Must define toolchain_tuple") + assert(defined(invoker.toolchain_package), "Must define toolchain_package") + assert(defined(invoker.toolchain_revision), "Must define toolchain_revision") + forward_variables_from(invoker, + [ + "toolchain_package", + "toolchain_revision", + ]) + + toolprefix = rebase_path("${nacl_toolchain_dir}/${toolchain_package}/bin/" + + invoker.toolchain_tuple + "-", + root_build_dir) + + if (host_os == "win") { + # Flip the slashes so that copy/paste of the commands works. + # This is also done throughout build\toolchain\win\BUILD.gn + toolprefix = string_replace(toolprefix, "/", "\\") + } + + nacl_toolchain("glibc_" + toolchain_cpu) { + cc = toolprefix + "gcc" + toolsuffix + cxx = toolprefix + "g++" + toolsuffix + ar = toolprefix + "ar" + toolsuffix + ld = cxx + readelf = toolprefix + "readelf" + toolsuffix + nm = toolprefix + "nm" + toolsuffix + strip = toolprefix + "strip" + toolsuffix + + toolchain_args = { + target_cpu = toolchain_cpu + + # reclient does not support gcc. + use_rbe = false + is_clang = false + is_nacl_glibc = true + use_lld = false + } + } +} + +nacl_glibc_toolchain("x86") { + toolchain_package = "nacl_x86_glibc" + toolchain_revision = nacl_x86_glibc_rev + + # Rely on the :compiler_cpu_abi config adding the -m32 flag here rather + # than using the i686-nacl binary directly. This is a because i686-nacl-gcc + # is a shell script wrapper around x86_64-nacl-gcc and goma has trouble with + # compiler executables that are shell scripts (so the i686 'compiler' is not + # currently in goma). + toolchain_tuple = "x86_64-nacl" +} + +nacl_glibc_toolchain("x64") { + toolchain_package = "nacl_x86_glibc" + toolchain_revision = nacl_x86_glibc_rev + toolchain_tuple = "x86_64-nacl" +} + +nacl_glibc_toolchain("arm") { + toolchain_package = "nacl_arm_glibc" + toolchain_revision = nacl_arm_glibc_rev + toolchain_tuple = "arm-nacl" +} + +template("nacl_clang_toolchain") { + toolchain_cpu = target_name + assert(defined(invoker.toolchain_tuple), "Must define toolchain_tuple") + + toolchain_package = "pnacl_newlib" + toolchain_revision = pnacl_newlib_rev + toolprefix = rebase_path("${nacl_toolchain_dir}/${toolchain_package}/bin/" + + invoker.toolchain_tuple + "-", + root_build_dir) + + if (host_os == "win") { + # Flip the slashes so that copy/paste of the commands works. + # This is also done throughout build\toolchain\win\BUILD.gn + toolprefix = string_replace(toolprefix, "/", "\\") + } + + nacl_toolchain("clang_newlib_" + toolchain_cpu) { + cc = toolprefix + "clang" + toolsuffix + cxx = toolprefix + "clang++" + toolsuffix + ar = toolprefix + "ar" + toolsuffix + ld = cxx + readelf = toolprefix + "readelf" + toolsuffix + nm = toolprefix + "nm" + toolsuffix + strip = toolprefix + "strip" + toolsuffix + + toolchain_args = { + target_cpu = toolchain_cpu + is_clang = true + use_lld = false + } + } +} + +template("nacl_irt_toolchain") { + toolchain_cpu = target_name + assert(defined(invoker.toolchain_tuple), "Must define toolchain_tuple") + + toolchain_package = "pnacl_newlib" + toolchain_revision = pnacl_newlib_rev + if (use_saigo) { + toolchain_package = "saigo_newlib" + toolchain_revision = saigo_newlib_rev + } + toolprefix = rebase_path("${nacl_toolchain_dir}/${toolchain_package}/bin/" + + invoker.toolchain_tuple + "-", + root_build_dir) + + if (host_os == "win") { + # Flip the slashes so that copy/paste of the commands works. + # This is also done throughout build\toolchain\win\BUILD.gn + toolprefix = string_replace(toolprefix, "/", "\\") + } + + link_irt = rebase_path("//native_client/build/link_irt.py", root_build_dir) + + tls_edit_label = + "//native_client/src/tools/tls_edit:tls_edit($host_toolchain)" + host_toolchain_out_dir = + rebase_path(get_label_info(tls_edit_label, "root_out_dir"), + root_build_dir) + tls_edit = "${host_toolchain_out_dir}/tls_edit" + + nacl_toolchain("irt_" + toolchain_cpu) { + cc = toolprefix + "clang" + toolsuffix + cxx = toolprefix + "clang++" + toolsuffix + ar = toolprefix + "ar" + toolsuffix + readelf = toolprefix + "readelf" + toolsuffix + nm = toolprefix + "nm" + toolsuffix + strip = toolprefix + "strip" + toolsuffix + + # Some IRT implementations (notably, Chromium's) contain C++ code, + # so we need to link w/ the C++ linker. + ld = "${python_path} ${link_irt} --tls-edit=${tls_edit} --link-cmd=${cxx} --readelf-cmd=${readelf}" + + toolchain_args = { + target_cpu = toolchain_cpu + is_clang = true + use_lld = false + is_nacl_saigo = use_saigo + } + + # TODO(ncbray): depend on link script + deps = [ tls_edit_label ] + } +} + +template("nacl_clang_toolchains") { + assert(defined(invoker.toolchain_tuple), "Must define toolchain_tuple") + nacl_clang_toolchain(target_name) { + toolchain_tuple = invoker.toolchain_tuple + } + nacl_irt_toolchain(target_name) { + toolchain_tuple = invoker.toolchain_tuple + } +} + +nacl_clang_toolchains("x86") { + # Rely on :compiler_cpu_abi adding -m32. See nacl_x86_glibc above. + toolchain_tuple = "x86_64-nacl" +} + +nacl_clang_toolchains("x64") { + toolchain_tuple = "x86_64-nacl" +} + +nacl_clang_toolchains("arm") { + toolchain_tuple = "arm-nacl" +} + +nacl_clang_toolchains("mipsel") { + toolchain_tuple = "mipsel-nacl" +} diff --git a/third_party/libwebrtc/build/toolchain/nacl_toolchain.gni b/third_party/libwebrtc/build/toolchain/nacl_toolchain.gni new file mode 100644 index 0000000000..25fbd6405b --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/nacl_toolchain.gni @@ -0,0 +1,73 @@ +# Copyright (c) 2014 The Native Client 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("//build/config/nacl/config.gni") +import("//build/toolchain/gcc_toolchain.gni") + +# This template defines a NaCl toolchain. +# +# It requires the following variables specifying the executables to run: +# - cc +# - cxx +# - ar +# - ld + +template("nacl_toolchain") { + assert(defined(invoker.cc), "nacl_toolchain() must specify a \"cc\" value") + assert(defined(invoker.cxx), "nacl_toolchain() must specify a \"cxx\" value") + assert(defined(invoker.ar), "nacl_toolchain() must specify a \"ar\" value") + assert(defined(invoker.ld), "nacl_toolchain() must specify a \"ld\" value") + gcc_toolchain(target_name) { + if (defined(invoker.executable_extension)) { + executable_extension = invoker.executable_extension + } else { + executable_extension = ".nexe" + } + rebuild_define = "NACL_TC_REV=" + invoker.toolchain_revision + + forward_variables_from(invoker, + [ + "ar", + "cc", + "cxx", + "deps", + "ld", + "link_outputs", + "nm", + "readelf", + "strip", + "extra_cppflags", + ]) + + toolchain_args = { + # Use all values set on the invoker's toolchain_args. + forward_variables_from(invoker.toolchain_args, "*") + + current_os = "nacl" + + # We do not support component builds with the NaCl toolchains. + is_component_build = false + + # We do not support tcmalloc in the NaCl toolchains. + use_allocator = "none" + use_allocator_shim = false + + # We do not support clang profiling in the NaCl toolchains. + use_clang_profiling = false + use_clang_coverage = false + coverage_instrumentation_input_file = "" + + if (use_remoteexec) { + if (is_win) { + rbe_cc_cfg_file = "${rbe_cfg_dir}/nacl/rewrapper_windows.cfg" + } else if (is_mac) { + rbe_cc_cfg_file = "${rbe_cfg_dir}/nacl/rewrapper_mac.cfg" + } else { + # TODO(ukai): non linux? + rbe_cc_cfg_file = "${rbe_cfg_dir}/nacl/rewrapper_linux.cfg" + } + } + } + } +} diff --git a/third_party/libwebrtc/build/toolchain/rbe.gni b/third_party/libwebrtc/build/toolchain/rbe.gni new file mode 100644 index 0000000000..baf7462bb5 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/rbe.gni @@ -0,0 +1,45 @@ +# Defines the configuration of Remote Build Execution (RBE). + +# The directory where the re-client tooling binaries are. +rbe_bin_dir = rebase_path("//buildtools/reclient", root_build_dir) + +# Execution root - this should be the root of the source tree. +# This is defined here instead of in the config file because +# this will vary depending on where the user has placed the +# chromium source on their system. +rbe_exec_root = rebase_path("//") + +declare_args() { + # Set to true to enable remote compilation using reclient. + use_remoteexec = false + + # Set to true to enable remote compilation using reclient (deprecated). + use_rbe = false + + # The directory where the re-client configuration files are. + rbe_cfg_dir = rebase_path("//buildtools/reclient_cfgs", root_build_dir) + + # Set to the path of the RBE reclient configuration file. + rbe_cc_cfg_file = "" + + # Set to the path of the RBE recleint wrapper for ChromeOS. + rbe_cros_cc_wrapper = "" +} + +# Set use_remoteexec if use_rbe is set. Remove this once use_rbe is no longer +# used to configure builds. +if (!use_remoteexec && use_rbe) { + use_remoteexec = true +} + +# Configuration file selection based on operating system. +if (is_linux || is_android || is_chromeos) { + rbe_cc_cfg_file = "${rbe_cfg_dir}/chromium-browser-clang/rewrapper_linux.cfg" +} +if (is_win) { + rbe_cc_cfg_file = + "${rbe_cfg_dir}/chromium-browser-clang/rewrapper_windows.cfg" +} +if (is_mac || is_ios) { + rbe_cc_cfg_file = "${rbe_cfg_dir}/chromium-browser-clang/rewrapper_mac.cfg" +} diff --git a/third_party/libwebrtc/build/toolchain/toolchain.gni b/third_party/libwebrtc/build/toolchain/toolchain.gni new file mode 100644 index 0000000000..6d8f162d82 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/toolchain.gni @@ -0,0 +1,107 @@ +# 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. + +# Toolchain-related configuration that may be needed outside the context of the +# toolchain() rules themselves. + +import("//build/config/chrome_build.gni") +import("//build/config/chromecast_build.gni") +import("//build_overrides/build.gni") + +declare_args() { + # If this is set to true, we use the revision in the llvm repo to determine + # the CLANG_REVISION to use, instead of the version hard-coded into + # //tools/clang/scripts/update.py. This should only be used in + # conjunction with setting the llvm_force_head_revision DEPS variable when + # `gclient runhooks` is run as well. + llvm_force_head_revision = false + + # Compile with Xcode version of clang instead of hermetic version shipped + # with the build. Used to be used iOS for official builds, but is now off by + # default for all configurations. + use_xcode_clang = false + + # Used for binary size analysis. + generate_linker_map = is_android && is_official_build +} + +if (generate_linker_map) { + assert(is_official_build || is_chromecast, + "Linker map files should only be generated when is_official_build = " + + "true or is_chromecast = true") + assert(current_os == "android" || current_os == "linux" || + target_os == "android" || target_os == "linux" || + target_os == "chromeos", + "Linker map files should only be generated for Android, Linux, " + + "or ChromeOS.") +} + +declare_args() { + clang_version = "14.0.0" +} + +# Check target_os here instead of is_ios as this file is loaded for secondary +# toolchain (host toolchain in particular) but the argument is the same for +# all toolchains. +assert(!use_xcode_clang || target_os == "ios", + "Using Xcode's clang is only supported in iOS builds") + +# Extension for shared library files (including leading dot). +if (is_apple) { + shlib_extension = ".dylib" +} else if (is_android && is_component_build) { + # By appending .cr, we prevent name collisions with libraries already + # loaded by the Android zygote. + shlib_extension = ".cr.so" +} else if (is_posix || is_fuchsia) { + shlib_extension = ".so" +} else if (is_win) { + shlib_extension = ".dll" +} else { + assert(false, "Platform not supported") +} + +# Prefix for shared library files. +if (is_posix || is_fuchsia) { + shlib_prefix = "lib" +} else { + shlib_prefix = "" +} + +# Directory for shared library files. +if (is_fuchsia) { + shlib_subdir = "/lib" +} else { + shlib_subdir = "" +} + +# While other "tool"s in a toolchain are specific to the target of that +# toolchain, the "stamp" and "copy" tools are really generic to the host; +# but each toolchain must define them separately. GN doesn't allow a +# template instantiation inside a toolchain definition, so some boilerplate +# has to be repeated in each toolchain to define these two tools. These +# four variables reduce the duplication in that boilerplate. +stamp_description = "STAMP {{output}}" +copy_description = "COPY {{source}} {{output}}" +if (host_os == "win") { + _tool_wrapper_path = + rebase_path("//build/toolchain/win/tool_wrapper.py", root_build_dir) + + stamp_command = "cmd /c type nul > \"{{output}}\"" + copy_command = + "$python_path $_tool_wrapper_path recursive-mirror {{source}} {{output}}" +} else { + stamp_command = "touch {{output}}" + copy_command = "ln -f {{source}} {{output}} 2>/dev/null || (rm -rf {{output}} && cp -af {{source}} {{output}})" +} + +# This variable is true if the current toolchain is one of the target +# toolchains, i.e. a toolchain which is being used to build the main Chrome +# binary. This generally means "not the host toolchain", but in the case where +# we're targeting the host it's true then as well. We do require current_os to +# match target_os so that for example we avoid considering Android as a target +# toolchain when targeting CrOS. +is_a_target_toolchain = + (current_toolchain != host_toolchain || + default_toolchain == host_toolchain) && current_os == target_os diff --git a/third_party/libwebrtc/build/toolchain/win/BUILD.gn b/third_party/libwebrtc/build/toolchain/win/BUILD.gn new file mode 100644 index 0000000000..571235286e --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/win/BUILD.gn @@ -0,0 +1,587 @@ +# Copyright (c) 2013 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("//build/config/clang/clang.gni") +import("//build/config/compiler/compiler.gni") +import("//build/config/rust.gni") +import("//build/config/sanitizers/sanitizers.gni") +import("//build/config/win/visual_studio_version.gni") +import("//build/toolchain/cc_wrapper.gni") +import("//build/toolchain/goma.gni") +import("//build/toolchain/rbe.gni") +import("//build/toolchain/toolchain.gni") + +# Should only be running on Windows. +assert(is_win) + +# Setup the Visual Studio state. +# +# Its arguments are the VS path and the compiler wrapper tool. It will write +# "environment.x86" and "environment.x64" to the build directory and return a +# list to us. + +# This tool will is used as a wrapper for various commands below. +tool_wrapper_path = rebase_path("tool_wrapper.py", root_build_dir) + +if (use_rbe) { + goma_prefix = "" + rbe_prefix = "${rbe_bin_dir}/rewrapper -cfg=${rbe_cc_cfg_file} -exec_root=${rbe_exec_root} " + clang_prefix = rbe_prefix +} else if (use_goma) { + if (host_os == "win") { + goma_prefix = "$goma_dir/gomacc.exe " + } else { + goma_prefix = "$goma_dir/gomacc " + } + clang_prefix = goma_prefix +} else { + goma_prefix = "" + if (cc_wrapper != "") { + clang_prefix = cc_wrapper + " " + } else { + clang_prefix = "" + } +} + +# Copy the VS runtime DLL for the default toolchain to the root build directory +# so things will run. +if (current_toolchain == default_toolchain) { + if (is_debug) { + configuration_name = "Debug" + } else { + configuration_name = "Release" + } + exec_script("../../vs_toolchain.py", + [ + "copy_dlls", + rebase_path(root_build_dir), + configuration_name, + target_cpu, + ]) +} + +if (host_os == "win") { + clang_cl = "clang-cl.exe" +} else { + clang_cl = "clang-cl" +} + +# Parameters: +# environment: File name of environment file. +# +# You would also define a toolchain_args variable with at least these set: +# target_cpu: target_cpu to pass as a build arg +# current_os: current_os to pass as a build arg +template("msvc_toolchain") { + toolchain(target_name) { + # When invoking this toolchain not as the default one, these args will be + # passed to the build. They are ignored when this is the default toolchain. + assert(defined(invoker.toolchain_args)) + toolchain_args = { + if (defined(invoker.toolchain_args)) { + forward_variables_from(invoker.toolchain_args, "*") + } + + # This value needs to be passed through unchanged. + host_toolchain = host_toolchain + } + + # Make these apply to all tools below. + lib_switch = "" + lib_dir_switch = "/LIBPATH:" + + # Object files go in this directory. + object_subdir = "{{target_out_dir}}/{{label_name}}" + + env = invoker.environment + + cl = invoker.cl + + if (use_lld) { + if (host_os == "win") { + lld_link = "lld-link.exe" + } else { + lld_link = "lld-link" + } + prefix = rebase_path("$clang_base_path/bin", root_build_dir) + + # lld-link includes a replacement for lib.exe that can produce thin + # archives and understands bitcode (for lto builds). + link = "$prefix/$lld_link" + if (host_os == "win") { + # Flip the slashes so that copy/paste of the commands works. + link = string_replace(link, "/", "\\") + } + lib = "$link /lib" + if (host_os != "win") { + # See comment adding --rsp-quoting to $cl above for more information. + link = "$link --rsp-quoting=posix" + } + } else { + lib = "lib.exe" + link = "link.exe" + } + + # If possible, pass system includes as flags to the compiler. When that's + # not possible, load a full environment file (containing %INCLUDE% and + # %PATH%) -- e.g. 32-bit MSVS builds require %PATH% to be set and just + # passing in a list of include directories isn't enough. + if (defined(invoker.sys_include_flags)) { + env_wrapper = "" + sys_include_flags = + "${invoker.sys_include_flags} " # Note trailing space. + } else { + # clang-cl doesn't need this env hoop, so omit it there. + assert((defined(toolchain_args.is_clang) && !toolchain_args.is_clang) || + !is_clang) + env_wrapper = "ninja -t msvc -e $env -- " # Note trailing space. + sys_include_flags = "" + } + + # ninja does not have -t msvc other than windows, and lld doesn't depend on + # mt.exe in PATH on non-Windows, so it's not needed there anyways. + if (host_os != "win") { + linker_wrapper = "" + sys_lib_flags = "${invoker.sys_lib_flags} " # Note trailing space. + } else if (defined(invoker.sys_lib_flags)) { + # Invoke ninja as wrapper instead of tool wrapper, because python + # invocation requires higher cpu usage compared to ninja invocation, and + # the python wrapper is only needed to work around link.exe problems. + # TODO(thakis): Remove wrapper once lld-link can merge manifests without + # relying on mt.exe being in %PATH% on Windows, https://crbug.com/872740 + linker_wrapper = "ninja -t msvc -e $env -- " # Note trailing space. + sys_lib_flags = "${invoker.sys_lib_flags} " # Note trailing space. + } else { + # Note trailing space: + linker_wrapper = + "$python_path $tool_wrapper_path link-wrapper $env False " + sys_lib_flags = "" + } + + if (defined(toolchain_args.use_clang_coverage)) { + toolchain_use_clang_coverage = toolchain_args.use_clang_coverage + } else { + toolchain_use_clang_coverage = use_clang_coverage + } + + if (toolchain_use_clang_coverage) { + assert(toolchain_args.is_clang, + "use_clang_coverage should only be used with Clang") + if (defined(toolchain_args.coverage_instrumentation_input_file)) { + toolchain_coverage_instrumentation_input_file = + toolchain_args.coverage_instrumentation_input_file + } else { + toolchain_coverage_instrumentation_input_file = + coverage_instrumentation_input_file + } + + coverage_wrapper = + rebase_path("//build/toolchain/clang_code_coverage_wrapper.py", + root_build_dir) + coverage_wrapper = coverage_wrapper + " --target-os=" + target_os + if (toolchain_coverage_instrumentation_input_file != "") { + coverage_wrapper = + coverage_wrapper + " --files-to-instrument=" + + rebase_path(toolchain_coverage_instrumentation_input_file, + root_build_dir) + } + coverage_wrapper = "$python_path " + coverage_wrapper + " " + } else { + coverage_wrapper = "" + } + + # Disabled with cc_wrapper because of https://github.com/mozilla/sccache/issues/1013 + if (toolchain_args.is_clang && cc_wrapper == "") { + # This flag omits system includes from /showIncludes output, to reduce the + # amount of data to parse and store in .ninja_deps. We do this on non-Windows too, + # and already make sure rebuilds after win sdk / libc++ / clang header updates happen via + # changing commandline flags. + show_includes = "/showIncludes:user" + } else { + show_includes = "/showIncludes" + } + + tool("cc") { + precompiled_header_type = "msvc" + pdbname = "{{target_out_dir}}/{{label_name}}_c.pdb" + + # Label names may have spaces in them so the pdbname must be quoted. The + # source and output don't need to be quoted because GN knows they're a + # full file name and will quote automatically when necessary. + depsformat = "msvc" + description = "CC {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.obj" ] + + # Note that the code coverage wrapper scripts assumes that {{source}} + # comes immediately after /c. + command = "$coverage_wrapper$env_wrapper$cl /c {{source}} /nologo $show_includes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} /Fo{{output}} /Fd\"$pdbname\"" + } + + tool("cxx") { + precompiled_header_type = "msvc" + + # The PDB name needs to be different between C and C++ compiled files. + pdbname = "{{target_out_dir}}/{{label_name}}_cc.pdb" + + # See comment in CC tool about quoting. + depsformat = "msvc" + description = "CXX {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.obj" ] + + # Note that the code coverage wrapper scripts assumes that {{source}} + # comes immediately after /c. + command = "$coverage_wrapper$env_wrapper$cl /c {{source}} /Fo{{output}} /nologo $show_includes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} /Fd\"$pdbname\"" + } + + tool("rc") { + command = "$python_path $tool_wrapper_path rc-wrapper $env rc.exe /nologo $sys_include_flags{{defines}} {{include_dirs}} /fo{{output}} {{source}}" + depsformat = "msvc" + outputs = [ "$object_subdir/{{source_name_part}}.res" ] + description = "RC {{output}}" + } + + tool("asm") { + is_msvc_assembler = true + + if (toolchain_args.target_cpu == "arm64") { + if (is_clang) { + prefix = rebase_path("$clang_base_path/bin", root_build_dir) + ml = "${clang_prefix}${prefix}/${clang_cl} --target=arm64-windows" + if (host_os == "win") { + # Flip the slashes so that copy/paste of the command works. + ml = string_replace(ml, "/", "\\") + } + ml += " -c -o{{output}}" + is_msvc_assembler = false + } else { + # Only affects Arm builds with is_clang = false, implemented for building + # V8 for Windows on Arm systems with the MSVC toolchain. + ml = "armasm64.exe" + } + } else { + # x86/x64 builds always use the MSVC assembler. + if (toolchain_args.target_cpu == "x64") { + ml = "ml64.exe" + } else { + ml = "ml.exe" + } + } + + if (is_msvc_assembler) { + ml += " /nologo /Fo{{output}}" + + # Suppress final-stage linking on x64/x86 builds. (Armasm64 does not + # require /c because it doesn't support linking.) + if (toolchain_args.target_cpu != "arm64") { + ml += " /c" + } + if (use_lld) { + # Wrap ml(64).exe with a script that makes its output deterministic. + # It's lld only because the script zaps obj Timestamp which + # link.exe /incremental looks at. + # TODO(https://crbug.com/762167): If we end up writing an llvm-ml64, + # make sure it has deterministic output (maybe with /Brepro or + # something) and remove this wrapper. + ml_py = rebase_path("ml.py", root_build_dir) + ml = "$python_path $ml_py $ml" + } + } + if (toolchain_args.target_cpu != "arm64" || is_clang) { + command = "$python_path $tool_wrapper_path asm-wrapper $env $ml {{defines}} {{include_dirs}} {{asmflags}} {{source}}" + } else { + # armasm64.exe does not support definitions passed via the command line. + # (Fortunately, they're not needed for compiling the V8 snapshot, which + # is the only time this assembler is required.) + command = "$python_path $tool_wrapper_path asm-wrapper $env $ml {{include_dirs}} {{asmflags}} {{source}}" + } + + description = "ASM {{output}}" + outputs = [ "$object_subdir/{{source_name_part}}.obj" ] + } + + if (toolchain_has_rust) { + tool("rust_staticlib") { + rust_outfile = "{{target_out_dir}}/{{crate_name}}.lib" + depfile = "{{crate_name}}.d" + command = "${rust_prefix}/rustc $rustc_common_args --emit=dep-info={{target_out_dir}}/$depfile,link -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + + tool("rust_rlib") { + rust_outfile = "{{target_out_dir}}/lib{{crate_name}}.rlib" + depfile = "{{crate_name}}.d" + command = "${rust_prefix}/rustc $rustc_common_args --emit=dep-info={{target_out_dir}}/$depfile,link -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + + if (rustc_can_link) { + tool("rust_bin") { + rust_outfile = "{{root_out_dir}}/{{crate_name}}.exe" + depfile = "{{crate_name}}.d" + command = "${rust_prefix}/rustc $rustc_common_args --emit=dep-info={{target_out_dir}}/$depfile,link -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + + tool("rust_cdylib") { + rust_outfile = "{{target_out_dir}}/lib{{crate_name}}.dll" + depfile = "{{crate_name}}.d" + command = "${rust_prefix}/rustc $rustc_common_args --emit=dep-info={{target_out_dir}}/$depfile,link -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + + tool("rust_macro") { + rust_outfile = "{{target_out_dir}}/lib{{crate_name}}.dll" + depfile = "{{crate_name}}.d" + command = "${rust_prefix}/rustc $rustc_common_args --emit=dep-info={{target_out_dir}}/$depfile,link -o $rust_outfile" + description = "RUST $rust_outfile" + outputs = [ rust_outfile ] + } + } + } + + tool("alink") { + rspfile = "{{output}}.rsp" + command = "$linker_wrapper$lib /OUT:{{output}} /nologo ${sys_lib_flags}{{arflags}} @$rspfile" + description = "LIB {{output}}" + outputs = [ + # Ignore {{output_extension}} and always use .lib, there's no reason to + # allow targets to override this extension on Windows. + "{{output_dir}}/{{target_output_name}}.lib", + ] + default_output_extension = ".lib" + default_output_dir = "{{target_out_dir}}" + + # The use of inputs_newline is to work around a fixed per-line buffer + # size in the linker. + rspfile_content = "{{inputs_newline}}" + } + + tool("solink") { + # E.g. "foo.dll": + dllname = "{{output_dir}}/{{target_output_name}}{{output_extension}}" + libname = "${dllname}.lib" # e.g. foo.dll.lib + pdbname = "${dllname}.pdb" + rspfile = "${dllname}.rsp" + pool = "//build/toolchain:link_pool($default_toolchain)" + + command = "$linker_wrapper$link /OUT:$dllname /nologo ${sys_lib_flags}/IMPLIB:$libname /DLL /PDB:$pdbname @$rspfile" + + default_output_extension = ".dll" + default_output_dir = "{{root_out_dir}}" + description = "LINK(DLL) {{output}}" + outputs = [ + dllname, + libname, + pdbname, + ] + link_output = libname + depend_output = libname + runtime_outputs = [ + dllname, + pdbname, + ] + + # Since the above commands only updates the .lib file when it changes, ask + # Ninja to check if the timestamp actually changed to know if downstream + # dependencies should be recompiled. + restat = true + + # The use of inputs_newline is to work around a fixed per-line buffer + # size in the linker. + rspfile_content = + "{{libs}} {{solibs}} {{inputs_newline}} {{ldflags}} {{rlibs}}" + } + + tool("solink_module") { + # E.g. "foo.dll": + dllname = "{{output_dir}}/{{target_output_name}}{{output_extension}}" + pdbname = "${dllname}.pdb" + rspfile = "${dllname}.rsp" + pool = "//build/toolchain:link_pool($default_toolchain)" + + command = "$linker_wrapper$link /OUT:$dllname /nologo ${sys_lib_flags}/DLL /PDB:$pdbname @$rspfile" + + default_output_extension = ".dll" + default_output_dir = "{{root_out_dir}}" + description = "LINK_MODULE(DLL) {{output}}" + outputs = [ + dllname, + pdbname, + ] + runtime_outputs = outputs + + # The use of inputs_newline is to work around a fixed per-line buffer + # size in the linker. + rspfile_content = + "{{libs}} {{solibs}} {{inputs_newline}} {{ldflags}} {{rlibs}}" + } + + tool("link") { + exename = "{{output_dir}}/{{target_output_name}}{{output_extension}}" + pdbname = "$exename.pdb" + rspfile = "$exename.rsp" + pool = "//build/toolchain:link_pool($default_toolchain)" + + command = "$linker_wrapper$link /OUT:$exename /nologo ${sys_lib_flags} /PDB:$pdbname @$rspfile" + + default_output_extension = ".exe" + default_output_dir = "{{root_out_dir}}" + description = "LINK {{output}}" + outputs = [ + exename, + pdbname, + ] + runtime_outputs = outputs + + # The use of inputs_newline is to work around a fixed per-line buffer + # size in the linker. + rspfile_content = + "{{inputs_newline}} {{libs}} {{solibs}} {{ldflags}} {{rlibs}}" + } + + # These two are really entirely generic, but have to be repeated in + # each toolchain because GN doesn't allow a template to be used here. + # See //build/toolchain/toolchain.gni for details. + tool("stamp") { + command = stamp_command + description = stamp_description + pool = "//build/toolchain:action_pool($default_toolchain)" + } + tool("copy") { + command = copy_command + description = copy_description + pool = "//build/toolchain:action_pool($default_toolchain)" + } + + tool("action") { + pool = "//build/toolchain:action_pool($default_toolchain)" + } + } +} + +template("win_toolchains") { + assert(defined(invoker.toolchain_arch)) + toolchain_arch = invoker.toolchain_arch + + win_toolchain_data = exec_script("setup_toolchain.py", + [ + visual_studio_path, + windows_sdk_path, + visual_studio_runtime_dirs, + "win", + toolchain_arch, + "environment." + toolchain_arch, + ], + "scope") + + # The toolchain using MSVC only makes sense when not doing cross builds. + # Chromium exclusively uses the win_clang_ toolchain below, but V8 and + # WebRTC still use this MSVC toolchain in some cases. + if (host_os == "win") { + msvc_toolchain(target_name) { + environment = "environment." + toolchain_arch + cl = "${goma_prefix}\"${win_toolchain_data.vc_bin_dir}/cl.exe\"" + + toolchain_args = { + if (defined(invoker.toolchain_args)) { + forward_variables_from(invoker.toolchain_args, "*") + } + is_clang = false + use_clang_coverage = false + current_os = "win" + target_cpu = "arm64" + } + } + } + + msvc_toolchain("win_clang_" + target_name) { + environment = "environment." + toolchain_arch + prefix = rebase_path("$clang_base_path/bin", root_build_dir) + cl = "${clang_prefix}$prefix/${clang_cl}" + _clang_lib_dir = + rebase_path("$clang_base_path/lib/clang/$clang_version/lib/windows", + root_build_dir) + if (host_os == "win") { + # Flip the slashes so that copy/paste of the command works. + cl = string_replace(cl, "/", "\\") + + # And to match the other -libpath flags. + _clang_lib_dir = string_replace(_clang_lib_dir, "/", "\\") + } + + sys_include_flags = "${win_toolchain_data.include_flags_imsvc}" + sys_lib_flags = + "-libpath:$_clang_lib_dir ${win_toolchain_data.libpath_flags}" + + toolchain_args = { + if (defined(invoker.toolchain_args)) { + forward_variables_from(invoker.toolchain_args, "*") + } + is_clang = true + current_os = "win" + target_cpu = "arm64" + } + } +} + +if (target_cpu == "x86" || target_cpu == "x64") { + win_toolchains("x86") { + toolchain_arch = "x86" + } + win_toolchains("x64") { + toolchain_arch = "x64" + } +} + +if (target_cpu == "arm64") { + win_toolchains("arm64") { + toolchain_arch = "arm64" + } + win_toolchains(host_cpu) { + toolchain_arch = host_cpu + } +} + +# The nacl_win64 toolchain is nearly identical to the plain x64 toolchain. +# It's used solely for building nacl64.exe (//components/nacl/broker:nacl64). +# The only reason it's a separate toolchain is so that it can force +# is_component_build to false in the toolchain_args() block, because +# building nacl64.exe in component style does not work. +win_toolchains("nacl_win64") { + toolchain_arch = "x64" + toolchain_args = { + is_component_build = false + } +} + +# WinUWP toolchains. Only define these when targeting them. + +if (target_os == "winuwp") { + assert(target_cpu == "x64" || target_cpu == "x86" || target_cpu == "arm" || + target_cpu == "arm64") + store_cpu_toolchain_data = exec_script("setup_toolchain.py", + [ + visual_studio_path, + windows_sdk_path, + visual_studio_runtime_dirs, + target_os, + target_cpu, + "environment.store_" + target_cpu, + ], + "scope") + + msvc_toolchain("uwp_" + target_cpu) { + environment = "environment.store_" + target_cpu + cl = "${goma_prefix}\"${store_cpu_toolchain_data.vc_bin_dir}/cl.exe\"" + toolchain_args = { + current_os = "winuwp" + target_cpu = target_cpu + is_clang = false + } + } +} diff --git a/third_party/libwebrtc/build/toolchain/win/midl.gni b/third_party/libwebrtc/build/toolchain/win/midl.gni new file mode 100644 index 0000000000..ecf02acd3c --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/win/midl.gni @@ -0,0 +1,199 @@ +# Copyright 2014 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. + +assert(is_win) + +import("//build/config/win/visual_studio_version.gni") + +# This template defines a rule to invoke the MS IDL compiler. The generated +# source code will be compiled and linked into targets that depend on this. +# +# Parameters +# +# sources +# List of .idl file to process. +# +# header_file (optional) +# File name of generated header file. Defaults to the basename of the +# source idl file with a .h extension. +# +# out_dir (optional) +# Directory to write the generated files to. Defaults to target_gen_dir. +# +# generated_dir (optional) +# Directory where generated files were previously persisted. +# Defaults to third_party\win_build_output\midl\|out_dir|. +# +# dynamic_guids (optional) +# If the GUIDs are not constant across builds, the current GUID +# substitutions. +# |dynamic_guids| is of the form: +# "PLACEHOLDER-GUID-158428a4-6014-4978-83ba-9fad0dabe791=" +# "3d852661-c795-4d20-9b95-5561e9a1d2d9," +# "PLACEHOLDER-GUID-63B8FFB1-5314-48C9-9C57-93EC8BC6184B=" +# "D0E1CACC-C63C-4192-94AB-BF8EAD0E3B83". +# See midl.py for more details. +# +# writes_tlb (optional) +# Whether a .tlb file should be added to outputs. Defaults to false. +# +# writes_proxy(optional) +# Whether a _p.c file should be added to outputs. Defaults to true. +# +# writes_dlldata(optional) +# Whether a .dlldata.c file should be added to outputs. Defaults to true. +# +# deps (optional) +# +# defines (optional) +# Build time defines to be passed to midl.exe as /D parameter. +# +# visibility (optional) + +template("midl") { + action_name = "${target_name}_idl_action" + source_set_name = target_name + + assert(defined(invoker.sources), "Source must be defined for $target_name") + + if (defined(invoker.out_dir)) { + out_dir = invoker.out_dir + } else { + out_dir = target_gen_dir + } + + if (defined(invoker.generated_dir)) { + generated_dir = rebase_path(invoker.generated_dir) + } else { + # midl.py expects 'gen' to be replaced with 'midl'. + generated_dir = rebase_path("//third_party/win_build_output") + "/midl/" + + rebase_path(out_dir, root_gen_dir) + } + + if (defined(invoker.dynamic_guids)) { + dynamic_guids = invoker.dynamic_guids + } else { + dynamic_guids = "none" + } + + if (defined(invoker.header_file)) { + header_file = invoker.header_file + } else { + header_file = "{{source_name_part}}.h" + } + + if (defined(invoker.writes_tlb)) { + writes_tlb = invoker.writes_tlb + } else { + writes_tlb = false + } + + if (defined(invoker.writes_proxy)) { + writes_proxy = invoker.writes_proxy + } else { + writes_proxy = true + } + + if (defined(invoker.writes_dlldata)) { + writes_dlldata = invoker.writes_dlldata + } else { + writes_dlldata = true + } + + if (writes_tlb) { + type_library_file = "{{source_name_part}}.tlb" + } else { + type_library_file = "none" + } + + if (writes_dlldata) { + dlldata_file = "{{source_name_part}}.dlldata.c" + } else { + dlldata_file = "none" + } + + if (writes_proxy) { + proxy_file = "{{source_name_part}}_p.c" + } else { + proxy_file = "none" + } + + interface_identifier_file = "{{source_name_part}}_i.c" + + action_foreach(action_name) { + visibility = [ ":$source_set_name" ] + script = "//build/toolchain/win/midl.py" + + sources = invoker.sources + + outputs = [ + "$out_dir/$header_file", + "$out_dir/$interface_identifier_file", + ] + + # These files are only added to outputs if the invoker so desires, as it + # they are not always generated depending on the content of the input idl + # file. + if (writes_tlb) { + outputs += [ "$out_dir/$type_library_file" ] + } + if (writes_dlldata) { + outputs += [ "$out_dir/$dlldata_file" ] + } + if (writes_proxy) { + outputs += [ "$out_dir/$proxy_file" ] + } + + if (target_cpu == "x86") { + win_tool_arch = "environment.x86" + idl_target_platform = "win32" + } else if (target_cpu == "x64") { + win_tool_arch = "environment.x64" + idl_target_platform = "x64" + } else if (target_cpu == "arm64") { + win_tool_arch = "environment.arm64" + idl_target_platform = "arm64" + } else { + assert(false, "Need environment for this arch") + } + + args = [ + win_tool_arch, + generated_dir, + rebase_path(out_dir, root_build_dir), + dynamic_guids, + type_library_file, + header_file, + dlldata_file, + interface_identifier_file, + proxy_file, + rebase_path("//third_party/llvm-build/Release+Asserts/bin/clang-cl.exe", + root_build_dir), + "{{source}}", + "/char", + "signed", + "/env", + idl_target_platform, + "/Oicf", + ] + + if (defined(invoker.defines)) { + foreach(define, invoker.defines) { + args += [ "/D" + define ] + } + } + + forward_variables_from(invoker, [ "deps" ]) + } + + source_set(target_name) { + forward_variables_from(invoker, [ "visibility" ]) + + # We only compile the IID files from the IDL tool rather than all outputs. + sources = process_file_template(invoker.sources, + [ "$out_dir/$interface_identifier_file" ]) + + public_deps = [ ":$action_name" ] + } +} diff --git a/third_party/libwebrtc/build/toolchain/win/midl.py b/third_party/libwebrtc/build/toolchain/win/midl.py new file mode 100644 index 0000000000..cfb4220133 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/win/midl.py @@ -0,0 +1,487 @@ +# Copyright 2017 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. + +from __future__ import division +from __future__ import print_function + +import array +import difflib +import distutils.dir_util +import filecmp +import io +import operator +import os +import posixpath +import re +import shutil +import struct +import subprocess +import sys +import tempfile +import uuid + +from functools import reduce + + +def ZapTimestamp(filename): + contents = open(filename, 'rb').read() + # midl.exe writes timestamp 2147483647 (2^31 - 1) as creation date into its + # outputs, but using the local timezone. To make the output timezone- + # independent, replace that date with a fixed string of the same length. + # Also blank out the minor version number. + if filename.endswith('.tlb'): + # See https://chromium-review.googlesource.com/c/chromium/src/+/693223 for + # a fairly complete description of the .tlb binary format. + # TLB files start with a 54 byte header. Offset 0x20 stores how many types + # are defined in the file, and the header is followed by that many uint32s. + # After that, 15 section headers appear. Each section header is 16 bytes, + # starting with offset and length uint32s. + # Section 12 in the file contains custom() data. custom() data has a type + # (int, string, etc). Each custom data chunk starts with a uint16_t + # describing its type. Type 8 is string data, consisting of a uint32_t + # len, followed by that many data bytes, followed by 'W' bytes to pad to a + # 4 byte boundary. Type 0x13 is uint32 data, followed by 4 data bytes, + # followed by two 'W' to pad to a 4 byte boundary. + # The custom block always starts with one string containing "Created by + # MIDL version 8...", followed by one uint32 containing 0x7fffffff, + # followed by another uint32 containing the MIDL compiler version (e.g. + # 0x0801026e for v8.1.622 -- 0x26e == 622). These 3 fields take 0x54 bytes. + # There might be more custom data after that, but these 3 blocks are always + # there for file-level metadata. + # All data is little-endian in the file. + assert contents[0:8] == b'MSFT\x02\x00\x01\x00' + ntypes, = struct.unpack_from('<I', contents, 0x20) + custom_off, custom_len = struct.unpack_from( + '<II', contents, 0x54 + 4*ntypes + 11*16) + assert custom_len >= 0x54 + # First: Type string (0x8), followed by 0x3e characters. + assert contents[custom_off:custom_off + 6] == b'\x08\x00\x3e\x00\x00\x00' + assert re.match( + br'Created by MIDL version 8\.\d\d\.\d{4} ' + br'at ... Jan 1. ..:..:.. 2038\n', + contents[custom_off + 6:custom_off + 6 + 0x3e]) + # Second: Type uint32 (0x13) storing 0x7fffffff (followed by WW / 0x57 pad) + assert contents[custom_off+6+0x3e:custom_off+6+0x3e+8] == \ + b'\x13\x00\xff\xff\xff\x7f\x57\x57' + # Third: Type uint32 (0x13) storing MIDL compiler version. + assert contents[custom_off + 6 + 0x3e + 8:custom_off + 6 + 0x3e + 8 + + 2] == b'\x13\x00' + # Replace "Created by" string with fixed string, and fixed MIDL version with + # 8.1.622 always. + contents = ( + contents[0:custom_off + 6] + + b'Created by MIDL version 8.xx.xxxx at a redacted point in time\n' + + # uint32 (0x13) val 0x7fffffff, WW, uint32 (0x13), val 0x0801026e, WW + b'\x13\x00\xff\xff\xff\x7f\x57\x57\x13\x00\x6e\x02\x01\x08\x57\x57' + + contents[custom_off + 0x54:]) + else: + contents = re.sub( + br'File created by MIDL compiler version 8\.\d\d\.\d{4} \*/\r\n' + br'/\* at ... Jan 1. ..:..:.. 2038', + br'File created by MIDL compiler version 8.xx.xxxx */\r\n' + br'/* at a redacted point in time', contents) + contents = re.sub( + br' Oicf, W1, Zp8, env=(.....) \(32b run\), ' + br'target_arch=(AMD64|X86) 8\.\d\d\.\d{4}', + br' Oicf, W1, Zp8, env=\1 (32b run), target_arch=\2 8.xx.xxxx', + contents) + # TODO(thakis): If we need more hacks than these, try to verify checked-in + # outputs when we're using the hermetic toolchain. + # midl.exe older than 8.1.622 omit '//' after #endif, fix that: + contents = contents.replace(b'#endif !_MIDL_USE_GUIDDEF_', + b'#endif // !_MIDL_USE_GUIDDEF_') + # midl.exe puts the midl version into code in one place. To have + # predictable output, lie about the midl version if it's not 8.1.622. + # This is unfortunate, but remember that there's beauty too in imperfection. + contents = contents.replace(b'0x801026c, /* MIDL Version 8.1.620 */', + b'0x801026e, /* MIDL Version 8.1.622 */') + open(filename, 'wb').write(contents) + + +def get_tlb_contents(tlb_file): + # See ZapTimestamp() for a short overview of the .tlb format. + contents = open(tlb_file, 'rb').read() + assert contents[0:8] == b'MSFT\x02\x00\x01\x00' + ntypes, = struct.unpack_from('<I', contents, 0x20) + type_off, type_len = struct.unpack_from('<II', contents, 0x54 + 4*ntypes) + + guid_off, guid_len = struct.unpack_from( + '<II', contents, 0x54 + 4*ntypes + 5*16) + assert guid_len % 24 == 0 + + contents = array.array('B', contents) + + return contents, ntypes, type_off, guid_off, guid_len + + +def recreate_guid_hashtable(contents, ntypes, guid_off, guid_len): + # This function is called after changing guids in section 6 (the "guid" + # section). This function recreates the GUID hashtable in section 5. Since the + # hash table uses chaining, it's easiest to recompute it from scratch rather + # than trying to patch it up. + hashtab = [0xffffffff] * (0x80 // 4) + for guidind in range(guid_off, guid_off + guid_len, 24): + guidbytes, typeoff, nextguid = struct.unpack_from( + '<16sII', contents, guidind) + words = struct.unpack('<8H', guidbytes) + # midl seems to use the following simple hash function for GUIDs: + guidhash = reduce(operator.xor, [w for w in words]) % (0x80 // 4) + nextguid = hashtab[guidhash] + struct.pack_into('<I', contents, guidind + 0x14, nextguid) + hashtab[guidhash] = guidind - guid_off + hash_off, hash_len = struct.unpack_from( + '<II', contents, 0x54 + 4*ntypes + 4*16) + for i, hashval in enumerate(hashtab): + struct.pack_into('<I', contents, hash_off + 4*i, hashval) + + +def overwrite_guids_h(h_file, dynamic_guids): + contents = open(h_file, 'rb').read() + for key in dynamic_guids: + contents = re.sub(key, dynamic_guids[key], contents, flags=re.I) + open(h_file, 'wb').write(contents) + + +def get_uuid_format(guid, prefix): + formatted_uuid = b'0x%s,0x%s,0x%s,' % (guid[0:8], guid[9:13], guid[14:18]) + formatted_uuid += b'%s0x%s,0x%s' % (prefix, guid[19:21], guid[21:23]) + for i in range(24, len(guid), 2): + formatted_uuid += b',0x' + guid[i:i + 2] + return formatted_uuid + + +def get_uuid_format_iid_file(guid): + # Convert from "D0E1CACC-C63C-4192-94AB-BF8EAD0E3B83" to + # 0xD0E1CACC,0xC63C,0x4192,0x94,0xAB,0xBF,0x8E,0xAD,0x0E,0x3B,0x83. + return get_uuid_format(guid, b'') + + +def overwrite_guids_iid(iid_file, dynamic_guids): + contents = open(iid_file, 'rb').read() + for key in dynamic_guids: + contents = re.sub(get_uuid_format_iid_file(key), + get_uuid_format_iid_file(dynamic_guids[key]), + contents, + flags=re.I) + open(iid_file, 'wb').write(contents) + + +def get_uuid_format_proxy_file(guid): + # Convert from "D0E1CACC-C63C-4192-94AB-BF8EAD0E3B83" to + # {0xD0E1CACC,0xC63C,0x4192,{0x94,0xAB,0xBF,0x8E,0xAD,0x0E,0x3B,0x83}}. + return get_uuid_format(guid, b'{') + + +def overwrite_guids_proxy(proxy_file, dynamic_guids): + contents = open(proxy_file, 'rb').read() + for key in dynamic_guids: + contents = re.sub(get_uuid_format_proxy_file(key), + get_uuid_format_proxy_file(dynamic_guids[key]), + contents, + flags=re.I) + open(proxy_file, 'wb').write(contents) + + +def getguid(contents, offset): + # Returns a guid string of the form "D0E1CACC-C63C-4192-94AB-BF8EAD0E3B83". + g0, g1, g2, g3 = struct.unpack_from('<IHH8s', contents, offset) + g3 = b''.join([b'%02X' % g for g in bytearray(g3)]) + return b'%08X-%04X-%04X-%s-%s' % (g0, g1, g2, g3[0:4], g3[4:]) + + +def setguid(contents, offset, guid): + guid = uuid.UUID(guid.decode('utf-8')) + struct.pack_into('<IHH8s', contents, offset, + *(guid.fields[0:3] + (guid.bytes[8:], ))) + + +def overwrite_guids_tlb(tlb_file, dynamic_guids): + contents, ntypes, type_off, guid_off, guid_len = get_tlb_contents(tlb_file) + + for i in range(0, guid_len, 24): + current_guid = getguid(contents, guid_off + i) + for key in dynamic_guids: + if key.lower() == current_guid.lower(): + setguid(contents, guid_off + i, dynamic_guids[key]) + + recreate_guid_hashtable(contents, ntypes, guid_off, guid_len) + open(tlb_file, 'wb').write(contents) + + +# Handle multiple guid substitutions, where |dynamic_guids| is of the form +# "PLACEHOLDER-GUID-158428a4-6014-4978-83ba-9fad0dabe791=" +# "3d852661-c795-4d20-9b95-5561e9a1d2d9," +# "PLACEHOLDER-GUID-63B8FFB1-5314-48C9-9C57-93EC8BC6184B=" +# "D0E1CACC-C63C-4192-94AB-BF8EAD0E3B83". +# +# Before specifying |dynamic_guids| in the build, the IDL file is first compiled +# with "158428a4-6014-4978-83ba-9fad0dabe791" and +# "63B8FFB1-5314-48C9-9C57-93EC8BC6184B". These are the "replaceable" guids, +# i.e., guids that can be replaced in future builds. The resulting MIDL outputs +# are copied over to src\third_party\win_build_output\. +# +# Then, in the future, any changes to these guids can be accomplished by +# providing |dynamic_guids| of the format above in the build file. These +# "dynamic" guid changes by themselves will not require the MIDL compiler and +# therefore will not require copying output over to +# src\third_party\win_build_output\. +# +# The pre-generated src\third_party\win_build_output\ files are used for +# cross-compiling on other platforms, since the MIDL compiler is Windows-only. +def overwrite_guids(h_file, iid_file, proxy_file, tlb_file, dynamic_guids): + # Fix up GUIDs in .h, _i.c, _p.c, and .tlb. + overwrite_guids_h(h_file, dynamic_guids) + overwrite_guids_iid(iid_file, dynamic_guids) + overwrite_guids_proxy(proxy_file, dynamic_guids) + if tlb_file: + overwrite_guids_tlb(tlb_file, dynamic_guids) + + +# This function removes all occurrences of 'PLACEHOLDER-GUID-' from the +# template, and if |dynamic_guids| is specified, also replaces the guids within +# the file. Finally, it writes the resultant output to the |idl| file. +def generate_idl_from_template(idl_template, dynamic_guids, idl): + contents = open(idl_template, 'rb').read() + contents = re.sub(b'PLACEHOLDER-GUID-', b'', contents, flags=re.I) + if dynamic_guids: + for key in dynamic_guids: + contents = re.sub(key, dynamic_guids[key], contents, flags=re.I) + open(idl, 'wb').write(contents) + + +# This function runs the MIDL compiler with the provided arguments. It creates +# and returns a tuple of |0,midl_output_dir| on success. +def run_midl(args, env_dict): + midl_output_dir = tempfile.mkdtemp() + delete_midl_output_dir = True + + try: + popen = subprocess.Popen(args + ['/out', midl_output_dir], + shell=True, + universal_newlines=True, + env=env_dict, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + out, _ = popen.communicate() + if popen.returncode != 0: + return popen.returncode, midl_output_dir + + # Filter junk out of stdout, and write filtered versions. Output we want + # to filter is pairs of lines that look like this: + # Processing C:\Program Files (x86)\Microsoft SDKs\...\include\objidl.idl + # objidl.idl + lines = out.splitlines() + prefixes = ('Processing ', '64 bit Processing ') + processing = set( + os.path.basename(x) for x in lines if x.startswith(prefixes)) + for line in lines: + if not line.startswith(prefixes) and line not in processing: + print(line) + + for f in os.listdir(midl_output_dir): + ZapTimestamp(os.path.join(midl_output_dir, f)) + + delete_midl_output_dir = False + finally: + if os.path.exists(midl_output_dir) and delete_midl_output_dir: + shutil.rmtree(midl_output_dir) + + return 0, midl_output_dir + + +# This function adds support for dynamic generation of guids: when values are +# specified as 'uuid5:name', this function will substitute the values with +# generated dynamic guids using the uuid5 function. The uuid5 function generates +# a guid based on the SHA-1 hash of a namespace identifier (which is the guid +# that comes after 'PLACEHOLDER-GUID-') and a name (which is a string, such as a +# version string "87.1.2.3"). +# +# For instance, when |dynamic_guid| is of the form: +# "PLACEHOLDER-GUID-158428a4-6014-4978-83ba-9fad0dabe791=uuid5:88.0.4307.0 +# ," +# "PLACEHOLDER-GUID-63B8FFB1-5314-48C9-9C57-93EC8BC6184B=uuid5:88.0.4307.0 +# " +# +# "PLACEHOLDER-GUID-158428a4-6014-4978-83ba-9fad0dabe791" would be substituted +# with uuid5("158428a4-6014-4978-83ba-9fad0dabe791", "88.0.4307.0"), which is +# "64700170-AD80-5DE3-924E-2F39D862CFD5". And +# "PLACEHOLDER-GUID-63B8FFB1-5314-48C9-9C57-93EC8BC6184B" would be +# substituted with uuid5("63B8FFB1-5314-48C9-9C57-93EC8BC6184B", "88.0.4307.0"), +# which is "7B6E7538-3C38-5565-BC92-42BCEE268D76". +def uuid5_substitutions(dynamic_guids): + for key, value in dynamic_guids.items(): + if value.startswith('uuid5:'): + name = value.split('uuid5:', 1)[1] + assert name + dynamic_guids[key] = str(uuid.uuid5(uuid.UUID(key), name)).upper() + + +def main(arch, gendir, outdir, dynamic_guids, tlb, h, dlldata, iid, proxy, + clang, idl, *flags): + # Copy checked-in outputs to final location. + source = gendir + if os.path.isdir(os.path.join(source, os.path.basename(idl))): + source = os.path.join(source, os.path.basename(idl)) + source = os.path.join(source, arch.split('.')[1]) # Append 'x86' or 'x64'. + source = os.path.normpath(source) + + source_exists = True + if not os.path.isdir(source): + source_exists = False + if sys.platform != 'win32': + print('Directory %s needs to be populated from Windows first' % source) + return 1 + + # This is a brand new IDL file that does not have outputs under + # third_party\win_build_output\midl. We create an empty directory for now. + os.makedirs(source) + + common_files = [h, iid] + if tlb != 'none': + # Not all projects use tlb files. + common_files += [tlb] + else: + tlb = None + + if dlldata != 'none': + # Not all projects use dlldta files. + common_files += [dlldata] + else: + dlldata = None + + # Not all projects use proxy files + if proxy != 'none': + # Not all projects use proxy files. + common_files += [proxy] + else: + proxy = None + + for source_file in common_files: + file_path = os.path.join(source, source_file) + if not os.path.isfile(file_path): + source_exists = False + if sys.platform != 'win32': + print('File %s needs to be generated from Windows first' % file_path) + return 1 + + # Either this is a brand new IDL file that does not have outputs under + # third_party\win_build_output\midl or the file is (unexpectedly) missing. + # We create an empty file for now. The rest of the machinery below will + # then generate the correctly populated file using the MIDL compiler and + # instruct the developer to copy that file under + # third_party\win_build_output\midl. + open(file_path, 'wb').close() + shutil.copy(file_path, outdir) + + if dynamic_guids != 'none': + assert '=' in dynamic_guids + if dynamic_guids.startswith("ignore_proxy_stub,"): + # TODO(ganesh): The custom proxy/stub file ("_p.c") is not generated + # correctly for dynamic IIDs (but correctly if there are only dynamic + # CLSIDs). The proxy/stub lookup functions generated by MIDL.exe within + # "_p.c" rely on a sorted set of vtable lists, which we are not currently + # regenerating. At the moment, no project in Chromium that uses dynamic + # IIDs is relying on the custom proxy/stub file. So for now, if + # |dynamic_guids| is prefixed with "ignore_proxy_stub,", we exclude the + # custom proxy/stub file from the directory comparisons. + common_files.remove(proxy) + dynamic_guids = dynamic_guids.split("ignore_proxy_stub,", 1)[1] + dynamic_guids = re.sub('PLACEHOLDER-GUID-', '', dynamic_guids, flags=re.I) + dynamic_guids = dynamic_guids.split(',') + dynamic_guids = dict(s.split('=') for s in dynamic_guids) + uuid5_substitutions(dynamic_guids) + dynamic_guids_bytes = { + k.encode('utf-8'): v.encode('utf-8') + for k, v in dynamic_guids.items() + } + if source_exists: + overwrite_guids(*(os.path.join(outdir, file) if file else None + for file in [h, iid, proxy, tlb]), + dynamic_guids=dynamic_guids_bytes) + else: + dynamic_guids = None + + # On non-Windows, that's all we can do. + if sys.platform != 'win32': + return 0 + + idl_template = None + if dynamic_guids: + idl_template = idl + + # posixpath is used here to keep the MIDL-generated files with a uniform + # separator of '/' instead of mixed '/' and '\\'. + idl = posixpath.join( + outdir, + os.path.splitext(os.path.basename(idl_template))[0] + '.idl') + + # |idl_template| can contain one or more occurrences of guids that are + # substituted with |dynamic_guids|, and then MIDL is run on the substituted + # IDL file. + generate_idl_from_template(idl_template, dynamic_guids_bytes, idl) + + # On Windows, run midl.exe on the input and check that its outputs are + # identical to the checked-in outputs (after replacing guids if + # |dynamic_guids| is specified). + + # Read the environment block from the file. This is stored in the format used + # by CreateProcess. Drop last 2 NULs, one for list terminator, one for + # trailing vs. separator. + env_pairs = open(arch).read()[:-2].split('\0') + env_dict = dict([item.split('=', 1) for item in env_pairs]) + + # Extract the /D options and send them to the preprocessor. + preprocessor_options = '-E -nologo -Wno-nonportable-include-path' + preprocessor_options += ''.join( + [' ' + flag for flag in flags if flag.startswith('/D')]) + args = ['midl', '/nologo'] + list(flags) + (['/tlb', tlb] if tlb else []) + [ + '/h', h + ] + (['/dlldata', dlldata] if dlldata else []) + ['/iid', iid] + ( + ['/proxy', proxy] if proxy else + []) + ['/cpp_cmd', clang, '/cpp_opt', preprocessor_options, idl] + + returncode, midl_output_dir = run_midl(args, env_dict) + if returncode != 0: + return returncode + + # Now compare the output in midl_output_dir to the copied-over outputs. + _, mismatch, errors = filecmp.cmpfiles(midl_output_dir, outdir, common_files) + assert not errors + + if mismatch: + print('midl.exe output different from files in %s, see %s' % + (outdir, midl_output_dir)) + for f in mismatch: + if f.endswith('.tlb'): continue + fromfile = os.path.join(outdir, f) + tofile = os.path.join(midl_output_dir, f) + print(''.join( + difflib.unified_diff( + io.open(fromfile).readlines(), + io.open(tofile).readlines(), fromfile, tofile))) + + if dynamic_guids: + # |idl_template| can contain one or more occurrences of guids prefixed + # with 'PLACEHOLDER-GUID-'. We first remove the extraneous + # 'PLACEHOLDER-GUID-' prefix and then run MIDL on the substituted IDL + # file. + # No guid substitutions are done at this point, because we want to compile + # with the placeholder guids and then instruct the user to copy the output + # over to |source| which is typically src\third_party\win_build_output\. + # In future runs, the placeholder guids in |source| are replaced with the + # guids specified in |dynamic_guids|. + generate_idl_from_template(idl_template, None, idl) + returncode, midl_output_dir = run_midl(args, env_dict) + if returncode != 0: + return returncode + + print('To rebaseline:') + print(r' copy /y %s\* %s' % (midl_output_dir, source)) + return 1 + + return 0 + + +if __name__ == '__main__': + sys.exit(main(*sys.argv[1:])) diff --git a/third_party/libwebrtc/build/toolchain/win/ml.py b/third_party/libwebrtc/build/toolchain/win/ml.py new file mode 100755 index 0000000000..6a1b6e577e --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/win/ml.py @@ -0,0 +1,290 @@ +#!/usr/bin/env python +# Copyright 2018 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. + +"""Wraps ml.exe or ml64.exe and postprocesses the output to be deterministic. +Sets timestamp in .obj file to 0, hence incompatible with link.exe /incremental. + +Use by prefixing the ml(64).exe invocation with this script: + python ml.py ml.exe [args...]""" + +import array +import collections +import struct +import subprocess +import sys + + +class Struct(object): + """A thin wrapper around the struct module that returns a namedtuple""" + def __init__(self, name, *args): + """Pass the name of the return type, and then an interleaved list of + format strings as used by the struct module and of field names.""" + self.fmt = '<' + ''.join(args[0::2]) + self.type = collections.namedtuple(name, args[1::2]) + + def pack_into(self, buffer, offset, data): + return struct.pack_into(self.fmt, buffer, offset, *data) + + def unpack_from(self, buffer, offset=0): + return self.type(*struct.unpack_from(self.fmt, buffer, offset)) + + def size(self): + return struct.calcsize(self.fmt) + + +def Subtract(nt, **kwargs): + """Subtract(nt, f=2) returns a new namedtuple with 2 subtracted from nt.f""" + return nt._replace(**{k: getattr(nt, k) - v for k, v in kwargs.items()}) + + +def MakeDeterministic(objdata): + # Takes data produced by ml(64).exe (without any special flags) and + # 1. Sets the timestamp to 0 + # 2. Strips the .debug$S section (which contains an unwanted absolute path) + + # This makes several assumptions about ml's output: + # - Section data is in the same order as the corresponding section headers: + # section headers preceding the .debug$S section header have their data + # preceding the .debug$S section data; likewise for section headers + # following the .debug$S section. + # - The .debug$S section contains only the absolute path to the obj file and + # nothing else, in particular there's only a single entry in the symbol + # table referring to the .debug$S section. + # - There are no COFF line number entries. + # - There's no IMAGE_SYM_CLASS_CLR_TOKEN symbol. + # These seem to hold in practice; if they stop holding this script needs to + # become smarter. + + objdata = array.array('b', objdata) # Writable, e.g. via struct.pack_into. + + # Read coff header. + COFFHEADER = Struct('COFFHEADER', + 'H', 'Machine', + 'H', 'NumberOfSections', + 'I', 'TimeDateStamp', + 'I', 'PointerToSymbolTable', + 'I', 'NumberOfSymbols', + + 'H', 'SizeOfOptionalHeader', + 'H', 'Characteristics') + coff_header = COFFHEADER.unpack_from(objdata) + assert coff_header.SizeOfOptionalHeader == 0 # Only set for binaries. + + # Read section headers following coff header. + SECTIONHEADER = Struct('SECTIONHEADER', + '8s', 'Name', + 'I', 'VirtualSize', + 'I', 'VirtualAddress', + + 'I', 'SizeOfRawData', + 'I', 'PointerToRawData', + 'I', 'PointerToRelocations', + 'I', 'PointerToLineNumbers', + + 'H', 'NumberOfRelocations', + 'H', 'NumberOfLineNumbers', + 'I', 'Characteristics') + section_headers = [] + debug_section_index = -1 + for i in range(0, coff_header.NumberOfSections): + section_header = SECTIONHEADER.unpack_from( + objdata, offset=COFFHEADER.size() + i * SECTIONHEADER.size()) + assert not section_header[0].startswith(b'/') # Support short names only. + section_headers.append(section_header) + + if section_header.Name == b'.debug$S': + assert debug_section_index == -1 + debug_section_index = i + assert debug_section_index != -1 + + data_start = COFFHEADER.size() + len(section_headers) * SECTIONHEADER.size() + + # Verify the .debug$S section looks like we expect. + assert section_headers[debug_section_index].Name == b'.debug$S' + assert section_headers[debug_section_index].VirtualSize == 0 + assert section_headers[debug_section_index].VirtualAddress == 0 + debug_size = section_headers[debug_section_index].SizeOfRawData + debug_offset = section_headers[debug_section_index].PointerToRawData + assert section_headers[debug_section_index].PointerToRelocations == 0 + assert section_headers[debug_section_index].PointerToLineNumbers == 0 + assert section_headers[debug_section_index].NumberOfRelocations == 0 + assert section_headers[debug_section_index].NumberOfLineNumbers == 0 + + # Make sure sections in front of .debug$S have their data preceding it. + for header in section_headers[:debug_section_index]: + assert header.PointerToRawData < debug_offset + assert header.PointerToRelocations < debug_offset + assert header.PointerToLineNumbers < debug_offset + + # Make sure sections after of .debug$S have their data following it. + for header in section_headers[debug_section_index + 1:]: + # Make sure the .debug$S data is at the very end of section data: + assert header.PointerToRawData > debug_offset + assert header.PointerToRelocations == 0 + assert header.PointerToLineNumbers == 0 + + # Make sure the first non-empty section's data starts right after the section + # headers. + for section_header in section_headers: + if section_header.PointerToRawData == 0: + assert section_header.PointerToRelocations == 0 + assert section_header.PointerToLineNumbers == 0 + continue + assert section_header.PointerToRawData == data_start + break + + # Make sure the symbol table (and hence, string table) appear after the last + # section: + assert (coff_header.PointerToSymbolTable >= + section_headers[-1].PointerToRawData + section_headers[-1].SizeOfRawData) + + # The symbol table contains a symbol for the no-longer-present .debug$S + # section. If we leave it there, lld-link will complain: + # + # lld-link: error: .debug$S should not refer to non-existent section 5 + # + # so we need to remove that symbol table entry as well. This shifts symbol + # entries around and we need to update symbol table indices in: + # - relocations + # - line number records (never present) + # - one aux symbol entry (IMAGE_SYM_CLASS_CLR_TOKEN; not present in ml output) + SYM = Struct('SYM', + '8s', 'Name', + 'I', 'Value', + 'h', 'SectionNumber', # Note: Signed! + 'H', 'Type', + + 'B', 'StorageClass', + 'B', 'NumberOfAuxSymbols') + i = 0 + debug_sym = -1 + while i < coff_header.NumberOfSymbols: + sym_offset = coff_header.PointerToSymbolTable + i * SYM.size() + sym = SYM.unpack_from(objdata, sym_offset) + + # 107 is IMAGE_SYM_CLASS_CLR_TOKEN, which has aux entry "CLR Token + # Definition", which contains a symbol index. Check it's never present. + assert sym.StorageClass != 107 + + # Note: sym.SectionNumber is 1-based, debug_section_index is 0-based. + if sym.SectionNumber - 1 == debug_section_index: + assert debug_sym == -1, 'more than one .debug$S symbol found' + debug_sym = i + # Make sure the .debug$S symbol looks like we expect. + # In particular, it should have exactly one aux symbol. + assert sym.Name == b'.debug$S' + assert sym.Value == 0 + assert sym.Type == 0 + assert sym.StorageClass == 3 + assert sym.NumberOfAuxSymbols == 1 + elif sym.SectionNumber > debug_section_index: + sym = Subtract(sym, SectionNumber=1) + SYM.pack_into(objdata, sym_offset, sym) + i += 1 + sym.NumberOfAuxSymbols + assert debug_sym != -1, '.debug$S symbol not found' + + # Note: Usually the .debug$S section is the last, but for files saying + # `includelib foo.lib`, like safe_terminate_process.asm in 32-bit builds, + # this isn't true: .drectve is after .debug$S. + + # Update symbol table indices in relocations. + # There are a few processor types that have one or two relocation types + # where SymbolTableIndex has a different meaning, but not for x86. + REL = Struct('REL', + 'I', 'VirtualAddress', + 'I', 'SymbolTableIndex', + 'H', 'Type') + for header in section_headers[0:debug_section_index]: + for j in range(0, header.NumberOfRelocations): + rel_offset = header.PointerToRelocations + j * REL.size() + rel = REL.unpack_from(objdata, rel_offset) + assert rel.SymbolTableIndex != debug_sym + if rel.SymbolTableIndex > debug_sym: + rel = Subtract(rel, SymbolTableIndex=2) + REL.pack_into(objdata, rel_offset, rel) + + # Update symbol table indices in line numbers -- just check they don't exist. + for header in section_headers: + assert header.NumberOfLineNumbers == 0 + + # Now that all indices are updated, remove the symbol table entry referring to + # .debug$S and its aux entry. + del objdata[coff_header.PointerToSymbolTable + debug_sym * SYM.size(): + coff_header.PointerToSymbolTable + (debug_sym + 2) * SYM.size()] + + # Now we know that it's safe to write out the input data, with just the + # timestamp overwritten to 0, the last section header cut out (and the + # offsets of all other section headers decremented by the size of that + # one section header), and the last section's data cut out. The symbol + # table offset needs to be reduced by one section header and the size of + # the missing section. + # (The COFF spec only requires on-disk sections to be aligned in image files, + # for obj files it's not required. If that wasn't the case, deleting slices + # if data would not generally be safe.) + + # Update section offsets and remove .debug$S section data. + for i in range(0, debug_section_index): + header = section_headers[i] + if header.SizeOfRawData: + header = Subtract(header, PointerToRawData=SECTIONHEADER.size()) + if header.NumberOfRelocations: + header = Subtract(header, PointerToRelocations=SECTIONHEADER.size()) + if header.NumberOfLineNumbers: + header = Subtract(header, PointerToLineNumbers=SECTIONHEADER.size()) + SECTIONHEADER.pack_into( + objdata, COFFHEADER.size() + i * SECTIONHEADER.size(), header) + for i in range(debug_section_index + 1, len(section_headers)): + header = section_headers[i] + shift = SECTIONHEADER.size() + debug_size + if header.SizeOfRawData: + header = Subtract(header, PointerToRawData=shift) + if header.NumberOfRelocations: + header = Subtract(header, PointerToRelocations=shift) + if header.NumberOfLineNumbers: + header = Subtract(header, PointerToLineNumbers=shift) + SECTIONHEADER.pack_into( + objdata, COFFHEADER.size() + i * SECTIONHEADER.size(), header) + + del objdata[debug_offset:debug_offset + debug_size] + + # Finally, remove .debug$S section header and update coff header. + coff_header = coff_header._replace(TimeDateStamp=0) + coff_header = Subtract(coff_header, + NumberOfSections=1, + PointerToSymbolTable=SECTIONHEADER.size() + debug_size, + NumberOfSymbols=2) + COFFHEADER.pack_into(objdata, 0, coff_header) + + del objdata[ + COFFHEADER.size() + debug_section_index * SECTIONHEADER.size(): + COFFHEADER.size() + (debug_section_index + 1) * SECTIONHEADER.size()] + + # All done! + if sys.version_info.major == 2: + return objdata.tostring() + else: + return objdata.tobytes() + + +def main(): + ml_result = subprocess.call(sys.argv[1:]) + if ml_result != 0: + return ml_result + + objfile = None + for i in range(1, len(sys.argv)): + if sys.argv[i].startswith('/Fo'): + objfile = sys.argv[i][len('/Fo'):] + assert objfile, 'failed to find ml output' + + with open(objfile, 'rb') as f: + objdata = f.read() + objdata = MakeDeterministic(objdata) + with open(objfile, 'wb') as f: + f.write(objdata) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/third_party/libwebrtc/build/toolchain/win/rc/.gitignore b/third_party/libwebrtc/build/toolchain/win/rc/.gitignore new file mode 100644 index 0000000000..e8fc4d3e1f --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/win/rc/.gitignore @@ -0,0 +1,3 @@ +linux64/rc +mac/rc +win/rc.exe diff --git a/third_party/libwebrtc/build/toolchain/win/rc/README.md b/third_party/libwebrtc/build/toolchain/win/rc/README.md new file mode 100644 index 0000000000..e6d38f9709 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/win/rc/README.md @@ -0,0 +1,30 @@ +# rc + +This contains a cross-platform reimplementation of rc.exe. + +This exists mainly to compile .rc files on non-Windows hosts for cross builds. +However, it also runs on Windows for two reasons: + +1. To compare the output of Microsoft's rc.exe and the reimplementation and to + check that they produce bitwise identical output. +2. The reimplementation supports printing resource files in /showIncludes + output, which helps getting build dependencies right. + +The resource compiler consists of two parts: + +1. A python script rc.py that serves as the driver. It does unicode + conversions, runs the input through the preprocessor, and then calls the + actual resource compiler. +2. The resource compiler, a C++ binary obtained via sha1 files from Google + Storage. The binary's code currenty lives at + https://github.com/nico/hack/tree/master/res, even though work is (slowly) + underway to upstream it into LLVM. + +To update the rc binary, run `upload_rc_binaries.sh` in this directory, on a +Mac. + +rc isn't built from source as part of the regular chrome build because +it's needed in a gn toolchain tool, and these currently cannot have deps. +Alternatively, gn could be taught about deps on tools, or rc invocations could +be not a tool but a template like e.g. yasm invocations (which can have deps), +then the prebuilt binaries wouldn't be needed. diff --git a/third_party/libwebrtc/build/toolchain/win/rc/linux64/rc.sha1 b/third_party/libwebrtc/build/toolchain/win/rc/linux64/rc.sha1 new file mode 100644 index 0000000000..ad14ca46a9 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/win/rc/linux64/rc.sha1 @@ -0,0 +1 @@ +2d0c766039264dc2514d005a42f074af4838a446
\ No newline at end of file diff --git a/third_party/libwebrtc/build/toolchain/win/rc/mac/rc.sha1 b/third_party/libwebrtc/build/toolchain/win/rc/mac/rc.sha1 new file mode 100644 index 0000000000..dbd6302a35 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/win/rc/mac/rc.sha1 @@ -0,0 +1 @@ +4c25c3bcb6608109bb52028d008835895cf72629
\ No newline at end of file diff --git a/third_party/libwebrtc/build/toolchain/win/rc/rc.py b/third_party/libwebrtc/build/toolchain/win/rc/rc.py new file mode 100755 index 0000000000..2ab41225fb --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/win/rc/rc.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python +# Copyright 2017 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. + +"""usage: rc.py [options] input.res +A resource compiler for .rc files. + +options: +-h, --help Print this message. +-I<dir> Add include path, used for both headers and resources. +-imsvc<dir> Add system include path, used for preprocessing only. +/winsysroot<d> Set winsysroot, used for preprocessing only. +-D<sym> Define a macro for the preprocessor. +/fo<out> Set path of output .res file. +/nologo Ignored (rc.py doesn't print a logo by default). +/showIncludes Print referenced header and resource files.""" + +from __future__ import print_function +from collections import namedtuple +import codecs +import os +import re +import subprocess +import sys +import tempfile + + +THIS_DIR = os.path.abspath(os.path.dirname(__file__)) +SRC_DIR = \ + os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(THIS_DIR)))) + + +def ParseFlags(): + """Parses flags off sys.argv and returns the parsed flags.""" + # Can't use optparse / argparse because of /fo flag :-/ + includes = [] + imsvcs = [] + winsysroot = [] + defines = [] + output = None + input = None + show_includes = False + # Parse. + for flag in sys.argv[1:]: + if flag == '-h' or flag == '--help': + print(__doc__) + sys.exit(0) + if flag.startswith('-I'): + includes.append(flag) + elif flag.startswith('-imsvc'): + imsvcs.append(flag) + elif flag.startswith('/winsysroot'): + winsysroot = [flag] + elif flag.startswith('-D'): + defines.append(flag) + elif flag.startswith('/fo'): + if output: + print('rc.py: error: multiple /fo flags', '/fo' + output, flag, + file=sys.stderr) + sys.exit(1) + output = flag[3:] + elif flag == '/nologo': + pass + elif flag == '/showIncludes': + show_includes = True + elif (flag.startswith('-') or + (flag.startswith('/') and not os.path.exists(flag))): + print('rc.py: error: unknown flag', flag, file=sys.stderr) + print(__doc__, file=sys.stderr) + sys.exit(1) + else: + if input: + print('rc.py: error: multiple inputs:', input, flag, file=sys.stderr) + sys.exit(1) + input = flag + # Validate and set default values. + if not input: + print('rc.py: error: no input file', file=sys.stderr) + sys.exit(1) + if not output: + output = os.path.splitext(input)[0] + '.res' + Flags = namedtuple('Flags', [ + 'includes', 'defines', 'output', 'imsvcs', 'winsysroot', 'input', + 'show_includes' + ]) + return Flags(includes=includes, + defines=defines, + output=output, + imsvcs=imsvcs, + winsysroot=winsysroot, + input=input, + show_includes=show_includes) + + +def ReadInput(input): + """"Reads input and returns it. For UTF-16LEBOM input, converts to UTF-8.""" + # Microsoft's rc.exe only supports unicode in the form of UTF-16LE with a BOM. + # Our rc binary sniffs for UTF-16LE. If that's not found, if /utf-8 is + # passed, the input is treated as UTF-8. If /utf-8 is not passed and the + # input is not UTF-16LE, then our rc errors out on characters outside of + # 7-bit ASCII. Since the driver always converts UTF-16LE to UTF-8 here (for + # the preprocessor, which doesn't support UTF-16LE), our rc will either see + # UTF-8 with the /utf-8 flag (for UTF-16LE input), or ASCII input. + # This is compatible with Microsoft rc.exe. If we wanted, we could expose + # a /utf-8 flag for the driver for UTF-8 .rc inputs too. + # TODO(thakis): Microsoft's rc.exe supports BOM-less UTF-16LE. We currently + # don't, but for chrome it currently doesn't matter. + is_utf8 = False + try: + with open(input, 'rb') as rc_file: + rc_file_data = rc_file.read() + if rc_file_data.startswith(codecs.BOM_UTF16_LE): + rc_file_data = rc_file_data[2:].decode('utf-16le').encode('utf-8') + is_utf8 = True + except IOError: + print('rc.py: failed to open', input, file=sys.stderr) + sys.exit(1) + except UnicodeDecodeError: + print('rc.py: failed to decode UTF-16 despite BOM', input, file=sys.stderr) + sys.exit(1) + return rc_file_data, is_utf8 + + +def Preprocess(rc_file_data, flags): + """Runs the input file through the preprocessor.""" + clang = os.path.join(SRC_DIR, 'third_party', 'llvm-build', + 'Release+Asserts', 'bin', 'clang-cl') + # Let preprocessor write to a temp file so that it doesn't interfere + # with /showIncludes output on stdout. + if sys.platform == 'win32': + clang += '.exe' + temp_handle, temp_file = tempfile.mkstemp(suffix='.i') + # Closing temp_handle immediately defeats the purpose of mkstemp(), but I + # can't figure out how to let write to the temp file on Windows otherwise. + os.close(temp_handle) + clang_cmd = [clang, '/P', '/DRC_INVOKED', '/TC', '-', '/Fi' + temp_file] + if flags.imsvcs: + clang_cmd += ['/X'] + if os.path.dirname(flags.input): + # This must precede flags.includes. + clang_cmd.append('-I' + os.path.dirname(flags.input)) + if flags.show_includes: + clang_cmd.append('/showIncludes') + clang_cmd += flags.imsvcs + flags.winsysroot + flags.includes + flags.defines + p = subprocess.Popen(clang_cmd, stdin=subprocess.PIPE) + p.communicate(input=rc_file_data) + if p.returncode != 0: + sys.exit(p.returncode) + preprocessed_output = open(temp_file, 'rb').read() + os.remove(temp_file) + + # rc.exe has a wacko preprocessor: + # https://msdn.microsoft.com/en-us/library/windows/desktop/aa381033(v=vs.85).aspx + # """RC treats files with the .c and .h extensions in a special manner. It + # assumes that a file with one of these extensions does not contain + # resources. If a file has the .c or .h file name extension, RC ignores all + # lines in the file except the preprocessor directives.""" + # Thankfully, the Microsoft headers are mostly good about putting everything + # in the system headers behind `if !defined(RC_INVOKED)`, so regular + # preprocessing with RC_INVOKED defined works. + return preprocessed_output + + +def RunRc(preprocessed_output, is_utf8, flags): + if sys.platform.startswith('linux'): + rc = os.path.join(THIS_DIR, 'linux64', 'rc') + elif sys.platform == 'darwin': + rc = os.path.join(THIS_DIR, 'mac', 'rc') + elif sys.platform == 'win32': + rc = os.path.join(THIS_DIR, 'win', 'rc.exe') + else: + print('rc.py: error: unsupported platform', sys.platform, file=sys.stderr) + sys.exit(1) + rc_cmd = [rc] + # Make sure rc-relative resources can be found: + if os.path.dirname(flags.input): + rc_cmd.append('/cd' + os.path.dirname(flags.input)) + rc_cmd.append('/fo' + flags.output) + if is_utf8: + rc_cmd.append('/utf-8') + # TODO(thakis): cl currently always prints full paths for /showIncludes, + # but clang-cl /P doesn't. Which one is right? + if flags.show_includes: + rc_cmd.append('/showIncludes') + # Microsoft rc.exe searches for referenced files relative to -I flags in + # addition to the pwd, so -I flags need to be passed both to both + # the preprocessor and rc. + rc_cmd += flags.includes + p = subprocess.Popen(rc_cmd, stdin=subprocess.PIPE) + p.communicate(input=preprocessed_output) + + if flags.show_includes and p.returncode == 0: + TOOL_DIR = os.path.dirname(os.path.relpath(THIS_DIR)).replace("\\", "/") + # Since tool("rc") can't have deps, add deps on this script and on rc.py + # and its deps here, so that rc edges become dirty if rc.py changes. + print('Note: including file: {}/tool_wrapper.py'.format(TOOL_DIR)) + print('Note: including file: {}/rc/rc.py'.format(TOOL_DIR)) + print( + 'Note: including file: {}/rc/linux64/rc.sha1'.format(TOOL_DIR)) + print('Note: including file: {}/rc/mac/rc.sha1'.format(TOOL_DIR)) + print( + 'Note: including file: {}/rc/win/rc.exe.sha1'.format(TOOL_DIR)) + + return p.returncode + + +def CompareToMsRcOutput(preprocessed_output, is_utf8, flags): + msrc_in = flags.output + '.preprocessed.rc' + + # Strip preprocessor line markers. + preprocessed_output = re.sub(br'^#.*$', b'', preprocessed_output, flags=re.M) + if is_utf8: + preprocessed_output = preprocessed_output.decode('utf-8').encode('utf-16le') + with open(msrc_in, 'wb') as f: + f.write(preprocessed_output) + + msrc_out = flags.output + '_ms_rc' + msrc_cmd = ['rc', '/nologo', '/x', '/fo' + msrc_out] + + # Make sure rc-relative resources can be found. rc.exe looks for external + # resource files next to the file, but the preprocessed file isn't where the + # input was. + # Note that rc searches external resource files in the order of + # 1. next to the input file + # 2. relative to cwd + # 3. next to -I directories + # Changing the cwd means we'd have to rewrite all -I flags, so just add + # the input file dir as -I flag. That technically gets the order of 1 and 2 + # wrong, but in Chromium's build the cwd is the gn out dir, and generated + # files there are in obj/ and gen/, so this difference doesn't matter in + # practice. + if os.path.dirname(flags.input): + msrc_cmd += [ '-I' + os.path.dirname(flags.input) ] + + # Microsoft rc.exe searches for referenced files relative to -I flags in + # addition to the pwd, so -I flags need to be passed both to both + # the preprocessor and rc. + msrc_cmd += flags.includes + + # Input must come last. + msrc_cmd += [ msrc_in ] + + rc_exe_exit_code = subprocess.call(msrc_cmd) + # Assert Microsoft rc.exe and rc.py produced identical .res files. + if rc_exe_exit_code == 0: + import filecmp + assert filecmp.cmp(msrc_out, flags.output) + return rc_exe_exit_code + + +def main(): + # This driver has to do these things: + # 1. Parse flags. + # 2. Convert the input from UTF-16LE to UTF-8 if needed. + # 3. Pass the input through a preprocessor (and clean up the preprocessor's + # output in minor ways). + # 4. Call rc for the heavy lifting. + flags = ParseFlags() + rc_file_data, is_utf8 = ReadInput(flags.input) + preprocessed_output = Preprocess(rc_file_data, flags) + rc_exe_exit_code = RunRc(preprocessed_output, is_utf8, flags) + + # 5. On Windows, we also call Microsoft's rc.exe and check that we produced + # the same output. + # Since Microsoft's rc has a preprocessor that only accepts 32 characters + # for macro names, feed the clang-preprocessed source into it instead + # of using ms rc's preprocessor. + if sys.platform == 'win32' and rc_exe_exit_code == 0: + rc_exe_exit_code = CompareToMsRcOutput(preprocessed_output, is_utf8, flags) + + return rc_exe_exit_code + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/third_party/libwebrtc/build/toolchain/win/rc/upload_rc_binaries.sh b/third_party/libwebrtc/build/toolchain/win/rc/upload_rc_binaries.sh new file mode 100755 index 0000000000..ec4df4cbce --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/win/rc/upload_rc_binaries.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# Copyright 2017 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. +set -eu + +# Builds new rc binaries at head and uploads them to google storage. +# The new .sha1 files will be in the tree after this has run. + +if [[ "$OSTYPE" != "darwin"* ]]; then + echo "this script must run on a mac" + exit 1 +fi + +DIR="$(cd "$(dirname "${0}" )" && pwd)" +SRC_DIR="$DIR/../../../.." + +# Make sure Linux and Windows sysroots are installed, for distrib.py. +$SRC_DIR/build/linux/sysroot_scripts/install-sysroot.py --arch amd64 +$SRC_DIR/build/vs_toolchain.py update --force + +# Make a temporary directory. +WORK_DIR=$(mktemp -d) +if [[ ! "$WORK_DIR" || ! -d "$WORK_DIR" ]]; then + echo "could not create temp dir" + exit 1 +fi +function cleanup { + rm -rf "$WORK_DIR" +} +trap cleanup EXIT + +# Check out rc and build it in the temporary directory. Copy binaries over. +pushd "$WORK_DIR" > /dev/null +git clone -q https://github.com/nico/hack +cd hack/res +./distrib.py "$SRC_DIR" +popd > /dev/null +cp "$WORK_DIR/hack/res/rc-linux64" "$DIR/linux64/rc" +cp "$WORK_DIR/hack/res/rc-mac" "$DIR/mac/rc" +cp "$WORK_DIR/hack/res/rc-win.exe" "$DIR/win/rc.exe" + +# Upload binaries to cloud storage. +upload_to_google_storage.py -b chromium-browser-clang/rc "$DIR/linux64/rc" +upload_to_google_storage.py -b chromium-browser-clang/rc "$DIR/mac/rc" +upload_to_google_storage.py -b chromium-browser-clang/rc "$DIR/win/rc.exe" diff --git a/third_party/libwebrtc/build/toolchain/win/rc/win/rc.exe.sha1 b/third_party/libwebrtc/build/toolchain/win/rc/win/rc.exe.sha1 new file mode 100644 index 0000000000..3fdbfc0c20 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/win/rc/win/rc.exe.sha1 @@ -0,0 +1 @@ +ba51d69039ffb88310b72b6568efa9f0de148f8f
\ No newline at end of file diff --git a/third_party/libwebrtc/build/toolchain/win/setup_toolchain.py b/third_party/libwebrtc/build/toolchain/win/setup_toolchain.py new file mode 100644 index 0000000000..1ff3608f0c --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/win/setup_toolchain.py @@ -0,0 +1,314 @@ +# Copyright (c) 2013 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. +# +# Copies the given "win tool" (which the toolchain uses to wrap compiler +# invocations) and the environment blocks for the 32-bit and 64-bit builds on +# Windows to the build directory. +# +# The arguments are the visual studio install location and the location of the +# win tool. The script assumes that the root build directory is the current dir +# and the files will be written to the current directory. + +from __future__ import print_function + +import errno +import json +import os +import re +import subprocess +import sys + +sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) +import gn_helpers + +SCRIPT_DIR = os.path.dirname(__file__) + +def _ExtractImportantEnvironment(output_of_set): + """Extracts environment variables required for the toolchain to run from + a textual dump output by the cmd.exe 'set' command.""" + envvars_to_save = ( + 'cipd_cache_dir', # needed by vpython + 'homedrive', # needed by vpython + 'homepath', # needed by vpython + 'goma_.*', # TODO(scottmg): This is ugly, but needed for goma. + 'include', + 'lib', + 'libpath', + 'luci_context', # needed by vpython + 'path', + 'pathext', + 'systemroot', + 'temp', + 'tmp', + 'userprofile', # needed by vpython + 'vpython_virtualenv_root' # needed by vpython + ) + env = {} + # This occasionally happens and leads to misleading SYSTEMROOT error messages + # if not caught here. + if output_of_set.count('=') == 0: + raise Exception('Invalid output_of_set. Value is:\n%s' % output_of_set) + for line in output_of_set.splitlines(): + for envvar in envvars_to_save: + if re.match(envvar + '=', line.lower()): + var, setting = line.split('=', 1) + if envvar == 'path': + # Our own rules and actions in Chromium rely on python being in the + # path. Add the path to this python here so that if it's not in the + # path when ninja is run later, python will still be found. + setting = os.path.dirname(sys.executable) + os.pathsep + setting + env[var.upper()] = setting + break + if sys.platform in ('win32', 'cygwin'): + for required in ('SYSTEMROOT', 'TEMP', 'TMP'): + if required not in env: + raise Exception('Environment variable "%s" ' + 'required to be set to valid path' % required) + return env + + +def _DetectVisualStudioPath(): + """Return path to the installed Visual Studio. + """ + + # Use the code in build/vs_toolchain.py to avoid duplicating code. + chromium_dir = os.path.abspath(os.path.join(SCRIPT_DIR, '..', '..', '..')) + sys.path.append(os.path.join(chromium_dir, 'build')) + import vs_toolchain + return vs_toolchain.DetectVisualStudioPath() + + +def _LoadEnvFromBat(args): + """Given a bat command, runs it and returns env vars set by it.""" + args = args[:] + args.extend(('&&', 'set')) + popen = subprocess.Popen( + args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + variables, _ = popen.communicate() + if popen.returncode != 0: + raise Exception('"%s" failed with error %d' % (args, popen.returncode)) + return variables.decode(errors='ignore') + + +def _LoadToolchainEnv(cpu, toolchain_root, sdk_dir, target_store): + """Returns a dictionary with environment variables that must be set while + running binaries from the toolchain (e.g. INCLUDE and PATH for cl.exe).""" + # Check if we are running in the SDK command line environment and use + # the setup script from the SDK if so. |cpu| should be either + # 'x86' or 'x64' or 'arm' or 'arm64'. + assert cpu in ('x86', 'x64', 'arm', 'arm64') + if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and sdk_dir: + # Load environment from json file. + env = os.path.normpath(os.path.join(sdk_dir, 'bin/SetEnv.%s.json' % cpu)) + env = json.load(open(env))['env'] + if env['VSINSTALLDIR'] == [["..", "..\\"]]: + # Old-style paths were relative to the win_sdk\bin directory. + json_relative_dir = os.path.join(sdk_dir, 'bin') + else: + # New-style paths are relative to the toolchain directory. + json_relative_dir = toolchain_root + for k in env: + entries = [os.path.join(*([json_relative_dir] + e)) for e in env[k]] + # clang-cl wants INCLUDE to be ;-separated even on non-Windows, + # lld-link wants LIB to be ;-separated even on non-Windows. Path gets :. + # The separator for INCLUDE here must match the one used in main() below. + sep = os.pathsep if k == 'PATH' else ';' + env[k] = sep.join(entries) + # PATH is a bit of a special case, it's in addition to the current PATH. + env['PATH'] = env['PATH'] + os.pathsep + os.environ['PATH'] + # Augment with the current env to pick up TEMP and friends. + for k in os.environ: + if k not in env: + env[k] = os.environ[k] + + varlines = [] + for k in sorted(env.keys()): + varlines.append('%s=%s' % (str(k), str(env[k]))) + variables = '\n'.join(varlines) + + # Check that the json file contained the same environment as the .cmd file. + if sys.platform in ('win32', 'cygwin'): + script = os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.cmd')) + arg = '/' + cpu + json_env = _ExtractImportantEnvironment(variables) + cmd_env = _ExtractImportantEnvironment(_LoadEnvFromBat([script, arg])) + assert _LowercaseDict(json_env) == _LowercaseDict(cmd_env) + else: + if 'GYP_MSVS_OVERRIDE_PATH' not in os.environ: + os.environ['GYP_MSVS_OVERRIDE_PATH'] = _DetectVisualStudioPath() + # We only support x64-hosted tools. + script_path = os.path.normpath(os.path.join( + os.environ['GYP_MSVS_OVERRIDE_PATH'], + 'VC/vcvarsall.bat')) + if not os.path.exists(script_path): + # vcvarsall.bat for VS 2017 fails if run after running vcvarsall.bat from + # VS 2013 or VS 2015. Fix this by clearing the vsinstalldir environment + # variable. Since vcvarsall.bat appends to the INCLUDE, LIB, and LIBPATH + # environment variables we need to clear those to avoid getting double + # entries when vcvarsall.bat has been run before gn gen. vcvarsall.bat + # also adds to PATH, but there is no clean way of clearing that and it + # doesn't seem to cause problems. + if 'VSINSTALLDIR' in os.environ: + del os.environ['VSINSTALLDIR'] + del os.environ['INCLUDE'] + del os.environ['LIB'] + del os.environ['LIBPATH'] + other_path = os.path.normpath(os.path.join( + os.environ['GYP_MSVS_OVERRIDE_PATH'], + 'VC/Auxiliary/Build/vcvarsall.bat')) + if not os.path.exists(other_path): + raise Exception('%s is missing - make sure VC++ tools are installed.' % + script_path) + script_path = other_path + cpu_arg = "amd64" + if (cpu != 'x64'): + # x64 is default target CPU thus any other CPU requires a target set + cpu_arg += '_' + cpu + args = [script_path, cpu_arg, ] + # Store target must come before any SDK version declaration + if (target_store): + args.append('store') + # Explicitly specifying the SDK version to build with to avoid accidentally + # building with a new and untested SDK. This should stay in sync with the + # packaged toolchain in build/vs_toolchain.py. + args.append('10.0.19041.0') + variables = _LoadEnvFromBat(args) + return _ExtractImportantEnvironment(variables) + + +def _FormatAsEnvironmentBlock(envvar_dict): + """Format as an 'environment block' directly suitable for CreateProcess. + Briefly this is a list of key=value\0, terminated by an additional \0. See + CreateProcess documentation for more details.""" + block = '' + nul = '\0' + for key, value in envvar_dict.items(): + block += key + '=' + value + nul + block += nul + return block + + +def _LowercaseDict(d): + """Returns a copy of `d` with both key and values lowercased. + + Args: + d: dict to lowercase (e.g. {'A': 'BcD'}). + + Returns: + A dict with both keys and values lowercased (e.g.: {'a': 'bcd'}). + """ + return {k.lower(): d[k].lower() for k in d} + + +def FindFileInEnvList(env, env_name, separator, file_name, optional=False): + parts = env[env_name].split(separator) + for path in parts: + if os.path.exists(os.path.join(path, file_name)): + return os.path.realpath(path) + assert optional, "%s is not found in %s:\n%s\nCheck if it is installed." % ( + file_name, env_name, '\n'.join(parts)) + return '' + + +def main(): + if len(sys.argv) != 7: + print('Usage setup_toolchain.py ' + '<visual studio path> <win sdk path> ' + '<runtime dirs> <target_os> <target_cpu> ' + '<environment block name|none>') + sys.exit(2) + # toolchain_root and win_sdk_path are only read if the hermetic Windows + # toolchain is set, that is if DEPOT_TOOLS_WIN_TOOLCHAIN is not set to 0. + # With the hermetic Windows toolchain, the visual studio path in argv[1] + # is the root of the Windows toolchain directory. + toolchain_root = sys.argv[1] + win_sdk_path = sys.argv[2] + + runtime_dirs = sys.argv[3] + target_os = sys.argv[4] + target_cpu = sys.argv[5] + environment_block_name = sys.argv[6] + if (environment_block_name == 'none'): + environment_block_name = '' + + if (target_os == 'winuwp'): + target_store = True + else: + target_store = False + + cpus = ('x86', 'x64', 'arm', 'arm64') + assert target_cpu in cpus + vc_bin_dir = 'fake_path/cl.exe' + vc_lib_path = 'fake_path/lib' + vc_lib_atlmfc_path = 'fake_path/atlmfc' + vc_lib_um_path = 'fake_path/lib_um' + include = '' + lib = '' + + # TODO(scottmg|goma): Do we need an equivalent of + # ninja_use_custom_environment_files? + + def relflag(s): # Make s relative to builddir when cwd and sdk on same drive. + try: + return os.path.relpath(s).replace('\\', '/') + except ValueError: + return s + + def q(s): # Quote s if it contains spaces or other weird characters. + return s if re.match(r'^[a-zA-Z0-9._/\\:-]*$', s) else '"' + s + '"' + +# for cpu in cpus: +# if cpu == target_cpu: +# # Extract environment variables for subprocesses. +# env = _LoadToolchainEnv(cpu, toolchain_root, win_sdk_path, target_store) +# env['PATH'] = runtime_dirs + os.pathsep + env['PATH'] +# +# vc_bin_dir = FindFileInEnvList(env, 'PATH', os.pathsep, 'cl.exe') +# vc_lib_path = FindFileInEnvList(env, 'LIB', ';', 'msvcrt.lib') +# vc_lib_atlmfc_path = FindFileInEnvList( +# env, 'LIB', ';', 'atls.lib', optional=True) +# vc_lib_um_path = FindFileInEnvList(env, 'LIB', ';', 'user32.lib') +# +# # The separator for INCLUDE here must match the one used in +# # _LoadToolchainEnv() above. +# include = [p.replace('"', r'\"') for p in env['INCLUDE'].split(';') if p] +# include = list(map(relflag, include)) +# +# lib = [p.replace('"', r'\"') for p in env['LIB'].split(';') if p] +# lib = list(map(relflag, lib)) +# +# include_I = ' '.join([q('/I' + i) for i in include]) +# include_imsvc = ' '.join([q('-imsvc' + i) for i in include]) +# libpath_flags = ' '.join([q('-libpath:' + i) for i in lib]) +# +# if (environment_block_name != ''): +# env_block = _FormatAsEnvironmentBlock(env) +# with open(environment_block_name, 'w') as f: +# f.write(env_block) + +# We don't really use any of this information so it can be skipped altogether + env = {} + env['PATH'] = '' + include_I = include + include_imsvc = include + libpath_flags = '' + print('vc_bin_dir = ' + gn_helpers.ToGNString(vc_bin_dir)) + print('include_flags_I = ' + gn_helpers.ToGNString(include_I)) + if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and win_sdk_path: + print('include_flags_imsvc = ' + + gn_helpers.ToGNString(q('/winsysroot' + relflag(toolchain_root)))) + else: + print('include_flags_imsvc = ' + gn_helpers.ToGNString(include_imsvc)) + print('vc_lib_path = ' + gn_helpers.ToGNString(vc_lib_path)) + # Possible atlmfc library path gets introduced in the future for store thus + # output result if a result exists. + if (vc_lib_atlmfc_path != ''): + print('vc_lib_atlmfc_path = ' + gn_helpers.ToGNString(vc_lib_atlmfc_path)) + print('vc_lib_um_path = ' + gn_helpers.ToGNString(vc_lib_um_path)) + print('paths = ' + gn_helpers.ToGNString(env['PATH'])) + print('libpath_flags = ' + gn_helpers.ToGNString(libpath_flags)) + + +if __name__ == '__main__': + main() diff --git a/third_party/libwebrtc/build/toolchain/win/tool_wrapper.py b/third_party/libwebrtc/build/toolchain/win/tool_wrapper.py new file mode 100644 index 0000000000..9327369181 --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/win/tool_wrapper.py @@ -0,0 +1,190 @@ +# Copyright (c) 2012 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. + +"""Utility functions for Windows builds. + +This file is copied to the build directory as part of toolchain setup and +is used to set up calls to tools used by the build that need wrappers. +""" + +from __future__ import print_function + +import os +import re +import shutil +import subprocess +import stat +import sys + + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + +# A regex matching an argument corresponding to the output filename passed to +# link.exe. +_LINK_EXE_OUT_ARG = re.compile('/OUT:(?P<out>.+)$', re.IGNORECASE) + +def main(args): + exit_code = WinTool().Dispatch(args) + if exit_code is not None: + sys.exit(exit_code) + + +class WinTool(object): + """This class performs all the Windows tooling steps. The methods can either + be executed directly, or dispatched from an argument list.""" + + def _UseSeparateMspdbsrv(self, env, args): + """Allows to use a unique instance of mspdbsrv.exe per linker instead of a + shared one.""" + if len(args) < 1: + raise Exception("Not enough arguments") + + if args[0] != 'link.exe': + return + + # Use the output filename passed to the linker to generate an endpoint name + # for mspdbsrv.exe. + endpoint_name = None + for arg in args: + m = _LINK_EXE_OUT_ARG.match(arg) + if m: + endpoint_name = re.sub(r'\W+', '', + '%s_%d' % (m.group('out'), os.getpid())) + break + + if endpoint_name is None: + return + + # Adds the appropriate environment variable. This will be read by link.exe + # to know which instance of mspdbsrv.exe it should connect to (if it's + # not set then the default endpoint is used). + env['_MSPDBSRV_ENDPOINT_'] = endpoint_name + + def Dispatch(self, args): + """Dispatches a string command to a method.""" + if len(args) < 1: + raise Exception("Not enough arguments") + + method = "Exec%s" % self._CommandifyName(args[0]) + return getattr(self, method)(*args[1:]) + + def _CommandifyName(self, name_string): + """Transforms a tool name like recursive-mirror to RecursiveMirror.""" + return name_string.title().replace('-', '') + + def _GetEnv(self, arch): + """Gets the saved environment from a file for a given architecture.""" + # The environment is saved as an "environment block" (see CreateProcess + # and msvs_emulation for details). We convert to a dict here. + # Drop last 2 NULs, one for list terminator, one for trailing vs. separator. + pairs = open(arch).read()[:-2].split('\0') + kvs = [item.split('=', 1) for item in pairs] + return dict(kvs) + + def ExecDeleteFile(self, path): + """Simple file delete command.""" + if os.path.exists(path): + os.unlink(path) + + def ExecRecursiveMirror(self, source, dest): + """Emulation of rm -rf out && cp -af in out.""" + if os.path.exists(dest): + if os.path.isdir(dest): + def _on_error(fn, path, dummy_excinfo): + # The operation failed, possibly because the file is set to + # read-only. If that's why, make it writable and try the op again. + if not os.access(path, os.W_OK): + os.chmod(path, stat.S_IWRITE) + fn(path) + shutil.rmtree(dest, onerror=_on_error) + else: + if not os.access(dest, os.W_OK): + # Attempt to make the file writable before deleting it. + os.chmod(dest, stat.S_IWRITE) + os.unlink(dest) + + if os.path.isdir(source): + shutil.copytree(source, dest) + else: + shutil.copy2(source, dest) + # Try to diagnose crbug.com/741603 + if not os.path.exists(dest): + raise Exception("Copying of %s to %s failed" % (source, dest)) + + def ExecLinkWrapper(self, arch, use_separate_mspdbsrv, *args): + """Filter diagnostic output from link that looks like: + ' Creating library ui.dll.lib and object ui.dll.exp' + This happens when there are exports from the dll or exe. + """ + env = self._GetEnv(arch) + if use_separate_mspdbsrv == 'True': + self._UseSeparateMspdbsrv(env, args) + if sys.platform == 'win32': + args = list(args) # *args is a tuple by default, which is read-only. + args[0] = args[0].replace('/', '\\') + # https://docs.python.org/2/library/subprocess.html: + # "On Unix with shell=True [...] if args is a sequence, the first item + # specifies the command string, and any additional items will be treated as + # additional arguments to the shell itself. That is to say, Popen does the + # equivalent of: + # Popen(['/bin/sh', '-c', args[0], args[1], ...])" + # For that reason, since going through the shell doesn't seem necessary on + # non-Windows don't do that there. + pe_name = None + for arg in args: + m = _LINK_EXE_OUT_ARG.match(arg) + if m: + pe_name = m.group('out') + link = subprocess.Popen(args, shell=sys.platform == 'win32', env=env, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + # Read output one line at a time as it shows up to avoid OOM failures when + # GBs of output is produced. + for line in link.stdout: + if (not line.startswith(b' Creating library ') + and not line.startswith(b'Generating code') + and not line.startswith(b'Finished generating code')): + print(line) + return link.wait() + + def ExecAsmWrapper(self, arch, *args): + """Filter logo banner from invocations of asm.exe.""" + env = self._GetEnv(arch) + if sys.platform == 'win32': + # Windows ARM64 uses clang-cl as assembler which has '/' as path + # separator, convert it to '\\' when running on Windows. + args = list(args) # *args is a tuple by default, which is read-only + args[0] = args[0].replace('/', '\\') + popen = subprocess.Popen(args, shell=True, env=env, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + out, _ = popen.communicate() + for line in out.decode('utf8').splitlines(): + if not line.startswith(' Assembling: '): + print(line) + return popen.returncode + + def ExecRcWrapper(self, arch, *args): + """Converts .rc files to .res files.""" + env = self._GetEnv(arch) + args = list(args) + rcpy_args = args[:] + rcpy_args[0:1] = [sys.executable, os.path.join(BASE_DIR, 'rc', 'rc.py')] + rcpy_args.append('/showIncludes') + return subprocess.call(rcpy_args, env=env) + + def ExecActionWrapper(self, arch, rspfile, *dirname): + """Runs an action command line from a response file using the environment + for |arch|. If |dirname| is supplied, use that as the working directory.""" + env = self._GetEnv(arch) + # TODO(scottmg): This is a temporary hack to get some specific variables + # through to actions that are set after GN-time. http://crbug.com/333738. + for k, v in os.environ.items(): + if k not in env: + env[k] = v + args = open(rspfile).read() + dirname = dirname[0] if dirname else None + return subprocess.call(args, shell=True, env=env, cwd=dirname) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/third_party/libwebrtc/build/toolchain/wrapper_utils.py b/third_party/libwebrtc/build/toolchain/wrapper_utils.py new file mode 100644 index 0000000000..5949a3727c --- /dev/null +++ b/third_party/libwebrtc/build/toolchain/wrapper_utils.py @@ -0,0 +1,93 @@ +# Copyright (c) 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. + +"""Helper functions for gcc_toolchain.gni wrappers.""" + +import gzip +import os +import re +import subprocess +import shlex +import shutil +import sys +import threading + +_BAT_PREFIX = 'cmd /c call ' + + +def _GzipThenDelete(src_path, dest_path): + # Results for Android map file with GCC on a z620: + # Uncompressed: 207MB + # gzip -9: 16.4MB, takes 8.7 seconds. + # gzip -1: 21.8MB, takes 2.0 seconds. + # Piping directly from the linker via -print-map (or via -Map with a fifo) + # adds a whopping 30-45 seconds! + with open(src_path, 'rb') as f_in, gzip.GzipFile(dest_path, 'wb', 1) as f_out: + shutil.copyfileobj(f_in, f_out) + os.unlink(src_path) + + +def CommandToRun(command): + """Generates commands compatible with Windows. + + When running on a Windows host and using a toolchain whose tools are + actually wrapper scripts (i.e. .bat files on Windows) rather than binary + executables, the |command| to run has to be prefixed with this magic. + The GN toolchain definitions take care of that for when GN/Ninja is + running the tool directly. When that command is passed in to this + script, it appears as a unitary string but needs to be split up so that + just 'cmd' is the actual command given to Python's subprocess module. + + Args: + command: List containing the UNIX style |command|. + + Returns: + A list containing the Windows version of the |command|. + """ + if command[0].startswith(_BAT_PREFIX): + command = command[0].split(None, 3) + command[1:] + return command + + +def RunLinkWithOptionalMapFile(command, env=None, map_file=None): + """Runs the given command, adding in -Wl,-Map when |map_file| is given. + + Also takes care of gzipping when |map_file| ends with .gz. + + Args: + command: List of arguments comprising the command. + env: Environment variables. + map_file: Path to output map_file. + + Returns: + The exit code of running |command|. + """ + tmp_map_path = None + if map_file and map_file.endswith('.gz'): + tmp_map_path = map_file + '.tmp' + command.append('-Wl,-Map,' + tmp_map_path) + elif map_file: + command.append('-Wl,-Map,' + map_file) + + result = subprocess.call(command, env=env) + + if tmp_map_path and result == 0: + threading.Thread( + target=lambda: _GzipThenDelete(tmp_map_path, map_file)).start() + elif tmp_map_path and os.path.exists(tmp_map_path): + os.unlink(tmp_map_path) + + return result + + +def CaptureCommandStderr(command, env=None): + """Returns the stderr of a command. + + Args: + command: A list containing the command and arguments. + env: Environment variables for the new process. + """ + child = subprocess.Popen(command, stderr=subprocess.PIPE, env=env) + _, stderr = child.communicate() + return child.returncode, stderr |