| # Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright |
| # 2012 Google Inc. All rights reserved. Use of this source code is governed by |
| # a BSD-style license that can be found in the LICENSE file. |
| |
| # This script determines the contents of the per-configuration `args.gn` files |
| # that are used to build CEF/Chromium with GN. See comments in CEF's top-level |
| # BUILD.gn file for general GN usage instructions. |
| # |
| # This script performs the following tasks: |
| # |
| # - Defines CEF's default and required arg values in cases where they differ |
| # from Chromium's. |
| # - Accepts user-defined arg values via the GN_DEFINES environment variable. |
| # - Verifies that user-defined arg values do not conflict with CEF's |
| # requirements. |
| # - Generates multiple configurations by combining user-defined arg values with |
| # CEF's default and required values. |
| # |
| # Before adding a new arg value in this script determine the following: |
| # |
| # - Chromium's default value. Default values are defined in the declare_args() |
| # sections of *.gni files. |
| # - Chromium's value requirements. Check for assert()s related to the value in |
| # Chromium code. |
| # - Whether a particular value is optional or required for CEF. |
| # - Under what conditions a particular value is required for CEF (platform, |
| # build type, CPU architecture, etc). |
| # |
| # If CEF can use Chromium's default value and has no additional validation |
| # requirements then do nothing. |
| # |
| # If CEF can use Chromium's default value but would like to enforce additional |
| # validation requirements then go to 3B. |
| # |
| # If CEF cannot or should not use Chromium's default value then choose one of |
| # the following: |
| # |
| # 1. If CEF requires a different value either globally or based on the platform: |
| # - Add an assert() for the value in CEF's top-level BUILD.gn file. |
| # - Add the required value in GetRequiredArgs(). |
| # - Result: CEF's required value will be used. The user cannot override the |
| # value via GN_DEFINES. |
| # |
| # 2. If CEF requires a different value based on the build type or CPU |
| # architecture: |
| # - Add an assert() for the value in CEF's top-level BUILD.gn file. |
| # - Add the required value in GetConfigArgs(). |
| # - Result: CEF's required value will be used. The user cannot override the |
| # value via GN_DEFINES. |
| # |
| # 3. If CEF recommends (but does not require) a different value either globally |
| # or based on the platform: |
| # A. Set the default value: |
| # - Add the recommended value in GetRecommendedDefaultArgs(). |
| # - Result: CEF's recommended value will be used by default. The user can |
| # override the value via GN_DEFINES. |
| # |
| # B. If CEF has additional validation requirements: |
| # - Add the default Chromium value in GetChromiumDefaultArgs(). |
| # - Perform validation in ValidateArgs(). |
| # - Result: An AssertionError will be thrown if validation fails. |
| |
| from __future__ import absolute_import |
| from __future__ import print_function |
| import os |
| import shlex |
| import sys |
| |
| # The CEF directory is the parent directory of _this_ script. |
| cef_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) |
| # The src directory is the parent directory of the CEF directory. |
| src_dir = os.path.abspath(os.path.join(cef_dir, os.pardir)) |
| |
| # Determine the platform. |
| if sys.platform == 'win32': |
| platform = 'windows' |
| elif sys.platform == 'darwin': |
| platform = 'macosx' |
| elif sys.platform.startswith('linux'): |
| platform = 'linux' |
| else: |
| print('Unknown operating system platform') |
| sys.exit() |
| |
| |
| def msg(msg): |
| print('NOTE: ' + msg) |
| |
| |
| def NameValueListToDict(name_value_list): |
| """ |
| Takes an array of strings of the form 'NAME=VALUE' and creates a dictionary |
| of the pairs. If a string is simply NAME, then the value in the dictionary |
| is set to True. If VALUE can be converted to a boolean or integer, it is. |
| """ |
| result = {} |
| for item in name_value_list: |
| tokens = item.split('=', 1) |
| if len(tokens) == 2: |
| token_value = tokens[1] |
| if token_value.lower() == 'true': |
| token_value = True |
| elif token_value.lower() == 'false': |
| token_value = False |
| else: |
| # If we can make it an int, use that, otherwise, use the string. |
| try: |
| token_value = int(token_value) |
| except ValueError: |
| pass |
| # Set the variable to the supplied value. |
| result[tokens[0]] = token_value |
| else: |
| # No value supplied, treat it as a boolean and set it. |
| result[tokens[0]] = True |
| return result |
| |
| |
| def ShlexEnv(env_name): |
| """ |
| Split an environment variable using shell-like syntax. |
| """ |
| flags = os.environ.get(env_name, []) |
| if flags: |
| flags = shlex.split(flags) |
| return flags |
| |
| |
| def MergeDicts(*dict_args): |
| """ |
| Given any number of dicts, shallow copy and merge into a new dict. |
| Precedence goes to key value pairs in latter dicts. |
| """ |
| result = {} |
| for dictionary in dict_args: |
| result.update(dictionary) |
| return result |
| |
| |
| def GetValueString(val): |
| """ |
| Return the string representation of |val| expected by GN. |
| """ |
| if isinstance(val, bool): |
| if val: |
| return 'true' |
| else: |
| return 'false' |
| elif isinstance(val, int): |
| return val |
| else: |
| return '"%s"' % val |
| return val |
| |
| |
| def GetChromiumDefaultArgs(): |
| """ |
| Return default GN args. These must match the Chromium defaults. |
| Only args that may be retrieved via GetArgValue() need to be specified here. |
| """ |
| # Search for these values in declare_args() sections of *.gni files to find |
| # the defaults. |
| |
| defaults = { |
| 'dcheck_always_on': False, |
| 'is_asan': False, |
| 'is_debug': True, |
| 'is_official_build': False, |
| 'target_cpu': 'x64', |
| } |
| |
| if platform == 'linux': |
| defaults['use_sysroot'] = True |
| |
| if platform == 'windows': |
| defaults['is_win_fastlink'] = False |
| defaults['visual_studio_path'] = '' |
| defaults['visual_studio_version'] = '' |
| defaults['visual_studio_runtime_dirs'] = '' |
| defaults['windows_sdk_path'] = '' |
| |
| return defaults |
| |
| |
| def GetArgValue(args, key): |
| """ |
| Return an existing GN arg value or the Chromium default. |
| """ |
| defaults = GetChromiumDefaultArgs() |
| assert key in defaults, "No default Chromium value specified for %s" % key |
| return args.get(key, defaults[key]) |
| |
| |
| def GetRecommendedDefaultArgs(): |
| """ |
| Return recommended default GN args that differ from Chromium defaults. |
| """ |
| # Search for these values in declare_args() sections of *.gni files to find |
| # the defaults. |
| |
| result = { |
| # Enable NaCL. Default is true. False is recommended for faster builds. |
| 'enable_nacl': False, |
| |
| # Disable component builds. Default depends on the platform. True results |
| # in faster local builds but False is required to create a CEF binary |
| # distribution. |
| 'is_component_build': False, |
| |
| # Don't enforce component builds in debug mode |
| 'forbid_non_component_debug_builds': False, |
| } |
| |
| if platform == 'linux': |
| # Use a sysroot environment. Default is true. False is recommended for local |
| # builds. |
| # Run the following commands to download the sysroot environment: |
| # x86 build only: $ export GYP_DEFINES='target_arch=ia32' |
| # x86 or x64 build: $ gclient runhooks |
| result['use_sysroot'] = False |
| |
| # Don't add the `-Wl,--fatal-warnings` linker flag when building on Ubuntu |
| # 14 (Trusty) host systems. It results in errors like the following: |
| # ld.lld: error: found local symbol '__bss_start' in global part of symbol |
| # table in file /usr/lib/x86_64-linux-gnu/libGL.so |
| # TODO(cef): Remove this flag once we require a newer host system. |
| result['fatal_linker_warnings'] = False |
| |
| return result |
| |
| |
| def GetGNEnvArgs(): |
| """ |
| Return GN args specified via the GN_DEFINES env variable. |
| """ |
| return NameValueListToDict(ShlexEnv('GN_DEFINES')) |
| |
| |
| def GetRequiredArgs(): |
| """ |
| Return required GN args. Also enforced by assert() in //cef/BUILD.gn. |
| """ |
| result = { |
| # Set ENABLE_PRINTING=1 ENABLE_BASIC_PRINTING=1. |
| 'enable_basic_printing': True, |
| # ENABLE_SERVICE_DISCOVERY=0 for print preview support |
| 'enable_print_preview': True, |
| 'optimize_webui': True, |
| # Enable support for Widevine CDM. |
| 'enable_widevine': True, |
| |
| # Don't use the chrome style plugin. |
| 'clang_use_chrome_plugins': False, |
| } |
| |
| if platform == 'linux': |
| # Don't generate Chromium installer packages. This avoids GN dependency |
| # errors with CEF (see issue #2301). |
| # Due to the way this variable is declared in chrome/installer/BUILD.gn it |
| # can't be enforced by assert(). |
| result['enable_linux_installer'] = False |
| |
| # Build without GTK dependencies (see issue #2014). |
| result['use_gtk'] = False |
| |
| if platform == 'macosx': |
| # Always generate dSYM files. The make_distrib script will fail if |
| # enable_dsyms=true is not explicitly set when is_official_build=false. |
| result['enable_dsyms'] = True |
| |
| return result |
| |
| |
| def GetMergedArgs(build_args): |
| """ |
| Return merged GN args. |
| """ |
| dict = MergeDicts(GetRecommendedDefaultArgs(), GetGNEnvArgs(), build_args) |
| |
| # Verify that the user is not trying to override required args. |
| required = GetRequiredArgs() |
| for key in required.keys(): |
| if key in dict: |
| assert dict[key] == required[key], \ |
| "%s=%s is required" % (key, GetValueString(required[key])) |
| |
| return MergeDicts(dict, required) |
| |
| |
| def ValidateArgs(args): |
| """ |
| Validate GN arg combinations that we know about. Also provide suggestions |
| where appropriate. |
| """ |
| dcheck_always_on = GetArgValue(args, 'dcheck_always_on') |
| is_asan = GetArgValue(args, 'is_asan') |
| is_debug = GetArgValue(args, 'is_debug') |
| is_official_build = GetArgValue(args, 'is_official_build') |
| target_cpu = GetArgValue(args, 'target_cpu') |
| |
| if platform == 'linux': |
| use_sysroot = GetArgValue(args, 'use_sysroot') |
| |
| if platform == 'windows': |
| is_win_fastlink = GetArgValue(args, 'is_win_fastlink') |
| visual_studio_path = GetArgValue(args, 'visual_studio_path') |
| visual_studio_version = GetArgValue(args, 'visual_studio_version') |
| visual_studio_runtime_dirs = GetArgValue(args, 'visual_studio_runtime_dirs') |
| windows_sdk_path = GetArgValue(args, 'windows_sdk_path') |
| |
| # Target CPU architecture. |
| # - Windows supports "x86" and "x64". |
| # - Mac supports only "x64". |
| # - Linux supports only "x64" unless using a sysroot environment. |
| if platform == 'macosx': |
| assert target_cpu == 'x64', 'target_cpu must be "x64"' |
| elif platform == 'windows': |
| assert target_cpu in ( |
| 'x86', 'x64', 'arm64'), 'target_cpu must be "x86", "x64" or "arm64"' |
| elif platform == 'linux': |
| assert target_cpu in ( |
| 'x86', 'x64', 'arm', |
| 'arm64'), 'target_cpu must be "x86", "x64", "arm" or "arm64"' |
| |
| if platform == 'linux': |
| if target_cpu == 'x86': |
| assert use_sysroot, 'target_cpu="x86" requires use_sysroot=true' |
| elif target_cpu == 'arm': |
| assert use_sysroot, 'target_cpu="arm" requires use_sysroot=true' |
| elif target_cpu == 'arm64': |
| assert use_sysroot, 'target_cpu="arm64" requires use_sysroot=true' |
| |
| # ASAN requires Release builds. |
| if is_asan: |
| assert not is_debug, "is_asan=true requires is_debug=false" |
| if not dcheck_always_on: |
| msg('is_asan=true recommends dcheck_always_on=true') |
| |
| # Official build requires Release builds. |
| if is_official_build: |
| assert not is_debug, "is_official_build=true requires is_debug=false" |
| |
| if platform == 'windows': |
| # Official builds should not use /DEBUG:FASTLINK. |
| if is_official_build: |
| assert not is_win_fastlink, "is_official_build=true precludes is_win_fastlink=true" |
| |
| # Windows custom toolchain requirements. |
| # |
| # Required GN arguments: |
| # visual_studio_path="<path to VS root>" |
| # The directory that contains Visual Studio. For example, a subset of |
| # "C:\Program Files (x86)\Microsoft Visual Studio 14.0". |
| # visual_studio_version="<VS version>" |
| # The VS version. For example, "2015". |
| # visual_studio_runtime_dirs="<path to VS CRT>" |
| # The directory that contains the VS CRT. For example, the contents of |
| # "C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x64" plus |
| # "C:\Windows\System32\ucrtbased.dll" |
| # windows_sdk_path="<path to WinSDK>" |
| # The directory that contains the Win SDK. For example, a subset of |
| # "C:\Program Files (x86)\Windows Kits\10". |
| # |
| # Required environment variables: |
| # DEPOT_TOOLS_WIN_TOOLCHAIN=0 |
| # GYP_MSVS_OVERRIDE_PATH=<path to VS root, must match visual_studio_path> |
| # GYP_MSVS_VERSION=<VS version, must match visual_studio_version> |
| # CEF_VCVARS=none |
| # INCLUDE=<VS include paths> |
| # LIB=<VS library paths> |
| # PATH=<VS executable paths> |
| # |
| # See comments in gclient_hook.py for environment variable usage. |
| # |
| if visual_studio_path != '': |
| assert visual_studio_version != '', 'visual_studio_path requires visual_studio_version' |
| assert visual_studio_runtime_dirs != '', 'visual_studio_path requires visual_studio_runtime_dirs' |
| assert windows_sdk_path != '', 'visual_studio_path requires windows_sdk_path' |
| |
| assert os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '') == '0', \ |
| "visual_studio_path requires DEPOT_TOOLS_WIN_TOOLCHAIN=0 env variable" |
| |
| msvs_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH', '') |
| assert msvs_path == visual_studio_path and os.path.exists(msvs_path), \ |
| "visual_studio_path requires matching GYP_MSVS_OVERRIDE_PATH env variable" |
| |
| msvs_version = os.environ.get('GYP_MSVS_VERSION', '') |
| assert msvs_version == visual_studio_version, \ |
| "visual_studio_version requires matching GYP_MSVS_VERSION env variable" |
| |
| assert os.environ.get('CEF_VCVARS', '') == 'none', \ |
| "visual_studio_path requires CEF_VCVARS=none env variable" |
| |
| assert 'INCLUDE' in os.environ \ |
| and 'LIB' in os.environ \ |
| and 'PATH' in os.environ, \ |
| "visual_studio_path requires INCLUDE, LIB and PATH env variables" |
| |
| # If "%GYP_MSVS_OVERRIDE_PATH%\VC\vcvarsall.bat" exists then environment |
| # variables will be derived from there and the specified INCLUDE/LIB/PATH |
| # values will be ignored by Chromium. If this file does not exist then the |
| # INCLUDE/LIB/PATH values are also required by Chromium. |
| vcvars_path = os.path.join(msvs_path, 'VC', 'vcvarsall.bat') |
| if (os.path.exists(vcvars_path)): |
| msg('INCLUDE/LIB/PATH values will be derived from %s' % vcvars_path) |
| |
| |
| def GetConfigArgs(args, is_debug, cpu): |
| """ |
| Return merged GN args for the configuration and validate. |
| """ |
| add_args = {} |
| |
| # Cannot create is_official_build=true is_debug=true builds. |
| # This restriction is enforced in //build/config/BUILDCONFIG.gn. |
| # Instead, our "official Debug" build is a Release build with dchecks and |
| # symbols. Symbols will be generated by default for official builds; see the |
| # definition of 'symbol_level' in //build/config/compiler/compiler.gni. |
| if is_debug and GetArgValue(args, 'is_official_build'): |
| is_debug = False |
| add_args['dcheck_always_on'] = True |
| |
| result = MergeDicts(args, add_args, { |
| 'is_debug': is_debug, |
| 'target_cpu': cpu, |
| }) |
| |
| if platform == 'linux' and not cpu.startswith('arm'): |
| # Remove any arm-related values from non-arm configs. |
| for key in result.keys(): |
| if key.startswith('arm_'): |
| del result[key] |
| |
| ValidateArgs(result) |
| return result |
| |
| |
| def GetConfigArgsSandbox(platform, args, is_debug, cpu): |
| """ |
| Return merged GN args for the cef_sandbox configuration and validate. |
| """ |
| add_args = { |
| # Avoid libucrt.lib linker errors. |
| 'use_allocator_shim': False, |
| |
| # Avoid /LTCG linker warnings and generate smaller lib files. |
| 'is_official_build': False, |
| |
| # Enable base target customizations necessary for distribution of the |
| # cef_sandbox static library. |
| 'is_cef_sandbox_build': True, |
| } |
| |
| if is_debug: |
| # Enable iterator debugging (_ITERATOR_DEBUG_LEVEL=2). |
| add_args['enable_iterator_debugging'] = True |
| |
| if platform == 'windows': |
| # Avoid Debug build linker errors caused by custom libc++. |
| add_args['use_custom_libcxx'] = False |
| |
| result = MergeDicts(args, add_args, { |
| 'is_debug': is_debug, |
| 'target_cpu': cpu, |
| }) |
| |
| ValidateArgs(result) |
| return result |
| |
| |
| def LinuxSysrootExists(cpu): |
| """ |
| Returns true if the sysroot for the specified |cpu| architecture exists. |
| """ |
| # Directory that contains sysroots. |
| sysroot_root = os.path.join(src_dir, 'build', 'linux') |
| # CPU-specific sysroot directory names. |
| # Should match the values in build/config/sysroot.gni. |
| if cpu == 'x86': |
| sysroot_name = 'debian_sid_i386-sysroot' |
| elif cpu == 'x64': |
| sysroot_name = 'debian_sid_amd64-sysroot' |
| elif cpu == 'arm': |
| sysroot_name = 'debian_sid_arm-sysroot' |
| elif cpu == 'arm64': |
| sysroot_name = 'debian_sid_arm64-sysroot' |
| else: |
| raise Exception('Unrecognized sysroot CPU: %s' % cpu) |
| |
| return os.path.isdir(os.path.join(sysroot_root, sysroot_name)) |
| |
| |
| def GetAllPlatformConfigs(build_args): |
| """ |
| Return a map of directory name to GN args for the current platform. |
| """ |
| result = {} |
| |
| # Merged args without validation. |
| args = GetMergedArgs(build_args) |
| |
| create_debug = True |
| |
| # Don't create debug directories for asan builds. |
| if GetArgValue(args, 'is_asan'): |
| create_debug = False |
| msg('Not generating Debug configuration due to is_asan=true') |
| |
| supported_cpus = [] |
| |
| if platform == 'linux': |
| use_sysroot = GetArgValue(args, 'use_sysroot') |
| if use_sysroot: |
| # Only generate configurations for sysroots that have been installed. |
| for cpu in ('x86', 'x64', 'arm', 'arm64'): |
| if LinuxSysrootExists(cpu): |
| supported_cpus.append(cpu) |
| else: |
| msg('Not generating %s configuration due to missing sysroot directory' |
| % cpu) |
| else: |
| supported_cpus = ['x64'] |
| elif platform == 'windows': |
| supported_cpus = ['x86', 'x64'] |
| if os.environ.get('CEF_ENABLE_ARM64', '') == '1': |
| supported_cpus.append('arm64') |
| elif platform == 'macosx': |
| supported_cpus = ['x64'] |
| else: |
| raise Exception('Unsupported platform') |
| |
| for cpu in supported_cpus: |
| if create_debug: |
| result['Debug_GN_' + cpu] = GetConfigArgs(args, True, cpu) |
| result['Release_GN_' + cpu] = GetConfigArgs(args, False, cpu) |
| |
| if platform in ('windows', 'macosx') and GetArgValue( |
| args, 'is_official_build'): |
| # Build cef_sandbox.lib with a different configuration. |
| if create_debug: |
| result['Debug_GN_' + cpu + '_sandbox'] = GetConfigArgsSandbox( |
| platform, args, True, cpu) |
| result['Release_GN_' + cpu + '_sandbox'] = GetConfigArgsSandbox( |
| platform, args, False, cpu) |
| |
| return result |
| |
| |
| def GetConfigFileContents(args): |
| """ |
| Generate config file contents for the arguments. |
| """ |
| pairs = [] |
| for k in sorted(args.keys()): |
| pairs.append("%s=%s" % (k, GetValueString(args[k]))) |
| return "\n".join(pairs) |
| |
| |
| # Program entry point. |
| if __name__ == '__main__': |
| import sys |
| |
| # Allow override of the platform via the command-line for testing. |
| if len(sys.argv) > 1: |
| platform = sys.argv[1] |
| if not platform in ('linux', 'macosx', 'windows'): |
| sys.stderr.write('Usage: %s <platform>\n' % sys.argv[0]) |
| sys.exit() |
| |
| print('Platform: %s' % platform) |
| |
| # Dump the configuration based on platform and environment. |
| configs = GetAllPlatformConfigs({}) |
| for dir, config in configs.items(): |
| print('\n\nout/%s:\n' % dir) |
| print(GetConfigFileContents(config)) |