/third_party/crashpad/crashpad/build/ios/setup_ios_gn.py
Python | 405 lines | 369 code | 15 blank | 21 comment | 8 complexity | b68f72bb6dc0adb1a21a1cec5e43f8be MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause
- #!/usr/bin/env python3
- # Copyright 2020 The Crashpad Authors. All rights reserved.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- import argparse
- import configparser
- import convert_gn_xcodeproj
- import errno
- import io
- import os
- import platform
- import re
- import shutil
- import subprocess
- import sys
- import tempfile
- SUPPORTED_TARGETS = ('iphoneos', 'iphonesimulator', 'maccatalyst')
- SUPPORTED_CONFIGS = ('Debug', 'Release', 'Profile', 'Official')
- # Pattern matching lines from ~/.lldbinit that must not be copied to the
- # generated .lldbinit file. They match what the user were told to add to
- # their global ~/.lldbinit file before setup-gn.py was updated to generate
- # a project specific file and thus must not be copied as they would cause
- # the settings to be overwritten.
- LLDBINIT_SKIP_PATTERNS = (
- re.compile('^script sys.path\\[:0\\] = \\[\'.*/src/tools/lldb\'\\]$'),
- re.compile('^script import lldbinit$'),
- re.compile('^settings append target.source-map .* /google/src/.*$'),
- )
- def HostCpuArch():
- '''Returns the arch of the host cpu for GN.'''
- HOST_CPU_ARCH = {
- 'arm64': '"arm64"',
- 'x86_64': '"x64"',
- }
- return HOST_CPU_ARCH[platform.machine()]
- class ConfigParserWithStringInterpolation(configparser.ConfigParser):
- '''A .ini file parser that supports strings and environment variables.'''
- ENV_VAR_PATTERN = re.compile(r'\$([A-Za-z0-9_]+)')
- def values(self, section):
- return filter(
- lambda val: val != '',
- map(lambda kv: self._UnquoteString(self._ExpandEnvVar(kv[1])),
- configparser.ConfigParser.items(self, section)))
- def getstring(self, section, option, fallback=''):
- try:
- raw_value = self.get(section, option)
- except configparser.NoOptionError:
- return fallback
- return self._UnquoteString(self._ExpandEnvVar(raw_value))
- def _UnquoteString(self, string):
- if not string or string[0] != '"' or string[-1] != '"':
- return string
- return string[1:-1]
- def _ExpandEnvVar(self, value):
- match = self.ENV_VAR_PATTERN.search(value)
- if not match:
- return value
- name, (begin, end) = match.group(1), match.span(0)
- prefix, suffix = value[:begin], self._ExpandEnvVar(value[end:])
- return prefix + os.environ.get(name, '') + suffix
- class GnGenerator(object):
- '''Holds configuration for a build and method to generate gn default files.'''
- FAT_BUILD_DEFAULT_ARCH = '64-bit'
- TARGET_CPU_VALUES = {
- 'iphoneos': '"arm64"',
- 'iphonesimulator': HostCpuArch(),
- 'maccatalyst': HostCpuArch(),
- }
- TARGET_ENVIRONMENT_VALUES = {
- 'iphoneos': '"device"',
- 'iphonesimulator': '"simulator"',
- 'maccatalyst': '"catalyst"'
- }
- def __init__(self, settings, config, target):
- assert target in SUPPORTED_TARGETS
- assert config in SUPPORTED_CONFIGS
- self._settings = settings
- self._config = config
- self._target = target
- def _GetGnArgs(self, extra_args=None):
- """Build the list of arguments to pass to gn.
- Returns:
- A list of tuple containing gn variable names and variable values (it
- is not a dictionary as the order needs to be preserved).
- """
- args = []
- is_debug = self._config == 'Debug'
- official = self._config == 'Official'
- is_optim = self._config in ('Profile', 'Official')
- args.append(('target_os', '"ios"'))
- args.append(('is_debug', is_debug))
- if os.environ.get('FORCE_MAC_TOOLCHAIN', '0') == '1':
- args.append(('use_system_xcode', False))
- args.append(('target_cpu', self.TARGET_CPU_VALUES[self._target]))
- args.append((
- 'target_environment',
- self.TARGET_ENVIRONMENT_VALUES[self._target]))
- # If extra arguments are passed to the function, pass them before the
- # user overrides (if any).
- if extra_args is not None:
- args.extend(extra_args)
- # Add user overrides after the other configurations so that they can
- # refer to them and override them.
- args.extend(self._settings.items('gn_args'))
- return args
- def Generate(self, gn_path, proj_name, root_path, build_dir):
- self.WriteArgsGn(build_dir, xcode_project_name=proj_name)
- subprocess.check_call(self.GetGnCommand(
- gn_path, root_path, build_dir, xcode_project_name=proj_name))
- def CreateGnRules(self, gn_path, root_path, build_dir):
- gn_command = self.GetGnCommand(gn_path, root_path, build_dir)
- self.WriteArgsGn(build_dir)
- self.WriteBuildNinja(gn_command, build_dir)
- self.WriteBuildNinjaDeps(build_dir)
- def WriteArgsGn(self, build_dir, xcode_project_name=None):
- with open(os.path.join(build_dir, 'args.gn'), 'w') as stream:
- stream.write('# This file was generated by setup-gn.py. Do not edit\n')
- stream.write('# but instead use ~/.setup-gn or $repo/.setup-gn files\n')
- stream.write('# to configure settings.\n')
- stream.write('\n')
- if self._target != 'maccatalyst':
- if self._settings.has_section('$imports$'):
- for import_rule in self._settings.values('$imports$'):
- stream.write('import("%s")\n' % import_rule)
- stream.write('\n')
- gn_args = self._GetGnArgs()
- for name, value in gn_args:
- if isinstance(value, bool):
- stream.write('%s = %s\n' % (name, str(value).lower()))
- elif isinstance(value, list):
- stream.write('%s = [%s' % (name, '\n' if len(value) > 1 else ''))
- if len(value) == 1:
- prefix = ' '
- suffix = ' '
- else:
- prefix = ' '
- suffix = ',\n'
- for item in value:
- if isinstance(item, bool):
- stream.write('%s%s%s' % (prefix, str(item).lower(), suffix))
- else:
- stream.write('%s%s%s' % (prefix, item, suffix))
- stream.write(']\n')
- else:
- # ConfigParser removes quote around empty string which confuse
- # `gn gen` so restore them.
- if not value:
- value = '""'
- stream.write('%s = %s\n' % (name, value))
- def WriteBuildNinja(self, gn_command, build_dir):
- with open(os.path.join(build_dir, 'build.ninja'), 'w') as stream:
- stream.write('ninja_required_version = 1.7.2\n')
- stream.write('\n')
- stream.write('rule gn\n')
- stream.write(' command = %s\n' % NinjaEscapeCommand(gn_command))
- stream.write(' description = Regenerating ninja files\n')
- stream.write('\n')
- stream.write('build build.ninja: gn\n')
- stream.write(' generator = 1\n')
- stream.write(' depfile = build.ninja.d\n')
- def WriteBuildNinjaDeps(self, build_dir):
- with open(os.path.join(build_dir, 'build.ninja.d'), 'w') as stream:
- stream.write('build.ninja: nonexistant_file.gn\n')
- def GetGnCommand(self, gn_path, src_path, out_path, xcode_project_name=None):
- gn_command = [ gn_path, '--root=%s' % os.path.realpath(src_path), '-q' ]
- if xcode_project_name is not None:
- gn_command.append('--ide=xcode')
- gn_command.append('--ninja-executable=autoninja')
- gn_command.append('--xcode-build-system=new')
- gn_command.append('--xcode-project=%s' % xcode_project_name)
- if self._settings.has_section('filters'):
- target_filters = self._settings.values('filters')
- if target_filters:
- gn_command.append('--filters=%s' % ';'.join(target_filters))
- else:
- gn_command.append('--check')
- gn_command.append('gen')
- gn_command.append('//%s' %
- os.path.relpath(os.path.abspath(out_path), os.path.abspath(src_path)))
- return gn_command
- def NinjaNeedEscape(arg):
- '''Returns True if |arg| needs to be escaped when written to .ninja file.'''
- return ':' in arg or '*' in arg or ';' in arg
- def NinjaEscapeCommand(command):
- '''Escapes |command| in order to write it to .ninja file.'''
- result = []
- for arg in command:
- if NinjaNeedEscape(arg):
- arg = arg.replace(':', '$:')
- arg = arg.replace(';', '\\;')
- arg = arg.replace('*', '\\*')
- else:
- result.append(arg)
-