mirror of
https://github.com/chromium/crashpad.git
synced 2025-01-14 01:08:01 +08:00
ios: Update build scripts to support Apple Silicon simulators.
Also update mini_chromium to 5654edb422 for target_environment arg. Change-Id: If350938bbeaddbdda123c2f0e9ff978075a60370 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3558170 Reviewed-by: Rohit Rao <rohitrao@chromium.org> Commit-Queue: Justin Cohen <justincohen@chromium.org>
This commit is contained in:
parent
20d6dee037
commit
fa01762894
2
DEPS
2
DEPS
@ -39,7 +39,7 @@ deps = {
|
|||||||
'e1e7b0ad8ee99a875b272c8e33e308472e897660',
|
'e1e7b0ad8ee99a875b272c8e33e308472e897660',
|
||||||
'crashpad/third_party/mini_chromium/mini_chromium':
|
'crashpad/third_party/mini_chromium/mini_chromium':
|
||||||
Var('chromium_git') + '/chromium/mini_chromium@' +
|
Var('chromium_git') + '/chromium/mini_chromium@' +
|
||||||
'f87a38442a9e7ba88d1c4f479e9167927eae84ed',
|
'5654edb4225bcad13901155c819febb5748e502b',
|
||||||
'crashpad/third_party/libfuzzer/src':
|
'crashpad/third_party/libfuzzer/src':
|
||||||
Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +
|
Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +
|
||||||
'fda403cf93ecb8792cb1d061564d89a6553ca020',
|
'fda403cf93ecb8792cb1d061564d89a6553ca020',
|
||||||
|
@ -26,14 +26,146 @@ import argparse
|
|||||||
import collections
|
import collections
|
||||||
import copy
|
import copy
|
||||||
import filecmp
|
import filecmp
|
||||||
import json
|
import functools
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
import string
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import xml.etree.ElementTree
|
||||||
|
|
||||||
|
|
||||||
|
LLDBINIT_PATH = '$(PROJECT_DIR)/.lldbinit'
|
||||||
|
|
||||||
|
PYTHON_RE = re.compile('[ /]python[23]?$')
|
||||||
|
|
||||||
|
XCTEST_PRODUCT_TYPES = frozenset((
|
||||||
|
'com.apple.product-type.bundle.unit-test',
|
||||||
|
'com.apple.product-type.bundle.ui-testing',
|
||||||
|
))
|
||||||
|
|
||||||
|
SCHEME_PRODUCT_TYPES = frozenset((
|
||||||
|
'com.apple.product-type.app-extension',
|
||||||
|
'com.apple.product-type.application',
|
||||||
|
'com.apple.product-type.framework'
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
class Template(string.Template):
|
||||||
|
|
||||||
|
"""A subclass of string.Template that changes delimiter."""
|
||||||
|
|
||||||
|
delimiter = '@'
|
||||||
|
|
||||||
|
|
||||||
|
@functools.lru_cache
|
||||||
|
def LoadSchemeTemplate(root, name):
|
||||||
|
"""Return a string.Template object for scheme file loaded relative to root."""
|
||||||
|
path = os.path.join(root, 'build', 'ios', name)
|
||||||
|
with open(path) as file:
|
||||||
|
return Template(file.read())
|
||||||
|
|
||||||
|
|
||||||
|
def CreateIdentifier(str_id):
|
||||||
|
"""Return a 24 characters string that can be used as an identifier."""
|
||||||
|
return hashlib.sha1(str_id.encode("utf-8")).hexdigest()[:24].upper()
|
||||||
|
|
||||||
|
|
||||||
|
def GenerateSchemeForTarget(root, project, old_project, name, path, tests):
|
||||||
|
"""Generates the .xcsheme file for target named |name|.
|
||||||
|
|
||||||
|
The file is generated in the new project schemes directory from a template.
|
||||||
|
If there is an existing previous project, then the old scheme file is copied
|
||||||
|
and the lldbinit setting is set. If lldbinit setting is already correct, the
|
||||||
|
file is not modified, just copied.
|
||||||
|
"""
|
||||||
|
project_name = os.path.basename(project)
|
||||||
|
relative_path = os.path.join('xcshareddata', 'xcschemes', name + '.xcscheme')
|
||||||
|
identifier = CreateIdentifier('%s %s' % (name, path))
|
||||||
|
|
||||||
|
scheme_path = os.path.join(project, relative_path)
|
||||||
|
if not os.path.isdir(os.path.dirname(scheme_path)):
|
||||||
|
os.makedirs(os.path.dirname(scheme_path))
|
||||||
|
|
||||||
|
old_scheme_path = os.path.join(old_project, relative_path)
|
||||||
|
if os.path.exists(old_scheme_path):
|
||||||
|
made_changes = False
|
||||||
|
|
||||||
|
tree = xml.etree.ElementTree.parse(old_scheme_path)
|
||||||
|
tree_root = tree.getroot()
|
||||||
|
|
||||||
|
for reference in tree_root.findall('.//BuildableReference'):
|
||||||
|
for (attr, value) in (
|
||||||
|
('BuildableName', path),
|
||||||
|
('BlueprintName', name),
|
||||||
|
('BlueprintIdentifier', identifier)):
|
||||||
|
if reference.get(attr) != value:
|
||||||
|
reference.set(attr, value)
|
||||||
|
made_changes = True
|
||||||
|
|
||||||
|
for child in tree_root:
|
||||||
|
if child.tag not in ('TestAction', 'LaunchAction'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if child.get('customLLDBInitFile') != LLDBINIT_PATH:
|
||||||
|
child.set('customLLDBInitFile', LLDBINIT_PATH)
|
||||||
|
made_changes = True
|
||||||
|
|
||||||
|
# Override the list of testables.
|
||||||
|
if child.tag == 'TestAction':
|
||||||
|
for subchild in child:
|
||||||
|
if subchild.tag != 'Testables':
|
||||||
|
continue
|
||||||
|
|
||||||
|
for elt in list(subchild):
|
||||||
|
subchild.remove(elt)
|
||||||
|
|
||||||
|
if tests:
|
||||||
|
template = LoadSchemeTemplate(root, 'xcodescheme-testable.template')
|
||||||
|
for (key, test_path, test_name) in sorted(tests):
|
||||||
|
testable = ''.join(template.substitute(
|
||||||
|
BLUEPRINT_IDENTIFIER=key,
|
||||||
|
BUILDABLE_NAME=test_path,
|
||||||
|
BLUEPRINT_NAME=test_name,
|
||||||
|
PROJECT_NAME=project_name))
|
||||||
|
|
||||||
|
testable_elt = xml.etree.ElementTree.fromstring(testable)
|
||||||
|
subchild.append(testable_elt)
|
||||||
|
|
||||||
|
if made_changes:
|
||||||
|
tree.write(scheme_path, xml_declaration=True, encoding='UTF-8')
|
||||||
|
|
||||||
|
else:
|
||||||
|
shutil.copyfile(old_scheme_path, scheme_path)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
testables = ''
|
||||||
|
if tests:
|
||||||
|
template = LoadSchemeTemplate(root, 'xcodescheme-testable.template')
|
||||||
|
testables = '\n' + ''.join(
|
||||||
|
template.substitute(
|
||||||
|
BLUEPRINT_IDENTIFIER=key,
|
||||||
|
BUILDABLE_NAME=test_path,
|
||||||
|
BLUEPRINT_NAME=test_name,
|
||||||
|
PROJECT_NAME=project_name)
|
||||||
|
for (key, test_path, test_name) in sorted(tests)).rstrip()
|
||||||
|
|
||||||
|
template = LoadSchemeTemplate(root, 'xcodescheme.template')
|
||||||
|
|
||||||
|
with open(scheme_path, 'w') as scheme_file:
|
||||||
|
scheme_file.write(
|
||||||
|
template.substitute(
|
||||||
|
TESTABLES=testables,
|
||||||
|
LLDBINIT_PATH=LLDBINIT_PATH,
|
||||||
|
BLUEPRINT_IDENTIFIER=identifier,
|
||||||
|
BUILDABLE_NAME=path,
|
||||||
|
BLUEPRINT_NAME=name,
|
||||||
|
PROJECT_NAME=project_name))
|
||||||
|
|
||||||
|
|
||||||
class XcodeProject(object):
|
class XcodeProject(object):
|
||||||
@ -46,7 +178,7 @@ class XcodeProject(object):
|
|||||||
while True:
|
while True:
|
||||||
self.counter += 1
|
self.counter += 1
|
||||||
str_id = "%s %s %d" % (parent_name, obj['isa'], self.counter)
|
str_id = "%s %s %d" % (parent_name, obj['isa'], self.counter)
|
||||||
new_id = hashlib.sha1(str_id.encode("utf-8")).hexdigest()[:24].upper()
|
new_id = CreateIdentifier(str_id)
|
||||||
|
|
||||||
# Make sure ID is unique. It's possible there could be an id conflict
|
# Make sure ID is unique. It's possible there could be an id conflict
|
||||||
# since this is run after GN runs.
|
# since this is run after GN runs.
|
||||||
@ -54,6 +186,93 @@ class XcodeProject(object):
|
|||||||
self.objects[new_id] = obj
|
self.objects[new_id] = obj
|
||||||
return new_id
|
return new_id
|
||||||
|
|
||||||
|
def IterObjectsByIsa(self, isa):
|
||||||
|
"""Iterates overs objects of the |isa| type."""
|
||||||
|
for key, obj in self.objects.items():
|
||||||
|
if obj['isa'] == isa:
|
||||||
|
yield (key, obj)
|
||||||
|
|
||||||
|
def IterNativeTargetByProductType(self, product_types):
|
||||||
|
"""Iterates over PBXNativeTarget objects of any |product_types| types."""
|
||||||
|
for key, obj in self.IterObjectsByIsa('PBXNativeTarget'):
|
||||||
|
if obj['productType'] in product_types:
|
||||||
|
yield (key, obj)
|
||||||
|
|
||||||
|
def UpdateBuildScripts(self):
|
||||||
|
"""Update build scripts to respect configuration and platforms."""
|
||||||
|
for key, obj in self.IterObjectsByIsa('PBXShellScriptBuildPhase'):
|
||||||
|
|
||||||
|
shell_path = obj['shellPath']
|
||||||
|
shell_code = obj['shellScript']
|
||||||
|
if shell_path.endswith('/sh'):
|
||||||
|
shell_code = shell_code.replace(
|
||||||
|
'ninja -C .',
|
||||||
|
'ninja -C "../${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}"')
|
||||||
|
elif PYTHON_RE.search(shell_path):
|
||||||
|
shell_code = shell_code.replace(
|
||||||
|
'''ninja_params = [ '-C', '.' ]''',
|
||||||
|
'''ninja_params = [ '-C', '../' + os.environ['CONFIGURATION']'''
|
||||||
|
''' + os.environ['EFFECTIVE_PLATFORM_NAME'] ]''')
|
||||||
|
|
||||||
|
# Replace the build script in the object.
|
||||||
|
obj['shellScript'] = shell_code
|
||||||
|
|
||||||
|
|
||||||
|
def UpdateBuildConfigurations(self, configurations):
|
||||||
|
"""Add new configurations, using the first one as default."""
|
||||||
|
|
||||||
|
# Create a list with all the objects of interest. This is needed
|
||||||
|
# because objects will be added to/removed from the project upon
|
||||||
|
# iterating this list and python dictionaries cannot be mutated
|
||||||
|
# during iteration.
|
||||||
|
for key, obj in list(self.IterObjectsByIsa('XCConfigurationList')):
|
||||||
|
# Use the first build configuration as template for creating all the
|
||||||
|
# new build configurations.
|
||||||
|
build_config_template = self.objects[obj['buildConfigurations'][0]]
|
||||||
|
build_config_template['buildSettings']['CONFIGURATION_BUILD_DIR'] = \
|
||||||
|
'$(PROJECT_DIR)/../$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)'
|
||||||
|
|
||||||
|
|
||||||
|
# Remove the existing build configurations from the project before
|
||||||
|
# creating the new ones.
|
||||||
|
for build_config_id in obj['buildConfigurations']:
|
||||||
|
del self.objects[build_config_id]
|
||||||
|
obj['buildConfigurations'] = []
|
||||||
|
|
||||||
|
for configuration in configurations:
|
||||||
|
build_config = copy.copy(build_config_template)
|
||||||
|
build_config['name'] = configuration
|
||||||
|
build_config_id = self.AddObject('products', build_config)
|
||||||
|
obj['buildConfigurations'].append(build_config_id)
|
||||||
|
|
||||||
|
def GetHostMappingForXCTests(self):
|
||||||
|
"""Returns a dict from targets to the list of their xctests modules."""
|
||||||
|
mapping = collections.defaultdict(list)
|
||||||
|
for key, obj in self.IterNativeTargetByProductType(XCTEST_PRODUCT_TYPES):
|
||||||
|
build_config_lists_id = obj['buildConfigurationList']
|
||||||
|
build_configs = self.objects[build_config_lists_id]['buildConfigurations']
|
||||||
|
|
||||||
|
# Use the first build configuration to get the name of the host target.
|
||||||
|
# This is arbitrary, but since the build configuration are all identical
|
||||||
|
# after UpdateBuildConfiguration, except for their 'name', it is fine.
|
||||||
|
build_config = self.objects[build_configs[0]]
|
||||||
|
if obj['productType'] == 'com.apple.product-type.bundle.unit-test':
|
||||||
|
# The test_host value will look like this:
|
||||||
|
# `$(BUILD_PRODUCTS_DIR)/host_app_name.app/host_app_name`
|
||||||
|
#
|
||||||
|
# Extract the `host_app_name.app` part as key for the output.
|
||||||
|
test_host_path = build_config['buildSettings']['TEST_HOST']
|
||||||
|
test_host_name = os.path.basename(os.path.dirname(test_host_path))
|
||||||
|
else:
|
||||||
|
test_host_name = build_config['buildSettings']['TEST_TARGET_NAME']
|
||||||
|
|
||||||
|
test_name = obj['name']
|
||||||
|
test_path = self.objects[obj['productReference']]['path']
|
||||||
|
|
||||||
|
mapping[test_host_name].append((key, test_name, test_path))
|
||||||
|
|
||||||
|
return dict(mapping)
|
||||||
|
|
||||||
|
|
||||||
def check_output(command):
|
def check_output(command):
|
||||||
"""Wrapper around subprocess.check_output that decode output as utf-8."""
|
"""Wrapper around subprocess.check_output that decode output as utf-8."""
|
||||||
@ -100,7 +319,7 @@ def WriteXcodeProject(output_path, json_string):
|
|||||||
os.path.join(output_path, 'project.pbxproj'))
|
os.path.join(output_path, 'project.pbxproj'))
|
||||||
|
|
||||||
|
|
||||||
def UpdateXcodeProject(project_dir, configurations, root_dir):
|
def UpdateXcodeProject(project_dir, old_project_dir, configurations, root_dir):
|
||||||
"""Update inplace Xcode project to support multiple configurations.
|
"""Update inplace Xcode project to support multiple configurations.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -113,41 +332,25 @@ def UpdateXcodeProject(project_dir, configurations, root_dir):
|
|||||||
json_data = json.loads(LoadXcodeProjectAsJSON(project_dir))
|
json_data = json.loads(LoadXcodeProjectAsJSON(project_dir))
|
||||||
project = XcodeProject(json_data['objects'])
|
project = XcodeProject(json_data['objects'])
|
||||||
|
|
||||||
objects_to_remove = []
|
project.UpdateBuildScripts()
|
||||||
for value in list(project.objects.values()):
|
project.UpdateBuildConfigurations(configurations)
|
||||||
isa = value['isa']
|
|
||||||
|
|
||||||
# Teach build shell script to look for the configuration and platform.
|
mapping = project.GetHostMappingForXCTests()
|
||||||
if isa == 'PBXShellScriptBuildPhase':
|
|
||||||
shell_path = value['shellPath']
|
|
||||||
if shell_path.endswith('/sh'):
|
|
||||||
value['shellScript'] = value['shellScript'].replace(
|
|
||||||
'ninja -C .',
|
|
||||||
'ninja -C "../${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}"')
|
|
||||||
elif re.search('[ /]python[23]?$', shell_path):
|
|
||||||
value['shellScript'] = value['shellScript'].replace(
|
|
||||||
'ninja_params = [ \'-C\', \'.\' ]',
|
|
||||||
'ninja_params = [ \'-C\', \'../\' + os.environ[\'CONFIGURATION\']'
|
|
||||||
' + os.environ[\'EFFECTIVE_PLATFORM_NAME\'] ]')
|
|
||||||
|
|
||||||
# Add new configuration, using the first one as default.
|
# Generate schemes for application, extensions and framework targets
|
||||||
if isa == 'XCConfigurationList':
|
for key, obj in project.IterNativeTargetByProductType(SCHEME_PRODUCT_TYPES):
|
||||||
value['defaultConfigurationName'] = configurations[0]
|
product = project.objects[obj['productReference']]
|
||||||
objects_to_remove.extend(value['buildConfigurations'])
|
product_path = product['path']
|
||||||
|
|
||||||
build_config_template = project.objects[value['buildConfigurations'][0]]
|
# For XCTests, the key is the product path, while for XCUITests, the key
|
||||||
build_config_template['buildSettings']['CONFIGURATION_BUILD_DIR'] = \
|
# is the target name. Use a sum of both possible keys (there should not
|
||||||
'$(PROJECT_DIR)/../$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)'
|
# be overlaps since different hosts are used for XCTests and XCUITests
|
||||||
|
# but this make the code simpler).
|
||||||
|
tests = mapping.get(product_path, []) + mapping.get(obj['name'], [])
|
||||||
|
GenerateSchemeForTarget(
|
||||||
|
root_dir, project_dir, old_project_dir,
|
||||||
|
obj['name'], product_path, tests)
|
||||||
|
|
||||||
value['buildConfigurations'] = []
|
|
||||||
for configuration in configurations:
|
|
||||||
new_build_config = copy.copy(build_config_template)
|
|
||||||
new_build_config['name'] = configuration
|
|
||||||
value['buildConfigurations'].append(
|
|
||||||
project.AddObject('products', new_build_config))
|
|
||||||
|
|
||||||
for object_id in objects_to_remove:
|
|
||||||
del project.objects[object_id]
|
|
||||||
|
|
||||||
source = GetOrCreateRootGroup(project, json_data['rootObject'], 'Source')
|
source = GetOrCreateRootGroup(project, json_data['rootObject'], 'Source')
|
||||||
AddMarkdownToProject(project, root_dir, source)
|
AddMarkdownToProject(project, root_dir, source)
|
||||||
@ -274,7 +477,7 @@ def GetFolderForPath(project, group_object, path):
|
|||||||
return group_object
|
return group_object
|
||||||
|
|
||||||
|
|
||||||
def ConvertGnXcodeProject(root_dir, input_dir, output_dir, configurations):
|
def ConvertGnXcodeProject(root_dir, proj_name, input_dir, output_dir, configs):
|
||||||
'''Tweak the Xcode project generated by gn to support multiple configurations.
|
'''Tweak the Xcode project generated by gn to support multiple configurations.
|
||||||
|
|
||||||
The Xcode projects generated by "gn gen --ide" only supports a single
|
The Xcode projects generated by "gn gen --ide" only supports a single
|
||||||
@ -284,34 +487,22 @@ def ConvertGnXcodeProject(root_dir, input_dir, output_dir, configurations):
|
|||||||
to select them in Xcode).
|
to select them in Xcode).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
root_dir: directory that is the root of the project
|
||||||
|
proj_name: name of the Xcode project "file" (usually `all.xcodeproj`)
|
||||||
input_dir: directory containing the XCode projects created by "gn gen --ide"
|
input_dir: directory containing the XCode projects created by "gn gen --ide"
|
||||||
output_dir: directory where the tweaked Xcode projects will be saved
|
output_dir: directory where the tweaked Xcode projects will be saved
|
||||||
configurations: list of string corresponding to the configurations that
|
configs: list of string corresponding to the configurations that need to be
|
||||||
need to be supported by the tweaked Xcode projects, must contains at
|
supported by the tweaked Xcode projects, must contains at least one
|
||||||
least one value.
|
value.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Update the project (supports legacy name "products.xcodeproj" or the new
|
UpdateXcodeProject(
|
||||||
# project name "all.xcodeproj").
|
os.path.join(input_dir, proj_name),
|
||||||
for project_name in ('all.xcodeproj', 'products.xcodeproj'):
|
os.path.join(output_dir, proj_name),
|
||||||
if os.path.exists(os.path.join(input_dir, project_name)):
|
configs, root_dir)
|
||||||
UpdateXcodeProject(
|
|
||||||
os.path.join(input_dir, project_name),
|
|
||||||
configurations, root_dir)
|
|
||||||
|
|
||||||
CopyTreeIfChanged(os.path.join(input_dir, project_name),
|
CopyTreeIfChanged(os.path.join(input_dir, proj_name),
|
||||||
os.path.join(output_dir, project_name))
|
os.path.join(output_dir, proj_name))
|
||||||
|
|
||||||
else:
|
|
||||||
shutil.rmtree(os.path.join(output_dir, project_name), ignore_errors=True)
|
|
||||||
|
|
||||||
# Copy all.xcworkspace if it exists (will be removed in a future gn version).
|
|
||||||
workspace_name = 'all.xcworkspace'
|
|
||||||
if os.path.exists(os.path.join(input_dir, workspace_name)):
|
|
||||||
CopyTreeIfChanged(os.path.join(input_dir, workspace_name),
|
|
||||||
os.path.join(output_dir, workspace_name))
|
|
||||||
else:
|
|
||||||
shutil.rmtree(os.path.join(output_dir, workspace_name), ignore_errors=True)
|
|
||||||
|
|
||||||
|
|
||||||
def Main(args):
|
def Main(args):
|
||||||
@ -329,33 +520,30 @@ def Main(args):
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--root', type=os.path.abspath, required=True,
|
'--root', type=os.path.abspath, required=True,
|
||||||
help='root directory of the project')
|
help='root directory of the project')
|
||||||
|
parser.add_argument(
|
||||||
|
'--project-name', default='all.xcodeproj', dest='proj_name',
|
||||||
|
help='name of the Xcode project (default: %(default)s)')
|
||||||
args = parser.parse_args(args)
|
args = parser.parse_args(args)
|
||||||
|
|
||||||
if not os.path.isdir(args.input):
|
if not os.path.isdir(args.input):
|
||||||
sys.stderr.write('Input directory does not exists.\n')
|
sys.stderr.write('Input directory does not exists.\n')
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
# Depending on the version of "gn", there should be either one project file
|
if args.proj_name not in os.listdir(args.input):
|
||||||
# named "all.xcodeproj" or a project file named "products.xcodeproj" and a
|
|
||||||
# workspace named "all.xcworkspace".
|
|
||||||
required_files_sets = [
|
|
||||||
set(("all.xcodeproj",)),
|
|
||||||
set(("products.xcodeproj", "all.xcworkspace")),
|
|
||||||
]
|
|
||||||
|
|
||||||
for required_files in required_files_sets:
|
|
||||||
if required_files.issubset(os.listdir(args.input)):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
sys.stderr.write(
|
sys.stderr.write(
|
||||||
'Input directory does not contain all necessary Xcode projects.\n')
|
'Input directory does not contain the Xcode project.\n')
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if not args.configurations:
|
if not args.configurations:
|
||||||
sys.stderr.write('At least one configuration required, see --add-config.\n')
|
sys.stderr.write('At least one configuration required, see --add-config.\n')
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
ConvertGnXcodeProject(args.root, args.input, args.output, args.configurations)
|
ConvertGnXcodeProject(
|
||||||
|
args.root,
|
||||||
|
args.proj_name,
|
||||||
|
args.input,
|
||||||
|
args.output,
|
||||||
|
args.configurations)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(Main(sys.argv[1:]))
|
sys.exit(Main(sys.argv[1:]))
|
||||||
|
@ -15,325 +15,391 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import configparser
|
||||||
import convert_gn_xcodeproj
|
import convert_gn_xcodeproj
|
||||||
import errno
|
import errno
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import configparser
|
|
||||||
import io
|
|
||||||
|
|
||||||
SUPPORTED_TARGETS = ('iphoneos', 'iphonesimulator')
|
|
||||||
SUPPORTED_CONFIGS = ('Debug', 'Release', 'Profile', 'Official', 'Coverage')
|
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):
|
class ConfigParserWithStringInterpolation(configparser.ConfigParser):
|
||||||
'''A .ini file parser that supports strings and environment variables.'''
|
|
||||||
|
|
||||||
ENV_VAR_PATTERN = re.compile(r'\$([A-Za-z0-9_]+)')
|
'''A .ini file parser that supports strings and environment variables.'''
|
||||||
|
|
||||||
def values(self, section):
|
ENV_VAR_PATTERN = re.compile(r'\$([A-Za-z0-9_]+)')
|
||||||
return [self._UnquoteString(self._ExpandEnvVar(kv[1])) for kv in configparser.ConfigParser.items(self, section)]
|
|
||||||
|
|
||||||
def getstring(self, section, option):
|
def values(self, section):
|
||||||
return self._UnquoteString(self._ExpandEnvVar(self.get(section,
|
return filter(
|
||||||
option)))
|
lambda val: val != '',
|
||||||
|
map(lambda kv: self._UnquoteString(self._ExpandEnvVar(kv[1])),
|
||||||
|
configparser.ConfigParser.items(self, section)))
|
||||||
|
|
||||||
def _UnquoteString(self, string):
|
def getstring(self, section, option, fallback=''):
|
||||||
if not string or string[0] != '"' or string[-1] != '"':
|
try:
|
||||||
return string
|
raw_value = self.get(section, option)
|
||||||
return string[1:-1]
|
except configparser.NoOptionError:
|
||||||
|
return fallback
|
||||||
|
return self._UnquoteString(self._ExpandEnvVar(raw_value))
|
||||||
|
|
||||||
def _ExpandEnvVar(self, value):
|
def _UnquoteString(self, string):
|
||||||
match = self.ENV_VAR_PATTERN.search(value)
|
if not string or string[0] != '"' or string[-1] != '"':
|
||||||
if not match:
|
return string
|
||||||
return value
|
return string[1:-1]
|
||||||
name, (begin, end) = match.group(1), match.span(0)
|
|
||||||
prefix, suffix = value[:begin], self._ExpandEnvVar(value[end:])
|
def _ExpandEnvVar(self, value):
|
||||||
return prefix + os.environ.get(name, '') + suffix
|
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):
|
class GnGenerator(object):
|
||||||
'''Holds configuration for a build and method to generate gn default
|
|
||||||
files.'''
|
|
||||||
|
|
||||||
FAT_BUILD_DEFAULT_ARCH = '64-bit'
|
'''Holds configuration for a build and method to generate gn default files.'''
|
||||||
|
|
||||||
TARGET_CPU_VALUES = {
|
FAT_BUILD_DEFAULT_ARCH = '64-bit'
|
||||||
'iphoneos': {
|
|
||||||
'32-bit': '"arm"',
|
|
||||||
'64-bit': '"arm64"',
|
|
||||||
},
|
|
||||||
'iphonesimulator': {
|
|
||||||
'32-bit': '"x86"',
|
|
||||||
'64-bit': '"x64"',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, settings, config, target):
|
TARGET_CPU_VALUES = {
|
||||||
assert target in SUPPORTED_TARGETS
|
'iphoneos': '"arm64"',
|
||||||
assert config in SUPPORTED_CONFIGS
|
'iphonesimulator': HostCpuArch(),
|
||||||
self._settings = settings
|
'maccatalyst': HostCpuArch(),
|
||||||
self._config = config
|
}
|
||||||
self._target = target
|
|
||||||
|
|
||||||
def _GetGnArgs(self):
|
TARGET_ENVIRONMENT_VALUES = {
|
||||||
"""Build the list of arguments to pass to gn.
|
'iphoneos': '"device"',
|
||||||
|
'iphonesimulator': '"simulator"',
|
||||||
|
'maccatalyst': '"catalyst"'
|
||||||
|
}
|
||||||
|
|
||||||
Returns:
|
def __init__(self, settings, config, target):
|
||||||
A list of tuple containing gn variable names and variable values (it
|
assert target in SUPPORTED_TARGETS
|
||||||
is not a dictionary as the order needs to be preserved).
|
assert config in SUPPORTED_CONFIGS
|
||||||
"""
|
self._settings = settings
|
||||||
args = []
|
self._config = config
|
||||||
|
self._target = target
|
||||||
|
|
||||||
args.append(('is_debug', self._config in ('Debug', 'Coverage')))
|
def _GetGnArgs(self, extra_args=None):
|
||||||
|
"""Build the list of arguments to pass to gn.
|
||||||
|
|
||||||
if os.environ.get('FORCE_MAC_TOOLCHAIN', '0') == '1':
|
Returns:
|
||||||
args.append(('use_system_xcode', False))
|
A list of tuple containing gn variable names and variable values (it
|
||||||
|
is not a dictionary as the order needs to be preserved).
|
||||||
|
"""
|
||||||
|
args = []
|
||||||
|
|
||||||
cpu_values = self.TARGET_CPU_VALUES[self._target]
|
is_debug = self._config == 'Debug'
|
||||||
build_arch = self._settings.getstring('build', 'arch')
|
official = self._config == 'Official'
|
||||||
if build_arch == 'fat':
|
is_optim = self._config in ('Profile', 'Official')
|
||||||
target_cpu = cpu_values[self.FAT_BUILD_DEFAULT_ARCH]
|
|
||||||
args.append(('target_cpu', target_cpu))
|
|
||||||
args.append(
|
|
||||||
('additional_target_cpus',
|
|
||||||
[cpu for cpu in cpu_values.values() if cpu != target_cpu]))
|
|
||||||
else:
|
|
||||||
args.append(('target_cpu', cpu_values[build_arch]))
|
|
||||||
|
|
||||||
# Add user overrides after the other configurations so that they can
|
args.append(('target_os', '"ios"'))
|
||||||
# refer to them and override them.
|
args.append(('is_debug', is_debug))
|
||||||
args.extend(self._settings.items('gn_args'))
|
|
||||||
return args
|
|
||||||
|
|
||||||
def Generate(self, gn_path, root_path, out_path):
|
if os.environ.get('FORCE_MAC_TOOLCHAIN', '0') == '1':
|
||||||
buf = io.StringIO()
|
args.append(('use_system_xcode', False))
|
||||||
self.WriteArgsGn(buf)
|
|
||||||
WriteToFileIfChanged(os.path.join(out_path, 'args.gn'),
|
|
||||||
buf.getvalue(),
|
|
||||||
overwrite=True)
|
|
||||||
|
|
||||||
subprocess.check_call(
|
args.append(('target_cpu', self.TARGET_CPU_VALUES[self._target]))
|
||||||
self.GetGnCommand(gn_path, root_path, out_path, True))
|
args.append((
|
||||||
|
'target_environment',
|
||||||
|
self.TARGET_ENVIRONMENT_VALUES[self._target]))
|
||||||
|
|
||||||
def CreateGnRules(self, gn_path, root_path, out_path):
|
# If extra arguments are passed to the function, pass them before the
|
||||||
buf = io.StringIO()
|
# user overrides (if any).
|
||||||
self.WriteArgsGn(buf)
|
if extra_args is not None:
|
||||||
WriteToFileIfChanged(os.path.join(out_path, 'args.gn'),
|
args.extend(extra_args)
|
||||||
buf.getvalue(),
|
|
||||||
overwrite=True)
|
|
||||||
|
|
||||||
buf = io.StringIO()
|
# Add user overrides after the other configurations so that they can
|
||||||
gn_command = self.GetGnCommand(gn_path, root_path, out_path, False)
|
# refer to them and override them.
|
||||||
self.WriteBuildNinja(buf, gn_command)
|
args.extend(self._settings.items('gn_args'))
|
||||||
WriteToFileIfChanged(os.path.join(out_path, 'build.ninja'),
|
return args
|
||||||
buf.getvalue(),
|
|
||||||
overwrite=False)
|
|
||||||
|
|
||||||
buf = io.StringIO()
|
|
||||||
self.WriteBuildNinjaDeps(buf)
|
|
||||||
WriteToFileIfChanged(os.path.join(out_path, 'build.ninja.d'),
|
|
||||||
buf.getvalue(),
|
|
||||||
overwrite=False)
|
|
||||||
|
|
||||||
def WriteArgsGn(self, stream):
|
def Generate(self, gn_path, proj_name, root_path, build_dir):
|
||||||
stream.write('# This file was generated by setup-gn.py. Do not edit\n')
|
self.WriteArgsGn(build_dir, xcode_project_name=proj_name)
|
||||||
stream.write('# but instead use ~/.setup-gn or $repo/.setup-gn files\n')
|
subprocess.check_call(self.GetGnCommand(
|
||||||
stream.write('# to configure settings.\n')
|
gn_path, root_path, build_dir, xcode_project_name=proj_name))
|
||||||
stream.write('\n')
|
|
||||||
|
|
||||||
|
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$'):
|
if self._settings.has_section('$imports$'):
|
||||||
for import_rule in self._settings.values('$imports$'):
|
for import_rule in self._settings.values('$imports$'):
|
||||||
stream.write('import("%s")\n' % import_rule)
|
stream.write('import("%s")\n' % import_rule)
|
||||||
stream.write('\n')
|
stream.write('\n')
|
||||||
|
|
||||||
gn_args = self._GetGnArgs()
|
gn_args = self._GetGnArgs()
|
||||||
for name, value in gn_args:
|
|
||||||
if isinstance(value, bool):
|
for name, value in gn_args:
|
||||||
stream.write('%s = %s\n' % (name, str(value).lower()))
|
if isinstance(value, bool):
|
||||||
elif isinstance(value, list):
|
stream.write('%s = %s\n' % (name, str(value).lower()))
|
||||||
stream.write('%s = [%s' %
|
elif isinstance(value, list):
|
||||||
(name, '\n' if len(value) > 1 else ''))
|
stream.write('%s = [%s' % (name, '\n' if len(value) > 1 else ''))
|
||||||
if len(value) == 1:
|
if len(value) == 1:
|
||||||
prefix = ' '
|
prefix = ' '
|
||||||
suffix = ' '
|
suffix = ' '
|
||||||
else:
|
else:
|
||||||
prefix = ' '
|
prefix = ' '
|
||||||
suffix = ',\n'
|
suffix = ',\n'
|
||||||
for item in value:
|
for item in value:
|
||||||
if isinstance(item, bool):
|
if isinstance(item, bool):
|
||||||
stream.write('%s%s%s' %
|
stream.write('%s%s%s' % (prefix, str(item).lower(), suffix))
|
||||||
(prefix, str(item).lower(), suffix))
|
|
||||||
else:
|
|
||||||
stream.write('%s%s%s' % (prefix, item, suffix))
|
|
||||||
stream.write(']\n')
|
|
||||||
else:
|
else:
|
||||||
stream.write('%s = %s\n' % (name, value))
|
stream.write('%s%s%s' % (prefix, item, suffix))
|
||||||
|
stream.write(']\n')
|
||||||
def WriteBuildNinja(self, stream, gn_command):
|
|
||||||
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, stream):
|
|
||||||
stream.write('build.ninja: nonexistant_file.gn\n')
|
|
||||||
|
|
||||||
def GetGnCommand(self, gn_path, src_path, out_path, generate_xcode_project):
|
|
||||||
gn_command = [gn_path, '--root=%s' % os.path.realpath(src_path), '-q']
|
|
||||||
if generate_xcode_project:
|
|
||||||
gn_command.append('--ide=xcode')
|
|
||||||
gn_command.append('--ninja-executable=autoninja')
|
|
||||||
if self._settings.has_section('filters'):
|
|
||||||
target_filters = self._settings.values('filters')
|
|
||||||
if target_filters:
|
|
||||||
gn_command.append('--filters=%s' % ';'.join(target_filters))
|
|
||||||
else:
|
else:
|
||||||
gn_command.append('--check')
|
# ConfigParser removes quote around empty string which confuse
|
||||||
gn_command.append('gen')
|
# `gn gen` so restore them.
|
||||||
gn_command.append('//%s' % os.path.relpath(os.path.abspath(out_path),
|
if not value:
|
||||||
os.path.abspath(src_path)))
|
value = '""'
|
||||||
return gn_command
|
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 WriteToFileIfChanged(filename, content, overwrite):
|
def WriteBuildNinjaDeps(self, build_dir):
|
||||||
'''Write |content| to |filename| if different. If |overwrite| is False
|
with open(os.path.join(build_dir, 'build.ninja.d'), 'w') as stream:
|
||||||
and the file already exists it is left untouched.'''
|
stream.write('build.ninja: nonexistant_file.gn\n')
|
||||||
if os.path.exists(filename):
|
|
||||||
if not overwrite:
|
def GetGnCommand(self, gn_path, src_path, out_path, xcode_project_name=None):
|
||||||
return
|
gn_command = [ gn_path, '--root=%s' % os.path.realpath(src_path), '-q' ]
|
||||||
with open(filename) as file:
|
if xcode_project_name is not None:
|
||||||
if file.read() == content:
|
gn_command.append('--ide=xcode')
|
||||||
return
|
gn_command.append('--ninja-executable=autoninja')
|
||||||
if not os.path.isdir(os.path.dirname(filename)):
|
gn_command.append('--xcode-build-system=new')
|
||||||
os.makedirs(os.path.dirname(filename))
|
gn_command.append('--xcode-project=%s' % xcode_project_name)
|
||||||
with open(filename, 'w') as file:
|
if self._settings.has_section('filters'):
|
||||||
file.write(content)
|
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):
|
def NinjaNeedEscape(arg):
|
||||||
'''Returns True if |arg| needs to be escaped when written to .ninja file.'''
|
'''Returns True if |arg| needs to be escaped when written to .ninja file.'''
|
||||||
return ':' in arg or '*' in arg or ';' in arg
|
return ':' in arg or '*' in arg or ';' in arg
|
||||||
|
|
||||||
|
|
||||||
def NinjaEscapeCommand(command):
|
def NinjaEscapeCommand(command):
|
||||||
'''Escapes |command| in order to write it to .ninja file.'''
|
'''Escapes |command| in order to write it to .ninja file.'''
|
||||||
result = []
|
result = []
|
||||||
for arg in command:
|
for arg in command:
|
||||||
if NinjaNeedEscape(arg):
|
if NinjaNeedEscape(arg):
|
||||||
arg = arg.replace(':', '$:')
|
arg = arg.replace(':', '$:')
|
||||||
arg = arg.replace(';', '\\;')
|
arg = arg.replace(';', '\\;')
|
||||||
arg = arg.replace('*', '\\*')
|
arg = arg.replace('*', '\\*')
|
||||||
else:
|
else:
|
||||||
result.append(arg)
|
result.append(arg)
|
||||||
return ' '.join(result)
|
return ' '.join(result)
|
||||||
|
|
||||||
|
|
||||||
def FindGn():
|
def FindGn():
|
||||||
'''Returns absolute path to gn binary looking at the PATH env variable.'''
|
'''Returns absolute path to gn binary looking at the PATH env variable.'''
|
||||||
for path in os.environ['PATH'].split(os.path.pathsep):
|
for path in os.environ['PATH'].split(os.path.pathsep):
|
||||||
gn_path = os.path.join(path, 'gn')
|
gn_path = os.path.join(path, 'gn')
|
||||||
if os.path.isfile(gn_path) and os.access(gn_path, os.X_OK):
|
if os.path.isfile(gn_path) and os.access(gn_path, os.X_OK):
|
||||||
return gn_path
|
return gn_path
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def GenerateXcodeProject(gn_path, root_dir, out_dir, settings):
|
def GenerateXcodeProject(gn_path, root_dir, proj_name, out_dir, settings):
|
||||||
'''Convert GN generated Xcode project into multi-configuration Xcode
|
'''Generate Xcode project with Xcode and convert to multi-configurations.'''
|
||||||
project.'''
|
prefix = os.path.abspath(os.path.join(out_dir, '_temp'))
|
||||||
|
temp_path = tempfile.mkdtemp(prefix=prefix)
|
||||||
|
try:
|
||||||
|
generator = GnGenerator(settings, 'Debug', 'iphonesimulator')
|
||||||
|
generator.Generate(gn_path, proj_name, root_dir, temp_path)
|
||||||
|
convert_gn_xcodeproj.ConvertGnXcodeProject(
|
||||||
|
root_dir,
|
||||||
|
'%s.xcodeproj' % proj_name,
|
||||||
|
os.path.join(temp_path),
|
||||||
|
os.path.join(out_dir, 'build'),
|
||||||
|
SUPPORTED_CONFIGS)
|
||||||
|
finally:
|
||||||
|
if os.path.exists(temp_path):
|
||||||
|
shutil.rmtree(temp_path)
|
||||||
|
|
||||||
temp_path = tempfile.mkdtemp(
|
def CreateLLDBInitFile(root_dir, out_dir, settings):
|
||||||
prefix=os.path.abspath(os.path.join(out_dir, '_temp')))
|
'''
|
||||||
try:
|
Generate an .lldbinit file for the project that load the script that fixes
|
||||||
generator = GnGenerator(settings, 'Debug', 'iphonesimulator')
|
the mapping of source files (see docs/ios/build_instructions.md#debugging).
|
||||||
generator.Generate(gn_path, root_dir, temp_path)
|
'''
|
||||||
convert_gn_xcodeproj.ConvertGnXcodeProject(
|
with open(os.path.join(out_dir, 'build', '.lldbinit'), 'w') as lldbinit:
|
||||||
root_dir, os.path.join(temp_path), os.path.join(out_dir, 'build'),
|
lldb_script_dir = os.path.join(os.path.abspath(root_dir), 'tools', 'lldb')
|
||||||
SUPPORTED_CONFIGS)
|
lldbinit.write('script sys.path[:0] = [\'%s\']\n' % lldb_script_dir)
|
||||||
finally:
|
lldbinit.write('script import lldbinit\n')
|
||||||
if os.path.exists(temp_path):
|
|
||||||
shutil.rmtree(temp_path)
|
workspace_name = settings.getstring(
|
||||||
|
'gn_args',
|
||||||
|
'ios_internal_citc_workspace_name')
|
||||||
|
|
||||||
|
if workspace_name != '':
|
||||||
|
username = os.environ['USER']
|
||||||
|
for shortname in ('googlemac', 'third_party', 'blaze-out'):
|
||||||
|
lldbinit.write('settings append target.source-map %s %s\n' % (
|
||||||
|
shortname,
|
||||||
|
'/google/src/cloud/%s/%s/google3/%s' % (
|
||||||
|
username, workspace_name, shortname)))
|
||||||
|
|
||||||
|
# Append the content of //ios/build/tools/lldbinit.defaults if it exists.
|
||||||
|
tools_dir = os.path.join(root_dir, 'ios', 'build', 'tools')
|
||||||
|
defaults_lldbinit_path = os.path.join(tools_dir, 'lldbinit.defaults')
|
||||||
|
if os.path.isfile(defaults_lldbinit_path):
|
||||||
|
with open(defaults_lldbinit_path) as defaults_lldbinit:
|
||||||
|
for line in defaults_lldbinit:
|
||||||
|
lldbinit.write(line)
|
||||||
|
|
||||||
|
# Append the content of ~/.lldbinit if it exists. Line that look like they
|
||||||
|
# are trying to configure source mapping are skipped as they probably date
|
||||||
|
# back from when setup-gn.py was not generating an .lldbinit file.
|
||||||
|
global_lldbinit_path = os.path.join(os.environ['HOME'], '.lldbinit')
|
||||||
|
if os.path.isfile(global_lldbinit_path):
|
||||||
|
with open(global_lldbinit_path) as global_lldbinit:
|
||||||
|
for line in global_lldbinit:
|
||||||
|
if any(pattern.match(line) for pattern in LLDBINIT_SKIP_PATTERNS):
|
||||||
|
continue
|
||||||
|
lldbinit.write(line)
|
||||||
|
|
||||||
|
|
||||||
def GenerateGnBuildRules(gn_path, root_dir, out_dir, settings):
|
def GenerateGnBuildRules(gn_path, root_dir, out_dir, settings):
|
||||||
'''Generates all template configurations for gn.'''
|
'''Generates all template configurations for gn.'''
|
||||||
for config in SUPPORTED_CONFIGS:
|
for config in SUPPORTED_CONFIGS:
|
||||||
for target in SUPPORTED_TARGETS:
|
for target in SUPPORTED_TARGETS:
|
||||||
build_dir = os.path.join(out_dir, '%s-%s' % (config, target))
|
build_dir = os.path.join(out_dir, '%s-%s' % (config, target))
|
||||||
generator = GnGenerator(settings, config, target)
|
if not os.path.isdir(build_dir):
|
||||||
generator.CreateGnRules(gn_path, root_dir, build_dir)
|
os.makedirs(build_dir)
|
||||||
|
|
||||||
|
generator = GnGenerator(settings, config, target)
|
||||||
|
generator.CreateGnRules(gn_path, root_dir, build_dir)
|
||||||
|
|
||||||
|
|
||||||
def Main(args):
|
def Main(args):
|
||||||
default_root = os.path.normpath(
|
default_root = os.path.normpath(os.path.join(
|
||||||
os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
|
os.path.dirname(__file__), os.pardir, os.pardir))
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='Generate build directories for use with gn.')
|
description='Generate build directories for use with gn.')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'root',
|
'root', default=default_root, nargs='?',
|
||||||
default=default_root,
|
help='root directory where to generate multiple out configurations')
|
||||||
nargs='?',
|
parser.add_argument(
|
||||||
help='root directory where to generate multiple out configurations')
|
'--import', action='append', dest='import_rules', default=[],
|
||||||
parser.add_argument('--import',
|
help='path to file defining default gn variables')
|
||||||
action='append',
|
parser.add_argument(
|
||||||
dest='import_rules',
|
'--gn-path', default=None,
|
||||||
default=[],
|
help='path to gn binary (default: look up in $PATH)')
|
||||||
help='path to file defining default gn variables')
|
parser.add_argument(
|
||||||
parser.add_argument('--gn-path',
|
'--build-dir', default='out',
|
||||||
default=None,
|
help='path where the build should be created (default: %(default)s)')
|
||||||
help='path to gn binary (default: look up in $PATH)')
|
parser.add_argument(
|
||||||
parser.add_argument(
|
'--config-path', default=os.path.expanduser('~/.setup-gn'),
|
||||||
'--build-dir',
|
help='path to the user config file (default: %(default)s)')
|
||||||
default='out',
|
parser.add_argument(
|
||||||
help='path where the build should be created (default: %(default)s)')
|
'--system-config-path', default=os.path.splitext(__file__)[0] + '.config',
|
||||||
args = parser.parse_args(args)
|
help='path to the default config file (default: %(default)s)')
|
||||||
|
parser.add_argument(
|
||||||
|
'--project-name', default='all', dest='proj_name',
|
||||||
|
help='name of the generated Xcode project (default: %(default)s)')
|
||||||
|
parser.add_argument(
|
||||||
|
'--no-xcode-project', action='store_true', default=False,
|
||||||
|
help='do not generate the build directory with XCode project')
|
||||||
|
args = parser.parse_args(args)
|
||||||
|
|
||||||
# Load configuration (first global and then any user overrides).
|
# Load configuration (first global and then any user overrides).
|
||||||
settings = ConfigParserWithStringInterpolation()
|
settings = ConfigParserWithStringInterpolation()
|
||||||
settings.read([
|
settings.read([
|
||||||
os.path.splitext(__file__)[0] + '.config',
|
args.system_config_path,
|
||||||
os.path.expanduser('~/.setup-gn'),
|
args.config_path,
|
||||||
])
|
])
|
||||||
|
|
||||||
# Add private sections corresponding to --import argument.
|
# Add private sections corresponding to --import argument.
|
||||||
if args.import_rules:
|
if args.import_rules:
|
||||||
settings.add_section('$imports$')
|
settings.add_section('$imports$')
|
||||||
for i, import_rule in enumerate(args.import_rules):
|
for i, import_rule in enumerate(args.import_rules):
|
||||||
if not import_rule.startswith('//'):
|
if not import_rule.startswith('//'):
|
||||||
import_rule = '//%s' % os.path.relpath(
|
import_rule = '//%s' % os.path.relpath(
|
||||||
os.path.abspath(import_rule), os.path.abspath(args.root))
|
os.path.abspath(import_rule), os.path.abspath(args.root))
|
||||||
settings.set('$imports$', '$rule%d$' % i, import_rule)
|
settings.set('$imports$', '$rule%d$' % i, import_rule)
|
||||||
|
|
||||||
# Validate settings.
|
# Validate settings.
|
||||||
if settings.getstring('build', 'arch') not in ('64-bit', '32-bit', 'fat'):
|
if settings.getstring('build', 'arch') not in ('64-bit', '32-bit', 'fat'):
|
||||||
sys.stderr.write('ERROR: invalid value for build.arch: %s\n' %
|
sys.stderr.write('ERROR: invalid value for build.arch: %s\n' %
|
||||||
settings.getstring('build', 'arch'))
|
settings.getstring('build', 'arch'))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Find path to gn binary either from command-line or in PATH.
|
# Find path to gn binary either from command-line or in PATH.
|
||||||
if args.gn_path:
|
if args.gn_path:
|
||||||
gn_path = args.gn_path
|
gn_path = args.gn_path
|
||||||
else:
|
else:
|
||||||
gn_path = FindGn()
|
gn_path = FindGn()
|
||||||
if gn_path is None:
|
if gn_path is None:
|
||||||
sys.stderr.write('ERROR: cannot find gn in PATH\n')
|
sys.stderr.write('ERROR: cannot find gn in PATH\n')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
out_dir = os.path.join(args.root, args.build_dir)
|
out_dir = os.path.join(args.root, args.build_dir)
|
||||||
if not os.path.isdir(out_dir):
|
if not os.path.isdir(out_dir):
|
||||||
os.makedirs(out_dir)
|
os.makedirs(out_dir)
|
||||||
|
|
||||||
GenerateXcodeProject(gn_path, args.root, out_dir, settings)
|
if not args.no_xcode_project:
|
||||||
GenerateGnBuildRules(gn_path, args.root, out_dir, settings)
|
GenerateXcodeProject(gn_path, args.root, args.proj_name, out_dir, settings)
|
||||||
|
CreateLLDBInitFile(args.root, out_dir, settings)
|
||||||
|
GenerateGnBuildRules(gn_path, args.root, out_dir, settings)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(Main(sys.argv[1:]))
|
sys.exit(Main(sys.argv[1:]))
|
||||||
|
10
build/ios/xcodescheme-testable.template
Normal file
10
build/ios/xcodescheme-testable.template
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<TestableReference
|
||||||
|
skipped = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "@{BLUEPRINT_IDENTIFIER}"
|
||||||
|
BuildableName = "@{BUILDABLE_NAME}"
|
||||||
|
BlueprintName = "@{BLUEPRINT_NAME}"
|
||||||
|
ReferencedContainer = "container:@{PROJECT_NAME}">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
80
build/ios/xcodescheme.template
Normal file
80
build/ios/xcodescheme.template
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1220"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "@{BLUEPRINT_IDENTIFIER}"
|
||||||
|
BuildableName = "@{BUILDABLE_NAME}"
|
||||||
|
BlueprintName = "@{BLUEPRINT_NAME}"
|
||||||
|
ReferencedContainer = "container:@{PROJECT_NAME}">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
customLLDBInitFile = "@{LLDBINIT_PATH}"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>@{TESTABLES}
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
customLLDBInitFile = "@{LLDBINIT_PATH}"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "@{BLUEPRINT_IDENTIFIER}"
|
||||||
|
BuildableName = "@{BUILDABLE_NAME}"
|
||||||
|
BlueprintName = "@{BLUEPRINT_NAME}"
|
||||||
|
ReferencedContainer = "container:@{PROJECT_NAME}">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Profile"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "@{BLUEPRINT_IDENTIFIER}"
|
||||||
|
BuildableName = "@{BUILDABLE_NAME}"
|
||||||
|
BlueprintName = "@{BLUEPRINT_NAME}"
|
||||||
|
ReferencedContainer = "container:@{PROJECT_NAME}">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Official"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
@ -104,6 +104,17 @@ GetProcessSnapshotMinidumpFromSinglePending() {
|
|||||||
return process_snapshot;
|
return process_snapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UIWindow* GetAnyWindow() {
|
||||||
|
#if defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0
|
||||||
|
if (@available(iOS 15.0, *)) {
|
||||||
|
UIWindowScene* scene = reinterpret_cast<UIWindowScene*>(
|
||||||
|
[UIApplication sharedApplication].connectedScenes.anyObject);
|
||||||
|
return scene.keyWindow;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return [UIApplication sharedApplication].windows[0];
|
||||||
|
}
|
||||||
|
|
||||||
[[clang::optnone]] void recurse(int counter) {
|
[[clang::optnone]] void recurse(int counter) {
|
||||||
// Fill up the stack faster.
|
// Fill up the stack faster.
|
||||||
int arr[1024];
|
int arr[1024];
|
||||||
@ -355,7 +366,7 @@ GetProcessSnapshotMinidumpFromSinglePending() {
|
|||||||
// crash, so dispatch this out of the sinkhole.
|
// crash, so dispatch this out of the sinkhole.
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
UIView* unattachedView = [[UIView alloc] init];
|
UIView* unattachedView = [[UIView alloc] init];
|
||||||
UIWindow* window = [UIApplication sharedApplication].windows[0];
|
UIWindow* window = GetAnyWindow();
|
||||||
[NSLayoutConstraint activateConstraints:@[
|
[NSLayoutConstraint activateConstraints:@[
|
||||||
[window.rootViewController.view.bottomAnchor
|
[window.rootViewController.view.bottomAnchor
|
||||||
constraintEqualToAnchor:unattachedView.bottomAnchor],
|
constraintEqualToAnchor:unattachedView.bottomAnchor],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user