├── .gitattributes ├── .gitignore ├── toolchain ├── win │ ├── stamp.py │ ├── recursive_mirror.py │ ├── asm_wrapper.py │ ├── message_compiler.py │ ├── midl_wrapper.py │ ├── link_wrapper.py │ ├── rc_wrapper.py │ ├── message_compiler.gni │ ├── midl.gni │ ├── manifest.gni │ └── settings.gni ├── clang_static_analyzer.gni ├── BUILD.gn ├── mac │ ├── mac_sdk_overrides.gni │ ├── settings.gni │ ├── BUILD.gn │ ├── find_sdk.py │ └── mac_sdk.gni ├── apple │ ├── get_tool_mtime.py │ ├── BUILD.gn │ ├── symbols.gni │ ├── filter_libtool.py │ ├── sdk_info.py │ └── linker_driver.py ├── concurrent_links.gni ├── clang.gni ├── cc_wrapper.gni ├── posix │ ├── settings.gni │ ├── toolchain.py │ └── BUILD.gn ├── gcc_compile_wrapper.py ├── coverage.gni ├── sysroot.gni ├── toolchain.gni ├── gcc_ar_wrapper.py ├── compiler_version.gni ├── clang_static_analyzer_wrapper.py ├── gcc_link_wrapper.py ├── gcc_solink_wrapper.py ├── android │ ├── BUILD.gn │ └── settings.gni └── wrapper_utils.py ├── apple ├── README.md ├── convert_plist.gni ├── xcrun.py ├── compile_entitlements.gni ├── write_pkg_info.py ├── apple_info_plist.gni ├── compile_plist.gni └── plist_util.py ├── mac ├── BuildInfo.plist ├── prepare_framework_version.py └── package_framework.py ├── gn_run_binary.py ├── config ├── mips.gni ├── ios │ └── BUILD.gn ├── posix │ └── BUILD.gn ├── precompiled_header.gni ├── compiler.gni ├── libc++ │ ├── settings.gni │ └── BUILD.gn ├── arm.gni ├── mac │ └── BUILD.gn ├── android │ └── BUILD.gn └── sanitizers │ └── sanitizers.gni ├── .github └── workflows │ ├── windows.yml │ └── posix.yml ├── compiled_action.gni ├── README.md └── gn_helpers_unittest.py /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.py text 4 | *.gn text 5 | *.gni text 6 | 7 | *.sh text eol=lf 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python artifacts (bytecode, PyCharm projects, ...) 2 | /.idea 3 | /__pycache__ 4 | *.pyc 5 | -------------------------------------------------------------------------------- /toolchain/win/stamp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | 4 | def main(path): 5 | """Simple stamp command.""" 6 | open(path, 'w').close() 7 | 8 | if __name__ == '__main__': 9 | sys.exit(main(*sys.argv[1:])) 10 | -------------------------------------------------------------------------------- /toolchain/clang_static_analyzer.gni: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # Defines the configuration of Clang static analysis tools. 6 | # See docs/clang_static_analyzer.md for more information. 7 | 8 | declare_args() { 9 | # Uses the Clang static analysis tools during compilation. 10 | use_clang_static_analyzer = false 11 | } 12 | -------------------------------------------------------------------------------- /toolchain/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import("//build/toolchain/concurrent_links.gni") 6 | 7 | declare_args() { 8 | # Pool for action tasks. 9 | action_pool_depth = 0 10 | } 11 | 12 | if (current_toolchain == default_toolchain) { 13 | pool("link_pool") { 14 | depth = concurrent_links 15 | } 16 | 17 | pool("action_pool") { 18 | depth = action_pool_depth 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apple/README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | `//build/apple` contains: 4 | * GN templates and configurations shared by Apple platforms 5 | * Python build scripts shared by Apple platforms 6 | 7 | This directory should only contain templates, configurations and scripts 8 | that are used exclusively on Apple platforms (currently iOS and macOS). 9 | They must also be independent of the specific platform. 10 | 11 | If a template, configuration or script is limited to only iOS or macOS, 12 | then they should instead be located in `//build/ios` or `//build/mac`. 13 | -------------------------------------------------------------------------------- /mac/BuildInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DTCompiler 6 | ${GCC_VERSION} 7 | DTSDKBuild 8 | ${MAC_SDK_BUILD} 9 | DTSDKName 10 | ${MAC_SDK_NAME} 11 | DTXcode 12 | ${XCODE_VERSION} 13 | DTXcodeBuild 14 | ${XCODE_BUILD} 15 | 16 | 17 | -------------------------------------------------------------------------------- /toolchain/mac/mac_sdk_overrides.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # This file contains arguments that subprojects may choose to override. It 6 | # asserts that those overrides are used, to prevent unused args warnings. 7 | 8 | _sdk_min_from_env = getenv("FORCE_MAC_SDK_MIN") 9 | declare_args() { 10 | # Minimum supported version of the Mac SDK. 11 | if (_sdk_min_from_env == "") { 12 | mac_sdk_min = "10.10" 13 | } else { 14 | mac_sdk_min = _sdk_min_from_env 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /toolchain/apple/get_tool_mtime.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | from __future__ import print_function 6 | 7 | import os 8 | import sys 9 | 10 | # Usage: python get_tool_mtime.py path/to/file1.py path/to/file2.py 11 | # 12 | # Prints a GN scope with the variable name being the basename sans-extension 13 | # and the value being the file modification time. A variable is emitted for 14 | # each file argument on the command line. 15 | 16 | if __name__ == '__main__': 17 | for f in sys.argv[1:]: 18 | variable = os.path.splitext(os.path.basename(f))[0] 19 | print('%s = %d' % (variable, os.path.getmtime(f))) 20 | -------------------------------------------------------------------------------- /toolchain/apple/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import("//build/toolchain/concurrent_links.gni") 6 | 7 | declare_args() { 8 | # Reduce the number of tasks using the copy_bundle_data and compile_xcassets 9 | # tools as they can cause lots of I/O contention when invoking ninja with a 10 | # large number of parallel jobs (e.g. when using distributed build like goma). 11 | bundle_pool_depth = -1 12 | } 13 | 14 | if (current_toolchain == default_toolchain) { 15 | pool("bundle_pool") { 16 | if (bundle_pool_depth == -1) { 17 | depth = concurrent_links 18 | } else { 19 | depth = bundle_pool_depth 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /toolchain/mac/settings.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2014 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # These variables used to live here 6 | import("//build/toolchain/apple/symbols.gni") 7 | 8 | declare_args() { 9 | # Use the system install of Xcode for tools like ibtool, libtool, etc. 10 | # This does not affect the compiler. When this variable is false, targets will 11 | # instead use a hermetic install of Xcode. 12 | use_system_xcode = true 13 | 14 | # The path to the hermetic install of Xcode. Only relevant when 15 | # use_system_xcode = false. 16 | hermetic_xcode_path = "" 17 | 18 | # Compile with Xcode version of clang instead of hermetic version shipped 19 | # with the build. 20 | use_xcode_clang = true 21 | } 22 | -------------------------------------------------------------------------------- /toolchain/concurrent_links.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # This file should only be imported from files that define toolchains. 6 | # There's no way to enforce this exactly, but all toolchains are processed 7 | # in the context of the default_toolchain, so we can at least check for that. 8 | assert(current_toolchain == default_toolchain) 9 | 10 | declare_args() { 11 | # Limit the number of concurrent links; we often want to run fewer 12 | # links at once than we do compiles, because linking is memory-intensive. 13 | # The default to use varies by platform and by the amount of memory 14 | # available, so we call out to a script to get the right value. 15 | concurrent_links = 0 16 | } 17 | -------------------------------------------------------------------------------- /toolchain/win/recursive_mirror.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import shutil 4 | import sys 5 | 6 | def main(source, dest): 7 | """Emulation of rm -rf out && cp -af in out.""" 8 | if os.path.exists(dest): 9 | if os.path.isdir(dest): 10 | def _on_error(fn, path, excinfo): 11 | # The operation failed, possibly because the file is set to 12 | # read-only. If that's why, make it writable and try the op again. 13 | if not os.access(path, os.W_OK): 14 | os.chmod(path, stat.S_IWRITE) 15 | fn(path) 16 | shutil.rmtree(dest, onerror=_on_error) 17 | else: 18 | if not os.access(dest, os.W_OK): 19 | # Attempt to make the file writable before deleting it. 20 | os.chmod(dest, stat.S_IWRITE) 21 | os.unlink(dest) 22 | 23 | if os.path.isdir(source): 24 | shutil.copytree(source, dest) 25 | else: 26 | shutil.copy2(source, dest) 27 | 28 | if __name__ == '__main__': 29 | sys.exit(main(*sys.argv[1:])) 30 | -------------------------------------------------------------------------------- /toolchain/clang.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # Toolchain-related configuration that may be needed outside the context of the 6 | # toolchain() rules themselves. 7 | 8 | declare_args() { 9 | # The path of your Clang installation folder (without /bin). 10 | clang_base_path = "" 11 | 12 | # Set to true to use lld, the LLVM linker. 13 | use_lld = false 14 | 15 | # Enables the experimental support of ThinLTO that links 3x-10x faster but 16 | # (as of now) does not have all the important optimizations such us 17 | # devirtualization implemented. See also 18 | # http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html 19 | # TODO(tim): Supported on Windows? 20 | use_thin_lto = false 21 | } 22 | 23 | if (is_win && is_clang) { 24 | assert(clang_base_path != "", 25 | "You must set clang_base_path when using Clang on Windows") 26 | } 27 | -------------------------------------------------------------------------------- /toolchain/cc_wrapper.gni: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # Defines the configuration of cc wrapper 6 | # ccache: a c/c++ compiler cache which can greatly reduce recompilation times. 7 | # icecc, distcc: it takes compile jobs from a build and distributes them among 8 | # remote machines allowing a parallel build. 9 | # 10 | # TIPS 11 | # 12 | # 1) ccache 13 | # Use ccache 3.2 or later to avoid clang unused argument warnings: 14 | # https://bugzilla.samba.org/show_bug.cgi?id=8118 15 | # 16 | # To avoid -Wparentheses-equality clang warnings, at some cost in terms of 17 | # speed, you can do: 18 | # export CCACHE_CPP2=yes 19 | # 20 | # 2) icecc 21 | # To use icecc and ccache together, set cc_wrapper = "ccache" with 22 | # export CCACHE_PREFIX=icecc 23 | 24 | declare_args() { 25 | # Set to "ccache", "icecc" or "distcc". Probably doesn't work on windows. 26 | cc_wrapper = "" 27 | } 28 | -------------------------------------------------------------------------------- /toolchain/posix/settings.gni: -------------------------------------------------------------------------------- 1 | if (is_clang) { 2 | import("//build/toolchain/clang.gni") 3 | } 4 | 5 | declare_args() { 6 | # Path of the GCC C compiler executable. 7 | gcc_cc = "gcc" 8 | 9 | # Path of the GCC C++ compiler executable. 10 | gcc_cxx = "g++" 11 | 12 | # Path of the Clang C compiler executable. 13 | # Defaults to either $clang_base_path/bin/clang or clang. 14 | clang_cc = 0 15 | 16 | # Path of the Clang C++ compiler executable. 17 | # Defaults to either $clang_base_path/bin/clang++ or clang++. 18 | clang_cxx = 0 19 | 20 | # Path of the 'readelf' utility. 21 | readelf = "readelf" 22 | 23 | # Path of the 'ar' utility. 24 | ar = "ar" 25 | 26 | # Path of the 'nm' utility. 27 | nm = "nm" 28 | } 29 | 30 | if (is_clang && clang_cc == 0) { 31 | if (clang_base_path != "") { 32 | clang_cc = "$clang_base_path/clang" 33 | } else { 34 | clang_cc = "clang" 35 | } 36 | } 37 | 38 | if (is_clang && clang_cxx == 0) { 39 | if (clang_base_path != "") { 40 | clang_cxx = "$clang_base_path/clang++" 41 | } else { 42 | clang_cxx = "clang++" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /toolchain/win/asm_wrapper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import subprocess 3 | import sys 4 | from toolchain import GetEnv 5 | 6 | 7 | def main(arch, *args): 8 | """Filter logo banner from invocations of asm.exe.""" 9 | env = GetEnv(arch) 10 | if sys.platform == 'win32': 11 | # Windows ARM64 uses clang-cl as assembler which has '/' as path 12 | # separator, convert it to '\\' when running on Windows. 13 | args = list(args) # *args is a tuple by default, which is read-only 14 | args[0] = args[0].replace('/', '\\') 15 | popen = subprocess.Popen(args, shell=True, env=env, universal_newlines=True, 16 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 17 | out, _ = popen.communicate() 18 | for line in out.splitlines(): 19 | if (not line.startswith('Copyright (C) Microsoft Corporation') and 20 | not line.startswith('Microsoft (R) Macro Assembler') and 21 | not line.startswith(' Assembling: ') and 22 | line): 23 | print(line) 24 | return popen.returncode 25 | 26 | if __name__ == '__main__': 27 | sys.exit(main(*sys.argv[1:])) 28 | -------------------------------------------------------------------------------- /toolchain/win/message_compiler.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # Runs the Microsoft Message Compiler (mc.exe). This Python adapter is for the 6 | # GN build, which can only run Python and not native binaries. 7 | # 8 | # Usage: message_compiler.py [*] 9 | 10 | import subprocess 11 | import sys 12 | 13 | # Read the environment block from the file. This is stored in the format used 14 | # by CreateProcess. Drop last 2 NULs, one for list terminator, one for trailing 15 | # vs. separator. 16 | env_pairs = open(sys.argv[1]).read()[:-2].split('\0') 17 | env_dict = dict([item.split('=', 1) for item in env_pairs]) 18 | 19 | # mc writes to stderr, so this explicitly redirects to stdout and eats it. 20 | try: 21 | # This needs shell=True to search the path in env_dict for the mc executable. 22 | subprocess.check_output(["mc.exe"] + sys.argv[2:], 23 | env=env_dict, 24 | stderr=subprocess.STDOUT, 25 | shell=True, universal_newlines=True) 26 | except subprocess.CalledProcessError as e: 27 | print e.output 28 | sys.exit(e.returncode) 29 | -------------------------------------------------------------------------------- /gn_run_binary.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | """Helper script for GN to run an arbitrary binary. See compiled_action.gni. 6 | 7 | Run with: 8 | python gn_run_binary.py [args ...] 9 | """ 10 | 11 | from __future__ import print_function 12 | 13 | import os 14 | import subprocess 15 | import sys 16 | 17 | # This script is designed to run binaries produced by the current build. We 18 | # may prefix it with "./" to avoid picking up system versions that might 19 | # also be on the path. 20 | path = sys.argv[1] 21 | if not os.path.isabs(path): 22 | path = './' + path 23 | 24 | # The rest of the arguments are passed directly to the executable. 25 | args = [path] + sys.argv[2:] 26 | 27 | ret = subprocess.call(args) 28 | if ret != 0: 29 | if ret <= -100: 30 | # Windows error codes such as 0xC0000005 and 0xC0000409 are much easier to 31 | # recognize and differentiate in hex. In order to print them as unsigned 32 | # hex we need to add 4 Gig to them. 33 | print('%s failed with exit code 0x%08X' % (sys.argv[1], ret + (1 << 32))) 34 | else: 35 | print('%s failed with exit code %d' % (sys.argv[1], ret)) 36 | sys.exit(ret) 37 | -------------------------------------------------------------------------------- /toolchain/win/midl_wrapper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import subprocess 4 | import sys 5 | from toolchain import GetEnv 6 | 7 | 8 | def main(arch, outdir, tlb, h, dlldata, iid, proxy, idl, *flags): 9 | """Filter noisy filenames output from MIDL compile step that isn't 10 | quietable via command line flags. 11 | """ 12 | args = ['midl', '/nologo'] + list(flags) + [ 13 | '/out', outdir, 14 | '/tlb', tlb, 15 | '/h', h, 16 | '/dlldata', dlldata, 17 | '/iid', iid, 18 | '/proxy', proxy, 19 | idl] 20 | env = GetEnv(arch) 21 | popen = subprocess.Popen(args, shell=True, env=env, universal_newlines=True, 22 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 23 | out, _ = popen.communicate() 24 | # Filter junk out of stdout, and write filtered versions. Output we want 25 | # to filter is pairs of lines that look like this: 26 | # Processing C:\Program Files (x86)\Microsoft SDKs\...\include\objidl.idl 27 | # objidl.idl 28 | lines = out.splitlines() 29 | prefixes = ('Processing ', '64 bit Processing ') 30 | processing = set(os.path.basename(x) 31 | for x in lines if x.startswith(prefixes)) 32 | for line in lines: 33 | if not line.startswith(prefixes) and line not in processing: 34 | print(line) 35 | return popen.returncode 36 | 37 | if __name__ == '__main__': 38 | sys.exit(main(*sys.argv[1:])) 39 | -------------------------------------------------------------------------------- /toolchain/apple/symbols.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # This file declares arguments and configs that control whether dSYM debug 6 | # info is produced and whether build products are stripped. 7 | 8 | declare_args() { 9 | # Produce dSYM files for targets that are configured to do so. dSYM 10 | # generation is controlled globally as it is a linker output (produced via 11 | # the //build/toolchain/apple/linker_driver.py. Enabling this will result in 12 | # all shared library, loadable module, and executable targets having a dSYM 13 | # generated. 14 | enable_dsyms = is_official_build 15 | 16 | # Strip symbols from linked targets by default. If this is enabled, the 17 | # //build/config/mac:strip_all config will be applied to all linked targets. 18 | # If custom stripping parameters are required, remove that config from a 19 | # linked target and apply custom -Wcrl,strip flags. See 20 | # //build/toolchain/apple/linker_driver.py for more information. 21 | enable_stripping = is_official_build 22 | } 23 | 24 | # Save unstripped copies of targets with a ".unstripped" suffix. This is 25 | # useful to preserve the original output when enable_stripping=true but 26 | # we're not actually generating real dSYMs. 27 | save_unstripped_output = enable_stripping && !enable_dsyms 28 | -------------------------------------------------------------------------------- /apple/convert_plist.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # Convert plist file to given format. 6 | # 7 | # Arguments 8 | # 9 | # source: 10 | # string, path to the plist file to convert 11 | # 12 | # output: 13 | # string, path to the converted plist, must be under $root_build_dir 14 | # 15 | # format: 16 | # string, the format to convert the plist to. Either "binary1" or "xml1". 17 | template("convert_plist") { 18 | assert(defined(invoker.source), "source must be defined for $target_name") 19 | assert(defined(invoker.output), "output must be defined for $target_name") 20 | assert(defined(invoker.format), "format must be defined for $target_name") 21 | 22 | action(target_name) { 23 | forward_variables_from(invoker, 24 | [ 25 | "visibility", 26 | "testonly", 27 | "deps", 28 | ]) 29 | 30 | script = "//build/apple/plist_util.py" 31 | sources = [ invoker.source ] 32 | outputs = [ invoker.output ] 33 | args = [ 34 | "merge", 35 | "--format=${invoker.format}", 36 | "-o", 37 | rebase_path(invoker.output, root_build_dir), 38 | rebase_path(invoker.source, root_build_dir), 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /toolchain/gcc_compile_wrapper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2016 The Chromium Authors. All rights reserved. 3 | # Use of this source code is governed by a BSD-style license that can be 4 | # found in the LICENSE file. 5 | 6 | """Runs a compilation command. 7 | 8 | This script exists to avoid using complex shell commands in 9 | gcc_toolchain.gni's tool("cxx") and tool("cc") in case the host running the 10 | compiler does not have a POSIX-like shell (e.g. Windows). 11 | """ 12 | 13 | import argparse 14 | import sys 15 | 16 | import wrapper_utils 17 | 18 | 19 | def main(): 20 | parser = argparse.ArgumentParser(description=__doc__) 21 | parser.add_argument('--resource-whitelist', 22 | help='Generate a resource whitelist for this target.', 23 | metavar='PATH') 24 | parser.add_argument('command', nargs=argparse.REMAINDER, 25 | help='Compilation command') 26 | args = parser.parse_args() 27 | 28 | returncode, stderr = wrapper_utils.CaptureCommandStderr( 29 | wrapper_utils.CommandToRun(args.command)) 30 | 31 | used_resources = wrapper_utils.ExtractResourceIdsFromPragmaWarnings(stderr) 32 | sys.stderr.write(stderr) 33 | 34 | if args.resource_whitelist: 35 | with open(args.resource_whitelist, 'w') as f: 36 | if used_resources: 37 | f.write('\n'.join(str(resource) for resource in used_resources)) 38 | f.write('\n') 39 | 40 | return returncode 41 | 42 | if __name__ == "__main__": 43 | sys.exit(main()) 44 | -------------------------------------------------------------------------------- /mac/prepare_framework_version.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import os 6 | import shutil 7 | import sys 8 | 9 | # Ensures that the current version matches the last-produced version, which is 10 | # stored in the version_file. If it does not, then the framework_root_dir is 11 | # obliterated. 12 | # Usage: python prepare_framework_version.py out/obj/version_file \ 13 | # out/Framework.framework \ 14 | # 'A' 15 | 16 | def PrepareFrameworkVersion(version_file, framework_root_dir, version): 17 | # Test what the current framework version is. Stop if it is up-to-date. 18 | try: 19 | with open(version_file, 'r') as f: 20 | current_version = f.read() 21 | if current_version == version: 22 | return 23 | except IOError: 24 | pass 25 | 26 | # The framework version has changed, so clobber the framework. 27 | if os.path.exists(framework_root_dir): 28 | shutil.rmtree(framework_root_dir) 29 | 30 | # Write out the new framework version file, making sure its containing 31 | # directory exists. 32 | dirname = os.path.dirname(version_file) 33 | if not os.path.isdir(dirname): 34 | os.makedirs(dirname, 0o700) 35 | 36 | with open(version_file, 'w+') as f: 37 | f.write(version) 38 | 39 | 40 | if __name__ == '__main__': 41 | PrepareFrameworkVersion(sys.argv[1], sys.argv[2], sys.argv[3]) 42 | sys.exit(0) 43 | -------------------------------------------------------------------------------- /toolchain/coverage.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import("//build/toolchain/toolchain.gni") 6 | 7 | # There are two ways to enable code coverage instrumentation: 8 | # 1. When |use_clang_coverage| or |use_jacoco_coverage| is true and 9 | # |coverage_instrumentation_input_file| is empty, all source files or 10 | # Java class files are instrumented. 11 | # 2. When |use_clang_coverage| or |use_jacoco_coverage| is true and 12 | # |coverage_instrumentation_input_file| is NOT empty and points to 13 | # a text file on the file system, ONLY source files specified in the 14 | # input file or Java class files related to source files are instrumented. 15 | declare_args() { 16 | # Enable Clang's Source-based Code Coverage. 17 | use_clang_coverage = false 18 | 19 | # Enables JaCoCo Java code coverage. 20 | use_jacoco_coverage = false 21 | 22 | # The path to the coverage instrumentation input file should be a source root 23 | # absolute path (e.g. //out/Release/coverage_instrumentation_input.txt), and 24 | # the file consists of multiple lines where each line represents a path to a 25 | # source file, and the paths must be relative to the root build directory. 26 | # e.g. ../../base/task/post_task.cc for build directory 'out/Release'. 27 | # 28 | # NOTE that this arg will be non-op if use_clang_coverage is false. 29 | coverage_instrumentation_input_file = "" 30 | } 31 | 32 | assert(!use_clang_coverage || is_clang, 33 | "Clang Source-based Code Coverage requires clang.") 34 | -------------------------------------------------------------------------------- /toolchain/posix/toolchain.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | 4 | import re 5 | import subprocess 6 | import sys 7 | import os 8 | 9 | def _get_compiler_version(path, major_define, minor_define, patchlevel_define): 10 | path = os.path.normpath(path) 11 | defines = subprocess.check_output('echo "" | "{}" -dM -E -'.format(path), shell=True, 12 | universal_newlines=True).split('\n') 13 | version = 0 14 | for define in defines: 15 | define = re.findall(r'#define ([a-zA-Z0-9_]+) (.*)', define) 16 | if not define: 17 | continue 18 | 19 | name, value = define[0] 20 | if name == major_define: 21 | version += 10000 * int(value) 22 | elif name == minor_define: 23 | version += 100 * int(value) 24 | elif name == patchlevel_define: 25 | value = int(value) 26 | if value < 100: 27 | version += int(value) 28 | 29 | return version 30 | 31 | 32 | def get_gcc_version(path): 33 | print(_get_compiler_version(path, '__GNUC__', '__GNUC_MINOR__', '__GNUC_PATCHLEVEL__')) 34 | 35 | 36 | def get_clang_version(path): 37 | print(_get_compiler_version(path, '__clang_major__', '__clang_minor__', '__clang_patchlevel__')) 38 | 39 | 40 | def main(): 41 | commands = { 42 | 'get_gcc_version': get_gcc_version, 43 | 'get_clang_version': get_clang_version, 44 | } 45 | 46 | if len(sys.argv) < 2 or sys.argv[1] not in commands: 47 | print('Expected one of: %s' % ', '.join(commands), file=sys.stderr) 48 | return 1 49 | 50 | return commands[sys.argv[1]](*sys.argv[2:]) 51 | 52 | 53 | if __name__ == '__main__': 54 | sys.exit(main()) 55 | -------------------------------------------------------------------------------- /toolchain/sysroot.gni: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # This header file defines the "sysroot" variable which is the absolute path 6 | # of the sysroot. If no sysroot applies, the variable will be an empty string. 7 | 8 | declare_args() { 9 | # The absolute path of the sysroot that is applied when compiling using 10 | # the target toolchain. 11 | target_sysroot = "" 12 | 13 | use_sysroot = false 14 | } 15 | 16 | if (current_os == target_os && current_cpu == target_cpu && 17 | target_sysroot != "") { 18 | sysroot = target_sysroot 19 | } else if (is_android) { 20 | import("//build/toolchain/android/settings.gni") 21 | if (current_cpu == "x86") { 22 | sysroot = "$android_ndk_root/$x86_android_sysroot_subdir" 23 | } else if (current_cpu == "arm") { 24 | sysroot = "$android_ndk_root/$arm_android_sysroot_subdir" 25 | } else if (current_cpu == "mipsel") { 26 | sysroot = "$android_ndk_root/$mips_android_sysroot_subdir" 27 | } else if (current_cpu == "x64") { 28 | sysroot = "$android_ndk_root/$x86_64_android_sysroot_subdir" 29 | } else if (current_cpu == "arm64") { 30 | sysroot = "$android_ndk_root/$arm64_android_sysroot_subdir" 31 | } else if (current_cpu == "mips64el") { 32 | sysroot = "$android_ndk_root/$mips64_android_sysroot_subdir" 33 | } else { 34 | sysroot = "" 35 | } 36 | } else if (is_mac) { 37 | import("//build/toolchain/mac/mac_sdk.gni") 38 | sysroot = mac_sdk_path 39 | } else if (is_ios) { 40 | import("//build/toolchain/mac/ios_sdk.gni") 41 | sysroot = ios_sdk_path 42 | } else { 43 | sysroot = "" 44 | } 45 | -------------------------------------------------------------------------------- /apple/xcrun.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # Copyright 2020 The Chromium Authors. All rights reserved. 3 | # Use of this source code is governed by a BSD-style license that can be 4 | # found in the LICENSE file. 5 | """ 6 | Wrapper around xcrun adding support for --developer-dir parameter to set 7 | the DEVELOPER_DIR environment variable, and for converting paths relative 8 | to absolute (since this is required by most of the tool run via xcrun). 9 | """ 10 | 11 | import argparse 12 | import os 13 | import subprocess 14 | import sys 15 | 16 | 17 | def xcrun(command, developer_dir): 18 | environ = dict(os.environ) 19 | if developer_dir: 20 | environ['DEVELOPER_DIR'] = os.path.abspath(developer_dir) 21 | 22 | processed_args = ['/usr/bin/xcrun'] 23 | for arg in command: 24 | if os.path.exists(arg): 25 | arg = os.path.abspath(arg) 26 | processed_args.append(arg) 27 | 28 | process = subprocess.Popen(processed_args, 29 | stdout=subprocess.PIPE, 30 | stderr=subprocess.PIPE, 31 | universal_newlines=True, 32 | env=environ) 33 | 34 | stdout, stderr = process.communicate() 35 | sys.stdout.write(stdout) 36 | if process.returncode: 37 | sys.stderr.write(stderr) 38 | sys.exit(process.returncode) 39 | 40 | 41 | def main(args): 42 | parser = argparse.ArgumentParser(add_help=False) 43 | parser.add_argument( 44 | '--developer-dir', 45 | help='path to developer dir to use for the invocation of xcrun') 46 | 47 | parsed, remaining_args = parser.parse_known_args(args) 48 | xcrun(remaining_args, parsed.developer_dir) 49 | 50 | 51 | if __name__ == '__main__': 52 | main(sys.argv[1:]) 53 | -------------------------------------------------------------------------------- /apple/compile_entitlements.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import("//build/apple/compile_plist.gni") 6 | 7 | # Template to merge multiple .entitlements files performing variable 8 | # substitutions. 9 | # 10 | # Arguments 11 | # 12 | # entitlements_templates: 13 | # string array, paths to entitlements files which will be used for the 14 | # bundle. 15 | # 16 | # substitutions: 17 | # string array, 'key=value' pairs used to replace ${key} by value 18 | # when generating the output plist file. 19 | # 20 | # output_name: 21 | # string, name of the generated entitlements file. 22 | template("compile_entitlements") { 23 | assert(defined(invoker.entitlements_templates), 24 | "A list of template plist files must be specified for $target_name") 25 | 26 | compile_plist(target_name) { 27 | forward_variables_from(invoker, 28 | "*", 29 | [ 30 | "entitlements_templates", 31 | "format", 32 | "plist_templates", 33 | ]) 34 | 35 | plist_templates = invoker.entitlements_templates 36 | 37 | # Entitlements files are always encoded in xml1. 38 | format = "xml1" 39 | 40 | # Entitlements files use unsubstitued variables, so define substitutions 41 | # to leave those variables untouched. 42 | if (!defined(substitutions)) { 43 | substitutions = [] 44 | } 45 | 46 | substitutions += [ 47 | "AppIdentifierPrefix=\$(AppIdentifierPrefix)", 48 | "CFBundleIdentifier=\$(CFBundleIdentifier)", 49 | ] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /config/mips.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # These are primarily relevant in current_cpu == "mips*" contexts, where 6 | # MIPS code is being compiled. But they can also be relevant in the 7 | # other contexts when the code will change its behavior based on the 8 | # cpu it wants to generate code for. 9 | if (current_cpu == "mipsel") { 10 | declare_args() { 11 | # MIPS arch variant. Possible values are: 12 | # "r1" 13 | # "r2" 14 | # "r6" 15 | mips_arch_variant = "r1" 16 | 17 | # MIPS DSP ASE revision. Possible values are: 18 | # 0: unavailable 19 | # 1: revision 1 20 | # 2: revision 2 21 | mips_dsp_rev = 0 22 | 23 | # MIPS SIMD Arch compilation flag. 24 | mips_use_msa = false 25 | 26 | # MIPS floating-point ABI. Possible values are: 27 | # "hard": sets the GCC -mhard-float option. 28 | # "soft": sets the GCC -msoft-float option. 29 | mips_float_abi = "hard" 30 | 31 | # MIPS32 floating-point register width. Possible values are: 32 | # "fp32": sets the GCC -mfp32 option. 33 | # "fp64": sets the GCC -mfp64 option. 34 | # "fpxx": sets the GCC -mfpxx option. 35 | mips_fpu_mode = "fp32" 36 | } 37 | } else if (current_cpu == "mips64el") { 38 | # MIPS arch variant. Possible values are: 39 | # "r2" 40 | # "r6" 41 | if (current_os == "android" || target_os == "android") { 42 | declare_args() { 43 | mips_arch_variant = "r6" 44 | 45 | # MIPS SIMD Arch compilation flag. 46 | mips_use_msa = true 47 | } 48 | } else { 49 | declare_args() { 50 | mips_arch_variant = "r2" 51 | 52 | # MIPS SIMD Arch compilation flag. 53 | mips_use_msa = false 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /config/ios/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2014 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import("//build/toolchain/sysroot.gni") 6 | import("//build/toolchain/mac/ios_sdk.gni") 7 | 8 | # This is included by reference in the //build/config/compiler:runtime_library 9 | # config that is applied to all targets. It is here to separate out the logic 10 | # that is iOS-only. Please see that target for advice on what should go in 11 | # :runtime_library vs. :compiler. 12 | config("runtime_library") { 13 | common_flags = [ 14 | "-isysroot", 15 | sysroot, 16 | "-stdlib=libc++", 17 | ] 18 | 19 | if (use_ios_simulator) { 20 | common_flags += [ "-mios-simulator-version-min=$ios_deployment_target" ] 21 | } else { 22 | common_flags += [ "-miphoneos-version-min=$ios_deployment_target" ] 23 | } 24 | 25 | asmflags = common_flags 26 | cflags = common_flags 27 | ldflags = common_flags 28 | 29 | # TODO(crbug.com/634373): Remove once Xcode's libc++ has LLVM r256325. Most 30 | # likely this means one Xcode 8 is released and required. 31 | #if (use_xcode_clang && get_path_info(ios_sdk_version, "name") != "10") { 32 | # common_cc_flags = [ 33 | # "-isystem", 34 | # rebase_path("//third_party/llvm-build/Release+Asserts/include/c++/v1", 35 | # root_build_dir), 36 | # ] 37 | # 38 | # cflags_cc = common_cc_flags 39 | # cflags_objcc = common_cc_flags 40 | #} 41 | } 42 | 43 | config("ios_dynamic_flags") { 44 | ldflags = [ "-Wl,-ObjC" ] # Always load Objective-C categories and class. 45 | } 46 | 47 | config("xctest_config") { 48 | common_flags = [ 49 | "-F", 50 | "$ios_sdk_platform_path/Developer/Library/Frameworks", 51 | ] 52 | 53 | cflags = common_flags 54 | ldflags = common_flags 55 | 56 | libs = [ 57 | "Foundation.framework", 58 | "XCTest.framework", 59 | ] 60 | } 61 | 62 | group("xctest") { 63 | public_configs = [ ":xctest_config" ] 64 | } 65 | -------------------------------------------------------------------------------- /mac/package_framework.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import argparse 6 | import errno 7 | import os 8 | import shutil 9 | import sys 10 | 11 | def Main(): 12 | parser = argparse.ArgumentParser(description='Create Mac Framework symlinks') 13 | parser.add_argument('--framework', action='store', type=str, required=True) 14 | parser.add_argument('--version', action='store', type=str) 15 | parser.add_argument('--contents', action='store', type=str, nargs='+') 16 | parser.add_argument('--stamp', action='store', type=str, required=True) 17 | args = parser.parse_args() 18 | 19 | VERSIONS = 'Versions' 20 | CURRENT = 'Current' 21 | 22 | # Ensure the Foo.framework/Versions/A/ directory exists and create the 23 | # Foo.framework/Versions/Current symlink to it. 24 | if args.version: 25 | try: 26 | os.makedirs(os.path.join(args.framework, VERSIONS, args.version), 0o755) 27 | except OSError as e: 28 | if e.errno != errno.EEXIST: 29 | raise e 30 | _Relink(os.path.join(args.version), 31 | os.path.join(args.framework, VERSIONS, CURRENT)) 32 | 33 | # Establish the top-level symlinks in the framework bundle. The dest of 34 | # the symlinks may not exist yet. 35 | if args.contents: 36 | for item in args.contents: 37 | _Relink(os.path.join(VERSIONS, CURRENT, item), 38 | os.path.join(args.framework, item)) 39 | 40 | # Write out a stamp file. 41 | if args.stamp: 42 | with open(args.stamp, 'w') as f: 43 | f.write(str(args)) 44 | 45 | return 0 46 | 47 | 48 | def _Relink(dest, link): 49 | """Creates a symlink to |dest| named |link|. If |link| already exists, 50 | it is overwritten.""" 51 | try: 52 | os.remove(link) 53 | except OSError as e: 54 | if e.errno != errno.ENOENT: 55 | shutil.rmtree(link) 56 | os.symlink(dest, link) 57 | 58 | 59 | if __name__ == '__main__': 60 | sys.exit(Main()) 61 | -------------------------------------------------------------------------------- /toolchain/apple/filter_libtool.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | from __future__ import print_function 6 | 7 | import os 8 | import re 9 | import subprocess 10 | import sys 11 | 12 | # This script executes libool and filters out logspam lines like: 13 | # '/path/to/libtool: file: foo.o has no symbols' 14 | 15 | SUPPRESSED_PATTERNS = [ 16 | re.compile(v) for v in [ 17 | r'^.*libtool: (?:for architecture: \S* )?file: .* has no symbols$', 18 | # Xcode 11 spelling of the "empty archive" warning. 19 | # TODO(thakis): Remove once we require Xcode 12. 20 | r'^.*libtool: warning for library: .* the table of contents is empty ' \ 21 | r'\(no object file members in the library define global symbols\)$', 22 | # Xcode 12 spelling of the "empty archive" warning. 23 | r'^warning: .*libtool: archive library: .* ' \ 24 | r'the table of contents is empty ', 25 | r'\(no object file members in the library define global symbols\)$', 26 | r'^.*libtool: warning same member name \(\S*\) in output file used ' \ 27 | r'for input files: \S* and: \S* \(due to use of basename, ' \ 28 | r'truncation, blank padding or duplicate input files\)$', 29 | ] 30 | ] 31 | 32 | 33 | def ShouldSuppressLine(line): 34 | """Returns whether the line should be filtered out.""" 35 | for pattern in SUPPRESSED_PATTERNS: 36 | if pattern.match(line): 37 | return True 38 | return False 39 | 40 | 41 | def Main(cmd_list): 42 | env = os.environ.copy() 43 | libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env) 44 | _, err = libtoolout.communicate() 45 | for line in err.decode('UTF-8').splitlines(): 46 | if not ShouldSuppressLine(line): 47 | print(line, file=sys.stderr) 48 | return libtoolout.returncode 49 | 50 | 51 | if __name__ == '__main__': 52 | sys.exit(Main(sys.argv[1:])) 53 | -------------------------------------------------------------------------------- /apple/write_pkg_info.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import argparse 6 | import os 7 | import plist_util 8 | import sys 9 | 10 | # This script creates a PkgInfo file for an OS X .app bundle's plist. 11 | # Usage: python write_pkg_info.py --plist Foo.app/Contents/Info.plist \ 12 | # --output Foo.app/Contents/PkgInfo 13 | 14 | 15 | def Main(): 16 | parser = argparse.ArgumentParser( 17 | description='A script to write PkgInfo files for .app bundles.') 18 | parser.add_argument('--plist', 19 | required=True, 20 | help='Path to the Info.plist for the .app.') 21 | parser.add_argument('--output', 22 | required=True, 23 | help='Path to the desired output file.') 24 | args = parser.parse_args() 25 | 26 | # Remove the output if it exists already. 27 | if os.path.exists(args.output): 28 | os.unlink(args.output) 29 | 30 | plist = plist_util.LoadPList(args.plist) 31 | package_type = plist['CFBundlePackageType'] 32 | if package_type != 'APPL': 33 | raise ValueError('Expected CFBundlePackageType to be %s, got %s' % \ 34 | ('APPL', package_type)) 35 | 36 | # The format of PkgInfo is eight characters, representing the bundle type 37 | # and bundle signature, each four characters. If that is missing, four 38 | # '?' characters are used instead. 39 | signature_code = plist.get('CFBundleSignature', '????') 40 | if len(signature_code) != 4: 41 | raise ValueError('CFBundleSignature should be exactly four characters, ' + 42 | 'got %s' % signature_code) 43 | 44 | with open(args.output, 'w') as fp: 45 | fp.write('%s%s' % (package_type, signature_code)) 46 | return 0 47 | 48 | 49 | if __name__ == '__main__': 50 | # TODO(https://crbug.com/941669): Temporary workaround until all scripts use 51 | # python3 by default. 52 | if sys.version_info[0] < 3: 53 | os.execvp('python3', ['python3'] + sys.argv) 54 | sys.exit(Main()) 55 | -------------------------------------------------------------------------------- /toolchain/win/link_wrapper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import re 4 | import subprocess 5 | import sys 6 | from toolchain import GetEnv 7 | 8 | 9 | # A regex matching an argument corresponding to the output filename passed to 10 | # link.exe. 11 | _LINK_EXE_OUT_ARG = re.compile('/OUT:(?P.+)$', re.IGNORECASE) 12 | 13 | 14 | def UseSeparateMspdbsrv(env, args): 15 | """Allows to use a unique instance of mspdbsrv.exe per linker instead of a 16 | shared one.""" 17 | if len(args) < 1: 18 | raise Exception("Not enough arguments") 19 | 20 | if args[0] != 'link.exe': 21 | return 22 | 23 | # Use the output filename passed to the linker to generate an endpoint name 24 | # for mspdbsrv.exe. 25 | endpoint_name = None 26 | for arg in args: 27 | m = _LINK_EXE_OUT_ARG.match(arg) 28 | if m: 29 | endpoint_name = re.sub(r'\W+', '', 30 | '%s_%d' % (m.group('out'), os.getpid())) 31 | break 32 | 33 | if endpoint_name is None: 34 | return 35 | 36 | # Adds the appropriate environment variable. This will be read by link.exe 37 | # to know which instance of mspdbsrv.exe it should connect to (if it's 38 | # not set then the default endpoint is used). 39 | env['_MSPDBSRV_ENDPOINT_'] = endpoint_name 40 | 41 | def main(arch, use_separate_mspdbsrv, *args): 42 | """Filter diagnostic output from link that looks like: 43 | ' Creating library ui.dll.lib and object ui.dll.exp' 44 | This happens when there are exports from the dll or exe. 45 | """ 46 | env = GetEnv(arch) 47 | if use_separate_mspdbsrv == 'True': 48 | UseSeparateMspdbsrv(env, args) 49 | link = subprocess.Popen([args[0].replace('/', '\\')] + list(args[1:]), 50 | shell=True, 51 | env=env, 52 | stdout=subprocess.PIPE, 53 | stderr=subprocess.STDOUT, 54 | universal_newlines=True) 55 | out, _ = link.communicate() 56 | for line in out.splitlines(): 57 | if not line.startswith(' Creating library '): 58 | print(line) 59 | return link.returncode 60 | 61 | if __name__ == '__main__': 62 | sys.exit(main(*sys.argv[1:])) 63 | -------------------------------------------------------------------------------- /apple/apple_info_plist.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import("//build/apple/compile_plist.gni") 6 | 7 | # The base template used to generate Info.plist files for iOS and Mac apps and 8 | # frameworks. 9 | # 10 | # Arguments 11 | # 12 | # plist_templates: 13 | # string array, paths to plist files which will be used for the bundle. 14 | # 15 | # executable_name: 16 | # string, name of the generated target used for the product 17 | # and executable name as specified in the output Info.plist. 18 | # 19 | # format: 20 | # string, the format to `plutil -convert` the plist to when 21 | # generating the output. 22 | # 23 | # extra_substitutions: 24 | # (optional) string array, 'key=value' pairs for extra fields which are 25 | # specified in a source Info.plist template. 26 | # 27 | # output_name: 28 | # (optional) string, name of the generated plist file, default to 29 | # "$target_gen_dir/$target_name.plist". 30 | template("apple_info_plist") { 31 | assert(defined(invoker.executable_name), 32 | "The executable_name must be specified for $target_name") 33 | executable_name = invoker.executable_name 34 | 35 | compile_plist(target_name) { 36 | forward_variables_from(invoker, 37 | [ 38 | "plist_templates", 39 | "testonly", 40 | "deps", 41 | "visibility", 42 | "format", 43 | ]) 44 | 45 | if (defined(invoker.output_name)) { 46 | output_name = invoker.output_name 47 | } else { 48 | output_name = "$target_gen_dir/$target_name.plist" 49 | } 50 | 51 | substitutions = [ 52 | "EXECUTABLE_NAME=$executable_name", 53 | "GCC_VERSION=com.apple.compilers.llvm.clang.1_0", 54 | "PRODUCT_NAME=$executable_name", 55 | ] 56 | if (defined(invoker.extra_substitutions)) { 57 | substitutions += invoker.extra_substitutions 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /toolchain/toolchain.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # Toolchain-related configuration that may be needed outside the context of the 6 | # toolchain() rules themselves. 7 | 8 | declare_args() { 9 | # How many symbols to include in the build. This affects the performance of 10 | # the build since the symbols are large and dealing with them is slow. 11 | # 2 means regular build with symbols. 12 | # 1 means minimal symbols, usually enough for backtraces only. 13 | # 0 means no symbols. 14 | symbol_level = 2 15 | } 16 | 17 | # If it wasn't manually set, set to an appropriate default. 18 | assert(symbol_level >= 0 && symbol_level <= 2, "Invalid symbol_level") 19 | 20 | # Extension for shared library files (including leading dot). 21 | if (is_mac) { 22 | shlib_extension = ".dylib" 23 | } else if (is_posix) { 24 | shlib_extension = ".so" 25 | } else if (is_win) { 26 | shlib_extension = ".dll" 27 | } else { 28 | assert(false, "Platform not supported") 29 | } 30 | 31 | # Prefix for shared library files. 32 | if (is_posix) { 33 | shlib_prefix = "lib" 34 | } else { 35 | shlib_prefix = "" 36 | } 37 | 38 | # While other "tool"s in a toolchain are specific to the target of that 39 | # toolchain, the "stamp" and "copy" tools are really generic to the host; 40 | # but each toolchain must define them separately. GN doesn't allow a 41 | # template instantiation inside a toolchain definition, so some boilerplate 42 | # has to be repeated in each toolchain to define these two tools. These 43 | # four variables reduce the duplication in that boilerplate. 44 | stamp_description = "STAMP {{output}}" 45 | copy_description = "COPY {{source}} {{output}}" 46 | if (host_os == "win") { 47 | copy_path = 48 | rebase_path("//build/toolchain/win/recursive_mirror.py", root_build_dir) 49 | 50 | stamp_command = "cmd /c type nul > \"{{output}}\"" 51 | copy_command = "$python_path $copy_path {{source}} {{output}}" 52 | } else { 53 | stamp_command = "touch {{output}}" 54 | copy_command = "ln -f {{source}} {{output}} 2>/dev/null || (rm -rf {{output}} && cp -af {{source}} {{output}})" 55 | } 56 | -------------------------------------------------------------------------------- /toolchain/gcc_ar_wrapper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2015 The Chromium Authors. All rights reserved. 3 | # Use of this source code is governed by a BSD-style license that can be 4 | # found in the LICENSE file. 5 | 6 | """Runs the 'ar' command after removing its output file first. 7 | 8 | This script is invoked like: 9 | python gcc_ar_wrapper.py --ar=$AR --output=$OUT $OP $INPUTS 10 | to do the equivalent of: 11 | rm -f $OUT && $AR $OP $OUT $INPUTS 12 | """ 13 | 14 | import argparse 15 | import os 16 | import subprocess 17 | import sys 18 | import errno 19 | 20 | import wrapper_utils 21 | 22 | 23 | def main(): 24 | parser = argparse.ArgumentParser(description=__doc__) 25 | parser.add_argument('--ar', 26 | required=True, 27 | help='The ar binary to run', 28 | metavar='PATH') 29 | parser.add_argument('--output', 30 | required=True, 31 | help='Output archive file', 32 | metavar='ARCHIVE') 33 | parser.add_argument('--plugin', 34 | help='Load plugin') 35 | parser.add_argument('--resource-whitelist', 36 | help='Merge all resource whitelists into a single file.', 37 | metavar='PATH') 38 | parser.add_argument('operation', 39 | help='Operation on the archive') 40 | parser.add_argument('inputs', nargs='+', 41 | help='Input files') 42 | args = parser.parse_args() 43 | 44 | if args.resource_whitelist: 45 | whitelist_candidates = wrapper_utils.ResolveRspLinks(args.inputs) 46 | wrapper_utils.CombineResourceWhitelists( 47 | whitelist_candidates, args.resource_whitelist) 48 | 49 | command = [args.ar, args.operation] 50 | if args.plugin is not None: 51 | command += ['--plugin', args.plugin] 52 | command.append(args.output) 53 | command += args.inputs 54 | 55 | # Remove the output file first. 56 | try: 57 | os.remove(args.output) 58 | except OSError as e: 59 | if e.errno != errno.ENOENT: 60 | raise 61 | 62 | # Now just run the ar command. 63 | return subprocess.call(wrapper_utils.CommandToRun(command)) 64 | 65 | 66 | if __name__ == "__main__": 67 | sys.exit(main()) 68 | -------------------------------------------------------------------------------- /config/posix/BUILD.gn: -------------------------------------------------------------------------------- 1 | import("//build/config/sanitizers/sanitizers.gni") 2 | import("//build/toolchain/sysroot.gni") 3 | 4 | assert(is_posix) 5 | 6 | # This is included by reference in the //build/config/compiler config that 7 | # is applied to all targets. It is here to separate out the logic that is 8 | # Posix-only. 9 | config("compiler") { 10 | cflags = [] 11 | ldflags = [] 12 | 13 | if (is_freebsd && is_clang) { 14 | # GCC seems to include /usr/local/include in its default include dirs. 15 | cflags += [ "-isystem/usr/local/include" ] 16 | ldflags += [ "-B/usr/local/bin" ] 17 | } 18 | } 19 | 20 | # This is included by reference in the //build/config/compiler:runtime_library 21 | # config that is applied to all targets. It is here to separate out the logic 22 | # that is Posix-only. Please see that target for advice on what should go in 23 | # :runtime_library vs. :compiler. 24 | config("runtime_library") { 25 | if (!is_mac && sysroot != "") { 26 | # Pass the sysroot to all C compiler variants, the assembler, and linker. 27 | cflags = [ "--sysroot=" + rebase_path(sysroot, root_build_dir) ] 28 | asmflags = cflags 29 | ldflags = cflags 30 | 31 | # TODO: Need to get some linker flags out of the sysroot. 32 | } 33 | } 34 | 35 | # Settings for executables. 36 | config("executable_ldconfig") { 37 | # Find the path containing shared libraries for this toolchain 38 | # relative to the build directory. ${root_out_dir} will be a 39 | # subdirectory of ${root_build_dir} when cross compiling. 40 | rpath_link = rebase_path(root_out_dir, root_build_dir) 41 | 42 | ldflags = [ 43 | # Want to pass "\$". GN will re-escape as required for ninja. 44 | "-Wl,-rpath=\$ORIGIN", 45 | "-Wl,-rpath-link=${rpath_link}", 46 | 47 | # Newer binutils don't set DT_RPATH unless you disable "new" dtags 48 | # and the new DT_RUNPATH doesn't work without --no-as-needed flag. 49 | "-Wl,--disable-new-dtags", 50 | ] 51 | 52 | if (current_cpu == "mipsel") { 53 | ldflags += [ "-pie" ] 54 | } 55 | } 56 | 57 | # This config ensures that targets do not refer to symbols unresolved at 58 | # compile time. 59 | # Disable this for DSOs that depend on symbols of the executables 60 | # that load them (e.g. Apache modules) 61 | config("no_undefined") { 62 | if (!using_sanitizer && !use_cfi_diag) { 63 | ldflags = [ "-Wl,-z,defs" ] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /toolchain/posix/BUILD.gn: -------------------------------------------------------------------------------- 1 | import("//build/toolchain/gcc_toolchain.gni") 2 | import("//build/toolchain/posix/settings.gni") 3 | 4 | gcc_toolchain("clang_x86") { 5 | cc = clang_cc 6 | cxx = clang_cxx 7 | ld = cxx 8 | 9 | readelf = readelf 10 | ar = ar 11 | nm = nm 12 | 13 | toolchain_args = { 14 | current_cpu = "x86" 15 | current_os = target_os 16 | is_clang = true 17 | } 18 | } 19 | 20 | gcc_toolchain("x86") { 21 | cc = gcc_cc 22 | cxx = gcc_cxx 23 | ld = cxx 24 | 25 | readelf = readelf 26 | ar = ar 27 | nm = nm 28 | 29 | toolchain_args = { 30 | current_cpu = "x86" 31 | current_os = target_os 32 | is_clang = false 33 | } 34 | } 35 | 36 | gcc_toolchain("clang_x64") { 37 | cc = clang_cc 38 | cxx = clang_cxx 39 | ld = cxx 40 | 41 | readelf = readelf 42 | ar = ar 43 | nm = nm 44 | 45 | toolchain_args = { 46 | current_cpu = "x64" 47 | current_os = target_os 48 | is_clang = true 49 | } 50 | } 51 | 52 | gcc_toolchain("x64") { 53 | cc = gcc_cc 54 | cxx = gcc_cxx 55 | ld = cxx 56 | 57 | readelf = readelf 58 | ar = ar 59 | nm = nm 60 | 61 | toolchain_args = { 62 | current_cpu = "x64" 63 | current_os = target_os 64 | is_clang = false 65 | } 66 | } 67 | 68 | gcc_toolchain("clang_arm") { 69 | cc = clang_cc 70 | cxx = clang_cxx 71 | ld = cxx 72 | 73 | readelf = readelf 74 | ar = ar 75 | nm = nm 76 | 77 | toolchain_args = { 78 | current_cpu = "arm" 79 | current_os = target_os 80 | is_clang = true 81 | } 82 | } 83 | 84 | gcc_toolchain("arm") { 85 | cc = gcc_cc 86 | cxx = gcc_cxx 87 | ld = cxx 88 | 89 | readelf = readelf 90 | ar = ar 91 | nm = nm 92 | 93 | toolchain_args = { 94 | current_cpu = "arm" 95 | current_os = target_os 96 | is_clang = false 97 | } 98 | } 99 | 100 | gcc_toolchain("clang_arm64") { 101 | cc = clang_cc 102 | cxx = clang_cxx 103 | ld = cxx 104 | 105 | readelf = readelf 106 | ar = ar 107 | nm = nm 108 | 109 | toolchain_args = { 110 | current_cpu = "arm64" 111 | current_os = target_os 112 | is_clang = true 113 | } 114 | } 115 | 116 | gcc_toolchain("arm64") { 117 | cc = gcc_cc 118 | cxx = gcc_cxx 119 | ld = cxx 120 | 121 | readelf = readelf 122 | ar = ar 123 | nm = nm 124 | 125 | toolchain_args = { 126 | current_cpu = "arm64" 127 | current_os = target_os 128 | is_clang = false 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /toolchain/win/rc_wrapper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import subprocess 3 | import sys 4 | import os 5 | from toolchain import GetEnv 6 | 7 | msvc_deps_prefix = 'Note: including file: ' 8 | 9 | def call(*popenargs, **kwargs): 10 | process = subprocess.Popen(stdout=subprocess.PIPE, universal_newlines=True, 11 | *popenargs, **kwargs) 12 | output, unused_err = process.communicate() 13 | return process.poll(), output 14 | 15 | def main(arch, source, output, rc_name, *args): 16 | """Output header dependencies and filter logo banner from invocations 17 | of rc.exe. Older versions of RC don't support the /nologo flag.""" 18 | env = GetEnv(arch) 19 | args = list(args) 20 | 21 | output_dir = os.path.split(output)[0] 22 | source_name = os.path.split(source)[1] 23 | 24 | # Try fixing all those relative include directories. 25 | def fix_dirs(arg): 26 | if not arg.startswith('-I'): 27 | return arg 28 | 29 | return '/I{}'.format(os.path.relpath(arg[2:], output_dir)) 30 | 31 | cl_args = list(map(fix_dirs, args)) 32 | 33 | # This needs shell=True to search the path in env for the cl executable. 34 | retcode, out = call(["cl.exe", "/nologo", "/showIncludes", "/wd4005"] + 35 | cl_args + ["/P", os.path.relpath(source, output_dir)], 36 | env=env, 37 | stderr=subprocess.STDOUT, 38 | shell=True, 39 | # This is necessary so our .i file (generated by /P) 40 | # doesn't get written to the root build directory. 41 | cwd=output_dir) 42 | if retcode: 43 | print(out) 44 | return retcode 45 | 46 | # Now we need to fix the relative paths of our included files 47 | for line in out.splitlines(): 48 | if not line or line.strip() == source_name: 49 | continue 50 | 51 | if not line.startswith(msvc_deps_prefix): 52 | print(line) 53 | continue 54 | 55 | filename = line[len(msvc_deps_prefix):].strip() 56 | filename = os.path.normpath(os.path.join(output_dir, filename)) 57 | 58 | print('{}{}'.format(msvc_deps_prefix, filename)) 59 | 60 | retcode, out = call([rc_name] + args + ["/fo" + output, source], 61 | shell=True, env=env, stderr=subprocess.STDOUT) 62 | for line in out.splitlines(): 63 | if (not line.startswith('Microsoft (R) Windows (R) Resource Compiler') and 64 | not line.startswith('Copyright (C) Microsoft Corporation') and 65 | line): 66 | print(line) 67 | return retcode 68 | 69 | if __name__ == '__main__': 70 | sys.exit(main(*sys.argv[1:])) 71 | -------------------------------------------------------------------------------- /toolchain/compiler_version.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import("//build/toolchain/toolchain.gni") 6 | 7 | # Bring all the windows toolchain definitions in. 8 | # Officially msc_ver etc. live here, but they're actually defined 9 | # in win/settings.gni. 10 | if (is_win) { 11 | import("//build/toolchain/win/settings.gni") 12 | } 13 | 14 | # Defines compiler versions for compilers used by multiple toolchains. 15 | 16 | # TODO(tim): Disable gcc_version on Windows? 17 | declare_args() { 18 | # Version of the GCC compiler. 19 | # Auto-detection is toolchain-specific and happens only if GCC is the active 20 | # compiler. 21 | # Format: MAJOR * 10000 + MINOR * 100 + PATCHLEVEL 22 | gcc_version = 0 23 | 24 | # Version of the Clang compiler. 25 | # Auto-detection is toolchain-specific and happens only if Clang is the 26 | # active compiler. 27 | # Format: MAJOR * 10000 + MINOR * 100 + PATCHLEVEL 28 | clang_version = 0 29 | } 30 | 31 | # If the user didn't specify the compiler version, attempt to autodetect it. 32 | 33 | if (gcc_version == 0 && !is_clang && !is_win) { 34 | if (is_android) { 35 | # sync with //build/toolchain/android/BUILD.gn 36 | import("//build/toolchain/android/settings.gni") 37 | _exe = "${android_tool_prefix}gcc" 38 | } else if (is_posix) { 39 | import("//build/toolchain/posix/settings.gni") 40 | _exe = gcc_cc 41 | } else { 42 | assert(false, "GCC isn't supported on this platform") 43 | } 44 | gcc_version = exec_script("posix/toolchain.py", 45 | [ 46 | "get_gcc_version", 47 | _exe, 48 | ], 49 | "trim value") 50 | } 51 | 52 | if (clang_version == 0 && is_clang && !is_win) { 53 | if (is_android) { 54 | # sync with //build/toolchain/android/BUILD.gn 55 | import("//build/toolchain/clang.gni") 56 | _exe = "$clang_base_path/bin/clang" 57 | } else if (is_posix) { 58 | import("//build/toolchain/posix/settings.gni") 59 | _exe = clang_cc 60 | } else { 61 | assert(false, "GCC isn't supported on this platform") 62 | } 63 | clang_version = exec_script("posix/toolchain.py", 64 | [ 65 | "get_clang_version", 66 | _exe, 67 | ], 68 | "trim value") 69 | } else if (clang_version == 0 && is_clang) { 70 | clang_version = win_clang_version 71 | } 72 | -------------------------------------------------------------------------------- /config/precompiled_header.gni: -------------------------------------------------------------------------------- 1 | declare_args() { 2 | disable_precompiled_headers = false 3 | } 4 | 5 | # Define a config setting up a precompiled header. 6 | # 7 | # Precompiled headers are done on a per-target basis. If you have just a couple 8 | # of files, the time it takes to precompile (~2 seconds) can actually be longer 9 | # than the time saved. On a Z620, a 100 file target compiles about 2 seconds 10 | # faster with precompiled headers, with greater savings for larger targets. 11 | # 12 | # Recommend precompiled headers for targets with more than 50 .cc files. 13 | # 14 | # precompiled_header (required) 15 | # A string referring to the header file. 16 | # 17 | # precompiled_source (required) 18 | # GN path of a source file which will be compiled to a PCH. 19 | template("precompiled_header") { 20 | assert(defined(invoker.precompiled_header), 21 | "Need precompiled_header in $target_name.") 22 | assert(defined(invoker.precompiled_source), 23 | "Need precompiled_header in $target_name.") 24 | 25 | config(target_name) { 26 | if (!disable_precompiled_headers) { 27 | # This is a string rather than a file GN knows about. It has to match 28 | # exactly what's in the /FI flag below, and what might appear in the source 29 | # code in quotes for an #include directive. 30 | precompiled_header = invoker.precompiled_header 31 | 32 | # This is a file that GN will compile with the above header. It will be 33 | # implicitly added to the sources (potentially multiple times, with one 34 | # variant for each language used in the target). 35 | 36 | if (is_win) { 37 | precompiled_source = invoker.precompiled_source 38 | 39 | # Force include the header. 40 | cflags = [ "/FI$precompiled_header" ] 41 | 42 | # Disable warning for "this file was empty after preprocessing". This 43 | # error is generated only in C mode for ANSI compatibility. It conflicts 44 | # with precompiled headers since the source file that's "compiled" for 45 | # making the precompiled header is empty. 46 | # 47 | # This error doesn't happen every time. In VS2013, it seems if the .pch 48 | # file doesn't exist, no error will be generated (probably MS tested this 49 | # case but forgot the other one?). To reproduce this error, do a build, 50 | # then delete the precompile.c.obj file, then build again. 51 | cflags_c = [ "/wd4206" ] 52 | } else { 53 | # TODO(tim): Dirty hack to mark the variable as used. 54 | precompiled_source = invoker.precompiled_source 55 | 56 | precompiled_source = invoker.precompiled_header 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /toolchain/mac/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import("//build/toolchain/mac/mac_sdk.gni") 6 | import("//build/toolchain/apple/toolchain.gni") 7 | 8 | # Specialisation of the apple_toolchain template to declare the toolchain 9 | # and its tools to build target for macOS platform. 10 | template("mac_toolchain") { 11 | assert(defined(invoker.toolchain_args), 12 | "Toolchains must declare toolchain_args") 13 | 14 | apple_toolchain(target_name) { 15 | forward_variables_from(invoker, "*", [ "toolchain_args" ]) 16 | 17 | bin_path = mac_bin_path 18 | 19 | toolchain_args = { 20 | forward_variables_from(invoker.toolchain_args, "*") 21 | current_os = "mac" 22 | 23 | if (target_os == "ios") { 24 | # TODO(crbug.com/753445): the use_sanitizer_coverage arg is currently 25 | # not supported by the Chromium mac_clang_x64 toolchain on iOS 26 | # distribution. 27 | use_sanitizer_coverage = false 28 | 29 | # Do not use Xcode version of clang when building macOS tools for the 30 | # host even if this is the version used to build for the iOS target. 31 | use_xcode_clang = false 32 | } 33 | } 34 | } 35 | } 36 | 37 | mac_toolchain("clang_arm") { 38 | toolchain_args = { 39 | current_cpu = "arm" 40 | } 41 | } 42 | 43 | mac_toolchain("clang_arm64") { 44 | toolchain_args = { 45 | current_cpu = "arm64" 46 | } 47 | } 48 | 49 | mac_toolchain("clang_x64") { 50 | toolchain_args = { 51 | current_cpu = "x64" 52 | } 53 | } 54 | 55 | mac_toolchain("clang_x86") { 56 | toolchain_args = { 57 | current_cpu = "x86" 58 | } 59 | } 60 | 61 | mac_toolchain("clang_x86_v8_arm") { 62 | toolchain_args = { 63 | current_cpu = "x86" 64 | 65 | if (defined(v8_current_cpu)) { 66 | v8_current_cpu = "arm" 67 | } 68 | } 69 | } 70 | 71 | mac_toolchain("clang_x86_v8_mipsel") { 72 | toolchain_args = { 73 | current_cpu = "x86" 74 | 75 | if (defined(v8_current_cpu)) { 76 | v8_current_cpu = "mipsel" 77 | } 78 | } 79 | } 80 | 81 | mac_toolchain("clang_x64_v8_arm64") { 82 | toolchain_args = { 83 | current_cpu = "x64" 84 | 85 | if (defined(v8_current_cpu)) { 86 | v8_current_cpu = "arm64" 87 | } 88 | } 89 | } 90 | 91 | mac_toolchain("clang_x64_v8_mips64el") { 92 | toolchain_args = { 93 | current_cpu = "x64" 94 | 95 | if (defined(v8_current_cpu)) { 96 | v8_current_cpu = "mips64el" 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /toolchain/clang_static_analyzer_wrapper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2017 The Chromium Authors. All rights reserved. 3 | # Use of this source code is governed by a BSD-style license that can be 4 | # found in the LICENSE file. 5 | 6 | """Adds an analysis build step to invocations of the Clang C/C++ compiler. 7 | 8 | Usage: clang_static_analyzer_wrapper.py [args...] 9 | """ 10 | 11 | import argparse 12 | import fnmatch 13 | import itertools 14 | import os 15 | import sys 16 | import wrapper_utils 17 | 18 | # Flags used to enable analysis for Clang invocations. 19 | analyzer_enable_flags = [ 20 | '--analyze', 21 | ] 22 | 23 | # Flags used to configure the analyzer's behavior. 24 | analyzer_option_flags = [ 25 | '-fdiagnostics-show-option', 26 | '-analyzer-checker=cplusplus', 27 | '-analyzer-opt-analyze-nested-blocks', 28 | '-analyzer-eagerly-assume', 29 | '-analyzer-output=text', 30 | '-analyzer-config', 31 | 'suppress-c++-stdlib=true', 32 | 33 | # List of checkers to execute. 34 | # The full list of checkers can be found at 35 | # https://clang-analyzer.llvm.org/available_checks.html. 36 | '-analyzer-checker=core', 37 | '-analyzer-checker=unix', 38 | '-analyzer-checker=deadcode', 39 | ] 40 | 41 | 42 | # Prepends every element of a list |args| with |token|. 43 | # e.g. ['-analyzer-foo', '-analyzer-bar'] => ['-Xanalyzer', '-analyzer-foo', 44 | # '-Xanalyzer', '-analyzer-bar'] 45 | def interleave_args(args, token): 46 | return list(sum(zip([token] * len(args), args), ())) 47 | 48 | 49 | def main(): 50 | parser = argparse.ArgumentParser() 51 | parser.add_argument('--mode', 52 | choices=['clang', 'cl'], 53 | required=True, 54 | help='Specifies the compiler argument convention to use.') 55 | parser.add_argument('args', nargs=argparse.REMAINDER) 56 | parsed_args = parser.parse_args() 57 | 58 | prefix = '-Xclang' if parsed_args.mode == 'cl' else '-Xanalyzer' 59 | cmd = parsed_args.args + analyzer_enable_flags + \ 60 | interleave_args(analyzer_option_flags, prefix) 61 | returncode, stderr = wrapper_utils.CaptureCommandStderr( 62 | wrapper_utils.CommandToRun(cmd)) 63 | sys.stderr.write(stderr) 64 | if returncode != 0: 65 | sys.stderr.write( 66 | """WARNING! The Clang static analyzer exited with error code %d. 67 | Please share the error details in crbug.com/695243 if this looks like 68 | a new regression.\n""" % (returncode)) 69 | 70 | returncode, stderr = wrapper_utils.CaptureCommandStderr( 71 | wrapper_utils.CommandToRun(parsed_args.args)) 72 | sys.stderr.write(stderr) 73 | 74 | return returncode 75 | 76 | if __name__ == '__main__': 77 | sys.exit(main()) 78 | -------------------------------------------------------------------------------- /config/compiler.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import("//build/config/sanitizers/sanitizers.gni") 6 | import("//build/config/arm.gni") 7 | 8 | declare_args() { 9 | # Compile in such a way as to enable profiling of the generated code. For 10 | # example, don't omit the frame pointer and leave in symbols. 11 | enable_profiling = false 12 | 13 | # Enable Link Time Optimization in optimized builds (output programs run 14 | # faster, but linking is up to 5-20x slower). 15 | enable_lto = is_official_build && is_win && !is_clang 16 | 17 | # Enable some optimizations that don't interfere with debugging. 18 | optimize_debug = false 19 | 20 | # use_debug_fission: whether to use split DWARF debug info 21 | # files. This can reduce link time significantly, but is incompatible 22 | # with some utilities such as icecc and ccache. Requires gold and 23 | # gcc >= 4.8 or clang. 24 | # http://gcc.gnu.org/wiki/DebugFission 25 | # 26 | # This is a placeholder value indicating that the code below should set 27 | # the default. This is necessary to delay the evaluation of the default 28 | # value expression until after its input values such as use_gold have 29 | # been set, e.g. by a toolchain_args() block. 30 | use_debug_fission = false 31 | 32 | # Whether to use the gold linker from binutils instead of lld or bfd. 33 | use_gold = false 34 | 35 | # Tell VS to create a PDB that references information in .obj files rather 36 | # than copying it all. This should improve linker performance. mspdbcmf.exe 37 | # can be used to convert a fastlink pdb to a normal one. 38 | is_win_fastlink = false 39 | } 40 | 41 | # Whether to emit frame pointers by default. 42 | if (is_mac || is_ios) { 43 | enable_frame_pointers = true 44 | } else if (is_win) { 45 | # 64-bit Windows ABI doesn't support frame pointers. 46 | if (target_cpu == "x64") { 47 | enable_frame_pointers = false 48 | } else { 49 | enable_frame_pointers = true 50 | } 51 | } else { 52 | # Explicitly ask for frame pointers, otherwise: 53 | # * Stacks may be missing for sanitizer and profiling builds. 54 | # * Debug tcmalloc can crash (crbug.com/636489). 55 | # * Stacks may be missing for arm64 crash dumps (crbug.com/391706). 56 | enable_frame_pointers = 57 | using_sanitizer || enable_profiling || is_debug || current_cpu == "arm64" 58 | } 59 | 60 | # Unwinding with frame pointers requires that frame pointers are enabled by 61 | # default for most translation units, and that the architecture isn't thumb, as 62 | # frame pointers are not correctly emitted for thumb. 63 | if (enable_frame_pointers && !(current_cpu == "arm" && arm_use_thumb)) { 64 | can_unwind_with_frame_pointers = true 65 | } else { 66 | can_unwind_with_frame_pointers = false 67 | } 68 | -------------------------------------------------------------------------------- /apple/compile_plist.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # Template to merge multiple plist files and perform variable substitutions. 6 | # 7 | # Arguments 8 | # 9 | # plist_templates: 10 | # string array, paths to plist files which will be used for the bundle. 11 | # 12 | # format: 13 | # string, the format to `plutil -convert` the plist to when 14 | # generating the output. 15 | # 16 | # substitutions: 17 | # string array, 'key=value' pairs used to replace ${key} by value 18 | # when generating the output plist file. 19 | # 20 | # output_name: 21 | # string, name of the generated plist file. 22 | template("compile_plist") { 23 | assert(defined(invoker.plist_templates), 24 | "A list of template plist files must be specified for $target_name") 25 | assert(defined(invoker.format), 26 | "The plist format must be specified for $target_name") 27 | assert(defined(invoker.substitutions), 28 | "A list of key=value pairs must be specified for $target_name") 29 | assert(defined(invoker.output_name), 30 | "The name of the output file must be specified for $target_name") 31 | 32 | _output_name = invoker.output_name 33 | _merged_name = get_path_info(_output_name, "dir") + "/" + 34 | get_path_info(_output_name, "name") + "_merged." + 35 | get_path_info(_output_name, "extension") 36 | 37 | _merge_target = target_name + "_merge" 38 | 39 | action(_merge_target) { 40 | forward_variables_from(invoker, 41 | [ 42 | "deps", 43 | "testonly", 44 | ]) 45 | 46 | script = "//build/apple/plist_util.py" 47 | sources = invoker.plist_templates 48 | outputs = [ _merged_name ] 49 | args = [ 50 | "merge", 51 | "-f=" + invoker.format, 52 | "-o=" + rebase_path(_merged_name, root_build_dir), 53 | ] + rebase_path(invoker.plist_templates, root_build_dir) 54 | } 55 | 56 | action(target_name) { 57 | forward_variables_from(invoker, 58 | [ 59 | "testonly", 60 | "visibility", 61 | ]) 62 | script = "//build/apple/plist_util.py" 63 | sources = [ _merged_name ] 64 | outputs = [ _output_name ] 65 | args = [ 66 | "substitute", 67 | "-f=" + invoker.format, 68 | "-o=" + rebase_path(_output_name, root_build_dir), 69 | "-t=" + rebase_path(_merged_name, root_build_dir), 70 | ] 71 | foreach(_substitution, invoker.substitutions) { 72 | args += [ "-s=$_substitution" ] 73 | } 74 | deps = [ ":$_merge_target" ] 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /toolchain/gcc_link_wrapper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2015 The Chromium Authors. All rights reserved. 3 | # Use of this source code is governed by a BSD-style license that can be 4 | # found in the LICENSE file. 5 | 6 | """Runs a linking command and optionally a strip command. 7 | 8 | This script exists to avoid using complex shell commands in 9 | gcc_toolchain.gni's tool("link"), in case the host running the compiler 10 | does not have a POSIX-like shell (e.g. Windows). 11 | """ 12 | 13 | import argparse 14 | import os 15 | import subprocess 16 | import sys 17 | 18 | import wrapper_utils 19 | 20 | 21 | # When running on a Windows host and using a toolchain whose tools are 22 | # actually wrapper scripts (i.e. .bat files on Windows) rather than binary 23 | # executables, the "command" to run has to be prefixed with this magic. 24 | # The GN toolchain definitions take care of that for when GN/Ninja is 25 | # running the tool directly. When that command is passed in to this 26 | # script, it appears as a unitary string but needs to be split up so that 27 | # just 'cmd' is the actual command given to Python's subprocess module. 28 | BAT_PREFIX = 'cmd /c call ' 29 | 30 | def CommandToRun(command): 31 | if command[0].startswith(BAT_PREFIX): 32 | command = command[0].split(None, 3) + command[1:] 33 | return command 34 | 35 | 36 | def main(): 37 | parser = argparse.ArgumentParser(description=__doc__) 38 | parser.add_argument('--strip', 39 | help='The strip binary to run', 40 | metavar='PATH') 41 | parser.add_argument('--unstripped-file', 42 | help='Executable file produced by linking command', 43 | metavar='FILE') 44 | parser.add_argument('--map-file', 45 | help=('Use --Wl,-Map to generate a map file. Will be ' 46 | 'gzipped if extension ends with .gz'), 47 | metavar='FILE') 48 | parser.add_argument('--output', 49 | required=True, 50 | help='Final output executable file', 51 | metavar='FILE') 52 | parser.add_argument('command', nargs='+', 53 | help='Linking command') 54 | args = parser.parse_args() 55 | 56 | # Work-around for gold being slow-by-default. http://crbug.com/632230 57 | fast_env = dict(os.environ) 58 | fast_env['LC_ALL'] = 'C' 59 | result = wrapper_utils.RunLinkWithOptionalMapFile(args.command, env=fast_env, 60 | map_file=args.map_file) 61 | if result != 0: 62 | return result 63 | 64 | # Finally, strip the linked executable (if desired). 65 | if args.strip: 66 | result = subprocess.call(CommandToRun([ 67 | args.strip, '--strip-unneeded', '-o', args.output, args.unstripped_file 68 | ])) 69 | 70 | return result 71 | 72 | 73 | if __name__ == "__main__": 74 | sys.exit(main()) 75 | -------------------------------------------------------------------------------- /toolchain/win/message_compiler.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | assert(is_win, "This only runs on Windows.") 6 | 7 | # Runs mc.exe over a list of sources. The outputs (a header and rc file) are 8 | # placed in the target gen dir, and compiled. 9 | # 10 | # sources 11 | # List of message files to process. 12 | # 13 | # user_mode_logging (optional bool) 14 | # Generates user-mode logging code. Defaults to false (no logging code). 15 | # 16 | # compile_generated_code (optional, deafults = true) 17 | # If unset or true, the generated code will be compiled and linked into 18 | # targets that depend on it. If set to false, the .h and .rc files will only 19 | # be generated. 20 | # 21 | # deps, public_deps, visibility 22 | # Normal meaning. 23 | template("message_compiler") { 24 | if (defined(invoker.compile_generated_code) && 25 | !invoker.compile_generated_code) { 26 | compile_generated_code = false 27 | action_name = target_name 28 | } else { 29 | compile_generated_code = true 30 | action_name = "${target_name}_mc" 31 | source_set_name = target_name 32 | } 33 | 34 | action_foreach(action_name) { 35 | if (compile_generated_code) { 36 | visibility = [ ":$source_set_name" ] 37 | } else { 38 | forward_variables_from(invoker, [ "visibility" ]) 39 | } 40 | 41 | script = "//build/toolchain/win/message_compiler.py" 42 | 43 | outputs = [ 44 | "$target_gen_dir/{{source_name_part}}.h", 45 | "$target_gen_dir/{{source_name_part}}.rc", 46 | ] 47 | 48 | args = [ 49 | # The first argument is the environment file saved to the build 50 | # directory. This is required because the Windows toolchain setup saves 51 | # the VC paths and such so that running "mc.exe" will work with the 52 | # configured toolchain. This file is in the root build dir. 53 | "environment.$current_cpu", 54 | 55 | # Where to put the header. 56 | "-h", 57 | rebase_path(target_gen_dir, root_build_dir), 58 | 59 | # Where to put the .rc file. 60 | "-r", 61 | rebase_path(target_gen_dir, root_build_dir), 62 | 63 | # Input is Unicode. 64 | "-u", 65 | ] 66 | if (defined(invoker.user_mode_logging) && invoker.user_mode_logging) { 67 | args += [ "-um" ] 68 | } 69 | args += [ "{{source}}" ] 70 | 71 | forward_variables_from(invoker, 72 | [ 73 | "deps", 74 | "public_deps", 75 | "sources", 76 | ]) 77 | } 78 | 79 | if (compile_generated_code) { 80 | # Compile the generated rc file. 81 | source_set(source_set_name) { 82 | forward_variables_from(invoker, [ "visibility" ]) 83 | sources = get_target_outputs(":$action_name") 84 | deps = [ 85 | ":$action_name", 86 | ] 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /toolchain/win/midl.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2014 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | assert(is_win) 6 | 7 | import("//build/toolchain/win/settings.gni") 8 | 9 | # This template defines a rule to invoke the MS IDL compiler. The generated 10 | # source code will be compiled and linked into targets that depend on this. 11 | # 12 | # Parameters 13 | # 14 | # sources 15 | # List of .idl file to process. 16 | # 17 | # out_dir (optional) 18 | # Directory to write the generated files to. Defaults to target_gen_dir. 19 | # 20 | # deps (optional) 21 | # visibility (optional) 22 | 23 | template("midl") { 24 | action_name = "${target_name}_idl_action" 25 | source_set_name = target_name 26 | 27 | assert(defined(invoker.sources), "Source must be defined for $target_name") 28 | 29 | if (defined(invoker.out_dir)) { 30 | out_dir = invoker.out_dir 31 | } else { 32 | out_dir = target_gen_dir 33 | } 34 | 35 | header_file = "{{source_name_part}}.h" 36 | dlldata_file = "{{source_name_part}}.dlldata.c" 37 | interface_identifier_file = "{{source_name_part}}_i.c" 38 | proxy_file = "{{source_name_part}}_p.c" 39 | type_library_file = "{{source_name_part}}.tlb" 40 | 41 | action_foreach(action_name) { 42 | visibility = [ ":$source_set_name" ] 43 | 44 | script = "//build/toolchain/win/midl_wrapper.py" 45 | 46 | sources = invoker.sources 47 | 48 | # Note that .tlb is not included in the outputs as it is not always 49 | # generated depending on the content of the input idl file. 50 | outputs = [ 51 | "$out_dir/$header_file", 52 | "$out_dir/$dlldata_file", 53 | "$out_dir/$interface_identifier_file", 54 | "$out_dir/$proxy_file", 55 | ] 56 | 57 | if (current_cpu == "x86") { 58 | win_tool_arch = x86_environment_path 59 | idl_target_platform = "win32" 60 | } else if (current_cpu == "x64") { 61 | win_tool_arch = x64_environment_path 62 | idl_target_platform = "x64" 63 | } else { 64 | assert(false, "Need environment for this arch") 65 | } 66 | 67 | args = [ 68 | win_tool_arch, 69 | rebase_path(out_dir, root_build_dir), 70 | type_library_file, 71 | header_file, 72 | dlldata_file, 73 | interface_identifier_file, 74 | proxy_file, 75 | "{{source}}", 76 | "/char", 77 | "signed", 78 | "/env", 79 | idl_target_platform, 80 | "/Oicf", 81 | ] 82 | 83 | forward_variables_from(invoker, [ "deps" ]) 84 | } 85 | 86 | source_set(target_name) { 87 | forward_variables_from(invoker, [ "visibility" ]) 88 | 89 | # We only compile the IID files from the IDL tool rather than all outputs. 90 | sources = process_file_template(invoker.sources, 91 | [ "$out_dir/$interface_identifier_file" ]) 92 | 93 | public_deps = [ 94 | ":$action_name", 95 | ] 96 | 97 | configs += [ "//build/config/win:midl_warnings" ] 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /config/libc++/settings.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import("//build/config/sanitizers/sanitizers.gni") 6 | import("//build/toolchain/mac/settings.gni") 7 | 8 | declare_args() { 9 | # Use in-tree libc++ (buildtools/third_party/libc++ and 10 | # buildtools/third_party/libc++abi) instead of the system C++ library for C++ 11 | # standard library support. 12 | # Don't check in changes that set this to false for more platforms; doing so 13 | # is not supported. 14 | use_custom_libcxx = is_fuchsia || is_android || is_mac || 15 | (is_ios && !use_xcode_clang) || (is_win && is_clang) || is_linux 16 | 17 | # Use libc++ instead of stdlibc++ when using the host_cpu toolchain, even if 18 | # use_custom_libcxx is false. This is useful for cross-compiles where a custom 19 | # toolchain for the target_cpu has been set as the default toolchain, but 20 | # use_custom_libcxx should still be true when building for the host. The 21 | # expected usage is to set use_custom_libcxx=false and 22 | # use_custom_libcxx_for_host=true in the passed in buildargs. 23 | use_custom_libcxx_for_host = false 24 | 25 | # Builds libcxx Natvis into the symbols for type visualization. 26 | # Set to false to workaround http://crbug.com/966676 and 27 | # http://crbug.com/966687. 28 | libcxx_natvis_include = true 29 | } 30 | 31 | use_custom_libcxx = 32 | use_custom_libcxx || (use_custom_libcxx_for_host && !is_a_target_toolchain) 33 | 34 | declare_args() { 35 | # WARNING: Setting this to a non-default value is highly discouraged. 36 | # If true, libc++ will be built as a shared library; otherwise libc++ will be 37 | # linked statically. Setting this to something other than the default is 38 | # unsupported and can be broken by libc++ rolls. Note that if this is set to 39 | # true, you must also set libcxx_abi_unstable=false, which is bad for 40 | # performance and memory use. 41 | libcxx_is_shared = use_custom_libcxx 42 | } 43 | 44 | # libc++abi needs to be exported from executables to be picked up by shared 45 | # libraries on certain instrumented builds. 46 | export_libcxxabi_from_executables = 47 | use_custom_libcxx && !is_ios && !is_win && (is_asan || is_ubsan_vptr) 48 | 49 | # On Android, many shared libraries get loaded from the context of a JRE. In 50 | # this case, there's no "main executable" to export libc++abi from. We could 51 | # export libc++abi from each "toplevel" shared library instead, but that would 52 | # require adding an explicit dependency for each one, and might introduce 53 | # subtle, hard-to-fix problems down the line if the dependency is missing. 54 | # 55 | # export_libcxxabi_from_executables was added to avoid having an RPATH set in 56 | # static sanitizer builds just for executables to find libc++. But on Android, 57 | # the Bionic dynamic loader doesn't even look at RPATH; instead, LD_LIBRARY_PATH 58 | # is set for tests. Because of this, we make libc++ a shared library on android 59 | # since it should get loaded properly. 60 | if (is_android && export_libcxxabi_from_executables) { 61 | export_libcxxabi_from_executables = false 62 | libcxx_is_shared = true 63 | } 64 | 65 | libcxx_prefix = "//buildtools/third_party/libc++/trunk" 66 | libcxxabi_prefix = "//buildtools/third_party/libc++abi/trunk" 67 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: Windows 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | - develop 8 | - feature/** 9 | push: 10 | branches: 11 | - master 12 | - develop 13 | - feature/** 14 | workflow_dispatch: 15 | release: 16 | types: published 17 | 18 | env: 19 | NINJA_BASE_URL: https://github.com/ninja-build/ninja/releases/download/ 20 | GN_BASE_URL: https://github.com/timniederhausen/gn/releases/download/2021.03/ 21 | 22 | jobs: 23 | windows: 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | include: 28 | # 2019 29 | - slug: windows-2019 debug 30 | gen_args: 'is_official_build = false' 31 | os: windows-2019 32 | ninja_release_name: v1.7.2/ninja-win.zip 33 | gn_release_name: gn-win-amd64.zip 34 | 35 | - slug: windows-2019 official 36 | gen_args: 'is_official_build = true' 37 | os: windows-2019 38 | ninja_release_name: v1.7.2/ninja-win.zip 39 | gn_release_name: gn-win-amd64.zip 40 | 41 | # 2022 42 | - slug: windows-2022 debug 43 | gen_args: 'is_official_build = false' 44 | os: windows-2022 45 | ninja_release_name: v1.7.2/ninja-win.zip 46 | gn_release_name: gn-win-amd64.zip 47 | 48 | - slug: windows-2022 official 49 | gen_args: 'is_official_build = true' 50 | os: windows-2022 51 | ninja_release_name: v1.7.2/ninja-win.zip 52 | gn_release_name: gn-win-amd64.zip 53 | 54 | runs-on: ${{ matrix.os }} 55 | 56 | steps: 57 | - uses: actions/checkout@v4 58 | with: 59 | # we need all everything for `git describe` to work correctly 60 | fetch-depth: 0 61 | 62 | - name: Install recent Ninja 63 | run: | 64 | Invoke-WebRequest -OutFile ninja.zip -Uri "${{ env.NINJA_BASE_URL }}${{ matrix.ninja_release_name }}" 65 | python -c 'import sys,zipfile;zipfile.ZipFile(sys.argv[1]).extractall()' ninja.zip 66 | 67 | - name: Install GN 68 | run: | 69 | Invoke-WebRequest -OutFile gn.zip -Uri "${{ env.GN_BASE_URL }}${{ matrix.gn_release_name }}" 70 | python -c 'import sys,zipfile;zipfile.ZipFile(sys.argv[1]).extractall()' gn.zip 71 | 72 | # Run gn_helpers unittests first - this should fail if we have an unsupported Python version 73 | # Only support Python3+ for now, otherwise we have to ship the mocking lib 74 | - name: Test gn_helpers 75 | run: | 76 | python3 gn_helpers_unittest.py 77 | 78 | # Setup test project for our //build 79 | - name: Setup test project 80 | run: | 81 | git clone --branch=testsrc --depth=1 https://github.com/timniederhausen/gn-build.git testsrc 82 | mkdir testsrc/build 83 | mv *.py testsrc/build/ 84 | mv config testsrc/build/ 85 | mv toolchain testsrc/build/ 86 | 87 | # Try to generate ninja files with different python versions 88 | - name: gen with python3 89 | run: | 90 | .\gn.exe gen out --args='${{ matrix.gen_args }}' --root=testsrc 91 | 92 | # Try to build the test project 93 | - name: Build 94 | run: | 95 | cat out/args.gn 96 | .\ninja.exe -C out 97 | cd out && ./hello 98 | -------------------------------------------------------------------------------- /config/arm.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2014 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # These are primarily relevant in current_cpu == "arm" contexts, where 6 | # ARM code is being compiled. But they can also be relevant in the 7 | # other contexts when the code will change its behavior based on the 8 | # cpu it wants to generate code for. 9 | if (current_cpu == "arm") { 10 | declare_args() { 11 | # Version of the ARM processor when compiling on ARM. Ignored on non-ARM 12 | # platforms. 13 | arm_version = 7 14 | 15 | # The ARM architecture. This will be a string like "armv6" or "armv7-a". 16 | # An empty string means to use the default for the arm_version. 17 | arm_arch = "" 18 | 19 | # The ARM floating point hardware. This will be a string like "neon" or 20 | # "vfpv3". An empty string means to use the default for the arm_version. 21 | arm_fpu = "" 22 | 23 | # The ARM floating point mode. This is either the string "hard", "soft", or 24 | # "softfp". An empty string means to use the default one for the 25 | # arm_version. 26 | arm_float_abi = "" 27 | 28 | # The ARM variant-specific tuning mode. This will be a string like "armv6" 29 | # or "cortex-a15". An empty string means to use the default for the 30 | # arm_version. 31 | arm_tune = "" 32 | 33 | # Whether to use the neon FPU instruction set or not. 34 | arm_use_neon = true 35 | 36 | # Whether to enable optional NEON code paths. 37 | arm_optionally_use_neon = false 38 | 39 | # Thumb is a reduced instruction set available on some ARM processors that 40 | # has increased code density. 41 | arm_use_thumb = true 42 | } 43 | 44 | assert(arm_float_abi == "" || arm_float_abi == "hard" || 45 | arm_float_abi == "soft" || arm_float_abi == "softfp") 46 | 47 | if (arm_version == 6) { 48 | if (arm_arch == "") { 49 | arm_arch = "armv6" 50 | } 51 | if (arm_tune != "") { 52 | arm_tune = "" 53 | } 54 | if (arm_float_abi == "") { 55 | arm_float_abi = "softfp" 56 | } 57 | if (arm_fpu == "") { 58 | arm_fpu = "vfp" 59 | } 60 | arm_use_thumb = false 61 | arm_use_neon = false 62 | } else if (arm_version == 7) { 63 | if (arm_arch == "") { 64 | arm_arch = "armv7-a" 65 | } 66 | if (arm_tune == "") { 67 | arm_tune = "generic-armv7-a" 68 | } 69 | 70 | if (arm_float_abi == "") { 71 | if (current_os == "android" || target_os == "android") { 72 | arm_float_abi = "softfp" 73 | } else { 74 | arm_float_abi = "hard" 75 | } 76 | } 77 | 78 | if (arm_fpu == "") { 79 | if (arm_use_neon) { 80 | arm_fpu = "neon" 81 | } else { 82 | arm_fpu = "vfpv3-d16" 83 | } 84 | } 85 | } else if (arm_version == 8) { 86 | if (arm_arch == "") { 87 | arm_arch = "armv8-a" 88 | } 89 | if (arm_tune == "") { 90 | arm_tune = "generic-armv8-a" 91 | } 92 | 93 | if (arm_float_abi == "") { 94 | if (current_os == "android" || target_os == "android") { 95 | arm_float_abi = "softfp" 96 | } else { 97 | arm_float_abi = "hard" 98 | } 99 | } 100 | 101 | if (arm_fpu == "") { 102 | if (arm_use_neon) { 103 | arm_fpu = "neon" 104 | } else { 105 | arm_fpu = "vfpv3-d16" 106 | } 107 | } 108 | } 109 | } else if (current_cpu == "arm64") { 110 | # arm64 supports only "hard". 111 | arm_float_abi = "hard" 112 | arm_use_neon = true 113 | } 114 | -------------------------------------------------------------------------------- /toolchain/mac/find_sdk.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2012 The Chromium Authors. All rights reserved. 3 | # Use of this source code is governed by a BSD-style license that can be 4 | # found in the LICENSE file. 5 | r"""Prints the lowest locally available SDK version greater than or equal to a 6 | given minimum sdk version to standard output. 7 | 8 | If --print_sdk_path is passed, then the script will also print the SDK path. 9 | If --print_bin_path is passed, then the script will also print the path to the 10 | toolchain bin dir. 11 | 12 | Usage: 13 | python find_sdk.py \ 14 | [--print_sdk_path] \ 15 | [--print_bin_path] \ 16 | 10.6 # Ignores SDKs < 10.6 17 | 18 | Sample Output: 19 | /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk 20 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ 21 | 10.14 22 | """ 23 | 24 | from __future__ import print_function 25 | 26 | import os 27 | import re 28 | import subprocess 29 | import sys 30 | 31 | from optparse import OptionParser 32 | 33 | 34 | class SdkError(Exception): 35 | def __init__(self, value): 36 | self.value = value 37 | def __str__(self): 38 | return repr(self.value) 39 | 40 | 41 | def parse_version(version_str): 42 | """'10.6' => [10, 6]""" 43 | return [int(s) for s in re.findall(r'(\d+)', version_str)] 44 | 45 | 46 | def main(): 47 | parser = OptionParser() 48 | parser.add_option("--print_sdk_path", 49 | action="store_true", dest="print_sdk_path", default=False, 50 | help="Additionally print the path the SDK (appears first).") 51 | parser.add_option("--print_bin_path", 52 | action="store_true", dest="print_bin_path", default=False, 53 | help="Additionally print the path the toolchain bin dir.") 54 | options, args = parser.parse_args() 55 | if len(args) != 1: 56 | parser.error('Please specify a minimum SDK version') 57 | min_sdk_version = args[0] 58 | 59 | 60 | job = subprocess.Popen(['xcode-select', '-print-path'], 61 | stdout=subprocess.PIPE, 62 | stderr=subprocess.STDOUT) 63 | out, err = job.communicate() 64 | if job.returncode != 0: 65 | print(out, file=sys.stderr) 66 | print(err, file=sys.stderr) 67 | raise Exception('Error %d running xcode-select' % job.returncode) 68 | dev_dir = out.decode('UTF-8').rstrip() 69 | sdk_dir = os.path.join( 70 | dev_dir, 'Platforms/MacOSX.platform/Developer/SDKs') 71 | 72 | if not os.path.isdir(sdk_dir): 73 | raise SdkError('Install Xcode, launch it, accept the license ' + 74 | 'agreement, and run `sudo xcode-select -s /path/to/Xcode.app` ' + 75 | 'to continue.') 76 | sdks = [re.findall('^MacOSX(\d+\.\d+)\.sdk$', s) for s in os.listdir(sdk_dir)] 77 | sdks = [s[0] for s in sdks if s] # [['10.5'], ['10.6']] => ['10.5', '10.6'] 78 | sdks = [s for s in sdks # ['10.5', '10.6'] => ['10.6'] 79 | if parse_version(s) >= parse_version(min_sdk_version)] 80 | if not sdks: 81 | raise Exception('No %s+ SDK found' % min_sdk_version) 82 | best_sdk = sorted(sdks, key=parse_version)[0] 83 | 84 | if options.print_sdk_path: 85 | sdk_name = 'MacOSX' + best_sdk + '.sdk' 86 | print(os.path.join(sdk_dir, sdk_name)) 87 | 88 | if options.print_bin_path: 89 | bin_path = 'Toolchains/XcodeDefault.xctoolchain/usr/bin/' 90 | print(os.path.join(dev_dir, bin_path)) 91 | 92 | return best_sdk 93 | 94 | 95 | if __name__ == '__main__': 96 | if sys.platform != 'darwin': 97 | raise Exception("This script only runs on Mac") 98 | print(main()) 99 | sys.exit(0) 100 | -------------------------------------------------------------------------------- /toolchain/win/manifest.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # HOW MANIFESTS WORK IN THE GN BUILD 6 | # 7 | # Use the windows_manifest template to declare a manifest generation step. 8 | # This will combine all listed .manifest files. To link this manifest, just 9 | # depend on the manifest target from your executable or shared library. 10 | # 11 | # This will define an empty placeholder target on non-Windows platforms so 12 | # the manifest declarations and dependencies do not need to be inside of OS 13 | # conditionals. 14 | # 15 | # A binary can depend on only one manifest target, but the manifest target 16 | # can depend on many individual .manifest files which will be merged. As a 17 | # result, only executables and shared libraries should depend on manifest 18 | # targets. If you want to add a manifest to a component, put the dependency 19 | # behind a "if (is_component_build)" conditional. 20 | # 21 | # Generally you will just want the defaults for the Chrome build. In this case 22 | # the binary should just depend on one of the targets in //build/win/. There 23 | # are also individual manifest files in that directory you can reference via 24 | # the *_manifest variables defined below to pick and choose only some defaults. 25 | # You might combine these with a custom manifest file to get specific behavior. 26 | 27 | # Construct a target to combine the given manifest files into a .rc file. 28 | # 29 | # Variables for the windows_manifest template: 30 | # 31 | # sources: (required) 32 | # List of source .manifest files to add. 33 | # 34 | # deps: (optional) 35 | # visibility: (optional) 36 | # Normal meaning. 37 | # 38 | # Example: 39 | # 40 | # windows_manifest("doom_melon_manifest") { 41 | # sources = [ 42 | # "doom_melon.manifest", # Custom values in here. 43 | # default_compatibility_manifest, # Want the normal OS compat list. 44 | # ] 45 | # } 46 | # 47 | # executable("doom_melon") { 48 | # deps = [ ":doom_melon_manifest" ] 49 | # ... 50 | # } 51 | 52 | if (is_win) { 53 | template("windows_manifest") { 54 | config_name = "${target_name}__config" 55 | source_set_name = target_name 56 | 57 | config(config_name) { 58 | visibility = [ ":$source_set_name" ] 59 | assert(defined(invoker.sources), 60 | "\"sources\" must be defined for a windows_manifest target") 61 | manifests = [] 62 | foreach(i, rebase_path(invoker.sources, root_build_dir)) { 63 | manifests += [ "/manifestinput:" + i ] 64 | } 65 | ldflags = [ 66 | "/manifest:embed", 67 | 68 | # We handle UAC by adding explicit .manifest files instead. 69 | "/manifestuac:no", 70 | ] + manifests 71 | } 72 | 73 | # This source set only exists to add a dep on the invoker's deps and to 74 | # add a public_config that sets ldflags on dependents. 75 | source_set(source_set_name) { 76 | forward_variables_from(invoker, [ "visibility" ]) 77 | public_configs = [ ":$config_name" ] 78 | 79 | # Apply any dependencies from the invoker to this target, since those 80 | # dependencies may have created the input manifest files. 81 | forward_variables_from(invoker, [ "deps" ]) 82 | } 83 | } 84 | } else { 85 | # Make a no-op group on non-Windows platforms so windows_manifest 86 | # instantiations don't need to be inside windows blocks. 87 | template("windows_manifest") { 88 | group(target_name) { 89 | # Prevent unused variable warnings on non-Windows platforms. 90 | assert(invoker.sources != "") 91 | assert(!defined(invoker.deps) || invoker.deps != "") 92 | assert(!defined(invoker.visibility) || invoker.visibility != "") 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /toolchain/mac/mac_sdk.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2014 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import("//build/toolchain/mac/mac_sdk_overrides.gni") 6 | import("//build/toolchain/mac/settings.gni") 7 | 8 | assert(current_os == "mac" || current_toolchain == default_toolchain) 9 | 10 | declare_args() { 11 | # The MACOSX_DEPLOYMENT_TARGET variable used when compiling. This partially 12 | # controls the minimum supported version of macOS for Chromium by 13 | # affecting the symbol availability rules. This may differ from 14 | # mac_min_system_version when dropping support for older macOSes but where 15 | # additional code changes are required to be compliant with the availability 16 | # rules. 17 | # Must be of the form x.x.x for Info.plist files. 18 | mac_deployment_target = "10.11.0" 19 | 20 | # The value of the LSMinimmumSystemVersion in Info.plist files. This partially 21 | # controls the minimum supported version of macOS for Chromium by 22 | # affecting the Info.plist. This may differ from mac_deployment_target when 23 | # dropping support for older macOSes. This should be greater than or equal to 24 | # the mac_deployment_target version. 25 | # Must be of the form x.x.x for Info.plist files. 26 | mac_min_system_version = "10.11.0" 27 | 28 | # Path to a specific version of the Mac SDK, not including a slash at the end. 29 | # If empty, the path to the lowest version greater than or equal to 30 | # mac_sdk_min is used. 31 | mac_sdk_path = "" 32 | 33 | # The SDK name as accepted by xcodebuild. 34 | mac_sdk_name = "macosx" 35 | 36 | # The SDK version used when making official builds. This is a single exact 37 | # version, not a minimum. If this version isn't available official builds 38 | # will fail. 39 | mac_sdk_official_version = "10.15" 40 | 41 | # If Xcode is not installed, and you are going to build standard C/C++ code only, 42 | # you should set it to false. 43 | mac_use_sdk = true 44 | } 45 | 46 | sdk_info_args = [] 47 | if (!use_system_xcode) { 48 | sdk_info_args += [ 49 | "--developer_dir", 50 | rebase_path(hermetic_xcode_path, "", root_build_dir), 51 | ] 52 | } 53 | 54 | sdk_info_args += [ mac_sdk_name ] 55 | 56 | if (mac_use_sdk) { 57 | _mac_sdk_result = exec_script("//build/toolchain/apple/sdk_info.py", sdk_info_args, "scope") 58 | xcode_version = _mac_sdk_result.xcode_version 59 | xcode_build = _mac_sdk_result.xcode_build 60 | 61 | if (use_system_xcode) { 62 | # The tool will print the SDK path on the first line, and the version on the 63 | # second line. 64 | find_sdk_args = [ 65 | "--print_sdk_path", 66 | "--print_bin_path", 67 | mac_sdk_min, 68 | ] 69 | find_sdk_lines = 70 | exec_script("//build/toolchain/mac/find_sdk.py", find_sdk_args, "list lines") 71 | mac_sdk_version = find_sdk_lines[2] 72 | if (mac_sdk_path == "") { 73 | mac_sdk_path = find_sdk_lines[0] 74 | mac_bin_path = find_sdk_lines[1] 75 | } else { 76 | mac_bin_path = find_sdk_lines[1] 77 | } 78 | } else { 79 | mac_sdk_version = mac_sdk_official_version 80 | _dev = hermetic_xcode_path + "/Contents/Developer" 81 | _sdk = "MacOSX${mac_sdk_version}.sdk" 82 | mac_sdk_path = _dev + "/Platforms/MacOSX.platform/Developer/SDKs/$_sdk" 83 | mac_bin_path = _dev + "/Toolchains/XcodeDefault.xctoolchain/usr/bin/" 84 | 85 | # If we're using hermetic Xcode, then we want the paths to be relative so that 86 | # generated ninja files are independent of the directory location. 87 | # TODO(thakis): Do this at the uses of this variable instead. 88 | mac_bin_path = rebase_path(mac_bin_path, root_build_dir) 89 | } 90 | } else { 91 | xcode_version = "" 92 | xcode_build = "" 93 | mac_sdk_version = "" 94 | mac_sdk_path = "" 95 | mac_bin_path = "" 96 | } 97 | -------------------------------------------------------------------------------- /config/mac/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import("//build/config/libc++/settings.gni") 6 | import("//build/toolchain/apple/symbols.gni") 7 | import("//build/toolchain/mac/mac_sdk.gni") 8 | import("//build/toolchain/sysroot.gni") 9 | 10 | # This is included by reference in the //build/config/compiler config that 11 | # is applied to all targets. It is here to separate out the logic. 12 | config("compiler") { 13 | # These flags are shared between the C compiler and linker. 14 | common_mac_flags = [] 15 | 16 | # CPU architecture. 17 | if (current_cpu == "x64") { 18 | clang_arch = "x86_64" 19 | } else if (current_cpu == "x86") { 20 | clang_arch = "i386" 21 | } else if (current_cpu == "armv7" || current_cpu == "arm") { 22 | clang_arch = "armv7" 23 | } else if (current_cpu == "arm64") { 24 | clang_arch = current_cpu 25 | } else { 26 | assert(false, "unknown current_cpu $current_cpu") 27 | } 28 | if (host_os == "mac") { 29 | common_mac_flags += [ 30 | "-arch", 31 | clang_arch, 32 | ] 33 | } else { 34 | common_mac_flags += [ "--target=$clang_arch-apple-macos" ] 35 | } 36 | 37 | # This is here so that all files get recompiled after an Xcode update. 38 | # (defines are passed via the command line, and build system rebuild things 39 | # when their commandline changes). Nothing should ever read this define. 40 | if (mac_use_sdk) { 41 | defines = [ "CR_XCODE_VERSION=$xcode_version" ] 42 | } 43 | 44 | asmflags = common_mac_flags 45 | cflags = common_mac_flags 46 | 47 | # Without this, the constructors and destructors of a C++ object inside 48 | # an Objective C struct won't be called, which is very bad. 49 | cflags_objcc = [ "-fobjc-call-cxx-cdtors" ] 50 | 51 | ldflags = common_mac_flags 52 | 53 | if (save_unstripped_output) { 54 | ldflags += [ "-Wcrl,unstripped," + rebase_path(root_out_dir) ] 55 | } 56 | 57 | if (export_libcxxabi_from_executables) { 58 | ldflags += [ "-Wl,-undefined,dynamic_lookup" ] 59 | } 60 | } 61 | 62 | # This is included by reference in the //build/config/compiler:runtime_library 63 | # config that is applied to all targets. It is here to separate out the logic 64 | # that is Mac-only. Please see that target for advice on what should go in 65 | # :runtime_library vs. :compiler. 66 | config("runtime_library") { 67 | if (mac_use_sdk) { 68 | common_flags = [ 69 | "-isysroot", 70 | rebase_path(sysroot, root_build_dir), 71 | "-mmacosx-version-min=$mac_deployment_target", 72 | ] 73 | } else { 74 | common_flags=[] 75 | } 76 | 77 | asmflags = common_flags 78 | cflags = common_flags 79 | ldflags = common_flags 80 | 81 | # Prevent Mac OS X AssertMacros.h (included by system header) from defining 82 | # macros that collide with common names, like 'check', 'require', and 83 | # 'verify'. 84 | # http://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/AssertMacros.h 85 | defines = [ "__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES=0" ] 86 | } 87 | 88 | # On Mac, this is used for everything except static libraries. 89 | config("mac_dynamic_flags") { 90 | ldflags = [ 91 | # Always load Objective-C categories and classes. 92 | "-Wl,-ObjC", 93 | 94 | # Load shared libraries next to the target. 95 | "-Wl,-rpath,@loader_path/.", 96 | ] 97 | } 98 | 99 | # On Mac, this is used only for executables. 100 | config("mac_executable_flags") { 101 | # Remove this when targeting >=10.7 since it is the default in that config. 102 | ldflags = [ "-Wl,-pie" ] # Position independent. 103 | } 104 | 105 | # The ldflags referenced below are handled by 106 | # //build/toolchain/apple/linker_driver.py. 107 | # Remove this config if a target wishes to change the arguments passed to the 108 | # strip command during linking. This config by default strips all symbols 109 | # from a binary, but some targets may wish to specify an exports file to 110 | # preserve specific symbols. 111 | config("strip_all") { 112 | if (enable_stripping) { 113 | ldflags = [ "-Wcrl,strip,-x,-S" ] 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /.github/workflows/posix.yml: -------------------------------------------------------------------------------- 1 | name: POSIX 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | - develop 8 | - feature/** 9 | push: 10 | branches: 11 | - master 12 | - develop 13 | - feature/** 14 | workflow_dispatch: 15 | release: 16 | types: published 17 | 18 | env: 19 | NINJA_BASE_URL: https://github.com/ninja-build/ninja/releases/download/ 20 | GN_BASE_URL: https://github.com/timniederhausen/gn/releases/download/2021.03/ 21 | 22 | jobs: 23 | posix: 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | include: 28 | - slug: linux-gcc-i386 29 | gen_args: 'gcc_cc="gcc-10" gcc_cxx="g++-10" target_cpu="x86" is_clang=false' 30 | os: ubuntu-22.04 31 | install: "g++-10-multilib" 32 | ninja_release_name: v1.7.2/ninja-linux.zip 33 | gn_release_name: gn-linux-amd64.tar.gz 34 | 35 | - slug: linux-gcc-amd64 36 | gen_args: 'gcc_cc="gcc-10" gcc_cxx="g++-10" target_cpu="x64" is_clang=false' 37 | os: ubuntu-22.04 38 | install: "g++-10" 39 | ninja_release_name: v1.7.2/ninja-linux.zip 40 | gn_release_name: gn-linux-amd64.tar.gz 41 | 42 | - slug: linux-clang-i386 43 | gen_args: 'clang_cc="clang-14" clang_cxx="clang++-14" target_cpu="x86" is_clang=true' 44 | os: ubuntu-22.04 45 | install: "clang-14 g++-multilib" 46 | ninja_release_name: v1.7.2/ninja-linux.zip 47 | gn_release_name: gn-linux-amd64.tar.gz 48 | 49 | - slug: linux-clang-amd64 50 | gen_args: 'clang_cc="clang-14" clang_cxx="clang++-14" target_cpu="x64" is_clang=true' 51 | os: ubuntu-22.04 52 | install: "clang-14" 53 | ninja_release_name: v1.7.2/ninja-linux.zip 54 | gn_release_name: gn-linux-amd64.tar.gz 55 | 56 | # disabled on master for now 57 | # see: https://github.com/timniederhausen/gn-build/runs/2483312992#step:9:24 58 | - slug: macos-amd64 59 | gen_args: 'is_clang=true is_official_build=true' 60 | os: macos-13 61 | ninja_release_name: v1.7.2/ninja-mac.zip 62 | gn_release_name: gn-macos-amd64.tar.gz 63 | 64 | runs-on: ${{ matrix.os }} 65 | 66 | steps: 67 | - name: Install packages 68 | if: matrix.install 69 | run: sudo apt install ${{ matrix.install }} 70 | 71 | - uses: actions/checkout@v4 72 | with: 73 | # we need all everything for `git describe` to work correctly 74 | fetch-depth: 0 75 | 76 | - name: Make dependencies directory 77 | run: | 78 | DEPS_DIR=$(cd ~; pwd)/deps 79 | mkdir -p ${DEPS_DIR} 80 | echo "export DEPS_DIR=$DEPS_DIR" >> "${GITHUB_WORKSPACE}/.env" 81 | 82 | - name: Install recent Ninja 83 | run: | 84 | source .env 85 | cd ${DEPS_DIR} 86 | wget --no-check-certificate --quiet -O ninja.zip "${NINJA_BASE_URL}${{ matrix.ninja_release_name }}" 87 | python -c 'import sys,zipfile;zipfile.ZipFile(sys.argv[1]).extractall()' ninja.zip 88 | chmod +x ninja 89 | 90 | - name: Install GN 91 | run: | 92 | source .env 93 | cd ${DEPS_DIR} 94 | wget --no-check-certificate --quiet -O gn.tgz "${GN_BASE_URL}${{ matrix.gn_release_name }}" 95 | tar xvf gn.tgz 96 | chmod +x gn 97 | 98 | # Run gn_helpers unittests first - this should fail if we have an unsupported Python version 99 | # Only support Python3+ for now, otherwise we have to ship the mocking lib 100 | - name: Test gn_helpers 101 | run: | 102 | python3 gn_helpers_unittest.py 103 | 104 | # Setup test project for our //build 105 | - name: Setup test project 106 | run: | 107 | source .env 108 | git clone --branch=testsrc --depth=1 https://github.com/timniederhausen/gn-build.git testsrc 109 | mkdir testsrc/build 110 | mv *.py testsrc/build/ 111 | mv config testsrc/build/ 112 | mv toolchain testsrc/build/ 113 | 114 | # Try to generate ninja files with different python versions 115 | - name: gen with python3 116 | run: | 117 | source .env 118 | echo script_executable = \"python3\" >> testsrc/.gn 119 | ${DEPS_DIR}/gn gen out --args='${{ matrix.gen_args }}' --root=testsrc 120 | 121 | # Try to build the test project 122 | - name: Build 123 | run: | 124 | source .env 125 | cat out/args.gn 126 | ${DEPS_DIR}/ninja -C out 127 | cd out && ./hello 128 | -------------------------------------------------------------------------------- /toolchain/win/settings.gni: -------------------------------------------------------------------------------- 1 | if (is_clang) { 2 | import("//build/toolchain/clang.gni") 3 | } 4 | 5 | declare_args() { 6 | # Desired version of Visual Studio. 7 | # If visual_studio_path is set, this must be 8 | # the version of the VS installation at the visual_studio_path. 9 | # 10 | # Use "2013" for Visual Studio 2013 or "latest" for automatically 11 | # choosing the highest version (visual_studio_path must be unset in 12 | # this case). 13 | visual_studio_version = "latest" 14 | 15 | # The path of your MSVC installation. 16 | # 17 | # If this is set you must set visual_studio_version as well. 18 | # Autodetected based on visual_studio_version. 19 | visual_studio_path = "" 20 | 21 | # Windows SDK version to use. 22 | # Can either be a full Windows 10 SDK number (e.g. 10.0.10240.0), 23 | # "8.1" for the Windows 8.1 SDK or "default" to use the VS default version. 24 | windows_sdk_version = "default" 25 | 26 | # Microsoft compiler version number clang-cl will report in _MSC_VER 27 | # (Defaults to the default version associated with the chosen VS version.) 28 | clang_msc_ver = 0 29 | 30 | # Allows us to avoid multiple toolchain.py invocations for multi-toolchain builds. 31 | cached_toolchain_data = "" 32 | } 33 | 34 | assert(visual_studio_version != "", "visual_studio_version must be non-empty") 35 | assert(visual_studio_path == "" || visual_studio_version != "latest", 36 | "You must set visual_studio_version if you set visual_studio_path") 37 | 38 | if (is_clang) { 39 | _clang_base_path_arg = clang_base_path 40 | _clang_msc_ver = "" + clang_msc_ver 41 | } else { 42 | _clang_base_path_arg = "" 43 | _clang_msc_ver = "" 44 | } 45 | 46 | if (host_os == "win") { 47 | clang_cl = "clang-cl.exe" 48 | } else { 49 | clang_cl = "clang-cl" 50 | } 51 | 52 | if (visual_studio_path == "") { 53 | # can't pass empty args 54 | visual_studio_path = "default" 55 | } 56 | 57 | if (cached_toolchain_data != "") { 58 | toolchain_data = cached_toolchain_data 59 | } else { 60 | toolchain_data = exec_script("toolchain.py", 61 | [ 62 | "setup_toolchain", 63 | visual_studio_version, 64 | visual_studio_path, 65 | windows_sdk_version, 66 | 67 | # Don't use clang_base_path directly, so we can 68 | # skip clang detection if not needed 69 | # (i.e. !is_clang). 70 | _clang_base_path_arg, 71 | _clang_msc_ver, 72 | ], 73 | "scope") 74 | } 75 | 76 | visual_studio_version = toolchain_data.visual_studio_version 77 | visual_studio_path = toolchain_data.visual_studio_path 78 | 79 | # Full path to the Windows SDK, not including a backslash at the end. 80 | windows_sdk_path = toolchain_data.windows_sdk_path 81 | 82 | # Value of the _MSC_VER variable. 83 | # see: https://msdn.microsoft.com/en-us/library/b0084kay.aspx 84 | msc_ver = toolchain_data.msc_ver 85 | 86 | # Value of the _MSC_FULL_VER variable. 87 | # see: https://msdn.microsoft.com/en-us/library/b0084kay.aspx 88 | msc_full_ver = toolchain_data.msc_full_ver 89 | 90 | if (defined(toolchain_data.clang_version)) { 91 | win_clang_version = toolchain_data.clang_version 92 | } else { 93 | win_clang_version = 0 94 | } 95 | 96 | # current_toolchain_data: Settings specific to current_os/current_cpu 97 | if (current_os == "win") { 98 | if (current_cpu == "x86") { 99 | assert(defined(toolchain_data.x86)) 100 | current_toolchain_data = toolchain_data.x86 101 | } else if (current_cpu == "x64") { 102 | assert(defined(toolchain_data.x64)) 103 | current_toolchain_data = toolchain_data.x64 104 | } else if (current_cpu == "arm") { 105 | assert(defined(toolchain_data.arm)) 106 | current_toolchain_data = toolchain_data.arm 107 | } else if (current_cpu == "arm64") { 108 | assert(defined(toolchain_data.arm64)) 109 | current_toolchain_data = toolchain_data.arm64 110 | } 111 | } else if (current_os == "winuwp") { 112 | if (current_cpu == "x86") { 113 | assert(defined(toolchain_data.x86_uwp)) 114 | current_toolchain_data = toolchain_data.x86_uwp 115 | } else if (current_cpu == "x64") { 116 | assert(defined(toolchain_data.x64_uwp)) 117 | current_toolchain_data = toolchain_data.x64_uwp 118 | } else if (current_cpu == "arm") { 119 | assert(defined(toolchain_data.arm_uwp)) 120 | current_toolchain_data = toolchain_data.arm_uwp 121 | } else if (current_cpu == "arm64") { 122 | assert(defined(toolchain_data.arm64_uwp)) 123 | current_toolchain_data = toolchain_data.arm64_uwp 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /toolchain/gcc_solink_wrapper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2015 The Chromium Authors. All rights reserved. 3 | # Use of this source code is governed by a BSD-style license that can be 4 | # found in the LICENSE file. 5 | 6 | """Runs 'ld -shared' and generates a .TOC file that's untouched when unchanged. 7 | 8 | This script exists to avoid using complex shell commands in 9 | gcc_toolchain.gni's tool("solink"), in case the host running the compiler 10 | does not have a POSIX-like shell (e.g. Windows). 11 | """ 12 | 13 | import argparse 14 | import os 15 | import subprocess 16 | import sys 17 | 18 | import wrapper_utils 19 | 20 | 21 | def CollectSONAME(args): 22 | """Replaces: readelf -d $sofile | grep SONAME""" 23 | toc = '' 24 | readelf = subprocess.Popen(wrapper_utils.CommandToRun( 25 | [args.readelf, '-d', args.sofile]), stdout=subprocess.PIPE, 26 | bufsize=-1, universal_newlines=True) 27 | for line in readelf.stdout: 28 | if 'SONAME' in line: 29 | toc += line 30 | return readelf.wait(), toc 31 | 32 | 33 | def CollectDynSym(args): 34 | """Replaces: nm --format=posix -g -D $sofile | cut -f1-2 -d' '""" 35 | toc = '' 36 | nm = subprocess.Popen(wrapper_utils.CommandToRun([ 37 | args.nm, '--format=posix', '-g', '-D', args.sofile]), 38 | stdout=subprocess.PIPE, bufsize=-1, universal_newlines=True) 39 | for line in nm.stdout: 40 | toc += ' '.join(line.split(' ', 2)[:2]) + '\n' 41 | return nm.wait(), toc 42 | 43 | 44 | def CollectTOC(args): 45 | result, toc = CollectSONAME(args) 46 | if result == 0: 47 | result, dynsym = CollectDynSym(args) 48 | toc += dynsym 49 | return result, toc 50 | 51 | 52 | def UpdateTOC(tocfile, toc): 53 | if os.path.exists(tocfile): 54 | old_toc = open(tocfile, 'r').read() 55 | else: 56 | old_toc = None 57 | if toc != old_toc: 58 | open(tocfile, 'w').write(toc) 59 | 60 | 61 | def main(): 62 | parser = argparse.ArgumentParser(description=__doc__) 63 | parser.add_argument('--readelf', 64 | required=True, 65 | help='The readelf binary to run', 66 | metavar='PATH') 67 | parser.add_argument('--nm', 68 | required=True, 69 | help='The nm binary to run', 70 | metavar='PATH') 71 | parser.add_argument('--strip', 72 | help='The strip binary to run', 73 | metavar='PATH') 74 | parser.add_argument('--sofile', 75 | required=True, 76 | help='Shared object file produced by linking command', 77 | metavar='FILE') 78 | parser.add_argument('--tocfile', 79 | required=True, 80 | help='Output table-of-contents file', 81 | metavar='FILE') 82 | parser.add_argument('--map-file', 83 | help=('Use --Wl,-Map to generate a map file. Will be ' 84 | 'gzipped if extension ends with .gz'), 85 | metavar='FILE') 86 | parser.add_argument('--output', 87 | required=True, 88 | help='Final output shared object file', 89 | metavar='FILE') 90 | parser.add_argument('--resource-whitelist', 91 | help='Merge all resource whitelists into a single file.', 92 | metavar='PATH') 93 | parser.add_argument('command', nargs='+', 94 | help='Linking command') 95 | args = parser.parse_args() 96 | 97 | # Work-around for gold being slow-by-default. http://crbug.com/632230 98 | fast_env = dict(os.environ) 99 | fast_env['LC_ALL'] = 'C' 100 | 101 | if args.resource_whitelist: 102 | whitelist_candidates = wrapper_utils.ResolveRspLinks(args.command) 103 | wrapper_utils.CombineResourceWhitelists( 104 | whitelist_candidates, args.resource_whitelist) 105 | 106 | # First, run the actual link. 107 | command = wrapper_utils.CommandToRun(args.command) 108 | result = wrapper_utils.RunLinkWithOptionalMapFile(command, env=fast_env, 109 | map_file=args.map_file) 110 | 111 | if result != 0: 112 | return result 113 | 114 | # Next, generate the contents of the TOC file. 115 | result, toc = CollectTOC(args) 116 | if result != 0: 117 | return result 118 | 119 | # If there is an existing TOC file with identical contents, leave it alone. 120 | # Otherwise, write out the TOC file. 121 | UpdateTOC(args.tocfile, toc) 122 | 123 | # Finally, strip the linked shared object file (if desired). 124 | if args.strip: 125 | result = subprocess.call(wrapper_utils.CommandToRun( 126 | [args.strip, '--strip-unneeded', '-o', args.output, args.sofile])) 127 | 128 | return result 129 | 130 | 131 | if __name__ == "__main__": 132 | sys.exit(main()) 133 | -------------------------------------------------------------------------------- /toolchain/android/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2013 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import("//build/toolchain/android/settings.gni") 6 | import("//build/toolchain/toolchain.gni") 7 | import("//build/toolchain/sysroot.gni") 8 | import("//build/toolchain/gcc_toolchain.gni") 9 | import("//build/toolchain/clang.gni") 10 | 11 | # The Android GCC toolchains share most of the same parameters, so we have this 12 | # wrapper around gcc_toolchain to avoid duplication of logic. 13 | # 14 | # Parameters: 15 | # - toolchain_root 16 | # Path to cpu-specific toolchain within the ndk. 17 | # - sysroot 18 | # Sysroot for this architecture. 19 | # - lib_dir 20 | # Subdirectory inside of sysroot where libs go. 21 | # - binary_prefix 22 | # Prefix of compiler executables. 23 | template("android_gcc_toolchain") { 24 | gcc_toolchain(target_name) { 25 | assert(defined(invoker.toolchain_args), 26 | "toolchain_args must be defined for android_gcc_toolchain()") 27 | toolchain_args = invoker.toolchain_args 28 | toolchain_args.current_os = "android" 29 | 30 | # Make our manually injected libs relative to the build dir. 31 | _ndk_lib = 32 | rebase_path(invoker.sysroot + "/" + invoker.lib_dir, root_build_dir) 33 | 34 | libs_section_prefix = "$_ndk_lib/crtbegin_dynamic.o" 35 | libs_section_postfix = "$_ndk_lib/crtend_android.o" 36 | 37 | solink_libs_section_prefix = "$_ndk_lib/crtbegin_so.o" 38 | solink_libs_section_postfix = "$_ndk_lib/crtend_so.o" 39 | 40 | _android_tool_prefix = 41 | "${invoker.toolchain_root}/bin/${invoker.binary_prefix}-" 42 | 43 | # The tools should be run relative to the build dir. 44 | _tool_prefix = rebase_path("$_android_tool_prefix", root_build_dir) 45 | 46 | # Use the clang specified by the toolchain if there is one. Otherwise fall 47 | # back to the global flag. 48 | if (defined(toolchain_args.is_clang)) { 49 | toolchain_uses_clang = toolchain_args.is_clang 50 | } else { 51 | toolchain_uses_clang = is_clang 52 | } 53 | 54 | if (toolchain_uses_clang) { 55 | _prefix = rebase_path("$clang_base_path/bin", root_build_dir) 56 | cc = "$_prefix/clang" 57 | cxx = "$_prefix/clang++" 58 | } else { 59 | cc = "${_tool_prefix}gcc" 60 | cxx = "${_tool_prefix}g++" 61 | } 62 | ar = _tool_prefix + "ar" 63 | ld = cxx 64 | readelf = _tool_prefix + "readelf" 65 | nm = _tool_prefix + "nm" 66 | strip = "${_tool_prefix}strip" 67 | 68 | # Don't use .cr.so for loadable_modules since they are always loaded via 69 | # absolute path. 70 | loadable_module_extension = ".so" 71 | } 72 | } 73 | 74 | template("android_gcc_toolchains_helper") { 75 | android_gcc_toolchain("android_$target_name") { 76 | forward_variables_from(invoker, "*") 77 | toolchain_args.is_clang = false 78 | } 79 | 80 | android_gcc_toolchain("android_clang_$target_name") { 81 | forward_variables_from(invoker, "*") 82 | toolchain_args.is_clang = true 83 | } 84 | } 85 | 86 | android_gcc_toolchains_helper("x86") { 87 | toolchain_root = x86_android_toolchain_root 88 | sysroot = "$android_ndk_root/$x86_android_sysroot_subdir" 89 | lib_dir = "usr/lib" 90 | binary_prefix = "i686-linux-android" 91 | toolchain_args = { 92 | current_cpu = "x86" 93 | } 94 | } 95 | 96 | android_gcc_toolchains_helper("arm") { 97 | toolchain_root = arm_android_toolchain_root 98 | sysroot = "$android_ndk_root/$arm_android_sysroot_subdir" 99 | lib_dir = "usr/lib" 100 | binary_prefix = "arm-linux-androideabi" 101 | toolchain_args = { 102 | current_cpu = "arm" 103 | } 104 | } 105 | 106 | android_gcc_toolchains_helper("mipsel") { 107 | toolchain_root = mips_android_toolchain_root 108 | sysroot = "$android_ndk_root/$mips_android_sysroot_subdir" 109 | lib_dir = "usr/lib" 110 | binary_prefix = "mipsel-linux-android" 111 | toolchain_args = { 112 | current_cpu = "mipsel" 113 | } 114 | } 115 | 116 | android_gcc_toolchains_helper("x64") { 117 | toolchain_root = x86_64_android_toolchain_root 118 | sysroot = "$android_ndk_root/$x86_64_android_sysroot_subdir" 119 | lib_dir = "usr/lib64" 120 | binary_prefix = "x86_64-linux-android" 121 | toolchain_args = { 122 | current_cpu = "x64" 123 | } 124 | } 125 | 126 | android_gcc_toolchains_helper("arm64") { 127 | toolchain_root = arm64_android_toolchain_root 128 | sysroot = "$android_ndk_root/$arm64_android_sysroot_subdir" 129 | lib_dir = "usr/lib" 130 | binary_prefix = "aarch64-linux-android" 131 | toolchain_args = { 132 | current_cpu = "arm64" 133 | } 134 | } 135 | 136 | android_gcc_toolchains_helper("mips64el") { 137 | toolchain_root = mips64_android_toolchain_root 138 | sysroot = "$android_ndk_root/$mips64_android_sysroot_subdir" 139 | lib_dir = "usr/lib64" 140 | binary_prefix = "mips64el-linux-android" 141 | toolchain_args = { 142 | current_cpu = "mips64el" 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /toolchain/wrapper_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | """Helper functions for gcc_toolchain.gni wrappers.""" 6 | 7 | import gzip 8 | import os 9 | import re 10 | import subprocess 11 | import shlex 12 | import shutil 13 | import sys 14 | import threading 15 | 16 | _BAT_PREFIX = 'cmd /c call ' 17 | _WHITELIST_RE = re.compile('whitelisted_resource_(?P[0-9]+)') 18 | 19 | 20 | def _GzipThenDelete(src_path, dest_path): 21 | # Results for Android map file with GCC on a z620: 22 | # Uncompressed: 207MB 23 | # gzip -9: 16.4MB, takes 8.7 seconds. 24 | # gzip -1: 21.8MB, takes 2.0 seconds. 25 | # Piping directly from the linker via -print-map (or via -Map with a fifo) 26 | # adds a whopping 30-45 seconds! 27 | with open(src_path, 'rb') as f_in, gzip.GzipFile(dest_path, 'wb', 1) as f_out: 28 | shutil.copyfileobj(f_in, f_out) 29 | os.unlink(src_path) 30 | 31 | 32 | def CommandToRun(command): 33 | """Generates commands compatible with Windows. 34 | 35 | When running on a Windows host and using a toolchain whose tools are 36 | actually wrapper scripts (i.e. .bat files on Windows) rather than binary 37 | executables, the |command| to run has to be prefixed with this magic. 38 | The GN toolchain definitions take care of that for when GN/Ninja is 39 | running the tool directly. When that command is passed in to this 40 | script, it appears as a unitary string but needs to be split up so that 41 | just 'cmd' is the actual command given to Python's subprocess module. 42 | 43 | Args: 44 | command: List containing the UNIX style |command|. 45 | 46 | Returns: 47 | A list containing the Windows version of the |command|. 48 | """ 49 | if command[0].startswith(_BAT_PREFIX): 50 | command = command[0].split(None, 3) + command[1:] 51 | return command 52 | 53 | 54 | def RunLinkWithOptionalMapFile(command, env=None, map_file=None): 55 | """Runs the given command, adding in -Wl,-Map when |map_file| is given. 56 | 57 | Also takes care of gzipping when |map_file| ends with .gz. 58 | 59 | Args: 60 | command: List of arguments comprising the command. 61 | env: Environment variables. 62 | map_file: Path to output map_file. 63 | 64 | Returns: 65 | The exit code of running |command|. 66 | """ 67 | tmp_map_path = None 68 | if map_file and map_file.endswith('.gz'): 69 | tmp_map_path = map_file + '.tmp' 70 | command.append('-Wl,-Map,' + tmp_map_path) 71 | elif map_file: 72 | command.append('-Wl,-Map,' + map_file) 73 | 74 | result = subprocess.call(command, env=env) 75 | 76 | if tmp_map_path and result == 0: 77 | threading.Thread( 78 | target=lambda: _GzipThenDelete(tmp_map_path, map_file)).start() 79 | elif tmp_map_path and os.path.exists(tmp_map_path): 80 | os.unlink(tmp_map_path) 81 | 82 | return result 83 | 84 | 85 | def ResolveRspLinks(inputs): 86 | """Return a list of files contained in a response file. 87 | 88 | Args: 89 | inputs: A command containing rsp files. 90 | 91 | Returns: 92 | A set containing the rsp file content.""" 93 | rspfiles = [a[1:] for a in inputs if a.startswith('@')] 94 | resolved = set() 95 | for rspfile in rspfiles: 96 | with open(rspfile, 'r') as f: 97 | resolved.update(shlex.split(f.read())) 98 | 99 | return resolved 100 | 101 | 102 | def CombineResourceWhitelists(whitelist_candidates, outfile): 103 | """Combines all whitelists for a resource file into a single whitelist. 104 | 105 | Args: 106 | whitelist_candidates: List of paths to rsp files containing all targets. 107 | outfile: Path to save the combined whitelist. 108 | """ 109 | whitelists = ('%s.whitelist' % candidate for candidate in whitelist_candidates 110 | if os.path.exists('%s.whitelist' % candidate)) 111 | 112 | resources = set() 113 | for whitelist in whitelists: 114 | with open(whitelist, 'r') as f: 115 | resources.update(f.readlines()) 116 | 117 | with open(outfile, 'w') as f: 118 | f.writelines(resources) 119 | 120 | 121 | def ExtractResourceIdsFromPragmaWarnings(text): 122 | """Returns set of resource IDs that are inside unknown pragma warnings. 123 | 124 | Args: 125 | text: The text that will be scanned for unknown pragma warnings. 126 | 127 | Returns: 128 | A set containing integers representing resource IDs. 129 | """ 130 | used_resources = set() 131 | lines = text.splitlines() 132 | for ln in lines: 133 | match = _WHITELIST_RE.search(ln) 134 | if match: 135 | resource_id = int(match.group('resource_id')) 136 | used_resources.add(resource_id) 137 | 138 | return used_resources 139 | 140 | 141 | def CaptureCommandStderr(command, env=None): 142 | """Returns the stderr of a command. 143 | 144 | Args: 145 | command: A list containing the command and arguments. 146 | env: Environment variables for the new process. 147 | """ 148 | child = subprocess.Popen(command, stderr=subprocess.PIPE, env=env, 149 | universal_newlines=True) 150 | _, stderr = child.communicate() 151 | return child.returncode, stderr 152 | -------------------------------------------------------------------------------- /config/libc++/BUILD.gn: -------------------------------------------------------------------------------- 1 | import("//build/config/libc++/c++.gni") 2 | 3 | assert(use_custom_libcxx, "should only be used if use_custom_libcxx is set") 4 | 5 | declare_args() { 6 | # lldb pretty printing only works when libc++ is built in the __1 (or __ndk1) 7 | # namespaces. For pretty printing to work out-of-the-box on Mac (where lldb 8 | # is primarily used), this flag is set to false to build with the __1 9 | # namespace (to maintain ABI compatibility, this implies building without 10 | # _LIBCPP_ABI_UNSTABLE). This is not necessary on non-component builds 11 | # because we leave the ABI version set to __1 in that case because libc++ 12 | # symbols are not exported. 13 | # TODO(thomasanderson): Set this to true by default once rL352899 is available 14 | # in MacOS's lldb. 15 | libcxx_abi_unstable = !(is_apple && is_debug && is_component_build) 16 | } 17 | 18 | # This is included by reference in the //build/config/compiler:runtime_library 19 | # config that is applied to all targets. It is here to separate out the logic 20 | # that is specific to libc++. Please see that target for advice on what should 21 | # go in :runtime_library vs. :compiler. 22 | config("runtime_library") { 23 | cflags = [] 24 | cflags_cc = [] 25 | defines = [] 26 | ldflags = [] 27 | libs = [] 28 | 29 | if (libcxx_abi_unstable) { 30 | defines += [ "_LIBCPP_ABI_UNSTABLE" ] 31 | } 32 | 33 | if (libcxx_is_shared) { 34 | # When libcxx_is_shared is true, symbols from libc++.so are exported for 35 | # all DSOs to use. If the system libc++ gets loaded (indirectly through 36 | # a system library), then it will conflict with our libc++.so. Add a 37 | # custom ABI version if we're building with _LIBCPP_ABI_UNSTABLE to avoid 38 | # conflicts. 39 | # 40 | # Windows doesn't need to set _LIBCPP_ABI_VERSION since there's no system 41 | # C++ library we could conflict with. 42 | if (libcxx_abi_unstable && !is_win) { 43 | defines += [ "_LIBCPP_ABI_VERSION=Cr" ] 44 | } 45 | } else { 46 | # Don't leak any symbols on a static build. 47 | defines += [ "_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS" ] 48 | if (!export_libcxxabi_from_executables && !is_win) { 49 | defines += [ "_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS" ] 50 | } 51 | } 52 | 53 | defines += [ 54 | "_LIBCPP_ENABLE_NODISCARD", 55 | 56 | # TODO(crbug.com/1166707): libc++ requires this macro. 57 | "_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS", 58 | ] 59 | 60 | # Work around a symbol conflict between GRPC and the Fuchsia SDK. 61 | # TODO(crbug.com/1166970): Remove this when resolved. 62 | if (is_fuchsia) { 63 | defines += [ "_LIBCPP_NO_NATIVE_SEMAPHORES" ] 64 | } 65 | 66 | # libc++ has two levels of debug mode. Setting _LIBCPP_DEBUG to zero 67 | # enables most assertions. Setting it to one additionally enables iterator 68 | # debugging. See https://libcxx.llvm.org/docs/DesignDocs/DebugMode.html 69 | if (enable_iterator_debugging) { 70 | defines += [ "_LIBCPP_DEBUG=1" ] 71 | } else if (is_debug || dcheck_always_on) { 72 | defines += [ "_LIBCPP_DEBUG=0" ] 73 | } 74 | 75 | if (is_win) { 76 | # Intentionally not using libc++abi on Windows because libc++abi only 77 | # implements the Itanium C++ ABI, and not the Microsoft ABI which we use on 78 | # Windows (and we need to use in order to interoperate correctly with COM 79 | # among other things). 80 | assert(!export_libcxxabi_from_executables, 81 | "Don't use libcxxabi on Windows.") 82 | 83 | cflags_cc += 84 | [ "-I" + rebase_path("$libcxx_prefix/include", root_build_dir) ] 85 | 86 | # Prevent libc++ from embedding linker flags to try to automatically link 87 | # against its runtime library. This is unnecessary with our build system, 88 | # and can also result in build failures if libc++'s name for a library 89 | # does not match ours. 90 | defines += [ "_LIBCPP_NO_AUTO_LINK" ] 91 | 92 | if (is_component_build) { 93 | # TODO(crbug.com/1090975): Disable the exclude_from_explicit_instantiation 94 | # to work around compiler bugs in the interaction between it and 95 | # dllimport/dllexport. 96 | defines += [ "_LIBCPP_HIDE_FROM_ABI=_LIBCPP_HIDDEN" ] 97 | } 98 | 99 | # Add a debug visualizer for Microsoft's debuggers so that they can display 100 | # libc++ types well. 101 | if (libcxx_natvis_include) { 102 | # chrome.natvis listed as an input in //buildtools/third_party/libc++ to 103 | # guarantee relinking on changes. 104 | ldflags += [ "/NATVIS:" + rebase_path("libc++.natvis", root_build_dir) ] 105 | } 106 | } else { 107 | cflags_cc += [ 108 | "-nostdinc++", 109 | "-isystem" + rebase_path("$libcxx_prefix/include", root_build_dir), 110 | "-isystem" + rebase_path("$libcxxabi_prefix/include", root_build_dir), 111 | ] 112 | cflags_objcc = cflags_cc 113 | 114 | defines += [ "CR_LIBCXX_REVISION=$libcxx_revision" ] 115 | 116 | # Make sure we don't link against the system libstdc++ or libc++. 117 | if (is_clang) { 118 | ldflags += [ "-nostdlib++" ] 119 | } else { 120 | # Gcc has a built-in abs() definition with default visibility. 121 | # If it was not disabled, it would conflict with libc++'s abs() 122 | # with hidden visibility. 123 | cflags += [ "-fno-builtin-abs" ] 124 | 125 | ldflags += [ "-nodefaultlibs" ] 126 | 127 | # Unfortunately, there's no way to disable linking against just libc++ 128 | # (gcc doesn't have -notstdlib++: 129 | # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83931); -nodefaultlibs 130 | # removes all of the default libraries, so add back the ones that we need. 131 | libs += [ 132 | "c", 133 | "gcc_s", 134 | "m", 135 | "rt", 136 | ] 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /compiled_action.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2014 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # This file introduces two related templates that act like action and 6 | # action_foreach but instead of running a Python script, it will compile a 7 | # given tool in the host toolchain and run that (either once or over the list 8 | # of inputs, depending on the variant). 9 | # 10 | # Parameters 11 | # 12 | # tool (required) 13 | # [label] Label of the tool to run. This should be an executable, and 14 | # this label should not include a toolchain (anything in parens). The 15 | # host compile of this tool will be used. 16 | # 17 | # outputs (required) 18 | # [list of files] Like the outputs of action (if using "compiled_action", 19 | # this would be just the list of outputs), or action_foreach (if using 20 | # "compiled_action_foreach", this would contain source expansions mapping 21 | # input to output files). 22 | # 23 | # args (required) 24 | # [list of strings] Same meaning as action/action_foreach. 25 | # 26 | # inputs (optional) 27 | # Files the binary takes as input. The step will be re-run whenever any 28 | # of these change. If inputs is empty, the step will run only when the 29 | # binary itself changes. 30 | # 31 | # depfile 32 | # deps 33 | # visibility (all optional) 34 | # Same meaning as action/action_foreach. 35 | # 36 | # 37 | # Example of usage: 38 | # 39 | # compiled_action("run_my_tool") { 40 | # tool = "//tools/something:mytool" 41 | # outputs = [ 42 | # "$target_gen_dir/mysource.cc", 43 | # "$target_gen_dir/mysource.h", 44 | # ] 45 | # 46 | # # The tool takes this input. 47 | # inputs = [ "my_input_file.idl" ] 48 | # 49 | # # In this case, the tool takes as arguments the input file and the output 50 | # # build dir (both relative to the "cd" that the script will be run in) 51 | # # and will produce the output files listed above. 52 | # args = [ 53 | # rebase_path("my_input_file.idl", root_build_dir), 54 | # "--output-dir", rebase_path(target_gen_dir, root_build_dir), 55 | # ] 56 | # } 57 | # 58 | # You would typically declare your tool like this: 59 | # if (host_toolchain == current_toolchain) { 60 | # executable("mytool") { 61 | # ... 62 | # } 63 | # } 64 | # The if statement around the executable is optional. That says "I only care 65 | # about this target in the host toolchain". Usually this is what you want, and 66 | # saves unnecessarily compiling your tool for the target platform. But if you 67 | # need a target build of your tool as well, just leave off the if statement. 68 | 69 | if (host_os == "win") { 70 | _host_executable_suffix = ".exe" 71 | } else { 72 | _host_executable_suffix = "" 73 | } 74 | 75 | template("compiled_action") { 76 | assert(defined(invoker.tool), "tool must be defined for $target_name") 77 | assert(defined(invoker.outputs), "outputs must be defined for $target_name") 78 | assert(defined(invoker.args), "args must be defined for $target_name") 79 | 80 | assert(!defined(invoker.sources), 81 | "compiled_action doesn't take a sources arg. Use inputs instead.") 82 | 83 | action(target_name) { 84 | forward_variables_from(invoker, 85 | [ 86 | "data_deps", 87 | "deps", 88 | "depfile", 89 | "inputs", 90 | "outputs", 91 | "testonly", 92 | "visibility", 93 | ]) 94 | if (!defined(deps)) { 95 | deps = [] 96 | } 97 | if (!defined(inputs)) { 98 | inputs = [] 99 | } 100 | 101 | script = "//build/gn_run_binary.py" 102 | 103 | # Constuct the host toolchain version of the tool. 104 | host_tool = invoker.tool + "($host_toolchain)" 105 | 106 | # Get the path to the executable. Currently, this assumes that the tool 107 | # does not specify output_name so that the target name is the name to use. 108 | # If that's not the case, we'll need another argument to the script to 109 | # specify this, since we can't know what the output name is (it might be in 110 | # another file not processed yet). 111 | host_executable = 112 | get_label_info(host_tool, "root_out_dir") + "/" + 113 | get_label_info(host_tool, "name") + _host_executable_suffix 114 | 115 | deps += [ host_tool ] 116 | 117 | # The script takes as arguments the binary to run, and then the arguments 118 | # to pass it. 119 | args = [ rebase_path(host_executable, root_build_dir) ] + invoker.args 120 | } 121 | } 122 | 123 | template("compiled_action_foreach") { 124 | assert(defined(invoker.sources), "sources must be defined for $target_name") 125 | assert(defined(invoker.tool), "tool must be defined for $target_name") 126 | assert(defined(invoker.outputs), "outputs must be defined for $target_name") 127 | assert(defined(invoker.args), "args must be defined for $target_name") 128 | 129 | action_foreach(target_name) { 130 | forward_variables_from(invoker, 131 | [ 132 | "deps", 133 | "depfile", 134 | "inputs", 135 | "outputs", 136 | "sources", 137 | "testonly", 138 | "visibility", 139 | ]) 140 | if (!defined(deps)) { 141 | deps = [] 142 | } 143 | if (!defined(inputs)) { 144 | inputs = [] 145 | } 146 | 147 | script = "//build/gn_run_binary.py" 148 | 149 | # Constuct the host toolchain version of the tool. 150 | host_tool = invoker.tool + "($host_toolchain)" 151 | 152 | # Get the path to the executable. Currently, this assumes that the tool 153 | # does not specify output_name so that the target name is the name to use. 154 | # If that's not the case, we'll need another argument to the script to 155 | # specify this, since we can't know what the output name is (it might be in 156 | # another file not processed yet). 157 | host_executable = 158 | get_label_info(host_tool, "root_out_dir") + "/" + 159 | get_label_info(host_tool, "name") + _host_executable_suffix 160 | 161 | deps += [ host_tool ] 162 | 163 | # The script takes as arguments the binary to run, and then the arguments 164 | # to pass it. 165 | args = [ rebase_path(host_executable, root_build_dir) ] + invoker.args 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /toolchain/apple/sdk_info.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | from __future__ import print_function 6 | 7 | import argparse 8 | import doctest 9 | import itertools 10 | import os 11 | import plistlib 12 | import re 13 | import subprocess 14 | import sys 15 | 16 | if sys.version_info.major < 3: 17 | basestring_compat = basestring 18 | else: 19 | basestring_compat = str 20 | 21 | # src directory 22 | ROOT_SRC_DIR = os.path.dirname( 23 | os.path.dirname(os.path.dirname(os.path.dirname( 24 | os.path.realpath(__file__))))) 25 | 26 | # This script prints information about the build system, the operating 27 | # system and the iOS or Mac SDK (depending on the platform "iphonesimulator", 28 | # "iphoneos" or "macosx" generally). 29 | 30 | 31 | def LoadPList(path): 32 | """Loads Plist at |path| and returns it as a dictionary.""" 33 | # Cloned from //build/apple/plist_util.py. 34 | if sys.version_info.major == 2: 35 | return plistlib.readPlist(path) 36 | with open(path, 'rb') as f: 37 | return plistlib.load(f) 38 | 39 | 40 | def SplitVersion(version): 41 | """Splits the Xcode version to 3 values. 42 | 43 | >>> list(SplitVersion('8.2.1.1')) 44 | ['8', '2', '1'] 45 | >>> list(SplitVersion('9.3')) 46 | ['9', '3', '0'] 47 | >>> list(SplitVersion('10.0')) 48 | ['10', '0', '0'] 49 | """ 50 | version = version.split('.') 51 | return itertools.islice(itertools.chain(version, itertools.repeat('0')), 0, 3) 52 | 53 | 54 | def FormatVersion(version): 55 | """Converts Xcode version to a format required for DTXcode in Info.plist 56 | 57 | >>> FormatVersion('8.2.1') 58 | '0821' 59 | >>> FormatVersion('9.3') 60 | '0930' 61 | >>> FormatVersion('10.0') 62 | '1000' 63 | """ 64 | major, minor, patch = SplitVersion(version) 65 | return ('%2s%s%s' % (major, minor, patch)).replace(' ', '0') 66 | 67 | 68 | def FillXcodeVersion(settings, developer_dir): 69 | """Fills the Xcode version and build number into |settings|.""" 70 | if developer_dir: 71 | xcode_version_plist_path = os.path.join(developer_dir, 72 | 'Contents/version.plist') 73 | version_plist = LoadPList(xcode_version_plist_path) 74 | settings['xcode_version'] = FormatVersion( 75 | version_plist['CFBundleShortVersionString']) 76 | settings['xcode_version_int'] = int(settings['xcode_version'], 10) 77 | settings['xcode_build'] = version_plist['ProductBuildVersion'] 78 | return 79 | 80 | lines = subprocess.check_output(['xcodebuild', 81 | '-version']).decode('UTF-8').splitlines() 82 | settings['xcode_version'] = FormatVersion(lines[0].split()[-1]) 83 | settings['xcode_version_int'] = int(settings['xcode_version'], 10) 84 | settings['xcode_build'] = lines[-1].split()[-1] 85 | 86 | 87 | def FillMachineOSBuild(settings): 88 | """Fills OS build number into |settings|.""" 89 | machine_os_build = subprocess.check_output(['sw_vers', '-buildVersion' 90 | ]).decode('UTF-8').strip() 91 | settings['machine_os_build'] = machine_os_build 92 | 93 | 94 | def FillSDKPathAndVersion(settings, platform, xcode_version): 95 | """Fills the SDK path and version for |platform| into |settings|.""" 96 | settings['sdk_path'] = subprocess.check_output( 97 | ['xcrun', '-sdk', platform, '--show-sdk-path']).decode('UTF-8').strip() 98 | settings['sdk_version'] = subprocess.check_output( 99 | ['xcrun', '-sdk', platform, 100 | '--show-sdk-version']).decode('UTF-8').strip() 101 | settings['sdk_platform_path'] = subprocess.check_output( 102 | ['xcrun', '-sdk', platform, 103 | '--show-sdk-platform-path']).decode('UTF-8').strip() 104 | settings['sdk_build'] = subprocess.check_output( 105 | ['xcrun', '-sdk', platform, 106 | '--show-sdk-build-version']).decode('UTF-8').strip() 107 | settings['toolchains_path'] = os.path.join( 108 | subprocess.check_output(['xcode-select', 109 | '-print-path']).decode('UTF-8').strip(), 110 | 'Toolchains/XcodeDefault.xctoolchain') 111 | 112 | 113 | def CreateXcodeSymlinkAt(src, dst): 114 | """Create symlink to Xcode directory at target location.""" 115 | 116 | if not os.path.isdir(dst): 117 | os.makedirs(dst) 118 | 119 | dst = os.path.join(dst, os.path.basename(src)) 120 | updated_value = '//' + os.path.relpath(dst, ROOT_SRC_DIR) 121 | 122 | # Update the symlink only if it is different from the current destination. 123 | if os.path.islink(dst): 124 | current_src = os.readlink(dst) 125 | if current_src == src: 126 | return updated_value 127 | os.unlink(dst) 128 | sys.stderr.write('existing symlink %s points %s; want %s. Removed.' % 129 | (dst, current_src, src)) 130 | os.symlink(src, dst) 131 | return updated_value 132 | 133 | 134 | if __name__ == '__main__': 135 | doctest.testmod() 136 | 137 | parser = argparse.ArgumentParser() 138 | parser.add_argument("--developer_dir", dest="developer_dir", required=False) 139 | parser.add_argument("--get_sdk_info", 140 | action="store_true", 141 | dest="get_sdk_info", 142 | default=False, 143 | help="Returns SDK info in addition to xcode info.") 144 | parser.add_argument("--get_machine_info", 145 | action="store_true", 146 | dest="get_machine_info", 147 | default=False, 148 | help="Returns machine info in addition to xcode info.") 149 | parser.add_argument("--create_symlink_at", 150 | action="store", 151 | dest="create_symlink_at", 152 | help="Create symlink of SDK at given location and " 153 | "returns the symlinked paths as SDK info instead " 154 | "of the original location.") 155 | args, unknownargs = parser.parse_known_args() 156 | if args.developer_dir: 157 | os.environ['DEVELOPER_DIR'] = args.developer_dir 158 | 159 | if len(unknownargs) != 1: 160 | sys.stderr.write('usage: %s [iphoneos|iphonesimulator|macosx]\n' % 161 | os.path.basename(sys.argv[0])) 162 | sys.exit(1) 163 | 164 | settings = {} 165 | if args.get_machine_info: 166 | FillMachineOSBuild(settings) 167 | FillXcodeVersion(settings, args.developer_dir) 168 | if args.get_sdk_info: 169 | FillSDKPathAndVersion(settings, unknownargs[0], settings['xcode_version']) 170 | 171 | for key in sorted(settings): 172 | value = settings[key] 173 | if args.create_symlink_at and '_path' in key: 174 | value = CreateXcodeSymlinkAt(value, args.create_symlink_at) 175 | if isinstance(value, basestring_compat): 176 | value = '"%s"' % value 177 | print('%s=%s' % (key, value)) 178 | -------------------------------------------------------------------------------- /config/android/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2014 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import("//build/toolchain/android/settings.gni") 6 | import("//build/config/sanitizers/sanitizers.gni") 7 | 8 | assert(is_android) 9 | 10 | # This is included by reference in the //build/config/compiler config that 11 | # is applied to all targets. It is here to separate out the logic that is 12 | # Android-only. 13 | config("compiler") { 14 | cflags = [ 15 | "-ffunction-sections", 16 | "-fno-short-enums", 17 | ] 18 | defines = [ 19 | "ANDROID", 20 | 21 | # The NDK has these things, but doesn't define the constants to say that it 22 | # does. Define them here instead. 23 | "HAVE_SYS_UIO_H", 24 | 25 | # Forces full rebuilds on NDK rolls. 26 | "ANDROID_NDK_VERSION=${android_ndk_version}", 27 | ] 28 | 29 | if (is_clang) { 30 | rebased_android_toolchain_root = 31 | rebase_path(android_toolchain_root, root_build_dir) 32 | assert(rebased_android_toolchain_root != "") # Mark as used. 33 | if (current_cpu == "mipsel" || current_cpu == "mips64el") { 34 | cflags += [ 35 | # TODO(gordanac) Enable integrated-as. 36 | "-fno-integrated-as", 37 | "-B${rebased_android_toolchain_root}/bin", # Else /usr/bin/as gets picked up. 38 | ] 39 | } 40 | } else { 41 | # Clang doesn't support these flags. 42 | cflags += [ "-finline-limit=64" ] 43 | } 44 | 45 | ldflags = [ 46 | "-Wl,--build-id=sha1", 47 | "-Wl,--no-undefined", 48 | 49 | # Don't allow visible symbols from libgcc or libc++ to be 50 | # re-exported. 51 | "-Wl,--exclude-libs=libgcc.a", 52 | "-Wl,--exclude-libs=libc++_static.a", 53 | ] 54 | 55 | if (is_clang) { 56 | _rebased_android_toolchain_root = 57 | rebase_path(android_toolchain_root, root_build_dir) 58 | 59 | # Let clang find the linker in the NDK. 60 | ldflags += [ "--gcc-toolchain=$_rebased_android_toolchain_root" ] 61 | 62 | if (current_cpu == "arm") { 63 | abi_target = "arm-linux-androideabi" 64 | } else if (current_cpu == "x86") { 65 | abi_target = "i686-linux-androideabi" 66 | } else if (current_cpu == "arm64") { 67 | abi_target = "aarch64-linux-android" 68 | } else if (current_cpu == "x64") { 69 | # Place holder for x64 support, not tested. 70 | # TODO: Enable clang support for Android x64. http://crbug.com/539781 71 | abi_target = "x86_64-linux-androideabi" 72 | } else if (current_cpu == "mipsel") { 73 | abi_target = "mipsel-linux-android" 74 | } else if (current_cpu == "mips64el") { 75 | # Place holder for mips64 support, not tested. 76 | abi_target = "mips64el-linux-androideabi" 77 | } else { 78 | assert(false, "Architecture not supported") 79 | } 80 | cflags += [ "--target=$abi_target" ] 81 | ldflags += [ "--target=$abi_target" ] 82 | } 83 | 84 | # Assign any flags set for the C compiler to asmflags so that they are sent 85 | # to the assembler. 86 | asmflags = cflags 87 | } 88 | 89 | # This is included by reference in the //build/config:runtime_library 90 | # config that is applied to all targets. It is here to separate out the logic 91 | # that is Android-only. Please see that target for advice on what should go in 92 | # :runtime_library vs. :compiler. 93 | config("runtime_library") { 94 | # NOTE: The libc++ header include paths below are specified in cflags_cc 95 | # rather than include_dirs because they need to come after include_dirs. 96 | # Think of them like system headers, but don't use '-isystem' because the 97 | # arm-linux-androideabi-4.4.3 toolchain (circa Gingerbread) will exhibit 98 | # strange errors. The include ordering here is important; change with 99 | # caution. 100 | cflags_cc = [] 101 | if (android_ndk_major_version >= 13) { 102 | libcxx_include_path = 103 | rebase_path("$android_libcpp_root/include", root_build_dir) 104 | libcxxabi_include_path = 105 | rebase_path("$android_ndk_root/sources/cxx-stl/llvm-libc++abi/include", 106 | root_build_dir) 107 | 108 | if (!is_clang) { 109 | # Per the release notes, GCC is not supported in the NDK starting with 110 | # r13. It's still present, though, and has conflicting declarations of 111 | # float abs(float). 112 | cflags_cc += [ "-Wno-attributes" ] 113 | } 114 | } else { 115 | libcxx_include_path = 116 | rebase_path("$android_libcpp_root/libcxx/include", root_build_dir) 117 | libcxxabi_include_path = rebase_path( 118 | "$android_ndk_root/sources/cxx-stl/llvm-libc++abi/libcxxabi/include", 119 | root_build_dir) 120 | } 121 | cflags_cc += [ 122 | "-isystem" + libcxx_include_path, 123 | "-isystem" + libcxxabi_include_path, 124 | "-isystem" + 125 | rebase_path("$android_ndk_root/sources/android/support/include", 126 | root_build_dir), 127 | ] 128 | 129 | defines = [ "__GNU_SOURCE=1" ] # Necessary for clone(). 130 | ldflags = [ "-nostdlib" ] 131 | lib_dirs = [ android_libcpp_lib_dir ] 132 | 133 | # The libc++ runtime library (must come first). 134 | # ASan needs to dynamically link to libc++ even in static builds so 135 | # that it can interpose operator new. 136 | if (is_asan) { 137 | libs = [ "c++_shared" ] 138 | } else { 139 | libs = [ "c++_static" ] 140 | } 141 | libs += [ 142 | "c++abi", 143 | "android_support", 144 | ] 145 | 146 | # arm builds of libc++ starting in NDK r12 depend on unwind. 147 | if (current_cpu == "arm") { 148 | libs += [ "unwind" ] 149 | } 150 | 151 | # Manually link the libgcc.a that the cross compiler uses. This is 152 | # absolute because the linker will look inside the sysroot if it's not. 153 | libs += [ 154 | rebase_path(android_libgcc_file), 155 | "c", 156 | ] 157 | 158 | # Clang with libc++ does not require an explicit atomic library reference. 159 | if (!is_clang) { 160 | libs += [ "atomic" ] 161 | } 162 | 163 | if (is_clang) { 164 | # Work around incompatibilities between bionic and clang headers. 165 | defines += [ 166 | "__compiler_offsetof=__builtin_offsetof", 167 | "nan=__builtin_nan", 168 | ] 169 | 170 | if (current_cpu == "x64" || current_cpu == "arm64" || 171 | current_cpu == "mips64el") { 172 | # 64-bit targets build with NDK 21, 32-bit targets with NDK 16 173 | # (see ./config.gni). When using clang, NDK 21 defines snprintf to 174 | # something for a kind of for of _FORTIFY_SOURCE support, see 175 | # third_party/android_tools/ndk/platforms/android-21/arch-x86_64/usr/include/stdio.h 176 | # Making snprintf a macro breaks base/strings/string_utils.h which 177 | # defines base::snprintf(). So define snprintf to itself to force the 178 | # NDK to not redefine it. This disables _chk for snprintf, but since 179 | # 32-bit versions use NDK 16 which doesn't have any fortify support, that 180 | # seems ok. b/32067310 tracks better fortify support with clang. 181 | # TODO(thakis): Remove this once b/32067310 is fixed. 182 | defines += [ "snprintf=snprintf" ] 183 | } 184 | } 185 | 186 | # TODO(jdduke) Re-enable on mips after resolving linking 187 | # issues with libc++ (crbug.com/456380). 188 | if (current_cpu != "mipsel" && current_cpu != "mips64el") { 189 | ldflags += [ "-Wl,--warn-shared-textrel" ] 190 | } 191 | } 192 | 193 | config("executable_config") { 194 | cflags = [ "-fPIE" ] 195 | asmflags = [ "-fPIE" ] 196 | ldflags = [ "-pie" ] 197 | } 198 | 199 | # Instrumentation ------------------------------------------------------------- 200 | # 201 | # The BUILDCONFIG file sets the "default_cygprofile_instrumentation" config on 202 | # targets by default. You can override whether the cygprofile instrumentation is 203 | # used on a per-target basis: 204 | # 205 | # configs -= [ "//build/config/android:default_cygprofile_instrumentation" ] 206 | # configs += [ "//build/config/android:no_cygprofile_instrumentation" ] 207 | 208 | config("default_cygprofile_instrumentation") { 209 | if (use_order_profiling) { 210 | configs = [ ":cygprofile_instrumentation" ] 211 | } else { 212 | configs = [ ":no_cygprofile_instrumentation" ] 213 | } 214 | } 215 | 216 | config("cygprofile_instrumentation") { 217 | defines = [ "CYGPROFILE_INSTRUMENTATION=1" ] 218 | cflags = [ "-finstrument-functions" ] 219 | 220 | if (!is_clang) { 221 | cflags += [ 222 | # Allow mmx intrinsics to inline, so that the compiler can expand the intrinsics. 223 | "-finstrument-functions-exclude-file-list=mmintrin.h", 224 | 225 | # Avoid errors with current NDK: 226 | # "third_party/android_tools/ndk/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/include/arm_neon.h:3426:3: error: argument must be a constant" 227 | "-finstrument-functions-exclude-file-list=arm_neon.h", 228 | ] 229 | } 230 | } 231 | 232 | config("no_cygprofile_instrumentation") { 233 | } 234 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # //build directory for GN-based projects 2 | 3 | This project provides a work-in-progress standalone version of the toolchains and configs used by the Chromium project. 4 | 5 | ## Supported platforms 6 | 7 | The toolchains have been tested on the following platforms: 8 | 9 | * Windows (MSVC 2013/2015/2017/2019/2022, Clang 3.8 - 17.0) 10 | * FreeBSD (GCC 6, Clang 11) 11 | * Linux (GCC 6, Clang 3.8) 12 | * OS X (Xcode 7.3.1) 13 | 14 | The [testsrc](https://github.com/timniederhausen/gn-build/tree/testsrc) 15 | branch contains the test/example project used by the CI tests. 16 | 17 | ## Reference 18 | 19 | ### Basic variables 20 | 21 | All variables described here are build args and can be overridden in the user's 22 | `args.gn` file. 23 | 24 | #### [`//build/config/BUILDCONFIG.gn`](config/BUILDCONFIG.gn) 25 | 26 | (these variables are available everywhere) 27 | 28 | * `is_debug` (default: true): Toggle between debug and release builds. 29 | * `is_clang` (default: false): Favor Clang over the platform default (GCC/MSVC). 30 | * `is_official_build` (default: !is_debug): Set to enable the official build 31 | level of optimization. This enables an additional level of optimization above 32 | release (!is_debug). 33 | * `external` (default: "//external"): Label of the external projects directory. 34 | By convention, all 3rd-party projects should end up in this directory, so they 35 | can depend on each other (e.g. $external/mysql_connector -> $external/zlib) 36 | 37 | #### [`//build/toolchain/clang.gni`](toolchain/clang.gni) 38 | 39 | * `use_lld` (default: false): Use the new LLD linker. 40 | This requires `is_clang` to be true. 41 | * `clang_base_path` (default: ""): The path of your Clang installation folder 42 | (without /bin). If you use Clang on Windows, you are required to set this, 43 | as the Clang installation isn't automatically detected. 44 | 45 | #### [`//build/toolchain/compiler_version.gni`](toolchain/compiler_version.gni) 46 | 47 | * `gcc_version` (default: auto-detected): Version of the GCC compiler. 48 | **Note:** Auto-detection is toolchain-specific and happens only if GCC is the 49 | active compiler.
50 | Format: `major` * 10000 + `minor` * 100 + `patchlevel` 51 | * `clang_version` (default: auto-detected): Version of the Clang compiler. 52 | **Note:** Auto-detection is toolchain-specific and happens only if Clang is 53 | the active compiler.
54 | Format: `major` * 10000 + `minor` * 100 + `patchlevel` 55 | * `msc_ver` (default: auto-detected): Value of the _MSC_VER variable. 56 | See https://msdn.microsoft.com/en-us/library/b0084kay.aspx. 57 | **Note:** Auto-detection happens only when targeting Windows. 58 | * `msc_full_ver` (default: auto-detected): Value of the _MSC_FULL_VER variable. 59 | See https://msdn.microsoft.com/en-us/library/b0084kay.aspx. 60 | **Note:** Auto-detection happens only when targeting Windows. 61 | 62 | ### Windows toolchain 63 | 64 | #### [`//build/toolchain/win/settings.gni`](toolchain/win/settings.gni) 65 | 66 | * `visual_studio_version` (default: "latest"): Desired version of Visual Studio. 67 | If `visual_studio_path` is set, this must be the version of the VS installation 68 | at the `visual_studio_path`. 69 | 70 | Use "2013" for Visual Studio 2013 or "latest" for automatically choosing the 71 | highest version (`visual_studio_path` must be unset in this case). 72 | * `visual_studio_path` (default: auto-detected): The path of your MSVC installation. 73 | If this is set you must set visual_studio_version as well. 74 | Autodetected based on `visual_studio_version`. 75 | * `windows_sdk_version` (default: auto-detected): Windows SDK version to use. 76 | Can either be a full Windows 10 SDK number (e.g. 10.0.10240.0), 77 | "8.1" for the Windows 8.1 SDK or "default" for the default SDK selected by VS. 78 | * `clang_msc_ver` (default: auto-detected): MSVC version `clang-cl` will report 79 | in `_MSC_VER`. 80 | 81 | ### POSIX toolchain 82 | 83 | This is the default toolchain for POSIX operating systems, 84 | which is used for all POSIX systems that don't have special toolchains. 85 | 86 | #### [`//build/toolchain/posix/settings.gni`](toolchain/posix/settings.gni) 87 | 88 | * `gcc_cc` (default: gcc): Path of the GCC C compiler executable. 89 | Does not have to be absolute. 90 | * `gcc_cxx` (default: g++): Path of the GCC C++ compiler executable. 91 | Does not have to be absolute. 92 | * `clang_cc` (default: clang): Path of the Clang C compiler executable. 93 | Does not have to be absolute. **Note:** If `clang_base_path` is set, 94 | the default will be `clang_base_path/bin/clang`. 95 | * `clang_cxx` (default: clang++): Path of the Clang C++ compiler executable. 96 | Does not have to be absolute. **Note:** If `clang_base_path` is set, 97 | the default will be `clang_base_path/bin/clang++`. 98 | 99 | ### Mac/iOS toolchain 100 | 101 | #### [`//build/toolchain/mac/settings.gni`](toolchain/mac/settings.gni) 102 | 103 | * `use_system_xcode` (default: true): Use the system install of Xcode for tools 104 | like ibtool, libtool, etc. This does not affect the compiler. When this 105 | variable is false, targets will instead use a hermetic install of Xcode. 106 | * `hermetic_xcode_path` (default: ""): The path to the hermetic install of 107 | Xcode. Only relevant when use_system_xcode = false. 108 | * `use_xcode_clang` (default: true): Compile with Xcode version of clang 109 | instead of hermetic version shipped with the build. If `true`, 110 | `clang_base_path` needs to be set. 111 | * `enable_dsyms` (default: true): Produce dSYM files for targets that are 112 | configured to do so. dSYM generation is controlled globally as it is a 113 | linker output (produced via the `//build/toolchain/mac/linker_driver.py`. 114 | Enabling this will result in all shared library, loadable module, and 115 | executable targets having a dSYM generated. 116 | * `enable_stripping` (default: `is_official_build`): Strip symbols from linked 117 | targets by default. If this is enabled, the //build/config/mac:strip_all 118 | config will be applied to all linked targets. If custom stripping parameters 119 | are required, remove that config from a linked target and apply custom 120 | `-Wcrl,strip` flags. See //build/toolchain/mac/linker_driver.py for more 121 | information. 122 | 123 | #### [`//build/toolchain/mac/mac_sdk.gni`](toolchain/mac/mac_sdk.gni) 124 | 125 | * `mac_sdk_min` (default: "10.10"): Minimum supported version of the Mac SDK. 126 | * `mac_deployment_target` (default: "10.9"): Minimum supported version of OSX. 127 | * `mac_sdk_path` (default: ""): Path to a specific version of the Mac SDK, not 128 | including a slash at the end. If empty, the path to the lowest version 129 | greater than or equal to `mac_sdk_min` is used. 130 | * `mac_sdk_name` (default: "macosx"): The SDK name as accepted by xcodebuild. 131 | 132 | #### [`//build/toolchain/mac/ios_sdk.gni`](toolchain/mac/ios_sdk.gni) 133 | 134 | * `ios_sdk_path` (default: ""): Path to a specific version of the iOS SDK, not 135 | including a slash at the end. When empty this will use the default SDK based 136 | on the value of use_ios_simulator. 137 | 138 | SDK properties (required when `ios_sdk_path` is non-empty): 139 | 140 | * `ios_sdk_name`: The SDK name as accepted by xcodebuild. 141 | * `ios_sdk_version` 142 | * `ios_sdk_platform` 143 | * `ios_sdk_platform_path` 144 | * `xcode_version` 145 | * `xcode_build` 146 | * `machine_os_build` 147 | 148 | * `ios_deployment_target` (default: "9.0"): Minimum supported version of OSX. 149 | 150 | ### Android toolchain 151 | 152 | #### [`//build/toolchain/android/settings.gni`](toolchain/android/settings.gni) 153 | 154 | * `android_ndk_root` (default: "$external/android_tools/ndk"): 155 | Path of the Android NDK. 156 | * `android_ndk_version` (default: "r12b"): NDK Version string. 157 | * `android_ndk_major_version` (default: 12): NDK Major version. 158 | * `android_sdk_root` (default: "$external/android_tools/sdk"): 159 | Path of the Android SDK. 160 | * `android_sdk_version` (default: "24"): Android SDK version. 161 | * `android_sdk_build_tools_version` (default: "24.0.2"): 162 | Version of the Build Tools contained in the SDK. 163 | * `android_libcpp_lib_dir` (default: ""): Libc++ library directory. 164 | Override to use a custom libc++ binary. 165 | * `use_order_profiling` (default: false): Adds intrumentation to each function. 166 | Writes a file with the order that functions are called at startup. 167 | 168 | ## Recommended workflow 169 | 170 | Fork this repo and add it as a submodule/subtree/`DEPS`-entry to your project. 171 | This way you can modify every part of the `//build` directory while still being 172 | able to easily merge upstream changes (e.g. support for new GN features that 173 | you don't want to implement yourself.) 174 | 175 | To ease sharing/composition of projects using this `//build` repo, 176 | it is recommended that you refrain from modifying large parts of the toolchains/configs. 177 | If changes are necessary, consider contributing them back ;) 178 | 179 | For more complex projects, it might be feasible to use a custom build-config file 180 | that just `import()s` [`//build/config/BUILDCONFIG.gn`](config/BUILDCONFIG.gn) and then overrides 181 | the defaults set inside `BUILDCONFIG.gn`. There's also GN's `default_args` scope, which can be used 182 | to provide project-specific argument overrides. 183 | -------------------------------------------------------------------------------- /toolchain/android/settings.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2014 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # This file contains common system config stuff for the Android build. 6 | 7 | declare_args() { 8 | android_ndk_root = "$external/android_tools/ndk" 9 | android_ndk_version = "r12b" 10 | android_ndk_major_version = 12 11 | 12 | android_sdk_root = "$external/android_tools/sdk" 13 | android_sdk_version = "24" 14 | android_sdk_build_tools_version = "24.0.2" 15 | 16 | lint_android_sdk_root = "$external/android_tools/sdk" 17 | lint_android_sdk_version = "24" 18 | 19 | # Libc++ library directory. Override to use a custom libc++ binary. 20 | android_libcpp_lib_dir = "" 21 | 22 | # Adds intrumentation to each function. Writes a file with the order that 23 | # functions are called at startup. 24 | use_order_profiling = false 25 | } 26 | 27 | # Host stuff ----------------------------------------------------------------- 28 | 29 | # Defines the name the Android build gives to the current host CPU 30 | # architecture, which is different than the names GN uses. 31 | if (host_cpu == "x64") { 32 | android_host_arch = "x86_64" 33 | } else if (host_cpu == "x86") { 34 | android_host_arch = "x86" 35 | } else { 36 | assert(false, "Need Android toolchain support for your build CPU arch.") 37 | } 38 | 39 | # Defines the name the Android build gives to the current host CPU 40 | # architecture, which is different than the names GN uses. 41 | if (host_os == "linux") { 42 | android_host_os = "linux" 43 | } else if (host_os == "mac") { 44 | android_host_os = "darwin" 45 | } else if (host_os == "win") { 46 | android_host_os = "windows" 47 | } else { 48 | assert(false, "Need Android toolchain support for your build OS.") 49 | } 50 | 51 | # Directories and files ------------------------------------------------------ 52 | # 53 | # We define may of the dirs strings here for each output architecture (rather 54 | # than just the current one) since these are needed by the Android toolchain 55 | # file to define toolchains for all possible targets in one pass. 56 | 57 | android_sdk = "${android_sdk_root}/platforms/android-${android_sdk_version}" 58 | 59 | # Path to the Android NDK and SDK. 60 | android_ndk_include_dir = "$android_ndk_root/usr/include" 61 | 62 | android_sdk_tools = "${android_sdk_root}/tools" 63 | android_sdk_build_tools = 64 | "${android_sdk_root}/build-tools/$android_sdk_build_tools_version" 65 | 66 | # Path to the SDK's android.jar 67 | android_sdk_jar = "$android_sdk/android.jar" 68 | 69 | zipalign_path = "$android_sdk_build_tools/zipalign" 70 | 71 | # Subdirectories inside android_ndk_root that contain the sysroot for the 72 | # associated platform. 73 | # If you raise this, reevaluate the snprintf=snprintf in ./BUILD.gn. 74 | _android_api_level = 16 75 | x86_android_sysroot_subdir = "platforms/android-${_android_api_level}/arch-x86" 76 | arm_android_sysroot_subdir = "platforms/android-${_android_api_level}/arch-arm" 77 | mips_android_sysroot_subdir = 78 | "platforms/android-${_android_api_level}/arch-mips" 79 | 80 | # If you raise this, reevaluate the snprintf=snprintf in ./BUILD.gn. 81 | _android64_api_level = 21 82 | x86_64_android_sysroot_subdir = 83 | "platforms/android-${_android64_api_level}/arch-x86_64" 84 | arm64_android_sysroot_subdir = 85 | "platforms/android-${_android64_api_level}/arch-arm64" 86 | mips64_android_sysroot_subdir = 87 | "platforms/android-${_android64_api_level}/arch-mips64" 88 | 89 | # Toolchain root directory for each build. The actual binaries are inside 90 | # a "bin" directory inside of these. 91 | _android_toolchain_version = "4.9" 92 | _android_toolchain_detailed_version = "4.9.x" 93 | x86_android_toolchain_root = "$android_ndk_root/toolchains/x86-${_android_toolchain_version}/prebuilt/${android_host_os}-${android_host_arch}" 94 | arm_android_toolchain_root = "$android_ndk_root/toolchains/arm-linux-androideabi-${_android_toolchain_version}/prebuilt/${android_host_os}-${android_host_arch}" 95 | mips_android_toolchain_root = "$android_ndk_root/toolchains/mipsel-linux-android-${_android_toolchain_version}/prebuilt/${android_host_os}-${android_host_arch}" 96 | x86_64_android_toolchain_root = "$android_ndk_root/toolchains/x86_64-${_android_toolchain_version}/prebuilt/${android_host_os}-${android_host_arch}" 97 | arm64_android_toolchain_root = "$android_ndk_root/toolchains/aarch64-linux-android-${_android_toolchain_version}/prebuilt/${android_host_os}-${android_host_arch}" 98 | mips64_android_toolchain_root = "$android_ndk_root/toolchains/mips64el-linux-android-${_android_toolchain_version}/prebuilt/${android_host_os}-${android_host_arch}" 99 | 100 | # Location of libgcc. This is only needed for the current GN toolchain, so we 101 | # only need to define the current one, rather than one for every platform 102 | # like the toolchain roots. 103 | if (current_cpu == "x86") { 104 | android_prebuilt_arch = "android-x86" 105 | _binary_prefix = "i686-linux-android" 106 | android_toolchain_root = "$x86_android_toolchain_root" 107 | android_libgcc_file = "$android_toolchain_root/lib/gcc/i686-linux-android/${_android_toolchain_detailed_version}/libgcc.a" 108 | } else if (current_cpu == "arm") { 109 | android_prebuilt_arch = "android-arm" 110 | _binary_prefix = "arm-linux-androideabi" 111 | android_toolchain_root = "$arm_android_toolchain_root" 112 | android_libgcc_file = "$android_toolchain_root/lib/gcc/arm-linux-androideabi/${_android_toolchain_detailed_version}/libgcc.a" 113 | } else if (current_cpu == "mipsel") { 114 | android_prebuilt_arch = "android-mips" 115 | _binary_prefix = "mipsel-linux-android" 116 | android_toolchain_root = "$mips_android_toolchain_root" 117 | android_libgcc_file = "$android_toolchain_root/lib/gcc/mipsel-linux-android/${_android_toolchain_detailed_version}/libgcc.a" 118 | } else if (current_cpu == "x64") { 119 | android_prebuilt_arch = "android-x86_64" 120 | _binary_prefix = "x86_64-linux-android" 121 | android_toolchain_root = "$x86_64_android_toolchain_root" 122 | android_libgcc_file = "$android_toolchain_root/lib/gcc/x86_64-linux-android/${_android_toolchain_detailed_version}/libgcc.a" 123 | } else if (current_cpu == "arm64") { 124 | android_prebuilt_arch = "android-arm64" 125 | _binary_prefix = "aarch64-linux-android" 126 | android_toolchain_root = "$arm64_android_toolchain_root" 127 | android_libgcc_file = "$android_toolchain_root/lib/gcc/aarch64-linux-android/${_android_toolchain_detailed_version}/libgcc.a" 128 | } else if (current_cpu == "mips64el") { 129 | android_prebuilt_arch = "android-mips64" 130 | _binary_prefix = "mips64el-linux-android" 131 | android_toolchain_root = "$mips64_android_toolchain_root" 132 | android_libgcc_file = "$android_toolchain_root/lib/gcc/mips64el-linux-android/${_android_toolchain_detailed_version}/libgcc.a" 133 | } else { 134 | assert(false, "Need android libgcc support for your target arch.") 135 | } 136 | 137 | android_tool_prefix = "$android_toolchain_root/bin/$_binary_prefix-" 138 | android_readelf = "${android_tool_prefix}readelf" 139 | android_objcopy = "${android_tool_prefix}objcopy" 140 | android_gdbserver = 141 | "$android_ndk_root/prebuilt/$android_prebuilt_arch/gdbserver/gdbserver" 142 | 143 | # Toolchain stuff ------------------------------------------------------------ 144 | 145 | android_libcpp_root = "$android_ndk_root/sources/cxx-stl/llvm-libc++" 146 | 147 | # ABI ------------------------------------------------------------------------ 148 | 149 | if (current_cpu == "x86") { 150 | android_app_abi = "x86" 151 | } else if (current_cpu == "arm") { 152 | import("//build/config/arm.gni") 153 | if (arm_version < 7) { 154 | android_app_abi = "armeabi" 155 | } else { 156 | android_app_abi = "armeabi-v7a" 157 | } 158 | } else if (current_cpu == "mipsel") { 159 | android_app_abi = "mips" 160 | } else if (current_cpu == "x64") { 161 | android_app_abi = "x86_64" 162 | } else if (current_cpu == "arm64") { 163 | android_app_abi = "arm64-v8a" 164 | } else if (current_cpu == "mips64el") { 165 | android_app_abi = "mips64" 166 | } else { 167 | assert(false, "Unknown Android ABI: " + current_cpu) 168 | } 169 | 170 | if (android_libcpp_lib_dir == "") { 171 | android_libcpp_lib_dir = "${android_libcpp_root}/libs/${android_app_abi}" 172 | } 173 | 174 | # Secondary ABI ------------------------------------------------------------- 175 | if (target_cpu == "arm64" || target_cpu == "x64" || target_cpu == "mips64el") { 176 | android_64bit_target_cpu = true 177 | } else if (target_cpu == "arm" || target_cpu == "x86" || 178 | target_cpu == "mipsel") { 179 | android_64bit_target_cpu = false 180 | } else { 181 | assert(false, "Unknown target CPU: $target_cpu") 182 | } 183 | 184 | # Intentionally do not define android_app_secondary_abi_cpu and 185 | # android_app_secondary_abi for 32-bit target_cpu, since they are not used. 186 | if (target_cpu == "arm64") { 187 | android_secondary_abi_cpu = "arm" 188 | android_app_secondary_abi = "armeabi-v7a" 189 | } else if (target_cpu == "x64") { 190 | android_secondary_abi_cpu = "x86" 191 | android_app_secondary_abi = "x86" 192 | } else if (target_cpu == "mips64el") { 193 | android_secondary_abi_cpu = "mipsel" 194 | android_app_secondary_abi = "mips" 195 | } 196 | 197 | if (defined(android_secondary_abi_cpu)) { 198 | if (is_clang) { 199 | android_secondary_abi_toolchain = 200 | "//build/toolchain/android:android_clang_${android_secondary_abi_cpu}" 201 | } else { 202 | android_secondary_abi_toolchain = 203 | "//build/toolchain/android:android_${android_secondary_abi_cpu}" 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /apple/plist_util.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import argparse 6 | import codecs 7 | import plistlib 8 | import os 9 | import re 10 | import subprocess 11 | import sys 12 | import tempfile 13 | import shlex 14 | 15 | if sys.version_info.major < 3: 16 | basestring_compat = basestring 17 | else: 18 | basestring_compat = str 19 | 20 | # Xcode substitutes variables like ${PRODUCT_NAME} or $(PRODUCT_NAME) when 21 | # compiling Info.plist. It also supports supports modifiers like :identifier 22 | # or :rfc1034identifier. SUBSTITUTION_REGEXP_LIST is a list of regular 23 | # expressions matching a variable substitution pattern with an optional 24 | # modifier, while INVALID_CHARACTER_REGEXP matches all characters that are 25 | # not valid in an "identifier" value (used when applying the modifier). 26 | INVALID_CHARACTER_REGEXP = re.compile(r'[_/\s]') 27 | SUBSTITUTION_REGEXP_LIST = ( 28 | re.compile(r'\$\{(?P[^}]*?)(?P:[^}]*)?\}'), 29 | re.compile(r'\$\((?P[^}]*?)(?P:[^}]*)?\)'), 30 | ) 31 | 32 | 33 | class SubstitutionError(Exception): 34 | def __init__(self, key): 35 | super(SubstitutionError, self).__init__() 36 | self.key = key 37 | 38 | def __str__(self): 39 | return "SubstitutionError: {}".format(self.key) 40 | 41 | 42 | def InterpolateString(value, substitutions): 43 | """Interpolates variable references into |value| using |substitutions|. 44 | 45 | Inputs: 46 | value: a string 47 | substitutions: a mapping of variable names to values 48 | 49 | Returns: 50 | A new string with all variables references ${VARIABLES} replaced by their 51 | value in |substitutions|. Raises SubstitutionError if a variable has no 52 | substitution. 53 | """ 54 | 55 | def repl(match): 56 | variable = match.group('id') 57 | if variable not in substitutions: 58 | raise SubstitutionError(variable) 59 | # Some values need to be identifier and thus the variables references may 60 | # contains :modifier attributes to indicate how they should be converted 61 | # to identifiers ("identifier" replaces all invalid characters by '_' and 62 | # "rfc1034identifier" replaces them by "-" to make valid URI too). 63 | modifier = match.group('modifier') 64 | if modifier == ':identifier': 65 | return INVALID_CHARACTER_REGEXP.sub('_', substitutions[variable]) 66 | elif modifier == ':rfc1034identifier': 67 | return INVALID_CHARACTER_REGEXP.sub('-', substitutions[variable]) 68 | else: 69 | return substitutions[variable] 70 | 71 | for substitution_regexp in SUBSTITUTION_REGEXP_LIST: 72 | value = substitution_regexp.sub(repl, value) 73 | return value 74 | 75 | 76 | def Interpolate(value, substitutions): 77 | """Interpolates variable references into |value| using |substitutions|. 78 | 79 | Inputs: 80 | value: a value, can be a dictionary, list, string or other 81 | substitutions: a mapping of variable names to values 82 | 83 | Returns: 84 | A new value with all variables references ${VARIABLES} replaced by their 85 | value in |substitutions|. Raises SubstitutionError if a variable has no 86 | substitution. 87 | """ 88 | if isinstance(value, dict): 89 | return {k: Interpolate(v, substitutions) for k, v in value.items()} 90 | if isinstance(value, list): 91 | return [Interpolate(v, substitutions) for v in value] 92 | if isinstance(value, basestring_compat): 93 | return InterpolateString(value, substitutions) 94 | return value 95 | 96 | 97 | def LoadPList(path): 98 | """Loads Plist at |path| and returns it as a dictionary.""" 99 | if sys.version_info.major == 2: 100 | fd, name = tempfile.mkstemp() 101 | try: 102 | subprocess.check_call(['plutil', '-convert', 'xml1', '-o', name, path]) 103 | with os.fdopen(fd, 'rb') as f: 104 | return plistlib.readPlist(f) 105 | finally: 106 | os.unlink(name) 107 | else: 108 | with open(path, 'rb') as f: 109 | return plistlib.load(f) 110 | 111 | 112 | def SavePList(path, format, data): 113 | """Saves |data| as a Plist to |path| in the specified |format|.""" 114 | # The below does not replace the destination file but update it in place, 115 | # so if more than one hardlink points to destination all of them will be 116 | # modified. This is not what is expected, so delete destination file if 117 | # it does exist. 118 | if os.path.exists(path): 119 | os.unlink(path) 120 | if sys.version_info.major == 2: 121 | fd, name = tempfile.mkstemp() 122 | try: 123 | with os.fdopen(fd, 'wb') as f: 124 | plistlib.writePlist(data, f) 125 | subprocess.check_call(['plutil', '-convert', format, '-o', path, name]) 126 | finally: 127 | os.unlink(name) 128 | else: 129 | with open(path, 'wb') as f: 130 | plist_format = {'binary1': plistlib.FMT_BINARY, 'xml1': plistlib.FMT_XML} 131 | plistlib.dump(data, f, fmt=plist_format[format]) 132 | 133 | 134 | def MergePList(plist1, plist2): 135 | """Merges |plist1| with |plist2| recursively. 136 | 137 | Creates a new dictionary representing a Property List (.plist) files by 138 | merging the two dictionary |plist1| and |plist2| recursively (only for 139 | dictionary values). List value will be concatenated. 140 | 141 | Args: 142 | plist1: a dictionary representing a Property List (.plist) file 143 | plist2: a dictionary representing a Property List (.plist) file 144 | 145 | Returns: 146 | A new dictionary representing a Property List (.plist) file by merging 147 | |plist1| with |plist2|. If any value is a dictionary, they are merged 148 | recursively, otherwise |plist2| value is used. If values are list, they 149 | are concatenated. 150 | """ 151 | result = plist1.copy() 152 | for key, value in plist2.items(): 153 | if isinstance(value, dict): 154 | old_value = result.get(key) 155 | if isinstance(old_value, dict): 156 | value = MergePList(old_value, value) 157 | if isinstance(value, list): 158 | value = plist1.get(key, []) + plist2.get(key, []) 159 | result[key] = value 160 | return result 161 | 162 | 163 | class Action(object): 164 | """Class implementing one action supported by the script.""" 165 | 166 | @classmethod 167 | def Register(cls, subparsers): 168 | parser = subparsers.add_parser(cls.name, help=cls.help) 169 | parser.set_defaults(func=cls._Execute) 170 | cls._Register(parser) 171 | 172 | 173 | class MergeAction(Action): 174 | """Class to merge multiple plist files.""" 175 | 176 | name = 'merge' 177 | help = 'merge multiple plist files' 178 | 179 | @staticmethod 180 | def _Register(parser): 181 | parser.add_argument('-o', 182 | '--output', 183 | required=True, 184 | help='path to the output plist file') 185 | parser.add_argument('-f', 186 | '--format', 187 | required=True, 188 | choices=('xml1', 'binary1'), 189 | help='format of the plist file to generate') 190 | parser.add_argument( 191 | '-x', 192 | '--xcode-version', 193 | help='version of Xcode, ignored (can be used to force rebuild)') 194 | parser.add_argument('path', nargs="+", help='path to plist files to merge') 195 | 196 | @staticmethod 197 | def _Execute(args): 198 | data = {} 199 | for filename in args.path: 200 | data = MergePList(data, LoadPList(filename)) 201 | SavePList(args.output, args.format, data) 202 | 203 | 204 | class SubstituteAction(Action): 205 | """Class implementing the variable substitution in a plist file.""" 206 | 207 | name = 'substitute' 208 | help = 'perform pattern substitution in a plist file' 209 | 210 | @staticmethod 211 | def _Register(parser): 212 | parser.add_argument('-o', 213 | '--output', 214 | required=True, 215 | help='path to the output plist file') 216 | parser.add_argument('-t', 217 | '--template', 218 | required=True, 219 | help='path to the template file') 220 | parser.add_argument('-s', 221 | '--substitution', 222 | action='append', 223 | default=[], 224 | help='substitution rule in the format key=value') 225 | parser.add_argument('-f', 226 | '--format', 227 | required=True, 228 | choices=('xml1', 'binary1'), 229 | help='format of the plist file to generate') 230 | parser.add_argument( 231 | '-x', 232 | '--xcode-version', 233 | help='version of Xcode, ignored (can be used to force rebuild)') 234 | 235 | @staticmethod 236 | def _Execute(args): 237 | substitutions = {} 238 | for substitution in args.substitution: 239 | key, value = substitution.split('=', 1) 240 | substitutions[key] = value 241 | data = Interpolate(LoadPList(args.template), substitutions) 242 | SavePList(args.output, args.format, data) 243 | 244 | 245 | def Main(): 246 | # Cache this codec so that plistlib can find it. See 247 | # https://crbug.com/1005190#c2 for more details. 248 | codecs.lookup('utf-8') 249 | 250 | parser = argparse.ArgumentParser(description='manipulate plist files') 251 | subparsers = parser.add_subparsers() 252 | 253 | for action in [MergeAction, SubstituteAction]: 254 | action.Register(subparsers) 255 | 256 | args = parser.parse_args() 257 | args.func(args) 258 | 259 | 260 | if __name__ == '__main__': 261 | # TODO(https://crbug.com/941669): Temporary workaround until all scripts use 262 | # python3 by default. 263 | if sys.version_info[0] < 3: 264 | os.execvp('python3', ['python3'] + sys.argv) 265 | sys.exit(Main()) 266 | -------------------------------------------------------------------------------- /config/sanitizers/sanitizers.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | declare_args() { 6 | # Compile for Address Sanitizer to find memory bugs. 7 | is_asan = false 8 | 9 | # Compile for Hardware-Assisted Address Sanitizer to find memory bugs 10 | # (android/arm64 only). 11 | # See http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html 12 | is_hwasan = false 13 | 14 | # Compile for Leak Sanitizer to find leaks. 15 | is_lsan = false 16 | 17 | # Compile for Memory Sanitizer to find uninitialized reads. 18 | is_msan = false 19 | 20 | # Compile for Thread Sanitizer to find threading bugs. 21 | is_tsan = false 22 | 23 | # Compile for Undefined Behaviour Sanitizer to find various types of 24 | # undefined behaviour (excludes vptr checks). 25 | is_ubsan = false 26 | 27 | # Halt the program if a problem is detected. 28 | is_ubsan_no_recover = false 29 | 30 | # Compile for Undefined Behaviour Sanitizer's null pointer checks. 31 | is_ubsan_null = false 32 | 33 | # Track where uninitialized memory originates from. From fastest to slowest: 34 | # 0 - no tracking, 1 - track only the initial allocation site, 2 - track the 35 | # chain of stores leading from allocation site to use site. 36 | msan_track_origins = 2 37 | 38 | # Use dynamic libraries instrumented by one of the sanitizers instead of the 39 | # standard system libraries. Set this flag to build the libraries from source. 40 | use_locally_built_instrumented_libraries = false 41 | 42 | # Enable building with SyzyAsan which can find certain types of memory 43 | # errors. Only works on Windows. See 44 | # https://github.com/google/syzygy/wiki/SyzyASanHowTo 45 | is_syzyasan = false 46 | 47 | # Compile with Control Flow Integrity to protect virtual calls and casts. 48 | # See http://clang.llvm.org/docs/ControlFlowIntegrity.html 49 | is_cfi = false 50 | 51 | # Enable checks for indirect function calls via a function pointer. 52 | # TODO(pcc): remove this when we're ready to add these checks by default. 53 | # https://crbug.com/701919 54 | # 55 | # TODO(crbug.com/1159424): Reassess the validity of the next expression. 56 | use_cfi_icall = false 57 | 58 | # Enable checks for bad casts: derived cast and unrelated cast. 59 | # TODO(krasin): remove this, when we're ready to add these checks by default. 60 | # https://crbug.com/626794 61 | use_cfi_cast = false 62 | 63 | # By default, Control Flow Integrity will crash the program if it detects a 64 | # violation. Set this to true to print detailed diagnostics instead. 65 | use_cfi_diag = false 66 | 67 | # Let Control Flow Integrity continue execution instead of crashing when 68 | # printing diagnostics (use_cfi_diag = true). 69 | use_cfi_recover = false 70 | 71 | # Compile for fuzzing with LLVM LibFuzzer. 72 | # See http://www.chromium.org/developers/testing/libfuzzer 73 | use_libfuzzer = false 74 | 75 | # Compile for fuzzing with AFL. 76 | use_afl = false 77 | 78 | # Compile for fuzzing with an external engine (e.g., Grammarinator). 79 | use_external_fuzzing_engine = false 80 | 81 | # Enables core ubsan security features. Will later be removed once it matches 82 | # is_ubsan. 83 | is_ubsan_security = false 84 | 85 | # Compile for fuzzing with Dr. Fuzz 86 | # See http://www.chromium.org/developers/testing/dr-fuzz 87 | use_drfuzz = false 88 | 89 | # Helper variable for testing builds with disabled libfuzzer. 90 | # Not for client use. 91 | disable_libfuzzer = false 92 | 93 | # Value for -fsanitize-coverage flag. Setting this causes 94 | # use_sanitizer_coverage to be enabled. 95 | # This flag is not used for libFuzzer (use_libfuzzer=true). Instead, we use: 96 | # -fsanitize=fuzzer-no-link 97 | # Default value when unset and use_fuzzing_engine=true: 98 | # trace-pc-guard 99 | # Default value when unset and use_sanitizer_coverage=true: 100 | # trace-pc-guard,indirect-calls 101 | sanitizer_coverage_flags = "" 102 | 103 | # When enabled, only relevant sanitizer defines are set, but compilation 104 | # happens with no extra flags. This is useful when in component build 105 | # enabling sanitizers only in some of the components. 106 | use_sanitizer_configs_without_instrumentation = false 107 | 108 | # When true, seed corpora archives are built. 109 | archive_seed_corpus = true 110 | } 111 | 112 | declare_args() { 113 | # Compile for Undefined Behaviour Sanitizer's vptr checks. 114 | is_ubsan_vptr = is_ubsan_security 115 | } 116 | 117 | # Disable sanitizers for non-default toolchains. 118 | if (current_toolchain != default_toolchain) { 119 | is_asan = false 120 | is_cfi = false 121 | is_hwasan = false 122 | is_lsan = false 123 | is_msan = false 124 | is_tsan = false 125 | is_ubsan = false 126 | is_ubsan_null = false 127 | is_ubsan_no_recover = false 128 | is_ubsan_security = false 129 | is_ubsan_vptr = false 130 | msan_track_origins = 0 131 | sanitizer_coverage_flags = "" 132 | use_afl = false 133 | use_cfi_diag = false 134 | use_cfi_recover = false 135 | use_libfuzzer = false 136 | use_locally_built_instrumented_libraries = false 137 | use_sanitizer_coverage = false 138 | } 139 | 140 | # Use dynamic libraries instrumented by one of the sanitizers instead of the 141 | # standard system libraries. We have instrumented system libraries for msan, 142 | # which requires them to prevent false positives. 143 | # TODO(thakis): Maybe remove this variable. 144 | use_prebuilt_instrumented_libraries = is_msan 145 | 146 | # Whether we are doing a fuzzer build. Normally this should be checked instead 147 | # of checking "use_libfuzzer || use_afl" because often developers forget to 148 | # check for "use_afl". 149 | use_fuzzing_engine = use_libfuzzer || use_afl || use_external_fuzzing_engine 150 | 151 | # Args that are in turn dependent on other args must be in a separate 152 | # declare_args block. User overrides are only applied at the end of a 153 | # declare_args block. 154 | declare_args() { 155 | # Enable -fsanitize-coverage. 156 | use_sanitizer_coverage = use_fuzzing_engine || sanitizer_coverage_flags != "" 157 | } 158 | 159 | if (use_fuzzing_engine && sanitizer_coverage_flags == "") { 160 | sanitizer_coverage_flags = "trace-pc-guard" 161 | } else if (use_sanitizer_coverage && sanitizer_coverage_flags == "") { 162 | sanitizer_coverage_flags = "trace-pc-guard,indirect-calls" 163 | } 164 | 165 | # Whether we are linking against a sanitizer runtime library. Among other 166 | # things, this changes the default symbol level and other settings in order to 167 | # prepare to create stack traces "live" using the sanitizer runtime. 168 | using_sanitizer = is_asan || is_hwasan || is_lsan || is_tsan || is_msan || 169 | is_ubsan || is_ubsan_null || is_ubsan_vptr || 170 | is_ubsan_security || use_sanitizer_coverage || use_cfi_diag 171 | 172 | assert(!using_sanitizer || is_clang, 173 | "Sanitizers (is_*san) require setting is_clang = true in 'gn args'") 174 | 175 | assert(!is_cfi || is_clang, 176 | "is_cfi requires setting is_clang = true in 'gn args'") 177 | 178 | prebuilt_instrumented_libraries_available = 179 | is_msan && (msan_track_origins == 0 || msan_track_origins == 2) 180 | 181 | if (use_libfuzzer && (is_linux || is_chromeos)) { 182 | if (is_asan) { 183 | # We do leak checking with libFuzzer on Linux. Set is_lsan for code that 184 | # relies on LEAK_SANITIZER define to avoid false positives. 185 | is_lsan = true 186 | } 187 | } 188 | 189 | # MSan only links Chrome properly in release builds (brettw -- 9/1/2015). The 190 | # same is possibly true for the other non-ASan sanitizers. But regardless of 191 | # whether it links, one would normally never run a sanitizer in debug mode. 192 | # Running in debug mode probably indicates you forgot to set the "is_debug = 193 | # false" flag in the build args. ASan seems to run fine in debug mode. 194 | # 195 | # If you find a use-case where you want to compile a sanitizer in debug mode 196 | # and have verified it works, ask brettw and we can consider removing it from 197 | # this condition. We may also be able to find another way to enable your case 198 | # without having people accidentally get broken builds by compiling an 199 | # unsupported or unadvisable configurations. 200 | # 201 | # For one-off testing, just comment this assertion out. 202 | assert(!is_debug || !(is_msan || is_ubsan || is_ubsan_null || is_ubsan_vptr), 203 | "Sanitizers should generally be used in release (set is_debug=false).") 204 | 205 | assert(!is_hwasan || (is_android && current_cpu == "arm64"), 206 | "HWASan only supported on Android ARM64 builds.") 207 | 208 | assert(!is_msan || ((is_linux || is_chromeos) && current_cpu == "x64"), 209 | "MSan currently only works on 64-bit Linux and ChromeOS builds.") 210 | 211 | assert(!is_lsan || is_asan, "is_lsan = true requires is_asan = true also.") 212 | 213 | # ASAN build on Windows is not working in debug mode. Intercepting memory 214 | # allocation functions is hard on Windows and not yet implemented in LLVM. 215 | assert(!is_win || !is_debug || !is_asan, 216 | "ASan on Windows doesn't work in debug (set is_debug=false).") 217 | 218 | # libFuzzer targets can fail to build or behave incorrectly when built without 219 | # ASAN on Windows. 220 | assert(!is_win || !use_libfuzzer || is_asan, 221 | "use_libfuzzer on Windows requires setting is_asan = true") 222 | 223 | # Make sure that if we recover on detection (i.e. not crash), diagnostics are 224 | # printed. 225 | assert(!use_cfi_recover || use_cfi_diag, 226 | "Only use CFI recovery together with diagnostics.") 227 | 228 | # TODO(crbug.com/753445): the use_sanitizer_coverage arg is currently 229 | # not supported by the Chromium mac_clang_x64 toolchain on iOS distribution. 230 | # The coverage works with iOS toolchain but it is broken when the mac 231 | # toolchain is used as a secondary one on iOS distribution. E.g., it should be 232 | # possible to build the "net" target for iOS with the sanitizer coverage 233 | # enabled. 234 | assert( 235 | !(use_sanitizer_coverage && is_mac && target_os == "ios"), 236 | "crbug.com/753445: use_sanitizer_coverage=true is not supported by the " + 237 | "Chromium mac_clang_x64 toolchain on iOS distribution. Please set " + 238 | "the argument value to false.") 239 | -------------------------------------------------------------------------------- /toolchain/apple/linker_driver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2016 The Chromium Authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style license that can be 5 | # found in the LICENSE file. 6 | 7 | import os 8 | import os.path 9 | import shutil 10 | import subprocess 11 | import sys 12 | 13 | # On mac, the values of these globals are modified when parsing -Wcrl, flags. On 14 | # ios, the script uses the defaults. 15 | DSYMUTIL_INVOKE = ['xcrun', 'dsymutil'] 16 | STRIP_INVOKE = ['xcrun', 'strip'] 17 | 18 | # Setting this flag will emit a deterministic binary by stripping dates from the 19 | # N_OSO field. 20 | DETERMINISTIC_FLAG = '--deterministic' 21 | 22 | # The linker_driver.py is responsible for forwarding a linker invocation to 23 | # the compiler driver, while processing special arguments itself. 24 | # 25 | # Usage: linker_driver.py clang++ main.o -L. -llib -o prog -Wcrl,dsym,out 26 | # 27 | # On Mac, the logical step of linking is handled by three discrete tools to 28 | # perform the image link, debug info link, and strip. The linker_driver.py 29 | # combines these three steps into a single tool. 30 | # 31 | # The command passed to the linker_driver.py should be the compiler driver 32 | # invocation for the linker. It is first invoked unaltered (except for the 33 | # removal of the special driver arguments, described below). Then the driver 34 | # performs additional actions, based on these arguments: 35 | # 36 | # -Wcrl,dsym, 37 | # After invoking the linker, this will run `dsymutil` on the linker's 38 | # output, producing a dSYM bundle, stored at dsym_path_prefix. As an 39 | # example, if the linker driver were invoked with: 40 | # "... -o out/gn/obj/foo/libbar.dylib ... -Wcrl,dsym,out/gn ..." 41 | # The resulting dSYM would be out/gn/libbar.dylib.dSYM/. 42 | # 43 | # -Wcrl,dsymutilpath, 44 | # Sets the path to the dsymutil to run with -Wcrl,dsym, in which case 45 | # `xcrun` is not used to invoke it. 46 | # 47 | # -Wcrl,unstripped, 48 | # After invoking the linker, and before strip, this will save a copy of 49 | # the unstripped linker output in the directory unstripped_path_prefix. 50 | # 51 | # -Wcrl,strip, 52 | # After invoking the linker, and optionally dsymutil, this will run 53 | # the strip command on the linker's output. strip_arguments are 54 | # comma-separated arguments to be passed to the strip command. 55 | # 56 | # -Wcrl,strippath, 57 | # Sets the path to the strip to run with -Wcrl,strip, in which case 58 | # `xcrun` is not used to invoke it. 59 | 60 | 61 | def Main(args): 62 | """Main function for the linker driver. Separates out the arguments for 63 | the main compiler driver and the linker driver, then invokes all the 64 | required tools. 65 | 66 | Args: 67 | args: list of string, Arguments to the script. 68 | """ 69 | 70 | if len(args) < 2: 71 | raise RuntimeError("Usage: linker_driver.py [linker-invocation]") 72 | 73 | # Collect arguments to the linker driver (this script) and remove them from 74 | # the arguments being passed to the compiler driver. 75 | linker_driver_actions = {} 76 | compiler_driver_args = [] 77 | deterministic = False 78 | for arg in args[1:]: 79 | if arg.startswith(_LINKER_DRIVER_ARG_PREFIX): 80 | # Convert driver actions into a map of name => lambda to invoke. 81 | driver_action = ProcessLinkerDriverArg(arg) 82 | assert driver_action[0] not in linker_driver_actions 83 | linker_driver_actions[driver_action[0]] = driver_action[1] 84 | elif arg == DETERMINISTIC_FLAG: 85 | deterministic = True 86 | else: 87 | compiler_driver_args.append(arg) 88 | 89 | linker_driver_outputs = [_FindLinkerOutput(compiler_driver_args)] 90 | 91 | try: 92 | # Zero the mtime in OSO fields for deterministic builds. 93 | # https://crbug.com/330262. 94 | env = os.environ.copy() 95 | if deterministic: 96 | env['ZERO_AR_DATE'] = '1' 97 | # Run the linker by invoking the compiler driver. 98 | subprocess.check_call(compiler_driver_args, env=env) 99 | 100 | # Run the linker driver actions, in the order specified by the actions list. 101 | for action in _LINKER_DRIVER_ACTIONS: 102 | name = action[0] 103 | if name in linker_driver_actions: 104 | linker_driver_outputs += linker_driver_actions[name](args) 105 | except: 106 | # If a linker driver action failed, remove all the outputs to make the 107 | # build step atomic. 108 | map(_RemovePath, linker_driver_outputs) 109 | 110 | # Re-report the original failure. 111 | raise 112 | 113 | 114 | def ProcessLinkerDriverArg(arg): 115 | """Processes a linker driver argument and returns a tuple containing the 116 | name and unary lambda to invoke for that linker driver action. 117 | 118 | Args: 119 | arg: string, The linker driver argument. 120 | 121 | Returns: 122 | A 2-tuple: 123 | 0: The driver action name, as in _LINKER_DRIVER_ACTIONS. 124 | 1: An 1-ary lambda that takes the full list of arguments passed to 125 | Main(). The lambda should call the linker driver action that 126 | corresponds to the argument and return a list of outputs from the 127 | action. 128 | """ 129 | if not arg.startswith(_LINKER_DRIVER_ARG_PREFIX): 130 | raise ValueError('%s is not a linker driver argument' % (arg, )) 131 | 132 | sub_arg = arg[len(_LINKER_DRIVER_ARG_PREFIX):] 133 | 134 | for driver_action in _LINKER_DRIVER_ACTIONS: 135 | (name, action) = driver_action 136 | if sub_arg.startswith(name): 137 | return (name, lambda full_args: action(sub_arg[len(name):], full_args)) 138 | 139 | raise ValueError('Unknown linker driver argument: %s' % (arg, )) 140 | 141 | 142 | def RunDsymUtil(dsym_path_prefix, full_args): 143 | """Linker driver action for -Wcrl,dsym,. Invokes dsymutil 144 | on the linker's output and produces a dsym file at |dsym_file| path. 145 | 146 | Args: 147 | dsym_path_prefix: string, The path at which the dsymutil output should be 148 | located. 149 | full_args: list of string, Full argument list for the linker driver. 150 | 151 | Returns: 152 | list of string, Build step outputs. 153 | """ 154 | if not len(dsym_path_prefix): 155 | raise ValueError('Unspecified dSYM output file') 156 | 157 | linker_out = _FindLinkerOutput(full_args) 158 | base = os.path.basename(linker_out) 159 | dsym_out = os.path.join(dsym_path_prefix, base + '.dSYM') 160 | 161 | # Remove old dSYMs before invoking dsymutil. 162 | _RemovePath(dsym_out) 163 | 164 | tools_paths = _FindToolsPaths(full_args) 165 | if os.environ.get('PATH'): 166 | tools_paths.append(os.environ['PATH']) 167 | dsymutil_env = os.environ.copy() 168 | dsymutil_env['PATH'] = ':'.join(tools_paths) 169 | subprocess.check_call(DSYMUTIL_INVOKE + ['-o', dsym_out, linker_out], 170 | env=dsymutil_env) 171 | return [dsym_out] 172 | 173 | 174 | def SetDsymutilPath(dsymutil_path, full_args): 175 | """Linker driver action for -Wcrl,dsymutilpath,. 176 | 177 | Sets the invocation command for dsymutil, which allows the caller to specify 178 | an alternate dsymutil. This action is always processed before the RunDsymUtil 179 | action. 180 | 181 | Args: 182 | dsymutil_path: string, The path to the dsymutil binary to run 183 | full_args: list of string, Full argument list for the linker driver. 184 | 185 | Returns: 186 | No output - this step is run purely for its side-effect. 187 | """ 188 | global DSYMUTIL_INVOKE 189 | DSYMUTIL_INVOKE = [dsymutil_path] 190 | return [] 191 | 192 | 193 | def RunSaveUnstripped(unstripped_path_prefix, full_args): 194 | """Linker driver action for -Wcrl,unstripped,. Copies 195 | the linker output to |unstripped_path_prefix| before stripping. 196 | 197 | Args: 198 | unstripped_path_prefix: string, The path at which the unstripped output 199 | should be located. 200 | full_args: list of string, Full argument list for the linker driver. 201 | 202 | Returns: 203 | list of string, Build step outputs. 204 | """ 205 | if not len(unstripped_path_prefix): 206 | raise ValueError('Unspecified unstripped output file') 207 | 208 | linker_out = _FindLinkerOutput(full_args) 209 | base = os.path.basename(linker_out) 210 | unstripped_out = os.path.join(unstripped_path_prefix, base + '.unstripped') 211 | 212 | shutil.copyfile(linker_out, unstripped_out) 213 | return [unstripped_out] 214 | 215 | 216 | def RunStrip(strip_args_string, full_args): 217 | """Linker driver action for -Wcrl,strip,. 218 | 219 | Args: 220 | strip_args_string: string, Comma-separated arguments for `strip`. 221 | full_args: list of string, Full arguments for the linker driver. 222 | 223 | Returns: 224 | list of string, Build step outputs. 225 | """ 226 | strip_command = list(STRIP_INVOKE) 227 | if len(strip_args_string) > 0: 228 | strip_command += strip_args_string.split(',') 229 | strip_command.append(_FindLinkerOutput(full_args)) 230 | subprocess.check_call(strip_command) 231 | return [] 232 | 233 | 234 | def SetStripPath(strip_path, full_args): 235 | """Linker driver action for -Wcrl,strippath,. 236 | 237 | Sets the invocation command for strip, which allows the caller to specify 238 | an alternate strip. This action is always processed before the RunStrip 239 | action. 240 | 241 | Args: 242 | strip_path: string, The path to the strip binary to run 243 | full_args: list of string, Full argument list for the linker driver. 244 | 245 | Returns: 246 | No output - this step is run purely for its side-effect. 247 | """ 248 | global STRIP_INVOKE 249 | STRIP_INVOKE = [strip_path] 250 | return [] 251 | 252 | 253 | def _FindLinkerOutput(full_args): 254 | """Finds the output of the linker by looking for the output flag in its 255 | argument list. As this is a required linker argument, raises an error if it 256 | cannot be found. 257 | """ 258 | # The linker_driver.py script may be used to wrap either the compiler linker 259 | # (uses -o to configure the output) or lipo (uses -output to configure the 260 | # output). Since wrapping the compiler linker is the most likely possibility 261 | # use try/except and fallback to checking for -output if -o is not found. 262 | try: 263 | output_flag_index = full_args.index('-o') 264 | except ValueError: 265 | output_flag_index = full_args.index('-output') 266 | return full_args[output_flag_index + 1] 267 | 268 | 269 | def _FindToolsPaths(full_args): 270 | """Finds all paths where the script should look for additional tools.""" 271 | paths = [] 272 | for idx, arg in enumerate(full_args): 273 | if arg in ['-B', '--prefix']: 274 | paths.append(full_args[idx + 1]) 275 | elif arg.startswith('-B'): 276 | paths.append(arg[2:]) 277 | elif arg.startswith('--prefix='): 278 | paths.append(arg[9:]) 279 | return paths 280 | 281 | 282 | def _RemovePath(path): 283 | """Removes the file or directory at |path| if it exists.""" 284 | if os.path.exists(path): 285 | if os.path.isdir(path): 286 | shutil.rmtree(path) 287 | else: 288 | os.unlink(path) 289 | 290 | 291 | _LINKER_DRIVER_ARG_PREFIX = '-Wcrl,' 292 | """List of linker driver actions. The sort order of this list affects the 293 | order in which the actions are invoked. The first item in the tuple is the 294 | argument's -Wcrl, and the second is the function to invoke. 295 | """ 296 | _LINKER_DRIVER_ACTIONS = [ 297 | ('dsymutilpath,', SetDsymutilPath), 298 | ('dsym,', RunDsymUtil), 299 | ('unstripped,', RunSaveUnstripped), 300 | ('strippath,', SetStripPath), 301 | ('strip,', RunStrip), 302 | ] 303 | 304 | if __name__ == '__main__': 305 | Main(sys.argv) 306 | sys.exit(0) 307 | -------------------------------------------------------------------------------- /gn_helpers_unittest.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import sys 6 | import textwrap 7 | import unittest 8 | 9 | import gn_helpers 10 | 11 | 12 | class UnitTest(unittest.TestCase): 13 | def test_ToGNString(self): 14 | test_cases = [ 15 | (42, '42', '42'), ('foo', '"foo"', '"foo"'), (True, 'true', 'true'), 16 | (False, 'false', 'false'), ('', '""', '""'), 17 | ('\\$"$\\', '"\\\\\\$\\"\\$\\\\"', '"\\\\\\$\\"\\$\\\\"'), 18 | (' \t\r\n', '" $0x09$0x0D$0x0A"', '" $0x09$0x0D$0x0A"'), 19 | (u'\u2713', '"$0xE2$0x9C$0x93"', '"$0xE2$0x9C$0x93"'), 20 | ([], '[ ]', '[]'), ([1], '[ 1 ]', '[\n 1\n]\n'), 21 | ([3, 1, 4, 1], '[ 3, 1, 4, 1 ]', '[\n 3,\n 1,\n 4,\n 1\n]\n'), 22 | (['a', True, 2], '[ "a", true, 2 ]', '[\n "a",\n true,\n 2\n]\n'), 23 | ({ 24 | 'single': 'item' 25 | }, 'single = "item"\n', 'single = "item"\n'), 26 | ({ 27 | 'kEy': 137, 28 | '_42A_Zaz_': [False, True] 29 | }, '_42A_Zaz_ = [ false, true ]\nkEy = 137\n', 30 | '_42A_Zaz_ = [\n false,\n true\n]\nkEy = 137\n'), 31 | ([1, 'two', 32 | ['"thr,.$\\', True, False, [], 33 | u'(\u2713)']], '[ 1, "two", [ "\\"thr,.\\$\\\\", true, false, ' + 34 | '[ ], "($0xE2$0x9C$0x93)" ] ]', '''[ 35 | 1, 36 | "two", 37 | [ 38 | "\\"thr,.\\$\\\\", 39 | true, 40 | false, 41 | [], 42 | "($0xE2$0x9C$0x93)" 43 | ] 44 | ] 45 | '''), 46 | ({ 47 | 's': 'foo', 48 | 'n': 42, 49 | 'b': True, 50 | 'a': [3, 'x'] 51 | }, 'a = [ 3, "x" ]\nb = true\nn = 42\ns = "foo"\n', 52 | 'a = [\n 3,\n "x"\n]\nb = true\nn = 42\ns = "foo"\n'), 53 | ( 54 | [[[], [[]]], []], 55 | '[ [ [ ], [ [ ] ] ], [ ] ]', 56 | '[\n [\n [],\n [\n []\n ]\n ],\n []\n]\n', 57 | ), 58 | ( 59 | [{ 60 | 'a': 1, 61 | 'c': { 62 | 'z': 8 63 | }, 64 | 'b': [] 65 | }], 66 | '[ { a = 1\nb = [ ]\nc = { z = 8 } } ]\n', 67 | '[\n {\n a = 1\n b = []\n c = {\n' + 68 | ' z = 8\n }\n }\n]\n', 69 | ) 70 | ] 71 | for obj, exp_ugly, exp_pretty in test_cases: 72 | out_ugly = gn_helpers.ToGNString(obj) 73 | self.assertEqual(exp_ugly, out_ugly) 74 | out_pretty = gn_helpers.ToGNString(obj, pretty=True) 75 | self.assertEqual(exp_pretty, out_pretty) 76 | 77 | def test_UnescapeGNString(self): 78 | # Backslash followed by a \, $, or " means the folling character without 79 | # the special meaning. Backslash followed by everything else is a literal. 80 | self.assertEqual( 81 | gn_helpers.UnescapeGNString('\\as\\$\\\\asd\\"'), 82 | '\\as$\\asd"') 83 | 84 | def test_FromGNString(self): 85 | self.assertEqual( 86 | gn_helpers.FromGNString('[1, -20, true, false,["as\\"", []]]'), 87 | [ 1, -20, True, False, [ 'as"', [] ] ]) 88 | 89 | with self.assertRaises(gn_helpers.GNError): 90 | parser = gn_helpers.GNValueParser('123 456') 91 | parser.Parse() 92 | 93 | def test_ParseBool(self): 94 | parser = gn_helpers.GNValueParser('true') 95 | self.assertEqual(parser.Parse(), True) 96 | 97 | parser = gn_helpers.GNValueParser('false') 98 | self.assertEqual(parser.Parse(), False) 99 | 100 | def test_ParseNumber(self): 101 | parser = gn_helpers.GNValueParser('123') 102 | self.assertEqual(parser.ParseNumber(), 123) 103 | 104 | with self.assertRaises(gn_helpers.GNError): 105 | parser = gn_helpers.GNValueParser('') 106 | parser.ParseNumber() 107 | with self.assertRaises(gn_helpers.GNError): 108 | parser = gn_helpers.GNValueParser('a123') 109 | parser.ParseNumber() 110 | 111 | def test_ParseString(self): 112 | parser = gn_helpers.GNValueParser('"asdf"') 113 | self.assertEqual(parser.ParseString(), 'asdf') 114 | 115 | with self.assertRaises(gn_helpers.GNError): 116 | parser = gn_helpers.GNValueParser('') # Empty. 117 | parser.ParseString() 118 | with self.assertRaises(gn_helpers.GNError): 119 | parser = gn_helpers.GNValueParser('asdf') # Unquoted. 120 | parser.ParseString() 121 | with self.assertRaises(gn_helpers.GNError): 122 | parser = gn_helpers.GNValueParser('"trailing') # Unterminated. 123 | parser.ParseString() 124 | 125 | def test_ParseList(self): 126 | parser = gn_helpers.GNValueParser('[1,]') # Optional end comma OK. 127 | self.assertEqual(parser.ParseList(), [ 1 ]) 128 | 129 | with self.assertRaises(gn_helpers.GNError): 130 | parser = gn_helpers.GNValueParser('') # Empty. 131 | parser.ParseList() 132 | with self.assertRaises(gn_helpers.GNError): 133 | parser = gn_helpers.GNValueParser('asdf') # No []. 134 | parser.ParseList() 135 | with self.assertRaises(gn_helpers.GNError): 136 | parser = gn_helpers.GNValueParser('[1, 2') # Unterminated 137 | parser.ParseList() 138 | with self.assertRaises(gn_helpers.GNError): 139 | parser = gn_helpers.GNValueParser('[1 2]') # No separating comma. 140 | parser.ParseList() 141 | 142 | def test_ParseScope(self): 143 | parser = gn_helpers.GNValueParser('{a = 1}') 144 | self.assertEqual(parser.ParseScope(), {'a': 1}) 145 | 146 | with self.assertRaises(gn_helpers.GNError): 147 | parser = gn_helpers.GNValueParser('') # Empty. 148 | parser.ParseScope() 149 | with self.assertRaises(gn_helpers.GNError): 150 | parser = gn_helpers.GNValueParser('asdf') # No {}. 151 | parser.ParseScope() 152 | with self.assertRaises(gn_helpers.GNError): 153 | parser = gn_helpers.GNValueParser('{a = 1') # Unterminated. 154 | parser.ParseScope() 155 | with self.assertRaises(gn_helpers.GNError): 156 | parser = gn_helpers.GNValueParser('{"a" = 1}') # Not identifier. 157 | parser.ParseScope() 158 | with self.assertRaises(gn_helpers.GNError): 159 | parser = gn_helpers.GNValueParser('{a = }') # No value. 160 | parser.ParseScope() 161 | 162 | def test_FromGNArgs(self): 163 | # Booleans and numbers should work; whitespace is allowed works. 164 | self.assertEqual(gn_helpers.FromGNArgs('foo = true\nbar = 1\n'), 165 | {'foo': True, 'bar': 1}) 166 | 167 | # Whitespace is not required; strings should also work. 168 | self.assertEqual(gn_helpers.FromGNArgs('foo="bar baz"'), 169 | {'foo': 'bar baz'}) 170 | 171 | # Comments should work (and be ignored). 172 | gn_args_lines = [ 173 | '# Top-level comment.', 174 | 'foo = true', 175 | 'bar = 1 # In-line comment followed by whitespace.', 176 | ' ', 177 | 'baz = false', 178 | ] 179 | self.assertEqual(gn_helpers.FromGNArgs('\n'.join(gn_args_lines)), { 180 | 'foo': True, 181 | 'bar': 1, 182 | 'baz': False 183 | }) 184 | 185 | # Lists should work. 186 | self.assertEqual(gn_helpers.FromGNArgs('foo=[1, 2, 3]'), 187 | {'foo': [1, 2, 3]}) 188 | 189 | # Empty strings should return an empty dict. 190 | self.assertEqual(gn_helpers.FromGNArgs(''), {}) 191 | self.assertEqual(gn_helpers.FromGNArgs(' \n '), {}) 192 | 193 | # Comments should work everywhere (and be ignored). 194 | gn_args_lines = [ 195 | '# Top-level comment.', 196 | '', 197 | '# Variable comment.', 198 | 'foo = true', 199 | 'bar = [', 200 | ' # Value comment in list.', 201 | ' 1,', 202 | ' 2,', 203 | ']', 204 | '', 205 | 'baz # Comment anywhere, really', 206 | ' = # also here', 207 | ' 4', 208 | ] 209 | self.assertEqual(gn_helpers.FromGNArgs('\n'.join(gn_args_lines)), { 210 | 'foo': True, 211 | 'bar': [1, 2], 212 | 'baz': 4 213 | }) 214 | 215 | # Scope should be parsed, even empty ones. 216 | gn_args_lines = [ 217 | 'foo = {', 218 | ' a = 1', 219 | ' b = [', 220 | ' { },', 221 | ' {', 222 | ' c = 1', 223 | ' },', 224 | ' ]', 225 | '}', 226 | ] 227 | self.assertEqual(gn_helpers.FromGNArgs('\n'.join(gn_args_lines)), 228 | {'foo': { 229 | 'a': 1, 230 | 'b': [ 231 | {}, 232 | { 233 | 'c': 1, 234 | }, 235 | ] 236 | }}) 237 | 238 | # Non-identifiers should raise an exception. 239 | with self.assertRaises(gn_helpers.GNError): 240 | gn_helpers.FromGNArgs('123 = true') 241 | 242 | # References to other variables should raise an exception. 243 | with self.assertRaises(gn_helpers.GNError): 244 | gn_helpers.FromGNArgs('foo = bar') 245 | 246 | # References to functions should raise an exception. 247 | with self.assertRaises(gn_helpers.GNError): 248 | gn_helpers.FromGNArgs('foo = exec_script("//build/baz.py")') 249 | 250 | # Underscores in identifiers should work. 251 | self.assertEqual(gn_helpers.FromGNArgs('_foo = true'), 252 | {'_foo': True}) 253 | self.assertEqual(gn_helpers.FromGNArgs('foo_bar = true'), 254 | {'foo_bar': True}) 255 | self.assertEqual(gn_helpers.FromGNArgs('foo_=true'), 256 | {'foo_': True}) 257 | 258 | def test_ReplaceImports(self): 259 | # Should be a no-op on args inputs without any imports. 260 | parser = gn_helpers.GNValueParser( 261 | textwrap.dedent(""" 262 | some_arg1 = "val1" 263 | some_arg2 = "val2" 264 | """)) 265 | parser.ReplaceImports() 266 | self.assertEqual( 267 | parser.input, 268 | textwrap.dedent(""" 269 | some_arg1 = "val1" 270 | some_arg2 = "val2" 271 | """)) 272 | 273 | # A single "import(...)" line should be replaced with the contents of the 274 | # file being imported. 275 | parser = gn_helpers.GNValueParser( 276 | textwrap.dedent(""" 277 | some_arg1 = "val1" 278 | import("//some/args/file.gni") 279 | some_arg2 = "val2" 280 | """)) 281 | fake_import = 'some_imported_arg = "imported_val"' 282 | if sys.version_info.major < 3: 283 | from mock import patch, mock_open 284 | builtin_var = '__builtin__' 285 | else: 286 | from unittest.mock import patch, mock_open 287 | builtin_var = 'builtins' 288 | open_fun = '{}.open'.format(builtin_var) 289 | with patch(open_fun, mock_open(read_data=fake_import)): 290 | parser.ReplaceImports() 291 | self.assertEqual( 292 | parser.input, 293 | textwrap.dedent(""" 294 | some_arg1 = "val1" 295 | some_imported_arg = "imported_val" 296 | some_arg2 = "val2" 297 | """)) 298 | 299 | # No trailing parenthesis should raise an exception. 300 | with self.assertRaises(gn_helpers.GNError): 301 | parser = gn_helpers.GNValueParser( 302 | textwrap.dedent('import("//some/args/file.gni"')) 303 | parser.ReplaceImports() 304 | 305 | # No double quotes should raise an exception. 306 | with self.assertRaises(gn_helpers.GNError): 307 | parser = gn_helpers.GNValueParser( 308 | textwrap.dedent('import(//some/args/file.gni)')) 309 | parser.ReplaceImports() 310 | 311 | # A path that's not source absolute should raise an exception. 312 | with self.assertRaises(gn_helpers.GNError): 313 | parser = gn_helpers.GNValueParser( 314 | textwrap.dedent('import("some/relative/args/file.gni")')) 315 | parser.ReplaceImports() 316 | 317 | 318 | if __name__ == '__main__': 319 | unittest.main() 320 | --------------------------------------------------------------------------------