diff options
Diffstat (limited to 'third_party/libwebrtc/build/config/ios')
24 files changed, 4531 insertions, 0 deletions
diff --git a/third_party/libwebrtc/build/config/ios/BUILD.gn b/third_party/libwebrtc/build/config/ios/BUILD.gn new file mode 100644 index 0000000000..525c439e7e --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/BUILD.gn @@ -0,0 +1,264 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/ios/ios_sdk.gni") +import("//build/toolchain/goma.gni") +import("//build/toolchain/toolchain.gni") +import("//build_overrides/build.gni") + +declare_args() { + # Enabling this option makes clang compile to an intermediate + # representation ("bitcode"), and not to native code. This is preferred + # when including WebRTC in the apps that will be sent to Apple's App Store + # and mandatory for the apps that run on watchOS or tvOS. + # The option only works when building with Xcode (use_xcode_clang = true). + # Mimicking how Xcode handles it, the production builds (is_debug = false) + # get real bitcode sections added, while the debug builds (is_debug = true) + # only get bitcode-section "markers" added in them. + enable_ios_bitcode = false +} + +# This is included by reference in the //build/config/compiler config that +# is applied to all targets. It is here to separate out the logic. +config("compiler") { + # These flags are shared between the C compiler and linker. + common_flags = [] + + # CPU architecture. + if (target_cpu == "x64") { + triplet_cpu = "x86_64" + } else if (target_cpu == "x86") { + triplet_cpu = "i386" + } else if (target_cpu == "arm" || target_cpu == "armv7") { + triplet_cpu = "armv7" + } else if (target_cpu == "arm64") { + triplet_cpu = "arm64" + } else { + assert(false, "unsupported cpu: $target_cpu") + } + + # Environment. + if (target_environment == "simulator") { + triplet_environment = "-simulator" + } else if (target_environment == "device") { + triplet_environment = "" + } else if (target_environment == "catalyst") { + triplet_environment = "-macabi" + } else { + assert(false, "unsupported environment: $target_environment") + } + + # OS. + triplet_os = "apple-ios" + + # Set target. + common_flags = [ + "-target", + "$triplet_cpu-$triplet_os$ios_deployment_target$triplet_environment", + ] + + # This is here so that all files get recompiled after an Xcode update. + # (defines are passed via the command line, and build system rebuild things + # when their commandline changes). Nothing should ever read this define. + defines = [ "CR_XCODE_VERSION=$xcode_version" ] + + asmflags = common_flags + cflags = common_flags + swiftflags = common_flags + + swiftflags += [ + "-swift-version", + "5", + ] + + # Without this, the constructors and destructors of a C++ object inside + # an Objective C struct won't be called, which is very bad. + cflags_objcc = [ "-fobjc-call-cxx-cdtors" ] + + ldflags = common_flags +} + +# This is included by reference in the //build/config/compiler:runtime_library +# config that is applied to all targets. It is here to separate out the logic +# that is iOS-only. Please see that target for advice on what should go in +# :runtime_library vs. :compiler. +config("runtime_library") { + # The variable ios_sdk_path is relative to root_build_dir when using Goma RBE + # and system Xcode (since Goma RBE only supports paths relative to source). + # Rebase the value in that case since gn does not convert paths in compiler + # flags (since it is not aware they are paths). + _sdk_root = ios_sdk_path + if (use_system_xcode && use_goma) { + _sdk_root = rebase_path(ios_sdk_path, root_build_dir) + } + + common_flags = [ + "-isysroot", + _sdk_root, + ] + swiftflags = [ + "-sdk", + _sdk_root, + ] + + if (target_environment == "catalyst") { + common_flags += [ + "-isystem", + "$_sdk_root/System/iOSSupport/usr/include", + "-iframework", + "$_sdk_root/System/iOSSupport/System/Library/Frameworks", + ] + } + + if (use_xcode_clang && enable_ios_bitcode) { + if (is_debug) { + common_flags += [ "-fembed-bitcode-marker" ] + } else { + common_flags += [ "-fembed-bitcode" ] + } + } + + asmflags = common_flags + cflags = common_flags + ldflags = common_flags +} + +config("ios_executable_flags") { + ldflags = [] + + # On "catalyst", the bundle structure is different (uses the same structure + # as a regular macOS app), so an additional -rpath is required. + if (target_environment == "catalyst") { + ldflags += [ "-Wl,-rpath,@loader_path/../Frameworks" ] + } + + ldflags += [ "-Wl,-rpath,@executable_path/Frameworks" ] +} + +config("ios_extension_executable_flags") { + configs = default_executable_configs + + ldflags = [ + "-e", + "_NSExtensionMain", + "-fapplication-extension", + ] + + # On "catalyst", the bundle structure is different (uses the same structure + # as a regular macOS app), so an additional -rpath is required. + if (target_environment == "catalyst") { + ldflags += [ "-Wl,-rpath,@loader_path/../../../../Frameworks" ] + } + + ldflags += [ "-Wl,-rpath,@executable_path/../../Frameworks" ] +} + +config("ios_dynamic_flags") { + ldflags = [ + # Always load Objective-C categories and class. + "-Wl,-ObjC", + ] + + # The path to the Swift compatibility libraries (required to run code built + # with version N of the SDK on older version of the OS) is relative to the + # toolchains directory and changes with the environment. + _swift_compatibility_libs_dir_prefix = "$ios_toolchains_path/usr/lib/swift" + if (target_environment == "simulator") { + _swift_compatibility_libs_dir = + "$_swift_compatibility_libs_dir_prefix/iphonesimulator" + } else if (target_environment == "device") { + _swift_compatibility_libs_dir = + "$_swift_compatibility_libs_dir_prefix/iphoneos" + } else if (target_environment == "catalyst") { + _swift_compatibility_libs_dir = + "$_swift_compatibility_libs_dir_prefix/maccatalyst" + } + + lib_dirs = [ + "$ios_sdk_path/usr/lib/swift", + _swift_compatibility_libs_dir, + ] +} + +config("ios_shared_library_flags") { + ldflags = [ + "-Wl,-rpath,@executable_path/Frameworks", + "-Wl,-rpath,@loader_path/Frameworks", + ] +} + +config("disable_implicit_retain_self_warning") { + cflags_objc = [ "-Wno-implicit-retain-self" ] + cflags_objcc = cflags_objc +} + +config("xctest_config") { + # Use -iframework for XCTest.framework so that the compiler will treat it as a + # system framework and ignore warnings in XCTest framework headers. + common_flags = [ + "-iframework", + rebase_path("$ios_sdk_platform_path/Developer/Library/Frameworks", + root_build_dir), + ] + cflags = common_flags + ldflags = common_flags + + frameworks = [ + "Foundation.framework", + "XCTest.framework", + ] +} + +group("xctest") { + public_configs = [ ":xctest_config" ] +} + +_xctrunner_path = + "$ios_sdk_platform_path/Developer/Library/Xcode/Agents/XCTRunner.app" + +# When building with Goma RBE, $ios_sdk_platform_path corresponds to a symlink +# below $root_build_dir that points to the real SDK to use. Because the files +# are below $root_build_dir, it is not possible to list them as a target input +# without gn complaining (as it can't find a target creating those files). +# +# The symlinks are created by //build/config/apple/sdk_info.py script invoked +# via exec_script() from //build/config/{ios/ios_sdk.gni,mac/mac_sdk.gni}. +# As the invocation is done by exec_script, there is no target that can list +# those files as output. +# +# To workaround this, add a target that pretends to create those files +# (but does nothing). See https://crbug.com/1061487 for why this is needed. +if (use_system_xcode && use_goma) { + action("copy_xctrunner_app") { + testonly = true + script = "//build/noop.py" + outputs = [ + "$_xctrunner_path/Info.plist", + "$_xctrunner_path/PkgInfo", + "$_xctrunner_path/XCTRunner", + ] + } +} + +# When creating the test runner for an XCUITest, the arm64e slice of the binary +# must be removed (at least until the app ships with arm64e slice which is not +# yet supported by Apple). +action("xctest_runner_without_arm64e") { + testonly = true + script = "//build/config/ios/strip_arm64e.py" + sources = [ "$_xctrunner_path/XCTRunner" ] + outputs = [ "$target_out_dir/XCTRunner" ] + args = [ + "--output", + rebase_path(outputs[0], root_build_dir), + "--input", + rebase_path(sources[0], root_build_dir), + "--xcode-version", + xcode_version, + ] + + if (use_system_xcode && use_goma) { + deps = [ ":copy_xctrunner_app" ] + } +} diff --git a/third_party/libwebrtc/build/config/ios/BuildInfo.plist b/third_party/libwebrtc/build/config/ios/BuildInfo.plist new file mode 100644 index 0000000000..3595e5aefb --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/BuildInfo.plist @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>BuildMachineOSBuild</key> + <string>${BUILD_MACHINE_OS_BUILD}</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>${IOS_SUPPORTED_PLATFORM}</string> + </array> + <key>DTCompiler</key> + <string>${GCC_VERSION}</string> + <key>DTPlatformName</key> + <string>${IOS_PLATFORM_NAME}</string> + <key>DTPlatformVersion</key> + <string>${IOS_PLATFORM_VERSION}</string> + <key>DTPlatformBuild</key> + <string>${IOS_PLATFORM_BUILD}</string> + <key>DTSDKBuild</key> + <string>${IOS_SDK_BUILD}</string> + <key>DTSDKName</key> + <string>${IOS_SDK_NAME}</string> + <key>MinimumOSVersion</key> + <string>${IOS_DEPLOYMENT_TARGET}</string> + <key>DTXcode</key> + <string>${XCODE_VERSION}</string> + <key>DTXcodeBuild</key> + <string>${XCODE_BUILD}</string> + <key>UIDeviceFamily</key> + <array> + <integer>1</integer> + <integer>2</integer> + </array> +</dict> +</plist> diff --git a/third_party/libwebrtc/build/config/ios/Host-Info.plist b/third_party/libwebrtc/build/config/ios/Host-Info.plist new file mode 100644 index 0000000000..9f6f5deef9 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/Host-Info.plist @@ -0,0 +1,126 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleDisplayName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>${IOS_BUNDLE_ID_PREFIX}.test.${EXECUTABLE_NAME:rfc1034identifier}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>LSRequiresIPhoneOS</key> + <true/> + <key>NSAppTransportSecurity</key> + <dict> + <key>NSAllowsArbitraryLoads</key> + <true/> + </dict> + <key>UIRequiredDeviceCapabilities</key> + <array> + <string>armv7</string> + </array> + <key>UILaunchImages</key> + <array> + <dict> + <key>UILaunchImageMinimumOSVersion</key> + <string>7.0</string> + <key>UILaunchImageName</key> + <string>Default</string> + <key>UILaunchImageOrientation</key> + <string>Portrait</string> + <key>UILaunchImageSize</key> + <string>{320, 480}</string> + </dict> + <dict> + <key>UILaunchImageMinimumOSVersion</key> + <string>7.0</string> + <key>UILaunchImageName</key> + <string>Default</string> + <key>UILaunchImageOrientation</key> + <string>Portrait</string> + <key>UILaunchImageSize</key> + <string>{320, 568}</string> + </dict> + <dict> + <key>UILaunchImageMinimumOSVersion</key> + <string>8.0</string> + <key>UILaunchImageName</key> + <string>Default</string> + <key>UILaunchImageOrientation</key> + <string>Portrait</string> + <key>UILaunchImageSize</key> + <string>{375, 667}</string> + </dict> + <dict> + <key>UILaunchImageMinimumOSVersion</key> + <string>8.0</string> + <key>UILaunchImageName</key> + <string>Default</string> + <key>UILaunchImageOrientation</key> + <string>Portrait</string> + <key>UILaunchImageSize</key> + <string>{414, 736}</string> + </dict> + <dict> + <key>UILaunchImageMinimumOSVersion</key> + <string>8.0</string> + <key>UILaunchImageName</key> + <string>Default</string> + <key>UILaunchImageOrientation</key> + <string>Landscape</string> + <key>UILaunchImageSize</key> + <string>{414, 736}</string> + </dict> + </array> + <key>UILaunchImages~ipad</key> + <array> + <dict> + <key>UILaunchImageMinimumOSVersion</key> + <string>7.0</string> + <key>UILaunchImageName</key> + <string>Default</string> + <key>UILaunchImageOrientation</key> + <string>Portrait</string> + <key>UILaunchImageSize</key> + <string>{768, 1024}</string> + </dict> + <dict> + <key>UILaunchImageMinimumOSVersion</key> + <string>7.0</string> + <key>UILaunchImageName</key> + <string>Default</string> + <key>UILaunchImageOrientation</key> + <string>Landscape</string> + <key>UILaunchImageSize</key> + <string>{768, 1024}</string> + </dict> + </array> + <key>UISupportedInterfaceOrientations</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> + <key>UISupportedInterfaceOrientations~ipad</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationPortraitUpsideDown</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> +</dict> +</plist> diff --git a/third_party/libwebrtc/build/config/ios/Module-Info.plist b/third_party/libwebrtc/build/config/ios/Module-Info.plist new file mode 100644 index 0000000000..d1bf77faf0 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/Module-Info.plist @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>${IOS_BUNDLE_ID_PREFIX}.${MODULE_BUNDLE_ID:rfc1034identifier}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>BNDL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>NSPrincipalClass</key> + <string>${XCTEST_BUNDLE_PRINCIPAL_CLASS}</string> +</dict> +</plist> diff --git a/third_party/libwebrtc/build/config/ios/OWNERS b/third_party/libwebrtc/build/config/ios/OWNERS new file mode 100644 index 0000000000..6f3324f07c --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/OWNERS @@ -0,0 +1 @@ +file://build/apple/OWNERS diff --git a/third_party/libwebrtc/build/config/ios/asset_catalog.gni b/third_party/libwebrtc/build/config/ios/asset_catalog.gni new file mode 100644 index 0000000000..84dd92cce2 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/asset_catalog.gni @@ -0,0 +1,150 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/ios/ios_sdk.gni") + +# This template declares a bundle_data target that references an asset +# catalog so that it is compiled to the asset catalog of the generated +# bundle. +# +# The create_bundle target requires that all asset catalogs are part of an +# .xcasset bundle. This requirement comes from actool that only receives +# the path to the .xcasset bundle directory and not to the individual +# .imageset directories. +# +# The requirement is a bit problematic as it prevents compiling only a +# subset of the asset catakig that are contained in a .xcasset. This template +# fixes that by instead copying the content of the asset catalog to temporary +# .xcasset directory (below $root_out_dir) and defining a bundle_data +# target that refers to those copies (this is efficient as the "copy" is +# implemented by hardlinking if possible on macOS). +# +# Since the create_data target will only refer to the .xcasset directory +# and additional "action" target that runs a dummy script is defined. It +# does nothing but pretends to generate the .xcassets directory (while +# it is really created as a side-effect of the "copy" step). This allows +# to workaround the check in "gn" that all inputs below $root_out_dir have +# to be outputs of another target with a public dependency path. +# +# This template also ensures that the file are only copied once when the +# build targets multiple architectures at the same time (aka "fat build"). +# +# Arguments +# +# sources: +# required, list of strings, paths to the file contained in the +# asset catalog directory; this must contain the Contents.json file +# and all the image referenced by it (not enforced by the template). +# +# asset_type: +# required, string, type of the asset catalog, that is the extension +# of the directory containing the images and the Contents.json file. +# +template("asset_catalog") { + assert(defined(invoker.sources) && invoker.sources != [], + "sources must be defined and not empty for $target_name") + + assert(defined(invoker.asset_type) && invoker.asset_type != "", + "asset_type must be defined and not empty for $target_name") + + if (is_fat_secondary_toolchain) { + group(target_name) { + public_deps = [ ":$target_name($primary_fat_toolchain_name)" ] + } + } else { + _copy_target_name = target_name + "__copy" + _data_target_name = target_name + + _sources = invoker.sources + _outputs = [] + + # The compilation of resources into Assets.car is enabled automatically + # by the "create_bundle" target if any of the "bundle_data" sources's + # path is in a .xcassets directory and matches one of the know asset + # catalog type. + _xcassets_dir = "$target_gen_dir/${target_name}.xcassets" + _output_dir = "$_xcassets_dir/" + + get_path_info(get_path_info(_sources[0], "dir"), "file") + + foreach(_source, invoker.sources) { + _dir = get_path_info(_source, "dir") + _outputs += [ "$_output_dir/" + get_path_info(_source, "file") ] + + assert(get_path_info(_dir, "extension") == invoker.asset_type, + "$_source dirname must have .${invoker.asset_type} extension") + } + + action(_copy_target_name) { + # Forward "deps", "public_deps" and "testonly" in case some of the + # source files are generated. + forward_variables_from(invoker, + [ + "deps", + "public_deps", + "testonly", + ]) + + script = "//build/config/ios/hardlink.py" + + visibility = [ ":$_data_target_name" ] + sources = _sources + outputs = _outputs + [ _xcassets_dir ] + + args = [ + rebase_path(get_path_info(_sources[0], "dir"), root_build_dir), + rebase_path(_output_dir, root_build_dir), + ] + } + + bundle_data(_data_target_name) { + forward_variables_from(invoker, + "*", + [ + "deps", + "outputs", + "public_deps", + "sources", + ]) + + sources = _outputs + outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ] + public_deps = [ ":$_copy_target_name" ] + } + } +} + +# Those templates are specialisation of the asset_catalog template for known +# types of asset catalog types (imageset, launchimage, appiconset). +# +# Arguments +# +# sources: +# required, list of strings, paths to the file contained in the +# asset catalog directory; this must contain the Contents.json file +# and all the image referenced by it (not enforced by the template). +# +template("appiconset") { + asset_catalog(target_name) { + forward_variables_from(invoker, "*", [ "asset_type" ]) + asset_type = "appiconset" + } +} +template("colorset") { + asset_catalog(target_name) { + forward_variables_from(invoker, "*", [ "asset_type" ]) + asset_type = "colorset" + } +} +template("imageset") { + asset_catalog(target_name) { + forward_variables_from(invoker, "*", [ "asset_type" ]) + asset_type = "imageset" + } +} +template("launchimage") { + asset_catalog(target_name) { + forward_variables_from(invoker, "*", [ "asset_type" ]) + asset_type = "launchimage" + } +} diff --git a/third_party/libwebrtc/build/config/ios/codesign.py b/third_party/libwebrtc/build/config/ios/codesign.py new file mode 100644 index 0000000000..15d25a78df --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/codesign.py @@ -0,0 +1,691 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +from __future__ import print_function + +import argparse +import codecs +import datetime +import fnmatch +import glob +import json +import os +import plistlib +import shutil +import subprocess +import sys +import tempfile + +if sys.version_info.major < 3: + basestring_compat = basestring +else: + basestring_compat = str + + +def GetProvisioningProfilesDir(): + """Returns the location of the installed mobile provisioning profiles. + + Returns: + The path to the directory containing the installed mobile provisioning + profiles as a string. + """ + return os.path.join( + os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles') + + +def ReadPlistFromString(plist_bytes): + """Parse property list from given |plist_bytes|. + + Args: + plist_bytes: contents of property list to load. Must be bytes in python 3. + + Returns: + The contents of property list as a python object. + """ + if sys.version_info.major == 2: + return plistlib.readPlistFromString(plist_bytes) + else: + return plistlib.loads(plist_bytes) + + +def LoadPlistFile(plist_path): + """Loads property list file at |plist_path|. + + Args: + plist_path: path to the property list file to load. + + Returns: + The content of the property list file as a python object. + """ + if sys.version_info.major == 2: + return plistlib.readPlistFromString( + subprocess.check_output( + ['xcrun', 'plutil', '-convert', 'xml1', '-o', '-', plist_path])) + else: + with open(plist_path, 'rb') as fp: + return plistlib.load(fp) + + +def CreateSymlink(value, location): + """Creates symlink with value at location if the target exists.""" + target = os.path.join(os.path.dirname(location), value) + if os.path.exists(location): + os.unlink(location) + os.symlink(value, location) + + +class Bundle(object): + """Wraps a bundle.""" + + def __init__(self, bundle_path, platform): + """Initializes the Bundle object with data from bundle Info.plist file.""" + self._path = bundle_path + self._kind = Bundle.Kind(platform, os.path.splitext(bundle_path)[-1]) + self._data = None + + def Load(self): + self._data = LoadPlistFile(self.info_plist_path) + + @staticmethod + def Kind(platform, extension): + if platform == 'iphonesimulator' or platform == 'iphoneos': + return 'ios' + if platform == 'macosx': + if extension == '.framework': + return 'mac_framework' + return 'mac' + raise ValueError('unknown bundle type %s for %s' % (extension, platform)) + + @property + def kind(self): + return self._kind + + @property + def path(self): + return self._path + + @property + def contents_dir(self): + if self._kind == 'mac': + return os.path.join(self.path, 'Contents') + if self._kind == 'mac_framework': + return os.path.join(self.path, 'Versions/A') + return self.path + + @property + def executable_dir(self): + if self._kind == 'mac': + return os.path.join(self.contents_dir, 'MacOS') + return self.contents_dir + + @property + def resources_dir(self): + if self._kind == 'mac' or self._kind == 'mac_framework': + return os.path.join(self.contents_dir, 'Resources') + return self.path + + @property + def info_plist_path(self): + if self._kind == 'mac_framework': + return os.path.join(self.resources_dir, 'Info.plist') + return os.path.join(self.contents_dir, 'Info.plist') + + @property + def signature_dir(self): + return os.path.join(self.contents_dir, '_CodeSignature') + + @property + def identifier(self): + return self._data['CFBundleIdentifier'] + + @property + def binary_name(self): + return self._data['CFBundleExecutable'] + + @property + def binary_path(self): + return os.path.join(self.executable_dir, self.binary_name) + + def Validate(self, expected_mappings): + """Checks that keys in the bundle have the expected value. + + Args: + expected_mappings: a dictionary of string to object, each mapping will + be looked up in the bundle data to check it has the same value (missing + values will be ignored) + + Returns: + A dictionary of the key with a different value between expected_mappings + and the content of the bundle (i.e. errors) so that caller can format the + error message. The dictionary will be empty if there are no errors. + """ + errors = {} + for key, expected_value in expected_mappings.items(): + if key in self._data: + value = self._data[key] + if value != expected_value: + errors[key] = (value, expected_value) + return errors + + +class ProvisioningProfile(object): + """Wraps a mobile provisioning profile file.""" + + def __init__(self, provisioning_profile_path): + """Initializes the ProvisioningProfile with data from profile file.""" + self._path = provisioning_profile_path + self._data = ReadPlistFromString( + subprocess.check_output([ + 'xcrun', 'security', 'cms', '-D', '-u', 'certUsageAnyCA', '-i', + provisioning_profile_path + ])) + + @property + def path(self): + return self._path + + @property + def team_identifier(self): + return self._data.get('TeamIdentifier', [''])[0] + + @property + def name(self): + return self._data.get('Name', '') + + @property + def application_identifier_pattern(self): + return self._data.get('Entitlements', {}).get('application-identifier', '') + + @property + def application_identifier_prefix(self): + return self._data.get('ApplicationIdentifierPrefix', [''])[0] + + @property + def entitlements(self): + return self._data.get('Entitlements', {}) + + @property + def expiration_date(self): + return self._data.get('ExpirationDate', datetime.datetime.now()) + + def ValidToSignBundle(self, bundle_identifier): + """Checks whether the provisioning profile can sign bundle_identifier. + + Args: + bundle_identifier: the identifier of the bundle that needs to be signed. + + Returns: + True if the mobile provisioning profile can be used to sign a bundle + with the corresponding bundle_identifier, False otherwise. + """ + return fnmatch.fnmatch( + '%s.%s' % (self.application_identifier_prefix, bundle_identifier), + self.application_identifier_pattern) + + def Install(self, installation_path): + """Copies mobile provisioning profile info to |installation_path|.""" + shutil.copy2(self.path, installation_path) + + +class Entitlements(object): + """Wraps an Entitlement plist file.""" + + def __init__(self, entitlements_path): + """Initializes Entitlements object from entitlement file.""" + self._path = entitlements_path + self._data = LoadPlistFile(self._path) + + @property + def path(self): + return self._path + + def ExpandVariables(self, substitutions): + self._data = self._ExpandVariables(self._data, substitutions) + + def _ExpandVariables(self, data, substitutions): + if isinstance(data, basestring_compat): + for key, substitution in substitutions.items(): + data = data.replace('$(%s)' % (key,), substitution) + return data + + if isinstance(data, dict): + for key, value in data.items(): + data[key] = self._ExpandVariables(value, substitutions) + return data + + if isinstance(data, list): + for i, value in enumerate(data): + data[i] = self._ExpandVariables(value, substitutions) + + return data + + def LoadDefaults(self, defaults): + for key, value in defaults.items(): + if key not in self._data: + self._data[key] = value + + def WriteTo(self, target_path): + with open(target_path, 'wb') as fp: + if sys.version_info.major == 2: + plistlib.writePlist(self._data, fp) + else: + plistlib.dump(self._data, fp) + + +def FindProvisioningProfile(bundle_identifier, required): + """Finds mobile provisioning profile to use to sign bundle. + + Args: + bundle_identifier: the identifier of the bundle to sign. + + Returns: + The ProvisioningProfile object that can be used to sign the Bundle + object or None if no matching provisioning profile was found. + """ + provisioning_profile_paths = glob.glob( + os.path.join(GetProvisioningProfilesDir(), '*.mobileprovision')) + + # Iterate over all installed mobile provisioning profiles and filter those + # that can be used to sign the bundle, ignoring expired ones. + now = datetime.datetime.now() + valid_provisioning_profiles = [] + one_hour = datetime.timedelta(0, 3600) + for provisioning_profile_path in provisioning_profile_paths: + provisioning_profile = ProvisioningProfile(provisioning_profile_path) + if provisioning_profile.expiration_date - now < one_hour: + sys.stderr.write( + 'Warning: ignoring expired provisioning profile: %s.\n' % + provisioning_profile_path) + continue + if provisioning_profile.ValidToSignBundle(bundle_identifier): + valid_provisioning_profiles.append(provisioning_profile) + + if not valid_provisioning_profiles: + if required: + sys.stderr.write( + 'Error: no mobile provisioning profile found for "%s".\n' % + bundle_identifier) + sys.exit(1) + return None + + # Select the most specific mobile provisioning profile, i.e. the one with + # the longest application identifier pattern (prefer the one with the latest + # expiration date as a secondary criteria). + selected_provisioning_profile = max( + valid_provisioning_profiles, + key=lambda p: (len(p.application_identifier_pattern), p.expiration_date)) + + one_week = datetime.timedelta(7) + if selected_provisioning_profile.expiration_date - now < 2 * one_week: + sys.stderr.write( + 'Warning: selected provisioning profile will expire soon: %s' % + selected_provisioning_profile.path) + return selected_provisioning_profile + + +def CodeSignBundle(bundle_path, identity, extra_args): + process = subprocess.Popen( + ['xcrun', 'codesign', '--force', '--sign', identity, '--timestamp=none'] + + list(extra_args) + [bundle_path], + stderr=subprocess.PIPE, + universal_newlines=True) + _, stderr = process.communicate() + if process.returncode: + sys.stderr.write(stderr) + sys.exit(process.returncode) + for line in stderr.splitlines(): + if line.endswith(': replacing existing signature'): + # Ignore warning about replacing existing signature as this should only + # happen when re-signing system frameworks (and then it is expected). + continue + sys.stderr.write(line) + sys.stderr.write('\n') + + +def InstallSystemFramework(framework_path, bundle_path, args): + """Install framework from |framework_path| to |bundle| and code-re-sign it.""" + installed_framework_path = os.path.join( + bundle_path, 'Frameworks', os.path.basename(framework_path)) + + if os.path.isfile(framework_path): + shutil.copy(framework_path, installed_framework_path) + elif os.path.isdir(framework_path): + if os.path.exists(installed_framework_path): + shutil.rmtree(installed_framework_path) + shutil.copytree(framework_path, installed_framework_path) + + CodeSignBundle(installed_framework_path, args.identity, + ['--deep', '--preserve-metadata=identifier,entitlements,flags']) + + +def GenerateEntitlements(path, provisioning_profile, bundle_identifier): + """Generates an entitlements file. + + Args: + path: path to the entitlements template file + provisioning_profile: ProvisioningProfile object to use, may be None + bundle_identifier: identifier of the bundle to sign. + """ + entitlements = Entitlements(path) + if provisioning_profile: + entitlements.LoadDefaults(provisioning_profile.entitlements) + app_identifier_prefix = \ + provisioning_profile.application_identifier_prefix + '.' + else: + app_identifier_prefix = '*.' + entitlements.ExpandVariables({ + 'CFBundleIdentifier': bundle_identifier, + 'AppIdentifierPrefix': app_identifier_prefix, + }) + return entitlements + + +def GenerateBundleInfoPlist(bundle, plist_compiler, partial_plist): + """Generates the bundle Info.plist for a list of partial .plist files. + + Args: + bundle: a Bundle instance + plist_compiler: string, path to the Info.plist compiler + partial_plist: list of path to partial .plist files to merge + """ + + # Filter empty partial .plist files (this happens if an application + # does not compile any asset catalog, in which case the partial .plist + # file from the asset catalog compilation step is just a stamp file). + filtered_partial_plist = [] + for plist in partial_plist: + plist_size = os.stat(plist).st_size + if plist_size: + filtered_partial_plist.append(plist) + + # Invoke the plist_compiler script. It needs to be a python script. + subprocess.check_call([ + 'python', + plist_compiler, + 'merge', + '-f', + 'binary1', + '-o', + bundle.info_plist_path, + ] + filtered_partial_plist) + + +class Action(object): + """Class implementing one action supported by the script.""" + + @classmethod + def Register(cls, subparsers): + parser = subparsers.add_parser(cls.name, help=cls.help) + parser.set_defaults(func=cls._Execute) + cls._Register(parser) + + +class CodeSignBundleAction(Action): + """Class implementing the code-sign-bundle action.""" + + name = 'code-sign-bundle' + help = 'perform code signature for a bundle' + + @staticmethod + def _Register(parser): + parser.add_argument( + '--entitlements', '-e', dest='entitlements_path', + help='path to the entitlements file to use') + parser.add_argument( + 'path', help='path to the iOS bundle to codesign') + parser.add_argument( + '--identity', '-i', required=True, + help='identity to use to codesign') + parser.add_argument( + '--binary', '-b', required=True, + help='path to the iOS bundle binary') + parser.add_argument( + '--framework', '-F', action='append', default=[], dest='frameworks', + help='install and resign system framework') + parser.add_argument( + '--disable-code-signature', action='store_true', dest='no_signature', + help='disable code signature') + parser.add_argument( + '--disable-embedded-mobileprovision', action='store_false', + default=True, dest='embedded_mobileprovision', + help='disable finding and embedding mobileprovision') + parser.add_argument( + '--platform', '-t', required=True, + help='platform the signed bundle is targeting') + parser.add_argument( + '--partial-info-plist', '-p', action='append', default=[], + help='path to partial Info.plist to merge to create bundle Info.plist') + parser.add_argument( + '--plist-compiler-path', '-P', action='store', + help='path to the plist compiler script (for --partial-info-plist)') + parser.set_defaults(no_signature=False) + + @staticmethod + def _Execute(args): + if not args.identity: + args.identity = '-' + + bundle = Bundle(args.path, args.platform) + + if args.partial_info_plist: + GenerateBundleInfoPlist(bundle, args.plist_compiler_path, + args.partial_info_plist) + + # The bundle Info.plist may have been updated by GenerateBundleInfoPlist() + # above. Load the bundle information from Info.plist after the modification + # have been written to disk. + bundle.Load() + + # According to Apple documentation, the application binary must be the same + # as the bundle name without the .app suffix. See crbug.com/740476 for more + # information on what problem this can cause. + # + # To prevent this class of error, fail with an error if the binary name is + # incorrect in the Info.plist as it is not possible to update the value in + # Info.plist at this point (the file has been copied by a different target + # and ninja would consider the build dirty if it was updated). + # + # Also checks that the name of the bundle is correct too (does not cause the + # build to be considered dirty, but still terminate the script in case of an + # incorrect bundle name). + # + # Apple documentation is available at: + # https://developer.apple.com/library/content/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html + bundle_name = os.path.splitext(os.path.basename(bundle.path))[0] + errors = bundle.Validate({ + 'CFBundleName': bundle_name, + 'CFBundleExecutable': bundle_name, + }) + if errors: + for key in sorted(errors): + value, expected_value = errors[key] + sys.stderr.write('%s: error: %s value incorrect: %s != %s\n' % ( + bundle.path, key, value, expected_value)) + sys.stderr.flush() + sys.exit(1) + + # Delete existing embedded mobile provisioning. + embedded_provisioning_profile = os.path.join( + bundle.path, 'embedded.mobileprovision') + if os.path.isfile(embedded_provisioning_profile): + os.unlink(embedded_provisioning_profile) + + # Delete existing code signature. + if os.path.exists(bundle.signature_dir): + shutil.rmtree(bundle.signature_dir) + + # Install system frameworks if requested. + for framework_path in args.frameworks: + InstallSystemFramework(framework_path, args.path, args) + + # Copy main binary into bundle. + if not os.path.isdir(bundle.executable_dir): + os.makedirs(bundle.executable_dir) + shutil.copy(args.binary, bundle.binary_path) + + if bundle.kind == 'mac_framework': + # Create Versions/Current -> Versions/A symlink + CreateSymlink('A', os.path.join(bundle.path, 'Versions/Current')) + + # Create $binary_name -> Versions/Current/$binary_name symlink + CreateSymlink(os.path.join('Versions/Current', bundle.binary_name), + os.path.join(bundle.path, bundle.binary_name)) + + # Create optional symlinks. + for name in ('Headers', 'Resources', 'Modules'): + target = os.path.join(bundle.path, 'Versions/A', name) + if os.path.exists(target): + CreateSymlink(os.path.join('Versions/Current', name), + os.path.join(bundle.path, name)) + else: + obsolete_path = os.path.join(bundle.path, name) + if os.path.exists(obsolete_path): + os.unlink(obsolete_path) + + if args.no_signature: + return + + codesign_extra_args = [] + + if args.embedded_mobileprovision: + # Find mobile provisioning profile and embeds it into the bundle (if a + # code signing identify has been provided, fails if no valid mobile + # provisioning is found). + provisioning_profile_required = args.identity != '-' + provisioning_profile = FindProvisioningProfile( + bundle.identifier, provisioning_profile_required) + if provisioning_profile and args.platform != 'iphonesimulator': + provisioning_profile.Install(embedded_provisioning_profile) + + if args.entitlements_path is not None: + temporary_entitlements_file = \ + tempfile.NamedTemporaryFile(suffix='.xcent') + codesign_extra_args.extend( + ['--entitlements', temporary_entitlements_file.name]) + + entitlements = GenerateEntitlements( + args.entitlements_path, provisioning_profile, bundle.identifier) + entitlements.WriteTo(temporary_entitlements_file.name) + + CodeSignBundle(bundle.path, args.identity, codesign_extra_args) + + +class CodeSignFileAction(Action): + """Class implementing code signature for a single file.""" + + name = 'code-sign-file' + help = 'code-sign a single file' + + @staticmethod + def _Register(parser): + parser.add_argument( + 'path', help='path to the file to codesign') + parser.add_argument( + '--identity', '-i', required=True, + help='identity to use to codesign') + parser.add_argument( + '--output', '-o', + help='if specified copy the file to that location before signing it') + parser.set_defaults(sign=True) + + @staticmethod + def _Execute(args): + if not args.identity: + args.identity = '-' + + install_path = args.path + if args.output: + + if os.path.isfile(args.output): + os.unlink(args.output) + elif os.path.isdir(args.output): + shutil.rmtree(args.output) + + if os.path.isfile(args.path): + shutil.copy(args.path, args.output) + elif os.path.isdir(args.path): + shutil.copytree(args.path, args.output) + + install_path = args.output + + CodeSignBundle(install_path, args.identity, + ['--deep', '--preserve-metadata=identifier,entitlements']) + + +class GenerateEntitlementsAction(Action): + """Class implementing the generate-entitlements action.""" + + name = 'generate-entitlements' + help = 'generate entitlements file' + + @staticmethod + def _Register(parser): + parser.add_argument( + '--entitlements', '-e', dest='entitlements_path', + help='path to the entitlements file to use') + parser.add_argument( + 'path', help='path to the entitlements file to generate') + parser.add_argument( + '--info-plist', '-p', required=True, + help='path to the bundle Info.plist') + + @staticmethod + def _Execute(args): + info_plist = LoadPlistFile(args.info_plist) + bundle_identifier = info_plist['CFBundleIdentifier'] + provisioning_profile = FindProvisioningProfile(bundle_identifier, False) + entitlements = GenerateEntitlements( + args.entitlements_path, provisioning_profile, bundle_identifier) + entitlements.WriteTo(args.path) + + +class FindProvisioningProfileAction(Action): + """Class implementing the find-codesign-identity action.""" + + name = 'find-provisioning-profile' + help = 'find provisioning profile for use by Xcode project generator' + + @staticmethod + def _Register(parser): + parser.add_argument('--bundle-id', + '-b', + required=True, + help='bundle identifier') + + @staticmethod + def _Execute(args): + provisioning_profile_info = {} + provisioning_profile = FindProvisioningProfile(args.bundle_id, False) + for key in ('team_identifier', 'name'): + if provisioning_profile: + provisioning_profile_info[key] = getattr(provisioning_profile, key) + else: + provisioning_profile_info[key] = '' + print(json.dumps(provisioning_profile_info)) + + +def Main(): + # Cache this codec so that plistlib can find it. See + # https://crbug.com/999461#c12 for more details. + codecs.lookup('utf-8') + + parser = argparse.ArgumentParser('codesign iOS bundles') + subparsers = parser.add_subparsers() + + actions = [ + CodeSignBundleAction, + CodeSignFileAction, + GenerateEntitlementsAction, + FindProvisioningProfileAction, + ] + + for action in actions: + action.Register(subparsers) + + args = parser.parse_args() + args.func(args) + + +if __name__ == '__main__': + sys.exit(Main()) diff --git a/third_party/libwebrtc/build/config/ios/compile_ib_files.py b/third_party/libwebrtc/build/config/ios/compile_ib_files.py new file mode 100644 index 0000000000..84781c1773 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/compile_ib_files.py @@ -0,0 +1,57 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +from __future__ import print_function + +import argparse +import logging +import os +import re +import subprocess +import sys + + +def main(): + parser = argparse.ArgumentParser( + description='A script to compile xib and storyboard.', + fromfile_prefix_chars='@') + parser.add_argument('-o', '--output', required=True, + help='Path to output bundle.') + parser.add_argument('-i', '--input', required=True, + help='Path to input xib or storyboard.') + args, unknown_args = parser.parse_known_args() + + ibtool_args = [ + 'xcrun', 'ibtool', + '--errors', '--warnings', '--notices', + '--output-format', 'human-readable-text' + ] + ibtool_args += unknown_args + ibtool_args += [ + '--compile', + os.path.abspath(args.output), + os.path.abspath(args.input) + ] + + ibtool_section_re = re.compile(r'/\*.*\*/') + ibtool_re = re.compile(r'.*note:.*is clipping its content') + try: + stdout = subprocess.check_output(ibtool_args) + except subprocess.CalledProcessError as e: + print(e.output) + raise + current_section_header = None + for line in stdout.splitlines(): + if ibtool_section_re.match(line): + current_section_header = line + elif not ibtool_re.match(line): + if current_section_header: + print(current_section_header) + current_section_header = None + print(line) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/third_party/libwebrtc/build/config/ios/compile_xcassets_unittests.py b/third_party/libwebrtc/build/config/ios/compile_xcassets_unittests.py new file mode 100644 index 0000000000..7655df8c05 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/compile_xcassets_unittests.py @@ -0,0 +1,141 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import unittest +import compile_xcassets + + +class TestFilterCompilerOutput(unittest.TestCase): + + relative_paths = { + '/Users/janedoe/chromium/src/Chromium.xcassets': + '../../Chromium.xcassets', + '/Users/janedoe/chromium/src/out/Default/Chromium.app/Assets.car': + 'Chromium.app/Assets.car', + } + + def testNoError(self): + self.assertEquals( + '', + compile_xcassets.FilterCompilerOutput( + '/* com.apple.actool.compilation-results */\n' + '/Users/janedoe/chromium/src/out/Default/Chromium.app/Assets.car\n', + self.relative_paths)) + + def testNoErrorRandomMessages(self): + self.assertEquals( + '', + compile_xcassets.FilterCompilerOutput( + '2017-07-04 04:59:19.460 ibtoold[23487:41214] CoreSimulator is att' + 'empting to unload a stale CoreSimulatorService job. Existing' + ' job (com.apple.CoreSimulator.CoreSimulatorService.179.1.E8tt' + 'yeDeVgWK) is from an older version and is being removed to pr' + 'event problems.\n' + '/* com.apple.actool.compilation-results */\n' + '/Users/janedoe/chromium/src/out/Default/Chromium.app/Assets.car\n', + self.relative_paths)) + + def testWarning(self): + self.assertEquals( + '/* com.apple.actool.document.warnings */\n' + '../../Chromium.xcassets:./image1.imageset/[universal][][][1x][][][][' + '][][]: warning: The file "image1.png" for the image set "image1"' + ' does not exist.\n', + compile_xcassets.FilterCompilerOutput( + '/* com.apple.actool.document.warnings */\n' + '/Users/janedoe/chromium/src/Chromium.xcassets:./image1.imageset/[' + 'universal][][][1x][][][][][][]: warning: The file "image1.png' + '" for the image set "image1" does not exist.\n' + '/* com.apple.actool.compilation-results */\n' + '/Users/janedoe/chromium/src/out/Default/Chromium.app/Assets.car\n', + self.relative_paths)) + + def testError(self): + self.assertEquals( + '/* com.apple.actool.errors */\n' + '../../Chromium.xcassets: error: The output directory "/Users/janedoe/' + 'chromium/src/out/Default/Chromium.app" does not exist.\n', + compile_xcassets.FilterCompilerOutput( + '/* com.apple.actool.errors */\n' + '/Users/janedoe/chromium/src/Chromium.xcassets: error: The output ' + 'directory "/Users/janedoe/chromium/src/out/Default/Chromium.a' + 'pp" does not exist.\n' + '/* com.apple.actool.compilation-results */\n', + self.relative_paths)) + + def testSpurious(self): + self.assertEquals( + '/* com.apple.actool.document.warnings */\n' + '../../Chromium.xcassets:./AppIcon.appiconset: warning: A 1024x1024 ap' + 'p store icon is required for iOS apps\n', + compile_xcassets.FilterCompilerOutput( + '/* com.apple.actool.document.warnings */\n' + '/Users/janedoe/chromium/src/Chromium.xcassets:./AppIcon.appiconse' + 't: warning: A 1024x1024 app store icon is required for iOS ap' + 'ps\n' + '/* com.apple.actool.document.notices */\n' + '/Users/janedoe/chromium/src/Chromium.xcassets:./AppIcon.appiconse' + 't/[][ipad][76x76][][][1x][][]: notice: (null)\n', + self.relative_paths)) + + def testComplexError(self): + self.assertEquals( + '/* com.apple.actool.errors */\n' + ': error: Failed to find a suitable device for the type SimDeviceType ' + ': com.apple.dt.Xcode.IBSimDeviceType.iPad-2x with runtime SimRunt' + 'ime : 10.3.1 (14E8301) - com.apple.CoreSimulator.SimRuntime.iOS-1' + '0-3\n' + ' Failure Reason: Failed to create SimDeviceSet at path /Users/jane' + 'doe/Library/Developer/Xcode/UserData/IB Support/Simulator Devices' + '. You\'ll want to check the logs in ~/Library/Logs/CoreSimulator ' + 'to see why creating the SimDeviceSet failed.\n' + ' Underlying Errors:\n' + ' Description: Failed to initialize simulator device set.\n' + ' Failure Reason: Failed to subscribe to notifications from Cor' + 'eSimulatorService.\n' + ' Underlying Errors:\n' + ' Description: Error returned in reply to notification requ' + 'est: Connection invalid\n' + ' Failure Reason: Software caused connection abort\n', + compile_xcassets.FilterCompilerOutput( + '2017-07-07 10:37:27.367 ibtoold[88538:12553239] CoreSimulator det' + 'ected Xcode.app relocation or CoreSimulatorService version ch' + 'ange. Framework path (/Applications/Xcode.app/Contents/Devel' + 'oper/Library/PrivateFrameworks/CoreSimulator.framework) and v' + 'ersion (375.21) does not match existing job path (/Library/De' + 'veloper/PrivateFrameworks/CoreSimulator.framework/Versions/A/' + 'XPCServices/com.apple.CoreSimulator.CoreSimulatorService.xpc)' + ' and version (459.13). Attempting to remove the stale servic' + 'e in order to add the expected version.\n' + '2017-07-07 10:37:27.625 ibtoold[88538:12553256] CoreSimulatorServ' + 'ice connection interrupted. Resubscribing to notifications.\n' + '2017-07-07 10:37:27.632 ibtoold[88538:12553264] CoreSimulatorServ' + 'ice connection became invalid. Simulator services will no lo' + 'nger be available.\n' + '2017-07-07 10:37:27.642 ibtoold[88538:12553274] CoreSimulatorServ' + 'ice connection became invalid. Simulator services will no lo' + 'nger be available.\n' + '/* com.apple.actool.errors */\n' + ': error: Failed to find a suitable device for the type SimDeviceT' + 'ype : com.apple.dt.Xcode.IBSimDeviceType.iPad-2x with runtime' + ' SimRuntime : 10.3.1 (14E8301) - com.apple.CoreSimulator.SimR' + 'untime.iOS-10-3\n' + ' Failure Reason: Failed to create SimDeviceSet at path /Users/' + 'janedoe/Library/Developer/Xcode/UserData/IB Support/Simulator' + ' Devices. You\'ll want to check the logs in ~/Library/Logs/Co' + 'reSimulator to see why creating the SimDeviceSet failed.\n' + ' Underlying Errors:\n' + ' Description: Failed to initialize simulator device set.\n' + ' Failure Reason: Failed to subscribe to notifications from' + ' CoreSimulatorService.\n' + ' Underlying Errors:\n' + ' Description: Error returned in reply to notification ' + 'request: Connection invalid\n' + ' Failure Reason: Software caused connection abort\n' + '/* com.apple.actool.compilation-results */\n', + self.relative_paths)) + + +if __name__ == '__main__': + unittest.main() diff --git a/third_party/libwebrtc/build/config/ios/config.gni b/third_party/libwebrtc/build/config/ios/config.gni new file mode 100644 index 0000000000..358c7ccd2a --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/config.gni @@ -0,0 +1,28 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +declare_args() { + # Configure the environment for which to build. Could be either "device", + # "simulator" or "catalyst". If unspecified, then it will be assumed to be + # "simulator" if the target_cpu is "x68" or "x64", "device" otherwise. The + # default is only there for compatibility reasons and will be removed (see + # crbug.com/1138425 for more details). + target_environment = "" + + # Control whether cronet is built (this is usually set by the script + # components/cronet/tools/cr_cronet.py as cronet requires specific + # gn args to build correctly). + is_cronet_build = false +} + +if (target_environment == "") { + if (target_cpu == "x86" || target_cpu == "x64") { + target_environment = "simulator" + } else { + target_environment = "device" + } +} + +assert(target_environment == "simulator" || target_environment == "device" || + target_environment == "catalyst") diff --git a/third_party/libwebrtc/build/config/ios/dummy.py b/third_party/libwebrtc/build/config/ios/dummy.py new file mode 100644 index 0000000000..b23b7dab96 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/dummy.py @@ -0,0 +1,15 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Empty script that does nothing and return success error code. + +This script is used by some gn targets that pretend creating some output +but instead depend on another target creating the output indirectly (in +general this output is a directory that is used as input by a bundle_data +target). + +It ignores all parameters and terminate with a success error code. It +does the same thing as the unix command "true", but gn can only invoke +python scripts. +""" diff --git a/third_party/libwebrtc/build/config/ios/entitlements.plist b/third_party/libwebrtc/build/config/ios/entitlements.plist new file mode 100644 index 0000000000..429762e3a3 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/entitlements.plist @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>application-identifier</key> + <string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string> + <key>keychain-access-groups</key> + <array> + <string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string> + </array> +</dict> +</plist> diff --git a/third_party/libwebrtc/build/config/ios/find_signing_identity.py b/third_party/libwebrtc/build/config/ios/find_signing_identity.py new file mode 100644 index 0000000000..d508e2bb46 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/find_signing_identity.py @@ -0,0 +1,89 @@ +# Copyright (c) 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +from __future__ import print_function + +import argparse +import os +import subprocess +import sys +import re + + +def Redact(value, from_nth_char=5): + """Redact value past the N-th character.""" + return value[:from_nth_char] + '*' * (len(value) - from_nth_char) + + +class Identity(object): + """Represents a valid identity.""" + + def __init__(self, identifier, name, team): + self.identifier = identifier + self.name = name + self.team = team + + def redacted(self): + return Identity(Redact(self.identifier), self.name, Redact(self.team)) + + def format(self): + return '%s: "%s (%s)"' % (self.identifier, self.name, self.team) + + +def ListIdentities(): + return subprocess.check_output([ + 'xcrun', + 'security', + 'find-identity', + '-v', + '-p', + 'codesigning', + ]).decode('utf8') + + +def FindValidIdentity(pattern): + """Find all identities matching the pattern.""" + lines = list(l.strip() for l in ListIdentities().splitlines()) + # Look for something like "2) XYZ "iPhone Developer: Name (ABC)"" + regex = re.compile('[0-9]+\) ([A-F0-9]+) "([^"(]*) \(([^)"]*)\)"') + + result = [] + for line in lines: + res = regex.match(line) + if res is None: + continue + if pattern is None or pattern in res.group(2): + result.append(Identity(*res.groups())) + return result + + +def Main(args): + parser = argparse.ArgumentParser('codesign iOS bundles') + parser.add_argument('--matching-pattern', + dest='pattern', + help='Pattern used to select the code signing identity.') + parsed = parser.parse_args(args) + + identities = FindValidIdentity(parsed.pattern) + if len(identities) == 1: + print(identities[0].identifier, end='') + return 0 + + all_identities = FindValidIdentity(None) + + print('Automatic code signing identity selection was enabled but could not') + print('find exactly one codesigning identity matching "%s".' % parsed.pattern) + print('') + print('Check that the keychain is accessible and that there is exactly one') + print('valid codesigning identity matching the pattern. Here is the parsed') + print('output of `xcrun security find-identity -v -p codesigning`:') + print() + for i, identity in enumerate(all_identities): + print(' %d) %s' % (i + 1, identity.redacted().format())) + print(' %d valid identities found' % (len(all_identities))) + return 1 + + +if __name__ == '__main__': + sys.exit(Main(sys.argv[1:])) diff --git a/third_party/libwebrtc/build/config/ios/generate_umbrella_header.py b/third_party/libwebrtc/build/config/ios/generate_umbrella_header.py new file mode 100644 index 0000000000..8547e18aa7 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/generate_umbrella_header.py @@ -0,0 +1,75 @@ +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Generates an umbrella header for an iOS framework.""" + +import argparse +import datetime +import os +import re +import string + + +HEADER_TEMPLATE = string.Template('''\ +// Copyright $year The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// This file is auto-generated by //build/ios/config/generate_umbrella_header.py + +#ifndef $header_guard +#define $header_guard + +$imports + +#endif // $header_guard +''') + + +def ComputeHeaderGuard(file_path): + """Computes the header guard for a file path. + + Args: + file_path: The path to convert into an header guard. + Returns: + The header guard string for the file_path. + """ + return re.sub(r'[.+/\\]', r'_', file_path.upper()) + '_' + + +def WriteUmbrellaHeader(output_path, imported_headers): + """Writes the umbrella header. + + Args: + output_path: The path to the umbrella header. + imported_headers: A list of headers to #import in the umbrella header. + """ + year = datetime.date.today().year + header_guard = ComputeHeaderGuard(output_path) + imports = '\n'.join([ + '#import "%s"' % os.path.basename(header) + for header in sorted(imported_headers) + ]) + with open(output_path, 'w') as output_file: + output_file.write( + HEADER_TEMPLATE.safe_substitute({ + 'year': year, + 'header_guard': header_guard, + 'imports': imports, + })) + + +def Main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--output-path', required=True, type=str, + help='Path to the generated umbrella header.') + parser.add_argument('imported_headers', type=str, nargs='+', + help='Headers to #import in the umbrella header.') + options = parser.parse_args() + + return WriteUmbrellaHeader(options.output_path, options.imported_headers) + + +if __name__ == '__main__': + Main() diff --git a/third_party/libwebrtc/build/config/ios/hardlink.py b/third_party/libwebrtc/build/config/ios/hardlink.py new file mode 100644 index 0000000000..38f60d4013 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/hardlink.py @@ -0,0 +1,71 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Recursively create hardlink to target named output.""" + + +import argparse +import os +import shutil + + +def CreateHardlinkHelper(target, output): + """Recursively create a hardlink named output pointing to target. + + Args: + target: path to an existing file or directory + output: path to the newly created hardlink + + This function assumes that output does not exists but that the parent + directory containing output does. If those conditions are false, then + the function will fails with an exception corresponding to an OS error. + """ + if os.path.islink(target): + os.symlink(os.readlink(target), output) + elif not os.path.isdir(target): + try: + os.link(target, output) + except: + shutil.copy(target, output) + else: + os.mkdir(output) + for name in os.listdir(target): + CreateHardlinkHelper( + os.path.join(target, name), + os.path.join(output, name)) + + +def CreateHardlink(target, output): + """Recursively create a hardlink named output pointing to target. + + Args: + target: path to an existing file or directory + output: path to the newly created hardlink + + If output already exists, it is first removed. In all cases, the + parent directory containing output is created. + """ + if os.path.isdir(output): + shutil.rmtree(output) + elif os.path.exists(output): + os.unlink(output) + + parent_dir = os.path.dirname(os.path.abspath(output)) + if not os.path.isdir(parent_dir): + os.makedirs(parent_dir) + + CreateHardlinkHelper(target, output) + + +def Main(): + parser = argparse.ArgumentParser() + parser.add_argument('target', help='path to the file or directory to link to') + parser.add_argument('output', help='name of the hardlink to create') + args = parser.parse_args() + + CreateHardlink(args.target, args.output) + + +if __name__ == '__main__': + Main() diff --git a/third_party/libwebrtc/build/config/ios/ios_sdk.gni b/third_party/libwebrtc/build/config/ios/ios_sdk.gni new file mode 100644 index 0000000000..ffff5921d8 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/ios_sdk.gni @@ -0,0 +1,185 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/ios/config.gni") +import("//build/config/ios/ios_sdk_overrides.gni") +import("//build/toolchain/goma.gni") +import("//build/toolchain/goma.gni") +import("//build/toolchain/toolchain.gni") +import("//build_overrides/build.gni") + +assert(current_os == "ios") +assert(use_system_xcode, "Hermetic xcode doesn't work for ios.") + +declare_args() { + # SDK path to use. When empty this will use the default SDK based on the + # value of target_environment. + ios_bin_path = "" + ios_sdk_path = "" + ios_sdk_name = "" + ios_sdk_version = "" + ios_sdk_platform = "" + ios_sdk_platform_path = "" + ios_toolchains_path = "" + xcode_version = "" + xcode_version_int = 0 + xcode_build = "" + machine_os_build = "" + + # Set DEVELOPER_DIR while running sdk_info.py. + ios_sdk_developer_dir = "" + + # Control whether codesiging is enabled (ignored for simulator builds). + ios_enable_code_signing = true + + # Explicitly select the identity to use for codesigning. If defined, must + # be set to a non-empty string that will be passed to codesigning. Can be + # left unspecified if ios_code_signing_identity_description is used instead. + ios_code_signing_identity = "" + + # Pattern used to select the identity to use for codesigning. If defined, + # must be a substring of the description of exactly one of the identities by + # `security find-identity -v -p codesigning`. + ios_code_signing_identity_description = "Apple Development" + + # Prefix for CFBundleIdentifier property of iOS bundles (correspond to the + # "Organization Identifier" in Xcode). Code signing will fail if no mobile + # provisioning for the selected code signing identify support that prefix. + ios_app_bundle_id_prefix = "org.chromium" + + # If non-empty, this list must contain valid cpu architecture, and the final + # build will be a multi-architecture build (aka fat build) supporting the + # main $target_cpu architecture and all of $additional_target_cpus. + # + # For example to build an application that will run on both arm64 and armv7 + # devices, you would use the following in args.gn file when running "gn args": + # + # target_os = "ios" + # target_cpu = "arm64" + # additional_target_cpus = [ "arm" ] + # + # You can also pass the value via "--args" parameter for "gn gen" command by + # using the syntax --args='additional_target_cpus=["arm"] target_cpu="arm64"'. + additional_target_cpus = [] +} + +declare_args() { + # This variable is set by the toolchain. It is set to true if the toolchain + # is a secondary toolchain as part of a "fat" build. + is_fat_secondary_toolchain = false + + # This variable is set by the toolchain. It is the name of the primary + # toolchain for the fat build (could be current_toolchain). + primary_fat_toolchain_name = "" +} + +# Official builds may not use goma. +assert(!(use_goma && is_chrome_branded && is_official_build && + target_cpu == "arm64"), + "goma use is forbidden for official iOS builds.") + +assert(custom_toolchain == "" || additional_target_cpus == [], + "cannot define both custom_toolchain and additional_target_cpus") + +# If codesigning is enabled, use must configure either a codesigning identity +# or a filter to automatically select the codesigning identity. +if (target_environment == "device" && ios_enable_code_signing) { + assert(ios_code_signing_identity == "" || + ios_code_signing_identity_description == "", + "You should either specify the precise identity to use with " + + "ios_code_signing_identity or let the code select an identity " + + "automatically (via find_signing_identity.py which use the " + + "variable ios_code_signing_identity_description to set the " + + "pattern to match the identity to use).") +} + +# Initialize additional_toolchains from additional_target_cpus. Assert here +# that the list does not contains $target_cpu nor duplicates as this would +# cause weird errors during the build. +additional_toolchains = [] +if (additional_target_cpus != [] && !is_fat_secondary_toolchain) { + foreach(_additional_target_cpu, additional_target_cpus) { + assert(_additional_target_cpu != target_cpu, + "target_cpu must not be listed in additional_target_cpus") + + _toolchain = "${current_toolchain}_fat_${_additional_target_cpu}" + foreach(_additional_toolchain, additional_toolchains) { + assert(_toolchain != _additional_toolchain, + "additional_target_cpus must not contains duplicate values") + } + + additional_toolchains += [ _toolchain ] + } +} + +if (ios_sdk_path == "") { + # Compute default target. + if (target_environment == "simulator") { + ios_sdk_name = "iphonesimulator" + ios_sdk_platform = "iPhoneSimulator" + } else if (target_environment == "device") { + ios_sdk_name = "iphoneos" + ios_sdk_platform = "iPhoneOS" + } else if (target_environment == "catalyst") { + ios_sdk_name = "macosx" + ios_sdk_platform = "MacOSX" + } else { + assert(false, "unsupported environment: $target_environment") + } + + ios_sdk_info_args = [ + "--get_sdk_info", + "--get_machine_info", + ] + ios_sdk_info_args += [ ios_sdk_name ] + if (ios_sdk_developer_dir != "") { + ios_sdk_info_args += [ + "--developer_dir", + ios_sdk_developer_dir, + ] + } + if (use_system_xcode && use_goma) { + ios_sdk_info_args += [ + "--create_symlink_at", + "sdk/xcode_links", + ] + } + script_name = "//build/config/apple/sdk_info.py" + _ios_sdk_result = exec_script(script_name, ios_sdk_info_args, "scope") + ios_bin_path = + rebase_path("${_ios_sdk_result.toolchains_path}/usr/bin/", root_build_dir) + ios_sdk_path = _ios_sdk_result.sdk_path + ios_sdk_platform_path = _ios_sdk_result.sdk_platform_path + ios_sdk_version = _ios_sdk_result.sdk_version + ios_sdk_build = _ios_sdk_result.sdk_build + ios_toolchains_path = _ios_sdk_result.toolchains_path + xcode_version = _ios_sdk_result.xcode_version + xcode_version_int = _ios_sdk_result.xcode_version_int + xcode_build = _ios_sdk_result.xcode_build + machine_os_build = _ios_sdk_result.machine_os_build + if (target_environment == "simulator") { + # This is weird, but Xcode sets DTPlatformBuild to an empty field for + # simulator builds. + ios_platform_build = "" + } else { + ios_platform_build = ios_sdk_build + } +} + +if (target_environment == "device" && ios_enable_code_signing) { + # Automatically select a codesigning identity if no identity is configured. + # This only applies to device build as simulator builds are not signed. + if (ios_code_signing_identity == "") { + find_signing_identity_args = [] + if (ios_code_signing_identity_description != "") { + find_signing_identity_args = [ + "--matching-pattern", + ios_code_signing_identity_description, + ] + } + ios_code_signing_identity = exec_script("find_signing_identity.py", + find_signing_identity_args, + "trim string") + } +} diff --git a/third_party/libwebrtc/build/config/ios/ios_sdk_overrides.gni b/third_party/libwebrtc/build/config/ios/ios_sdk_overrides.gni new file mode 100644 index 0000000000..12d52521da --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/ios_sdk_overrides.gni @@ -0,0 +1,17 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This file contains arguments that subprojects may choose to override. It +# asserts that those overrides are used, to prevent unused args warnings. + +declare_args() { + # Version of iOS that we're targeting. + ios_deployment_target = "14.0" +} + +# Always assert that ios_deployment_target is used on non-iOS platforms to +# prevent unused args warnings. +if (!is_ios) { + assert(ios_deployment_target == "14.0" || true) +} diff --git a/third_party/libwebrtc/build/config/ios/ios_test_runner_wrapper.gni b/third_party/libwebrtc/build/config/ios/ios_test_runner_wrapper.gni new file mode 100644 index 0000000000..115db7ffdb --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/ios_test_runner_wrapper.gni @@ -0,0 +1,143 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/coverage/coverage.gni") +import("//build/config/ios/ios_sdk.gni") +import("//build/util/generate_wrapper.gni") + +# Invokes generate_wrapper to create an executable script wrapping iOS' +# run.py with baked in arguments. Only takes effect when test entry in +# gn_isolate_map.pyl is updated to type="generated_script" with script +# set to the wrapper output path. +# +# Arguments: +# +# data +# (optional, default [ "//ios/build/bots/scripts/" ]) list of files or +# directories required to run target +# +# data_deps +# (optional) list of target non-linked labels +# +# deps +# (optional) list of files or directories required to run target +# +# executable_args +# (optional) a list of string arguments to pass to run.py +# +# retries +# (optional, default 3) number of retry attempts +# +# shards +# (optional, default 1) number of shards to execute tests in parallel. not +# the same as swarming shards. +# +# wrapper_output_name +# (optional, default "run_${target_name}") name of the wrapper script +# +template("ios_test_runner_wrapper") { + generate_wrapper(target_name) { + forward_variables_from(invoker, + [ + "deps", + "retries", + "shards", + "wrapper_output_name", + ]) + testonly = true + executable = "//testing/test_env.py" + + # iOS main test runner + _runner_path = + rebase_path("//ios/build/bots/scripts/run.py", root_build_dir) + + executable_args = [ "@WrappedPath(${_runner_path})" ] + + # arguments passed to run.py + if (defined(invoker.executable_args)) { + executable_args += invoker.executable_args + } + + _rebased_mac_toolchain = rebase_path("//mac_toolchain", root_build_dir) + _rebased_xcode_path = rebase_path("//Xcode.app", root_build_dir) + _rebased_ios_runtime_cache_prefix = + rebase_path("//Runtime-ios-", root_build_dir) + + # --out-dir argument is specified in gn_isolate_map.pyl because + # ${ISOLATED_OUTDIR} doesn't get resolved through this wrapper. + executable_args += [ + "--xcode-path", + "@WrappedPath(${_rebased_xcode_path})", + "--mac-toolchain-cmd", + "@WrappedPath(${_rebased_mac_toolchain})", + "--runtime-cache-prefix", + "@WrappedPath(${_rebased_ios_runtime_cache_prefix})", + ] + + # Default retries to 3 + if (!defined(retries)) { + retries = 3 + } + executable_args += [ + "--retries", + "${retries}", + ] + + # Default shards to 1 + if (!defined(shards)) { + shards = 1 + } + executable_args += [ + "--shards", + "${shards}", + ] + + data_deps = [ "//testing:test_scripts_shared" ] + if (defined(invoker.data_deps)) { + data_deps += invoker.data_deps + } + + # test runner relies on iossim for simulator builds. + if (target_environment == "simulator") { + _rebased_root_build_dir = rebase_path("${root_build_dir}", root_build_dir) + data_deps += [ "//testing/iossim" ] + + executable_args += [ + "--iossim", + "@WrappedPath(${_rebased_root_build_dir}/iossim)", + ] + } + + if (use_clang_coverage) { + executable_args += [ "--use-clang-coverage" ] + } + + if (!is_debug) { + executable_args += [ "--release" ] + } + + # wrapper script output name and path + if (!defined(wrapper_output_name)) { + _wrapper_output_name = "run_${target_name}" + } else { + _wrapper_output_name = wrapper_output_name + } + + # Test targets may attempt to generate multiple wrappers for a suite with + # multiple different toolchains when running with additional_target_cpus. + # Generate the wrapper script into root_out_dir rather than root_build_dir + # to ensure those wrappers are distinct. + wrapper_script = "${root_out_dir}/bin/${_wrapper_output_name}" + + data = [] + if (defined(invoker.data)) { + data += invoker.data + } + data += [ + "//.vpython", + "//ios/build/bots/scripts/", + "//testing/test_env.py", + ] + } +} diff --git a/third_party/libwebrtc/build/config/ios/resources/XCTRunnerAddition+Info.plist b/third_party/libwebrtc/build/config/ios/resources/XCTRunnerAddition+Info.plist new file mode 100644 index 0000000000..cf9463f694 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/resources/XCTRunnerAddition+Info.plist @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleIdentifier</key> + <string>com.apple.test.${EXECUTABLE_NAME}</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> +</dict> +</plist> diff --git a/third_party/libwebrtc/build/config/ios/rules.gni b/third_party/libwebrtc/build/config/ios/rules.gni new file mode 100644 index 0000000000..04d605dd97 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/rules.gni @@ -0,0 +1,2173 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/apple/apple_info_plist.gni") +import("//build/config/apple/symbols.gni") +import("//build/config/ios/ios_sdk.gni") +import("//build/toolchain/goma.gni") +import("//build/toolchain/toolchain.gni") +import("//build_overrides/build.gni") + +declare_args() { + # Set to true if an Xcode project is generated for this build. Set this to + # false if you do not plan to run `gn gen --ide=xcode` in this directory. + # This will speed up the generation at the cost of generating an invalid + # Xcode project if `gn gen --ide=xcode` is used. Defaults to true (favor + # correctness over speed). + ios_set_attributes_for_xcode_project_generation = true +} + +# Constants corresponding to the bundle type identifiers use application, +# application extension, XCTest and XCUITest targets respectively. +_ios_xcode_app_bundle_id = "com.apple.product-type.application" +_ios_xcode_appex_bundle_id = "com.apple.product-type.app-extension" +_ios_xcode_xctest_bundle_id = "com.apple.product-type.bundle.unit-test" +_ios_xcode_xcuitest_bundle_id = "com.apple.product-type.bundle.ui-testing" + +# Invokes lipo on multiple arch-specific binaries to create a fat binary. +# +# Arguments +# +# arch_binary_target +# name of the target generating the arch-specific binaries, they must +# be named $target_out_dir/$toolchain_cpu/$arch_binary_output. +# +# arch_binary_output +# (optional, defaults to the name of $arch_binary_target) base name of +# the arch-specific binary generated by arch_binary_target. +# +# output_name +# (optional, defaults to $target_name) base name of the target output, +# the full path will be $target_out_dir/$output_name. +# +# configs +# (optional) a list of configurations, this is used to check whether +# the binary should be stripped, when "enable_stripping" is true. +# +template("lipo_binary") { + assert(defined(invoker.arch_binary_target), + "arch_binary_target must be defined for $target_name") + + assert(!is_fat_secondary_toolchain, + "lipo_binary can only be used in the primary toolchain of a fat build") + + _target_name = target_name + _output_name = target_name + if (defined(invoker.output_name)) { + _output_name = invoker.output_name + } + + _all_target_cpu = [ target_cpu ] + additional_target_cpus + _all_toolchains = [ current_toolchain ] + additional_toolchains + + _arch_binary_target = invoker.arch_binary_target + _arch_binary_output = get_label_info(_arch_binary_target, "name") + if (defined(invoker.arch_binary_output)) { + _arch_binary_output = invoker.arch_binary_output + } + + action(_target_name) { + forward_variables_from(invoker, + "*", + [ + "arch_binary_output", + "arch_binary_target", + "configs", + "output_name", + ]) + + script = "//build/toolchain/apple/linker_driver.py" + + # http://crbug.com/762840. Fix for bots running out of memory. + pool = "//build/toolchain:link_pool($default_toolchain)" + + outputs = [ "$target_out_dir/$_output_name" ] + + deps = [] + _index = 0 + inputs = [] + foreach(_cpu, _all_target_cpu) { + _toolchain = _all_toolchains[_index] + _index = _index + 1 + + inputs += + [ get_label_info("$_arch_binary_target($_toolchain)", + "target_out_dir") + "/$_cpu/$_arch_binary_output" ] + + deps += [ "$_arch_binary_target($_toolchain)" ] + } + + args = [ + "xcrun", + "lipo", + "-create", + "-output", + rebase_path("$target_out_dir/$_output_name", root_build_dir), + ] + rebase_path(inputs, root_build_dir) + + if (enable_dsyms) { + _dsyms_output_dir = "$root_out_dir/$_output_name.dSYM" + outputs += [ + "$_dsyms_output_dir/", + "$_dsyms_output_dir/Contents/Info.plist", + "$_dsyms_output_dir/Contents/Resources/DWARF/$_output_name", + ] + args += [ "-Wcrl,dsym," + rebase_path("$root_out_dir/.", root_build_dir) ] + if (!use_xcode_clang) { + args += [ "-Wcrl,dsymutilpath," + + rebase_path("//tools/clang/dsymutil/bin/dsymutil", + root_build_dir) ] + } + } + + if (enable_stripping) { + args += [ "-Wcrl,strip,-x,-S" ] + if (save_unstripped_output) { + outputs += [ "$root_out_dir/$_output_name.unstripped" ] + args += [ "-Wcrl,unstripped," + + rebase_path("$root_out_dir/.", root_build_dir) ] + } + } + } +} + +# Wrapper around create_bundle taking care of code signature settings. +# +# Arguments +# +# product_type +# string, product type for the generated Xcode project. +# +# bundle_gen_dir +# (optional) directory where the bundle is generated; must be below +# root_out_dir and defaults to root_out_dir if omitted. +# +# bundle_deps +# (optional) list of additional dependencies. +# +# bundle_deps_filter +# (optional) list of dependencies to filter (for more information +# see "gn help bundle_deps_filter"). +# +# bundle_extension +# string, extension of the bundle, used to generate bundle name. +# +# bundle_binary_target +# (optional) string, label of the target generating the bundle main +# binary. This target and bundle_binary_path are mutually exclusive. +# +# bundle_binary_output +# (optional) string, base name of the binary generated by the +# bundle_binary_target target, defaults to the target name. +# +# bundle_binary_path +# (optional) string, path to the bundle main binary. This target and +# bundle_binary_target are mutually exclusive. +# +# output_name: +# (optional) string, name of the generated application, if omitted, +# defaults to the target_name. +# +# extra_system_frameworks +# (optional) list of system framework to copy to the bundle. +# +# enable_code_signing +# (optional) boolean, control whether code signing is enabled or not, +# default to ios_enable_code_signing if not defined. +# +# entitlements_path: +# (optional) path to the template to use to generate the application +# entitlements by performing variable substitutions, defaults to +# //build/config/ios/entitlements.plist. +# +# entitlements_target: +# (optional) label of the target generating the application +# entitlements (must generate a single file as output); cannot be +# defined if entitlements_path is set. +# +# has_public_headers: +# (optional) boolean, defaults to false; only meaningful if the bundle +# is a framework bundle; if true, then the frameworks includes public +# headers +# +# disable_entitlements +# (optional, defaults to false) boolean, control whether entitlements willi +# be embedded in the application during signature. If false and no +# entitlements are provided, default empty entitlements will be used. +# +# disable_embedded_mobileprovision +# (optional, default to false) boolean, control whether mobile provisions +# will be embedded in the bundle. If true, the existing +# embedded.mobileprovision will be deleted. +# +# xcode_extra_attributes +# (optional) scope, extra attributes for Xcode projects. +# +# xcode_test_application_name: +# (optional) string, name of the test application for Xcode unit or ui +# test target. +# +# xcode_product_bundle_id: +# (optional) string, the bundle ID that will be added in the XCode +# attributes to enable some features when debugging (e.g. MetricKit). +# +# primary_info_plist: +# (optional) path to Info.plist to merge with the $partial_info_plist +# generated by the compilation of the asset catalog. +# +# partial_info_plist: +# (optional) path to the partial Info.plist generated by the asset +# catalog compiler; if defined $primary_info_plist must also be defined. +# +template("create_signed_bundle") { + assert(defined(invoker.product_type), + "product_type must be defined for $target_name") + assert(defined(invoker.bundle_extension), + "bundle_extension must be defined for $target_name") + assert(defined(invoker.bundle_binary_target) != + defined(invoker.bundle_binary_path), + "Only one of bundle_binary_target or bundle_binary_path may be " + + "specified for $target_name") + assert(!defined(invoker.partial_info_plist) || + defined(invoker.primary_info_plist), + "primary_info_plist must be defined when partial_info_plist is " + + "defined for $target_name") + + if (defined(invoker.xcode_test_application_name)) { + assert( + invoker.product_type == _ios_xcode_xctest_bundle_id || + invoker.product_type == _ios_xcode_xcuitest_bundle_id, + "xcode_test_application_name can be only defined for Xcode unit or ui test target.") + } + + _target_name = target_name + _output_name = target_name + if (defined(invoker.output_name)) { + _output_name = invoker.output_name + } + + if (defined(invoker.bundle_binary_path)) { + _bundle_binary_path = invoker.bundle_binary_path + } else { + _bundle_binary_target = invoker.bundle_binary_target + _bundle_binary_output = get_label_info(_bundle_binary_target, "name") + if (defined(invoker.bundle_binary_output)) { + _bundle_binary_output = invoker.bundle_binary_output + } + _bundle_binary_path = + get_label_info(_bundle_binary_target, "target_out_dir") + + "/$_bundle_binary_output" + } + + _bundle_gen_dir = root_out_dir + if (defined(invoker.bundle_gen_dir)) { + _bundle_gen_dir = invoker.bundle_gen_dir + } + + _bundle_extension = invoker.bundle_extension + + _enable_embedded_mobileprovision = true + if (defined(invoker.disable_embedded_mobileprovision)) { + _enable_embedded_mobileprovision = !invoker.disable_embedded_mobileprovision + } + + if (target_environment == "catalyst") { + _enable_embedded_mobileprovision = false + } + + _enable_entitlements = true + if (defined(invoker.disable_entitlements)) { + _enable_entitlements = !invoker.disable_entitlements + } + + if (_enable_entitlements) { + if (!defined(invoker.entitlements_target)) { + _entitlements_path = "//build/config/ios/entitlements.plist" + if (defined(invoker.entitlements_path)) { + _entitlements_path = invoker.entitlements_path + } + } else { + assert(!defined(invoker.entitlements_path), + "Cannot define both entitlements_path and entitlements_target " + + "for $target_name") + + _entitlements_target_outputs = + get_target_outputs(invoker.entitlements_target) + _entitlements_path = _entitlements_target_outputs[0] + } + } + + _enable_code_signing = ios_enable_code_signing + if (defined(invoker.enable_code_signing)) { + _enable_code_signing = invoker.enable_code_signing + } + + if (!ios_set_attributes_for_xcode_project_generation) { + not_needed(invoker, + [ + "xcode_product_bundle_id", + "xcode_extra_attributes", + ]) + } + + create_bundle(_target_name) { + forward_variables_from(invoker, + [ + "bundle_deps_filter", + "data_deps", + "deps", + "partial_info_plist", + "product_type", + "public_configs", + "public_deps", + "testonly", + "visibility", + "xcode_test_application_name", + ]) + + bundle_root_dir = "$_bundle_gen_dir/$_output_name$_bundle_extension" + if (target_environment == "simulator" || target_environment == "device") { + bundle_contents_dir = bundle_root_dir + bundle_resources_dir = bundle_contents_dir + bundle_executable_dir = bundle_contents_dir + } else if (target_environment == "catalyst") { + if (_bundle_extension != ".framework") { + bundle_contents_dir = "$bundle_root_dir/Contents" + bundle_resources_dir = "$bundle_contents_dir/Resources" + bundle_executable_dir = "$bundle_contents_dir/MacOS" + } else { + bundle_contents_dir = "$bundle_root_dir/Versions/A" + bundle_resources_dir = "$bundle_contents_dir/Resources" + bundle_executable_dir = bundle_contents_dir + } + } + + if (!defined(public_deps)) { + public_deps = [] + } + + if (ios_set_attributes_for_xcode_project_generation) { + _xcode_product_bundle_id = "" + if (defined(invoker.xcode_product_bundle_id)) { + _xcode_product_bundle_id = invoker.xcode_product_bundle_id + } + + if (_xcode_product_bundle_id != "") { + _ios_provisioning_profile_info = + exec_script("//build/config/ios/codesign.py", + [ + "find-provisioning-profile", + "-b=" + _xcode_product_bundle_id, + ], + "json") + } + + xcode_extra_attributes = { + IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target + if (_xcode_product_bundle_id != "") { + CODE_SIGN_IDENTITY = "iPhone Developer" + DEVELOPMENT_TEAM = _ios_provisioning_profile_info.team_identifier + PRODUCT_BUNDLE_IDENTIFIER = _xcode_product_bundle_id + PROVISIONING_PROFILE_SPECIFIER = _ios_provisioning_profile_info.name + } + + # If invoker has defined extra attributes, they override the defaults. + if (defined(invoker.xcode_extra_attributes)) { + forward_variables_from(invoker.xcode_extra_attributes, "*") + } + } + } + + if (defined(invoker.bundle_binary_target)) { + public_deps += [ invoker.bundle_binary_target ] + } + + if (defined(invoker.bundle_deps)) { + if (!defined(deps)) { + deps = [] + } + deps += invoker.bundle_deps + } + if (!defined(deps)) { + deps = [] + } + + code_signing_script = "//build/config/ios/codesign.py" + code_signing_sources = [ _bundle_binary_path ] + if (_enable_entitlements) { + if (defined(invoker.entitlements_target)) { + deps += [ invoker.entitlements_target ] + } + code_signing_sources += [ _entitlements_path ] + } + code_signing_outputs = [ "$bundle_executable_dir/$_output_name" ] + if (_enable_code_signing) { + code_signing_outputs += + [ "$bundle_contents_dir/_CodeSignature/CodeResources" ] + } + if (ios_code_signing_identity != "" && target_environment == "device" && + _enable_embedded_mobileprovision) { + code_signing_outputs += + [ "$bundle_contents_dir/embedded.mobileprovision" ] + } + if (_bundle_extension == ".framework") { + if (target_environment == "catalyst") { + code_signing_outputs += [ + "$bundle_root_dir/Versions/Current", + "$bundle_root_dir/$_output_name", + ] + + if (defined(invoker.has_public_headers) && invoker.has_public_headers) { + code_signing_outputs += [ + "$bundle_root_dir/Headers", + "$bundle_root_dir/Modules", + ] + } + } else { + not_needed(invoker, [ "has_public_headers" ]) + } + } + + if (defined(invoker.extra_system_frameworks)) { + foreach(_framework, invoker.extra_system_frameworks) { + code_signing_outputs += [ "$bundle_contents_dir/Frameworks/" + + get_path_info(_framework, "file") ] + } + } + + code_signing_args = [ + "code-sign-bundle", + "-t=" + ios_sdk_name, + "-i=" + ios_code_signing_identity, + "-b=" + rebase_path(_bundle_binary_path, root_build_dir), + ] + if (_enable_entitlements) { + code_signing_args += + [ "-e=" + rebase_path(_entitlements_path, root_build_dir) ] + } + if (!_enable_embedded_mobileprovision) { + code_signing_args += [ "--disable-embedded-mobileprovision" ] + } + code_signing_args += [ rebase_path(bundle_root_dir, root_build_dir) ] + if (!_enable_code_signing) { + code_signing_args += [ "--disable-code-signature" ] + } + if (defined(invoker.extra_system_frameworks)) { + # All framework in extra_system_frameworks are expected to be system + # framework and the path to be already system absolute so do not use + # rebase_path here unless using Goma RBE and system Xcode (as in that + # case the system framework are found via a symlink in root_build_dir). + foreach(_framework, invoker.extra_system_frameworks) { + if (use_system_xcode && use_goma) { + _framework_path = rebase_path(_framework, root_build_dir) + } else { + _framework_path = _framework + } + code_signing_args += [ "-F=$_framework_path" ] + } + } + if (defined(invoker.partial_info_plist)) { + _partial_info_plists = [ + invoker.primary_info_plist, + invoker.partial_info_plist, + ] + + _plist_compiler_path = "//build/apple/plist_util.py" + + code_signing_sources += _partial_info_plists + code_signing_sources += [ _plist_compiler_path ] + if (target_environment != "catalyst" || + _bundle_extension != ".framework") { + code_signing_outputs += [ "$bundle_contents_dir/Info.plist" ] + } else { + code_signing_outputs += [ "$bundle_resources_dir/Info.plist" ] + } + + code_signing_args += + [ "-P=" + rebase_path(_plist_compiler_path, root_build_dir) ] + foreach(_partial_info_plist, _partial_info_plists) { + code_signing_args += + [ "-p=" + rebase_path(_partial_info_plist, root_build_dir) ] + } + } + } +} + +# Generates Info.plist files for Mac apps and frameworks. +# +# Arguments +# +# info_plist: +# (optional) string, path to the Info.plist file that will be used for +# the bundle. +# +# info_plist_target: +# (optional) string, if the info_plist is generated from an action, +# rather than a regular source file, specify the target name in lieu +# of info_plist. The two arguments are mutually exclusive. +# +# executable_name: +# string, name of the generated target used for the product +# and executable name as specified in the output Info.plist. +# +# extra_substitutions: +# (optional) string array, 'key=value' pairs for extra fields which are +# specified in a source Info.plist template. +template("ios_info_plist") { + assert(defined(invoker.info_plist) != defined(invoker.info_plist_target), + "Only one of info_plist or info_plist_target may be specified in " + + target_name) + + if (defined(invoker.info_plist)) { + _info_plist = invoker.info_plist + } else { + _info_plist_target_output = get_target_outputs(invoker.info_plist_target) + _info_plist = _info_plist_target_output[0] + } + + apple_info_plist(target_name) { + format = "binary1" + extra_substitutions = [] + if (defined(invoker.extra_substitutions)) { + extra_substitutions = invoker.extra_substitutions + } + extra_substitutions += [ + "IOS_BUNDLE_ID_PREFIX=$ios_app_bundle_id_prefix", + "IOS_PLATFORM_BUILD=$ios_platform_build", + "IOS_PLATFORM_NAME=$ios_sdk_name", + "IOS_PLATFORM_VERSION=$ios_sdk_version", + "IOS_SDK_BUILD=$ios_sdk_build", + "IOS_SDK_NAME=$ios_sdk_name$ios_sdk_version", + "IOS_SUPPORTED_PLATFORM=$ios_sdk_platform", + "BUILD_MACHINE_OS_BUILD=$machine_os_build", + "IOS_DEPLOYMENT_TARGET=$ios_deployment_target", + "XCODE_BUILD=$xcode_build", + "XCODE_VERSION=$xcode_version", + ] + plist_templates = [ + "//build/config/ios/BuildInfo.plist", + _info_plist, + ] + if (defined(invoker.info_plist_target)) { + deps = [ invoker.info_plist_target ] + } + forward_variables_from(invoker, + [ + "executable_name", + "output_name", + "visibility", + "testonly", + ]) + } +} + +# Template to build an application bundle for iOS. +# +# This should be used instead of "executable" built-in target type on iOS. +# As the template forward the generation of the application executable to +# an "executable" target, all arguments supported by "executable" targets +# are also supported by this template. +# +# Arguments +# +# output_name: +# (optional) string, name of the generated application, if omitted, +# defaults to the target_name. +# +# extra_substitutions: +# (optional) list of string in "key=value" format, each value will +# be used as an additional variable substitution rule when generating +# the application Info.plist +# +# info_plist: +# (optional) string, path to the Info.plist file that will be used for +# the bundle. +# +# info_plist_target: +# (optional) string, if the info_plist is generated from an action, +# rather than a regular source file, specify the target name in lieu +# of info_plist. The two arguments are mutually exclusive. +# +# entitlements_path: +# (optional) path to the template to use to generate the application +# entitlements by performing variable substitutions, defaults to +# //build/config/ios/entitlements.plist. +# +# entitlements_target: +# (optional) label of the target generating the application +# entitlements (must generate a single file as output); cannot be +# defined if entitlements_path is set. +# +# product_type +# (optional) string, product type for the generated Xcode project, +# default to "com.apple.product-type.application". Should only be +# overriden when building application extension. +# +# enable_code_signing +# (optional) boolean, control whether code signing is enabled or not, +# default to ios_enable_code_signing if not defined. +# +# variants +# (optional) list of scopes, each scope needs to define the attributes +# "name" and "bundle_deps"; if defined and non-empty, then one bundle +# named $target_out_dir/$variant/$output_name will be created for each +# variant with the same binary but the correct bundle_deps, the bundle +# at $target_out_dir/$output_name will be a copy of the first variant. +# +# xcode_product_bundle_id: +# (optional) string, the bundle ID that will be added in the XCode +# attributes to enable some features when debugging (e.g. MetricKit). +# defaults to "$ios_app_bundle_id_prefix.$output_name". +# +# For more information, see "gn help executable". +template("ios_app_bundle") { + _output_name = target_name + _target_name = target_name + if (defined(invoker.output_name)) { + _output_name = invoker.output_name + } + + _primary_toolchain = current_toolchain + if (is_fat_secondary_toolchain) { + _primary_toolchain = primary_fat_toolchain_name + } + + assert( + !defined(invoker.bundle_extension), + "bundle_extension must not be set for ios_app_bundle template for $target_name") + + _xcode_product_bundle_id = "$ios_app_bundle_id_prefix.$_output_name" + if (defined(invoker.xcode_product_bundle_id)) { + _xcode_product_bundle_id = invoker.xcode_product_bundle_id + _xcode_product_bundle_id = + "$ios_app_bundle_id_prefix.$_xcode_product_bundle_id" + } else if (defined(invoker.bundle_id)) { + _xcode_product_bundle_id = invoker.bundle_id + } + + # Bundle ID should respect rfc1034 and replace _ with -. + _xcode_product_bundle_id = + string_replace("$_xcode_product_bundle_id", "_", "-") + + _arch_executable_source = _target_name + "_arch_executable_sources" + _arch_executable_target = _target_name + "_arch_executable" + _lipo_executable_target = _target_name + "_executable" + + if (defined(invoker.variants) && invoker.variants != []) { + _variants = [] + + foreach(_variant, invoker.variants) { + assert(defined(_variant.name) && _variant.name != "", + "name must be defined for all $target_name variants") + + assert(defined(_variant.bundle_deps), + "bundle_deps must be defined for all $target_name variants") + + _variants += [ + { + name = _variant.name + bundle_deps = _variant.bundle_deps + target_name = "${_target_name}_variants_${_variant.name}" + bundle_gen_dir = "$root_out_dir/variants/${_variant.name}" + }, + ] + } + } else { + # If no variants are passed to the template, use a fake variant with + # no name to avoid duplicating code. As no variant can have an empty + # name except this fake variant, it is possible to know if a variant + # is fake or not. + _variants = [ + { + name = "" + bundle_deps = [] + target_name = _target_name + bundle_gen_dir = root_out_dir + }, + ] + } + + _default_variant = _variants[0] + + source_set(_arch_executable_source) { + forward_variables_from(invoker, + "*", + [ + "bundle_deps", + "bundle_deps_filter", + "bundle_extension", + "enable_code_signing", + "entitlements_path", + "entitlements_target", + "extra_substitutions", + "extra_system_frameworks", + "info_plist", + "info_plist_target", + "output_name", + "product_type", + "visibility", + "xcode_extra_attributes", + ]) + + visibility = [ ":$_arch_executable_target" ] + } + + if (!is_fat_secondary_toolchain || target_environment == "simulator") { + _generate_entitlements_target = _target_name + "_gen_entitlements" + _generate_entitlements_output = + get_label_info(":$_generate_entitlements_target($_primary_toolchain)", + "target_out_dir") + "/$_output_name.xcent" + } + + _product_type = _ios_xcode_app_bundle_id + if (defined(invoker.product_type)) { + _product_type = invoker.product_type + } + + if (_product_type == _ios_xcode_app_bundle_id) { + _bundle_extension = ".app" + } else if (_product_type == _ios_xcode_appex_bundle_id) { + _bundle_extension = ".appex" + } else { + assert(false, "unknown product_type \"$product_type\" for $_target_name") + } + + _is_app_bundle = _product_type == _ios_xcode_app_bundle_id + + executable(_arch_executable_target) { + forward_variables_from(invoker, + "*", + [ + "bundle_deps", + "bundle_deps_filter", + "bundle_extension", + "enable_code_signing", + "entitlements_path", + "entitlements_target", + "extra_substitutions", + "extra_system_frameworks", + "info_plist", + "info_plist_target", + "output_name", + "product_type", + "sources", + "visibility", + "xcode_extra_attributes", + ]) + + visibility = [ ":$_lipo_executable_target($_primary_toolchain)" ] + if (is_fat_secondary_toolchain) { + visibility += [ ":$_target_name" ] + } + + if (!defined(deps)) { + deps = [] + } + deps += [ ":$_arch_executable_source" ] + + if (!defined(frameworks)) { + frameworks = [] + } + frameworks += [ "UIKit.framework" ] + + if (target_environment == "simulator") { + deps += [ ":$_generate_entitlements_target($_primary_toolchain)" ] + + if (!defined(inputs)) { + inputs = [] + } + inputs += [ _generate_entitlements_output ] + + if (!defined(ldflags)) { + ldflags = [] + } + ldflags += [ "-Wl,-sectcreate,__TEXT,__entitlements," + + rebase_path(_generate_entitlements_output, root_build_dir) ] + } + + output_name = _output_name + output_prefix_override = true + output_dir = "$target_out_dir/$target_cpu" + } + + if (is_fat_secondary_toolchain) { + # For fat builds, only the default toolchain will generate an application + # bundle. For the other toolchains, the template is only used for building + # the arch-specific binary, thus the default target is just a group(). + + group(_target_name) { + forward_variables_from(invoker, + [ + "visibility", + "testonly", + ]) + public_deps = [ ":$_arch_executable_target" ] + } + } else { + lipo_binary(_lipo_executable_target) { + forward_variables_from(invoker, + [ + "configs", + "testonly", + ]) + + visibility = [] + foreach(_variant, _variants) { + visibility += [ ":${_variant.target_name}" ] + } + + output_name = _output_name + arch_binary_target = ":$_arch_executable_target" + arch_binary_output = _output_name + } + + _generate_info_plist = target_name + "_generate_info_plist" + ios_info_plist(_generate_info_plist) { + forward_variables_from(invoker, + [ + "extra_substitutions", + "info_plist", + "info_plist_target", + ]) + + executable_name = _output_name + } + + if (!is_fat_secondary_toolchain) { + if (!defined(invoker.entitlements_target)) { + _entitlements_path = "//build/config/ios/entitlements.plist" + if (defined(invoker.entitlements_path)) { + _entitlements_path = invoker.entitlements_path + } + } else { + assert(!defined(invoker.entitlements_path), + "Cannot define both entitlements_path and entitlements_target" + + "for $_target_name") + + _entitlements_target_outputs = + get_target_outputs(invoker.entitlements_target) + _entitlements_path = _entitlements_target_outputs[0] + } + + action(_generate_entitlements_target) { + _gen_info_plist_outputs = get_target_outputs(":$_generate_info_plist") + _info_plist_path = _gen_info_plist_outputs[0] + + script = "//build/config/ios/codesign.py" + deps = [ ":$_generate_info_plist" ] + if (defined(invoker.entitlements_target)) { + deps += [ invoker.entitlements_target ] + } + sources = [ + _entitlements_path, + _info_plist_path, + ] + outputs = [ _generate_entitlements_output ] + + args = [ + "generate-entitlements", + "-e=" + rebase_path(_entitlements_path, root_build_dir), + "-p=" + rebase_path(_info_plist_path, root_build_dir), + ] + rebase_path(outputs, root_build_dir) + } + } + + # Only write PkgInfo for real application, not application extension. + if (_is_app_bundle) { + _create_pkg_info = target_name + "_pkg_info" + action(_create_pkg_info) { + forward_variables_from(invoker, [ "testonly" ]) + script = "//build/apple/write_pkg_info.py" + inputs = [ "//build/apple/plist_util.py" ] + sources = get_target_outputs(":$_generate_info_plist") + outputs = [ + # Cannot name the output PkgInfo as the name will not be unique if + # multiple ios_app_bundle are defined in the same BUILD.gn file. The + # file is renamed in the bundle_data outputs to the correct name. + "$target_gen_dir/$target_name", + ] + args = [ "--plist" ] + rebase_path(sources, root_build_dir) + + [ "--output" ] + rebase_path(outputs, root_build_dir) + deps = [ ":$_generate_info_plist" ] + } + + _bundle_data_pkg_info = target_name + "_bundle_data_pkg_info" + bundle_data(_bundle_data_pkg_info) { + forward_variables_from(invoker, [ "testonly" ]) + sources = get_target_outputs(":$_create_pkg_info") + outputs = [ "{{bundle_resources_dir}}/PkgInfo" ] + public_deps = [ ":$_create_pkg_info" ] + } + } + + foreach(_variant, _variants) { + create_signed_bundle(_variant.target_name) { + forward_variables_from(invoker, + [ + "bundle_deps", + "bundle_deps_filter", + "data_deps", + "deps", + "enable_code_signing", + "entitlements_path", + "entitlements_target", + "extra_system_frameworks", + "public_configs", + "public_deps", + "testonly", + "visibility", + "xcode_extra_attributes", + ]) + + output_name = _output_name + bundle_gen_dir = _variant.bundle_gen_dir + bundle_binary_target = ":$_lipo_executable_target" + bundle_binary_output = _output_name + bundle_extension = _bundle_extension + product_type = _product_type + xcode_product_bundle_id = _xcode_product_bundle_id + + _generate_info_plist_outputs = + get_target_outputs(":$_generate_info_plist") + primary_info_plist = _generate_info_plist_outputs[0] + partial_info_plist = + "$target_gen_dir/${_variant.target_name}_partial_info.plist" + + if (!defined(deps)) { + deps = [] + } + deps += [ ":$_generate_info_plist" ] + + if (!defined(bundle_deps)) { + bundle_deps = [] + } + if (_is_app_bundle) { + bundle_deps += [ ":$_bundle_data_pkg_info" ] + } + bundle_deps += _variant.bundle_deps + + if (target_environment == "simulator") { + if (!defined(data_deps)) { + data_deps = [] + } + data_deps += [ "//testing/iossim" ] + } + } + } + + if (_default_variant.name != "") { + _bundle_short_name = "$_output_name$_bundle_extension" + action(_target_name) { + forward_variables_from(invoker, [ "testonly" ]) + + script = "//build/config/ios/hardlink.py" + public_deps = [] + foreach(_variant, _variants) { + public_deps += [ ":${_variant.target_name}" ] + } + + sources = [ "${_default_variant.bundle_gen_dir}/$_bundle_short_name" ] + outputs = [ "$root_out_dir/$_bundle_short_name" ] + + args = rebase_path(sources, root_build_dir) + + rebase_path(outputs, root_build_dir) + } + } + } + + if (is_fat_secondary_toolchain) { + not_needed("*") + } +} + +set_defaults("ios_app_bundle") { + configs = default_executable_configs +} + +# Template to build an application extension bundle for iOS. +# +# This should be used instead of "executable" built-in target type on iOS. +# As the template forward the generation of the application executable to +# an "executable" target, all arguments supported by "executable" targets +# are also supported by this template. +# +# Arguments +# +# output_name: +# (optional) string, name of the generated application, if omitted, +# defaults to the target_name. +# +# extra_substitutions: +# (optional) list of string in "key=value" format, each value will +# be used as an additional variable substitution rule when generating +# the application Info.plist +# +# info_plist: +# (optional) string, path to the Info.plist file that will be used for +# the bundle. +# +# info_plist_target: +# (optional) string, if the info_plist is generated from an action, +# rather than a regular source file, specify the target name in lieu +# of info_plist. The two arguments are mutually exclusive. +# +# For more information, see "gn help executable". +template("ios_appex_bundle") { + ios_app_bundle(target_name) { + forward_variables_from(invoker, + "*", + [ + "bundle_extension", + "product_type", + ]) + product_type = _ios_xcode_appex_bundle_id + } +} + +set_defaults("ios_appex_bundle") { + configs = [ "//build/config/ios:ios_extension_executable_flags" ] +} + +# Template to compile .xib and .storyboard files. +# +# Arguments +# +# sources: +# list of string, sources to compile +# +# ibtool_flags: +# (optional) list of string, additional flags to pass to the ibtool +template("compile_ib_files") { + action_foreach(target_name) { + forward_variables_from(invoker, + [ + "testonly", + "visibility", + ]) + assert(defined(invoker.sources), + "sources must be specified for $target_name") + assert(defined(invoker.output_extension), + "output_extension must be specified for $target_name") + + ibtool_flags = [] + if (defined(invoker.ibtool_flags)) { + ibtool_flags = invoker.ibtool_flags + } + + _output_extension = invoker.output_extension + + script = "//build/config/ios/compile_ib_files.py" + sources = invoker.sources + outputs = [ + "$target_gen_dir/$target_name/{{source_name_part}}.$_output_extension", + ] + args = [ + "--input", + "{{source}}", + "--output", + rebase_path( + "$target_gen_dir/$target_name/{{source_name_part}}.$_output_extension", + root_build_dir), + ] + args += ibtool_flags + } +} + +# Compile a xib or storyboard file and add it to a bundle_data so that it is +# available at runtime in the bundle. +# +# Arguments +# +# source: +# string, path of the xib or storyboard to compile. +# +# Forwards all variables to the bundle_data target. +template("bundle_data_ib_file") { + assert(defined(invoker.source), "source needs to be defined for $target_name") + + _source_extension = get_path_info(invoker.source, "extension") + assert(_source_extension == "xib" || _source_extension == "storyboard", + "source must be a .xib or .storyboard for $target_name") + + _target_name = target_name + if (_source_extension == "xib") { + _compile_ib_file = target_name + "_compile_xib" + _output_extension = "nib" + } else { + _compile_ib_file = target_name + "_compile_storyboard" + _output_extension = "storyboardc" + } + + compile_ib_files(_compile_ib_file) { + sources = [ invoker.source ] + output_extension = _output_extension + visibility = [ ":$_target_name" ] + ibtool_flags = [ + "--minimum-deployment-target", + ios_deployment_target, + "--auto-activate-custom-fonts", + "--target-device", + "iphone", + "--target-device", + "ipad", + ] + } + + bundle_data(_target_name) { + forward_variables_from(invoker, "*", [ "source" ]) + + if (!defined(public_deps)) { + public_deps = [] + } + public_deps += [ ":$_compile_ib_file" ] + + sources = get_target_outputs(":$_compile_ib_file") + + outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ] + } +} + +# Compile a strings file and add it to a bundle_data so that it is available +# at runtime in the bundle. +# +# Arguments +# +# source: +# string, path of the strings file to compile. +# +# output: +# string, path of the compiled file in the final bundle. +# +# Forwards all variables to the bundle_data target. +template("bundle_data_strings") { + assert(defined(invoker.source), "source needs to be defined for $target_name") + assert(defined(invoker.output), "output needs to be defined for $target_name") + + _source_extension = get_path_info(invoker.source, "extension") + assert(_source_extension == "strings", + "source must be a .strings for $target_name") + + _target_name = target_name + _convert_target = target_name + "_compile_strings" + + convert_plist(_convert_target) { + visibility = [ ":$_target_name" ] + source = invoker.source + output = + "$target_gen_dir/$_target_name/" + get_path_info(invoker.source, "file") + format = "binary1" + } + + bundle_data(_target_name) { + forward_variables_from(invoker, + "*", + [ + "source", + "output", + ]) + + if (!defined(public_deps)) { + public_deps = [] + } + public_deps += [ ":$_convert_target" ] + + sources = get_target_outputs(":$_convert_target") + + outputs = [ invoker.output ] + } +} + +# Template to package a shared library into an iOS framework bundle. +# +# By default, the bundle target this template generates does not link the +# resulting framework into anything that depends on it. If a dependency wants +# a link-time (as well as build-time) dependency on the framework bundle, +# depend against "$target_name+link". If only the build-time dependency is +# required (e.g., for copying into another bundle), then use "$target_name". +# +# Arguments +# +# output_name: +# (optional) string, name of the generated framework without the +# .framework suffix. If omitted, defaults to target_name. +# +# public_headers: +# (optional) list of paths to header file that needs to be copied +# into the framework bundle Headers subdirectory. If omitted or +# empty then the Headers subdirectory is not created. +# +# sources +# (optional) list of files. Needs to be defined and non-empty if +# public_headers is defined and non-empty. +# +# enable_code_signing +# (optional) boolean, control whether code signing is enabled or not, +# default to ios_enable_code_signing if not defined. +# +# This template provides two targets for the resulting framework bundle. The +# link-time behavior varies depending on which of the two targets below is +# added as a dependency: +# - $target_name only adds a build-time dependency. Targets that depend on +# it will not link against the framework. +# - $target_name+link adds a build-time and link-time dependency. Targets +# that depend on it will link against the framework. +# +# The build-time-only dependency is used for when a target needs to use the +# framework either only for resources, or because the target loads it at run- +# time, via dlopen() or NSBundle. The link-time dependency will cause the +# dependee to have the framework loaded by dyld at launch. +# +# Example of build-time only dependency: +# +# framework_bundle("CoreTeleportation") { +# sources = [ ... ] +# } +# +# bundle_data("core_teleportation_bundle_data") { +# deps = [ ":CoreTeleportation" ] +# sources = [ "$root_out_dir/CoreTeleportation.framework" ] +# outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ] +# } +# +# app_bundle("GoatTeleporter") { +# sources = [ ... ] +# deps = [ +# ":core_teleportation_bundle_data", +# ] +# } +# +# The GoatTeleporter.app will not directly link against +# CoreTeleportation.framework, but it will be included in the bundle's +# Frameworks directory. +# +# Example of link-time dependency: +# +# framework_bundle("CoreTeleportation") { +# sources = [ ... ] +# ldflags = [ +# "-install_name", +# "@executable_path/../Frameworks/$target_name.framework" +# ] +# } +# +# bundle_data("core_teleportation_bundle_data") { +# deps = [ ":CoreTeleportation+link" ] +# sources = [ "$root_out_dir/CoreTeleportation.framework" ] +# outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ] +# } +# +# app_bundle("GoatTeleporter") { +# sources = [ ... ] +# deps = [ +# ":core_teleportation_bundle_data", +# ] +# } +# +# Note that the framework is still copied to the app's bundle, but dyld will +# load this library when the app is launched because it uses the "+link" +# target as a dependency. This also requires that the framework set its +# install_name so that dyld can locate it. +# +# See "gn help shared_library" for more information on arguments supported +# by shared library target. +template("ios_framework_bundle") { + _target_name = target_name + _output_name = target_name + if (defined(invoker.output_name)) { + _output_name = invoker.output_name + } + + _has_public_headers = + defined(invoker.public_headers) && invoker.public_headers != [] + + _primary_toolchain = current_toolchain + if (is_fat_secondary_toolchain) { + _primary_toolchain = primary_fat_toolchain_name + } + + # Public configs are not propagated across toolchain (see crbug.com/675224) + # so some configs have to be defined for both default_toolchain and all others + # toolchains when performing a fat build. Use "get_label_info" to construct + # the path since they need to be relative to the default_toolchain. + + _default_toolchain_root_out_dir = + get_label_info("$_target_name($_primary_toolchain)", "root_out_dir") + + _arch_shared_library_source = _target_name + "_arch_shared_library_sources" + _arch_shared_library_target = _target_name + "_arch_shared_library" + _lipo_shared_library_target = _target_name + "_shared_library" + _link_target_name = _target_name + "+link" + + if (_has_public_headers) { + _default_toolchain_target_gen_dir = + get_label_info("$_target_name($_primary_toolchain)", "target_gen_dir") + + _framework_headers_target = _target_name + "_framework_headers" + + _headers_map_config = _target_name + "_headers_map" + _header_map_filename = + "$_default_toolchain_target_gen_dir/$_output_name.headers.hmap" + config(_headers_map_config) { + visibility = [ + ":${_arch_shared_library_source}", + ":${_target_name}_signed_bundle", + ] + include_dirs = [ _header_map_filename ] + } + } + + _framework_headers_config = _target_name + "_framework_headers_config" + config(_framework_headers_config) { + framework_dirs = [ _default_toolchain_root_out_dir ] + } + + _framework_public_config = _target_name + "_public_config" + config(_framework_public_config) { + configs = [ ":$_framework_headers_config" ] + frameworks = [ "$_output_name.framework" ] + } + + source_set(_arch_shared_library_source) { + forward_variables_from(invoker, + "*", + [ + "bundle_deps", + "bundle_deps_filter", + "data_deps", + "enable_code_signing", + "extra_substitutions", + "info_plist", + "info_plist_target", + "output_name", + "public_configs", + "visibility", + ]) + + visibility = [ ":$_arch_shared_library_target" ] + + if (_has_public_headers) { + configs += [ ":$_headers_map_config" ] + + if (!defined(deps)) { + deps = [] + } + deps += [ ":$_framework_headers_target($_primary_toolchain)" ] + } + } + + shared_library(_arch_shared_library_target) { + forward_variables_from(invoker, + "*", + [ + "bundle_deps", + "bundle_deps_filter", + "data_deps", + "enable_code_signing", + "extra_substitutions", + "info_plist", + "info_plist_target", + "output_name", + "sources", + "public_configs", + "visibility", + ]) + + visibility = [ ":$_lipo_shared_library_target($_primary_toolchain)" ] + if (is_fat_secondary_toolchain) { + visibility += [ + ":${_target_name}", + ":${_target_name}_signed_bundle", + ] + } + + if (!defined(deps)) { + deps = [] + } + deps += [ ":$_arch_shared_library_source" ] + if (_has_public_headers) { + deps += [ ":$_framework_headers_target($_primary_toolchain)" ] + } + if (!defined(ldflags)) { + ldflags = [] + } + ldflags += + [ "-Wl,-install_name,@rpath/$_output_name.framework/$_output_name" ] + + output_extension = "" + output_name = _output_name + output_prefix_override = true + output_dir = "$target_out_dir/$target_cpu" + } + + if (is_fat_secondary_toolchain) { + # For fat builds, only the default toolchain will generate a framework + # bundle. For the other toolchains, the template is only used for building + # the arch-specific binary, thus the default target is just a group(). + + group(_target_name) { + forward_variables_from(invoker, + [ + "visibility", + "testonly", + ]) + public_deps = [ ":$_arch_shared_library_target" ] + } + + group(_link_target_name) { + forward_variables_from(invoker, + [ + "public_configs", + "visibility", + "testonly", + ]) + public_deps = [ ":$_link_target_name($_primary_toolchain)" ] + + if (_has_public_headers) { + if (!defined(public_configs)) { + public_configs = [] + } + public_configs += [ ":$_framework_headers_config" ] + } + if (!defined(all_dependent_configs)) { + all_dependent_configs = [] + } + all_dependent_configs += [ ":$_framework_public_config" ] + } + + group("$_target_name+bundle") { + forward_variables_from(invoker, [ "testonly" ]) + public_deps = [ ":$_target_name+bundle($_primary_toolchain)" ] + } + + not_needed(invoker, "*") + } else { + if (_has_public_headers) { + _public_headers = invoker.public_headers + + _framework_root_dir = "$root_out_dir/$_output_name.framework" + if (target_environment == "simulator" || target_environment == "device") { + _framework_contents_dir = _framework_root_dir + } else if (target_environment == "catalyst") { + _framework_contents_dir = "$_framework_root_dir/Versions/A" + } + + _compile_headers_map_target = _target_name + "_compile_headers_map" + action(_compile_headers_map_target) { + visibility = [ ":$_framework_headers_target" ] + forward_variables_from(invoker, + [ + "deps", + "public_deps", + "testonly", + ]) + script = "//build/config/ios/write_framework_hmap.py" + outputs = [ _header_map_filename ] + + # The header map generation only wants the list of headers, not all of + # sources, so filter any non-header source files from "sources". It is + # less error prone that having the developer duplicate the list of all + # headers in addition to "sources". + sources = [] + foreach(_source, invoker.sources) { + if (get_path_info(_source, "extension") == "h") { + sources += [ _source ] + } + } + + args = [ + rebase_path(_header_map_filename), + rebase_path(_framework_root_dir, root_build_dir), + ] + rebase_path(sources, root_build_dir) + } + + _create_module_map_target = _target_name + "_module_map" + action(_create_module_map_target) { + visibility = [ ":$_framework_headers_target" ] + script = "//build/config/ios/write_framework_modulemap.py" + outputs = [ "$_framework_contents_dir/Modules/module.modulemap" ] + args = [ + _output_name, + rebase_path("$_framework_contents_dir/Modules", root_build_dir), + ] + } + + _copy_public_headers_target = _target_name + "_copy_public_headers" + copy(_copy_public_headers_target) { + forward_variables_from(invoker, + [ + "testonly", + "deps", + ]) + visibility = [ ":$_framework_headers_target" ] + sources = _public_headers + outputs = [ "$_framework_contents_dir/Headers/{{source_file_part}}" ] + + # Do not use forward_variables_from for "public_deps" as + # we do not want to forward those dependencies. + if (defined(invoker.public_deps)) { + if (!defined(deps)) { + deps = [] + } + deps += invoker.public_deps + } + } + + group(_framework_headers_target) { + forward_variables_from(invoker, [ "testonly" ]) + deps = [ + ":$_compile_headers_map_target", + ":$_create_module_map_target", + ] + public_deps = [ ":$_copy_public_headers_target" ] + } + } + + lipo_binary(_lipo_shared_library_target) { + forward_variables_from(invoker, + [ + "configs", + "testonly", + ]) + + visibility = [ ":${_target_name}_signed_bundle" ] + output_name = _output_name + arch_binary_target = ":$_arch_shared_library_target" + arch_binary_output = _output_name + } + + _info_plist_target = _target_name + "_info_plist" + _info_plist_bundle = _target_name + "_info_plist_bundle" + ios_info_plist(_info_plist_target) { + visibility = [ ":$_info_plist_bundle" ] + executable_name = _output_name + forward_variables_from(invoker, + [ + "extra_substitutions", + "info_plist", + "info_plist_target", + ]) + } + + bundle_data(_info_plist_bundle) { + visibility = [ ":${_target_name}_signed_bundle" ] + forward_variables_from(invoker, [ "testonly" ]) + sources = get_target_outputs(":$_info_plist_target") + public_deps = [ ":$_info_plist_target" ] + + if (target_environment != "catalyst") { + outputs = [ "{{bundle_contents_dir}}/Info.plist" ] + } else { + outputs = [ "{{bundle_resources_dir}}/Info.plist" ] + } + } + + create_signed_bundle(_target_name + "_signed_bundle") { + forward_variables_from(invoker, + [ + "bundle_deps", + "bundle_deps_filter", + "data_deps", + "deps", + "enable_code_signing", + "public_configs", + "public_deps", + "testonly", + "visibility", + ]) + + product_type = "com.apple.product-type.framework" + bundle_extension = ".framework" + + output_name = _output_name + bundle_binary_target = ":$_lipo_shared_library_target" + bundle_binary_output = _output_name + + has_public_headers = _has_public_headers + + # Framework do not have entitlements nor mobileprovision because they use + # the one from the bundle using them (.app or .appex) as they are just + # dynamic library with shared code. + disable_entitlements = true + disable_embedded_mobileprovision = true + + if (!defined(deps)) { + deps = [] + } + deps += [ ":$_info_plist_bundle" ] + } + + group(_target_name) { + forward_variables_from(invoker, + [ + "public_configs", + "public_deps", + "testonly", + "visibility", + ]) + if (!defined(public_deps)) { + public_deps = [] + } + public_deps += [ ":${_target_name}_signed_bundle" ] + + if (_has_public_headers) { + if (!defined(public_configs)) { + public_configs = [] + } + public_configs += [ ":$_framework_headers_config" ] + } + } + + group(_link_target_name) { + forward_variables_from(invoker, + [ + "public_configs", + "public_deps", + "testonly", + "visibility", + ]) + if (!defined(public_deps)) { + public_deps = [] + } + public_deps += [ ":$_target_name" ] + + if (!defined(all_dependent_configs)) { + all_dependent_configs = [] + } + all_dependent_configs += [ ":$_framework_public_config" ] + } + + bundle_data(_target_name + "+bundle") { + forward_variables_from(invoker, + [ + "testonly", + "visibility", + ]) + public_deps = [ ":$_target_name" ] + sources = [ "$root_out_dir/$_output_name.framework" ] + outputs = [ "{{bundle_contents_dir}}/Frameworks/$_output_name.framework" ] + } + } +} + +set_defaults("ios_framework_bundle") { + configs = default_shared_library_configs +} + +# Template to build a xctest bundle that contains a loadable module for iOS. +# +# Arguments +# +# deps: +# list of labels to depends on, these values are used to create the +# loadable module. +# +# product_type +# string, product type for the generated Xcode project, use +# "com.apple.product-type.bundle.unit-test" for unit test and +# "com.apple.product-type.bundle.ui-testing" for UI testing. +# +# host_target: +# string, name of the target that depends on the generated bundle, this +# value is used to restrict visibilities. +# +# xcode_test_application_name: +# string, name of the test application for Xcode unit or ui test target. +# +# output_name +# (optional) string, name of the generated application, if omitted, +# defaults to the target_name. +# +# This template defines two targets, one named "${target_name}" is the xctest +# bundle, and the other named "${target_name}_bundle" is a bundle_data that +# wraps the xctest bundle and that only the "${host_target}" can depend on. +# +template("ios_xctest_bundle") { + assert(defined(invoker.deps), "deps must be defined for $target_name") + assert(defined(invoker.product_type), + "product_type must be defined for $target_name") + assert(invoker.product_type == _ios_xcode_xctest_bundle_id || + invoker.product_type == _ios_xcode_xcuitest_bundle_id, + "product_type defined for $target_name is invalid.") + assert(defined(invoker.host_target), + "host_target must be defined for $target_name") + assert(defined(invoker.xcode_test_application_name), + "xcode_test_application_name must be defined for $target_name") + + # Silence "assignment had no effect" error for non-default toolchains as + # following variables are only used in the expansion of the template for the + # default toolchain. + if (is_fat_secondary_toolchain) { + not_needed(invoker, "*") + } + + _target_name = target_name + _output_name = target_name + + if (defined(invoker.output_name)) { + _output_name = invoker.output_name + } + + _arch_loadable_module_source = _target_name + "_arch_loadable_module_source" + _arch_loadable_module_target = _target_name + "_arch_loadable_module" + _lipo_loadable_module_target = _target_name + "_loadable_module" + + _primary_toolchain = current_toolchain + if (is_fat_secondary_toolchain) { + _primary_toolchain = primary_fat_toolchain_name + } + + source_set(_arch_loadable_module_source) { + forward_variables_from(invoker, [ "deps" ]) + + testonly = true + visibility = [ ":$_arch_loadable_module_target" ] + } + + loadable_module(_arch_loadable_module_target) { + testonly = true + visibility = [ ":$_lipo_loadable_module_target($_primary_toolchain)" ] + if (is_fat_secondary_toolchain) { + visibility += [ ":$_target_name" ] + } + + deps = [ ":$_arch_loadable_module_source" ] + configs += [ "//build/config/ios:xctest_config" ] + + output_dir = "$target_out_dir/$target_cpu" + output_name = _output_name + output_prefix_override = true + output_extension = "" + } + + if (is_fat_secondary_toolchain) { + # For fat builds, only the default toolchain will generate a test bundle. + # For the other toolchains, the template is only used for building the + # arch-specific binary, thus the default target is just a group(). + group(_target_name) { + forward_variables_from(invoker, [ "visibility" ]) + testonly = true + + public_deps = [ ":$_arch_loadable_module_target" ] + } + + not_needed(invoker, "*") + } else { + _info_plist_target = _target_name + "_info_plist" + _info_plist_bundle = _target_name + "_info_plist_bundle" + + ios_info_plist(_info_plist_target) { + testonly = true + visibility = [ ":$_info_plist_bundle" ] + + info_plist = "//build/config/ios/Module-Info.plist" + executable_name = _output_name + + if (defined(invoker.xctest_bundle_principal_class)) { + _principal_class = invoker.xctest_bundle_principal_class + } else { + # Fall back to a reasonable default value. + _principal_class = "NSObject" + } + extra_substitutions = [ + "XCTEST_BUNDLE_PRINCIPAL_CLASS=${_principal_class}", + "MODULE_BUNDLE_ID=gtest.$_output_name", + ] + } + + bundle_data(_info_plist_bundle) { + testonly = true + visibility = [ ":$_target_name" ] + + public_deps = [ ":$_info_plist_target" ] + + sources = get_target_outputs(":$_info_plist_target") + outputs = [ "{{bundle_contents_dir}}/Info.plist" ] + } + + lipo_binary(_lipo_loadable_module_target) { + forward_variables_from(invoker, [ "configs" ]) + + testonly = true + visibility = [ ":$_target_name" ] + + output_name = _output_name + arch_binary_target = ":$_arch_loadable_module_target" + arch_binary_output = _output_name + } + + _xctest_bundle = _target_name + "_bundle" + create_signed_bundle(_target_name) { + forward_variables_from(invoker, + [ + "bundle_id", + "data_deps", + "enable_code_signing", + "product_type", + "xcode_test_application_name", + ]) + + testonly = true + visibility = [ ":$_xctest_bundle" ] + + bundle_extension = ".xctest" + + output_name = _output_name + bundle_binary_target = ":$_lipo_loadable_module_target" + bundle_binary_output = _output_name + + if (ios_set_attributes_for_xcode_project_generation) { + _xcode_product_bundle_id = + "$ios_app_bundle_id_prefix.gtest.$_output_name" + + _ios_provisioning_profile_info = + exec_script("//build/config/ios/codesign.py", + [ + "find-provisioning-profile", + "-b=" + _xcode_product_bundle_id, + ], + "json") + + xcode_extra_attributes = { + IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target + CODE_SIGN_IDENTITY = "iPhone Developer" + DEVELOPMENT_TEAM = _ios_provisioning_profile_info.team_identifier + PRODUCT_BUNDLE_IDENTIFIER = _xcode_product_bundle_id + PROVISIONING_PROFILE_SPECIFIER = _ios_provisioning_profile_info.name + + # For XCUITest, Xcode requires specifying the host application name + # via the TEST_TARGET_NAME attribute. + if (invoker.product_type == _ios_xcode_xcuitest_bundle_id) { + TEST_TARGET_NAME = invoker.xcode_test_application_name + } + + # For XCTest, Xcode requires specifying the host application path via + # both BUNDLE_LOADER and TEST_HOST attributes. + if (invoker.product_type == _ios_xcode_xctest_bundle_id) { + _xcode_app_name = invoker.xcode_test_application_name + if (defined(invoker.xcode_test_application_output_name)) { + _xcode_app_name = invoker.xcode_test_application_output_name + } + + BUNDLE_LOADER = "\$(TEST_HOST)" + TEST_HOST = "\$(BUILT_PRODUCTS_DIR)/" + + "${_xcode_app_name}.app/${_xcode_app_name}" + } + } + } else { + not_needed(invoker, + [ + "xcode_test_application_name", + "xcode_test_application_output_name", + ]) + } + + deps = [ ":$_info_plist_bundle" ] + } + + bundle_data(_xctest_bundle) { + forward_variables_from(invoker, [ "host_target" ]) + + testonly = true + visibility = [ ":$host_target" ] + + public_deps = [ ":$_target_name" ] + sources = [ "$root_out_dir/$_output_name.xctest" ] + outputs = [ "{{bundle_contents_dir}}/PlugIns/$_output_name.xctest" ] + } + } +} + +set_defaults("ios_xctest_bundle") { + configs = default_shared_library_configs +} + +# For Chrome on iOS we want to run XCTests for all our build configurations +# (Debug, Release, ...). In addition, the symbols visibility is configured to +# private by default. To simplify testing with those constraints, our tests are +# compiled in the TEST_HOST target instead of the .xctest bundle. +template("ios_xctest_test") { + _target_name = target_name + _output_name = target_name + if (defined(invoker.output_name)) { + _output_name = invoker.output_name + } + + _xctest_target = _target_name + "_module" + _xctest_output = _output_name + "_module" + + _host_target = _target_name + _host_output = _output_name + + # Allow invokers to specify their own target for the xctest module, but + # fall back to a default (empty) module otherwise. + if (defined(invoker.xctest_module_target)) { + _xctest_module_target = invoker.xctest_module_target + } else { + _xctest_module_target_name = _xctest_target + "shell_source" + _xctest_module_target = ":$_xctest_module_target_name" + source_set(_xctest_module_target_name) { + sources = [ "//build/config/ios/xctest_shell.mm" ] + + configs += [ "//build/config/ios:xctest_config" ] + } + } + + ios_xctest_bundle(_xctest_target) { + forward_variables_from(invoker, [ "data_deps" ]) + output_name = _xctest_output + product_type = _ios_xcode_xctest_bundle_id + host_target = _host_target + + # TODO(crbug.com/1056328) The change in output name results in a mismatch + # between this value and the ios_app_bundle target name. To mitigate, this + # has been modified to _host_target. output_name is set to _host_output + # to mitigate the naming. + xcode_test_application_name = _host_target + xcode_test_application_output_name = _host_output + + deps = [ _xctest_module_target ] + } + + ios_app_bundle(_host_target) { + forward_variables_from(invoker, "*", [ "testonly" ]) + + testonly = true + output_name = _host_output + configs += [ "//build/config/ios:xctest_config" ] + + if (!defined(invoker.info_plist) && !defined(invoker.info_plist_target)) { + info_plist = "//build/config/ios/Host-Info.plist" + } + + # Xcode needs the following frameworks installed in the application (and + # signed) for the XCTest to run, so install them using + # extra_system_frameworks. + extra_system_frameworks = [ + "$ios_sdk_platform_path/Developer/Library/Frameworks/XCTest.framework", + "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework", + "$ios_sdk_platform_path/Developer/usr/lib/libXCTestBundleInject.dylib", + ] + + # Xcode 13 now depends on XCTestCore. To keep things future proof, copy over + # everything that Xcode copies. + if (xcode_version_int >= 1300) { + extra_system_frameworks += [ + "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestCore.framework", + "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUIAutomation.framework", + "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUnit.framework", + "$ios_sdk_platform_path/Developer/usr/lib/libXCTestSwiftSupport.dylib", + ] + } + + _xctest_bundle = _xctest_target + "_bundle" + if (!is_fat_secondary_toolchain) { + if (!defined(bundle_deps)) { + bundle_deps = [] + } + bundle_deps += [ ":$_xctest_bundle" ] + } + } +} + +set_defaults("ios_xctest_test") { + configs = default_executable_configs +} + +# Template to build a xcuitest test runner bundle. +# +# Xcode requires a test runner application with a copy of the XCTest dynamic +# library bundle in it for the XCUITest to run. The test runner bundle is created +# by copying the system bundle XCTRunner.app from Xcode SDK with the plist file +# being properly tweaked, and a xctest and it needs to be code signed in order +# to run on devices. +# +# Arguments +# +# xctest_bundle +# string, name of the dependent xctest bundle target. +# +# output_name +# (optional) string, name of the generated application, if omitted, +# defaults to the target_name. +# +template("ios_xcuitest_test_runner_bundle") { + assert(defined(invoker.xctest_bundle), + "xctest_bundle must be defined for $target_name") + + _target_name = target_name + _output_name = target_name + if (defined(invoker.output_name)) { + _output_name = invoker.output_name + } + + _xctrunner_path = + "$ios_sdk_platform_path/Developer/Library/Xcode/Agents/XCTRunner.app" + + _info_plist_merge_plist = _target_name + "_info_plist_merge_plist" + _info_plist_target = _target_name + "_info_plist" + _info_plist_bundle = _target_name + "_info_plist_bundle" + + action(_info_plist_merge_plist) { + testonly = true + script = "//build/apple/plist_util.py" + + sources = [ + "$_xctrunner_path/Info.plist", + + # NOTE: The XCTRunnerAddition+Info.plist must come after the Info.plist + # because it overrides the values under "CFBundleIdentifier" and + # "CFBundleName". + "//build/config/ios/resources/XCTRunnerAddition+Info.plist", + ] + + _output_name = "$target_gen_dir/${_target_name}_merged.plist" + outputs = [ _output_name ] + args = [ + "merge", + "-f=xml1", + "-x=$xcode_version", + "-o=" + rebase_path(_output_name, root_build_dir), + ] + rebase_path(sources, root_build_dir) + + if (use_system_xcode && use_goma) { + deps = [ "//build/config/ios:copy_xctrunner_app" ] + } + } + + ios_info_plist(_info_plist_target) { + testonly = true + visibility = [ ":$_info_plist_bundle" ] + + executable_name = _output_name + info_plist_target = ":$_info_plist_merge_plist" + } + + bundle_data(_info_plist_bundle) { + testonly = true + visibility = [ ":$_target_name" ] + + public_deps = [ ":$_info_plist_target" ] + + sources = get_target_outputs(":$_info_plist_target") + outputs = [ "{{bundle_contents_dir}}/Info.plist" ] + } + + _pkginfo_bundle = _target_name + "_pkginfo_bundle" + bundle_data(_pkginfo_bundle) { + testonly = true + visibility = [ ":$_target_name" ] + + sources = [ "$_xctrunner_path/PkgInfo" ] + + outputs = [ "{{bundle_contents_dir}}/PkgInfo" ] + + if (use_system_xcode && use_goma) { + public_deps = [ "//build/config/ios:copy_xctrunner_app" ] + } + } + + _xctest_bundle = invoker.xctest_bundle + create_signed_bundle(_target_name) { + testonly = true + + bundle_binary_target = "//build/config/ios:xctest_runner_without_arm64e" + bundle_binary_output = "XCTRunner" + bundle_extension = ".app" + product_type = _ios_xcode_app_bundle_id + + output_name = _output_name + + # Xcode needs the following frameworks installed in the application + # (and signed) for the XCUITest to run, so install them using + # extra_system_frameworks. + extra_system_frameworks = [ + "$ios_sdk_platform_path/Developer/Library/Frameworks/XCTest.framework", + "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework", + ] + + # Xcode 13 now depends on XCTestCore. To keep things future proof, copy over + # everything that Xcode copies. + if (xcode_version_int >= 1300) { + extra_system_frameworks += [ + "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestCore.framework", + "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUIAutomation.framework", + "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUnit.framework", + "$ios_sdk_platform_path/Developer/usr/lib/libXCTestSwiftSupport.dylib", + ] + } + + bundle_deps = [] + if (defined(invoker.bundle_deps)) { + bundle_deps += invoker.bundle_deps + } + bundle_deps += [ + ":$_info_plist_bundle", + ":$_pkginfo_bundle", + ":$_xctest_bundle", + ] + } +} + +# Template to build a XCUITest that consists of two parts: the test runner +# application bundle and the xctest dynamic library. +# +# Arguments +# +# deps: +# list of labels to depends on, these values are used to create the +# xctest dynamic library. +# +# xcode_test_application_name: +# string, name of the test application for the ui test target. +# +# This template defines two targets, one named "${target_name}_module" is the +# xctest dynamic library, and the other named "${target_name}_runner" is the +# test runner application bundle. +# +template("ios_xcuitest_test") { + assert(defined(invoker.deps), "deps must be defined for $target_name") + assert(defined(invoker.xcode_test_application_name), + "xcode_test_application_name must be defined for $target_name") + + _xcuitest_target = target_name + if (defined(invoker.output_name)) { + _xcuitest_target = invoker.output_name + } + + _xcuitest_runner_target = _xcuitest_target + "_runner" + _xcuitest_module_target = _xcuitest_target + "_module" + + group(target_name) { + testonly = true + + deps = [ ":$_xcuitest_runner_target" ] + } + + _xcuitest_module_output = _xcuitest_target + ios_xctest_bundle(_xcuitest_module_target) { + forward_variables_from(invoker, + [ + "xcode_test_application_name", + "xctest_bundle_principal_class", + "data_deps", + ]) + + product_type = _ios_xcode_xcuitest_bundle_id + host_target = _xcuitest_runner_target + output_name = _xcuitest_module_output + + deps = invoker.deps + } + + _xcuitest_runner_output = _xcuitest_target + "-Runner" + ios_xcuitest_test_runner_bundle(_xcuitest_runner_target) { + output_name = _xcuitest_runner_output + xctest_bundle = _xcuitest_module_target + "_bundle" + forward_variables_from(invoker, [ "bundle_deps" ]) + } +} + +set_defaults("ios_xcuitest_test") { + configs = default_executable_configs +} diff --git a/third_party/libwebrtc/build/config/ios/strip_arm64e.py b/third_party/libwebrtc/build/config/ios/strip_arm64e.py new file mode 100644 index 0000000000..f21baf423d --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/strip_arm64e.py @@ -0,0 +1,70 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +"""Strip arm64e architecture from a binary if present.""" + +import argparse +import os +import shutil +import subprocess +import sys + + +def check_output(command): + """Returns the output from |command| or propagates error, quitting script.""" + process = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + outs, errs = process.communicate() + if process.returncode: + sys.stderr.write('error: command failed with retcode %d: %s\n\n' % + (process.returncode, ' '.join(map(repr, command)))) + sys.stderr.write(errs.decode('UTF-8', errors='ignore')) + sys.exit(process.returncode) + return outs.decode('UTF-8') + + +def check_call(command): + """Invokes |command| or propagates error.""" + check_output(command) + + +def parse_args(args): + """Parses the command-line.""" + parser = argparse.ArgumentParser() + parser.add_argument('--input', required=True, help='Path to input binary') + parser.add_argument('--output', required=True, help='Path to output binary') + parser.add_argument('--xcode-version', required=True, help='Version of Xcode') + return parser.parse_args(args) + + +def get_archs(path): + """Extracts the architectures present in binary at |path|.""" + outputs = check_output(["xcrun", "lipo", "-info", os.path.abspath(path)]) + return outputs.split(': ')[-1].split() + + +def main(args): + parsed = parse_args(args) + + outdir = os.path.dirname(parsed.output) + if not os.path.isdir(outdir): + os.makedirs(outdir) + + if os.path.exists(parsed.output): + os.unlink(parsed.output) + + # As "lipo" fails with an error if asked to remove an architecture that is + # not included, only use it if "arm64e" is present in the binary. Otherwise + # simply copy the file. + if 'arm64e' in get_archs(parsed.input): + check_output([ + "xcrun", "lipo", "-remove", "arm64e", "-output", + os.path.abspath(parsed.output), + os.path.abspath(parsed.input) + ]) + else: + shutil.copy(parsed.input, parsed.output) + + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/third_party/libwebrtc/build/config/ios/write_framework_hmap.py b/third_party/libwebrtc/build/config/ios/write_framework_hmap.py new file mode 100644 index 0000000000..ac467ee92a --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/write_framework_hmap.py @@ -0,0 +1,103 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +from __future__ import print_function + +import os +import struct +import sys + +def Main(args): + if len(args) < 4: + print( + "Usage: %s output.hmap Foo.framework header1.h..." % args[0], + file=sys.stderr) + return 1 + + (out, framework, all_headers) = args[1], args[2], args[3:] + + framework_name = os.path.basename(framework).split('.')[0] + all_headers = map(os.path.abspath, all_headers) + filelist = {} + for header in all_headers: + filename = os.path.basename(header) + filelist[filename] = header + filelist[os.path.join(framework_name, filename)] = header + WriteHmap(out, filelist) + return 0 + + +def NextGreaterPowerOf2(x): + return 2**(x).bit_length() + + +def WriteHmap(output_name, filelist): + """Generates a header map based on |filelist|. + + Per Mark Mentovai: + A header map is structured essentially as a hash table, keyed by names used + in #includes, and providing pathnames to the actual files. + + The implementation below and the comment above comes from inspecting: + http://www.opensource.apple.com/source/distcc/distcc-2503/distcc_dist/include_server/headermap.py?txt + while also looking at the implementation in clang in: + https://llvm.org/svn/llvm-project/cfe/trunk/lib/Lex/HeaderMap.cpp + """ + magic = 1751998832 + version = 1 + _reserved = 0 + count = len(filelist) + capacity = NextGreaterPowerOf2(count) + strings_offset = 24 + (12 * capacity) + max_value_length = len(max(filelist.values(), key=lambda v: len(v))) + + out = open(output_name, 'wb') + out.write(struct.pack('<LHHLLLL', magic, version, _reserved, strings_offset, + count, capacity, max_value_length)) + + # Create empty hashmap buckets. + buckets = [None] * capacity + for file, path in filelist.items(): + key = 0 + for c in file: + key += ord(c.lower()) * 13 + + # Fill next empty bucket. + while buckets[key & capacity - 1] is not None: + key = key + 1 + buckets[key & capacity - 1] = (file, path) + + next_offset = 1 + for bucket in buckets: + if bucket is None: + out.write(struct.pack('<LLL', 0, 0, 0)) + else: + (file, path) = bucket + key_offset = next_offset + prefix_offset = key_offset + len(file) + 1 + suffix_offset = prefix_offset + len(os.path.dirname(path) + os.sep) + 1 + next_offset = suffix_offset + len(os.path.basename(path)) + 1 + out.write(struct.pack('<LLL', key_offset, prefix_offset, suffix_offset)) + + # Pad byte since next offset starts at 1. + out.write(struct.pack('<x')) + + for bucket in buckets: + if bucket is not None: + (file, path) = bucket + base = os.path.dirname(path) + os.sep + path = os.path.basename(path) + file = file.encode('UTF-8') + base = base.encode('UTF-8') + path = path.encode('UTF-8') + out.write(struct.pack('<%ds' % len(file), file)) + out.write(struct.pack('<s', b'\0')) + out.write(struct.pack('<%ds' % len(base), base)) + out.write(struct.pack('<s', b'\0')) + out.write(struct.pack('<%ds' % len(path), path)) + out.write(struct.pack('<s', b'\0')) + + +if __name__ == '__main__': + sys.exit(Main(sys.argv)) diff --git a/third_party/libwebrtc/build/config/ios/write_framework_modulemap.py b/third_party/libwebrtc/build/config/ios/write_framework_modulemap.py new file mode 100644 index 0000000000..dcc88a8ea6 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/write_framework_modulemap.py @@ -0,0 +1,28 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import os +import sys + +MODULE_MAP_TEMPLATE = '''\ +framework module %(framework_name)s { + umbrella header "%(framework_name)s.h" + + export * + module * { export * } +} +''' + + +def Main(framework_name, modules_dir): + # Find the name of the binary based on the part before the ".framework". + if not os.path.isdir(modules_dir): + os.makedirs(modules_dir) + + with open(os.path.join(modules_dir, 'module.modulemap'), 'w') as module_file: + module_file.write(MODULE_MAP_TEMPLATE % {'framework_name': framework_name}) + + +if __name__ == '__main__': + Main(*sys.argv[1:]) diff --git a/third_party/libwebrtc/build/config/ios/xctest_shell.mm b/third_party/libwebrtc/build/config/ios/xctest_shell.mm new file mode 100644 index 0000000000..dcf5bad5e7 --- /dev/null +++ b/third_party/libwebrtc/build/config/ios/xctest_shell.mm @@ -0,0 +1,19 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import <UIKit/UIKit.h> +#import <XCTest/XCTest.h> + +// For Chrome on iOS we want to run EarlGrey tests (that are XCTests) for all +// our build configurations (Debug, Release, ...). In addition, the symbols +// visibility is configured to private by default. To simplify testing with +// those constraints, our tests are compiled in the TEST_HOST target instead +// of the .xctest bundle that all link against this single test (just there to +// ensure that the bundle is not empty). + +@interface XCTestShellEmptyClass : NSObject +@end + +@implementation XCTestShellEmptyClass +@end |