blob: c6d40925c4c97e2c8c1649941e88c54b3ffcf4dc [file] [log] [blame]
# Copyright 2015 The Chromium Authors
# 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/compiler/compiler.gni")
import("//build/config/ios/ios_sdk.gni")
import("//build/toolchain/goma.gni")
import("//build/toolchain/rbe.gni")
import("//build/toolchain/toolchain.gni")
import("//build_overrides/build.gni")
# 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"
# 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
}
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 = []
}
_bundle_identifier = ""
if (defined(invoker.xcode_product_bundle_id)) {
_bundle_identifier = invoker.xcode_product_bundle_id
assert(_bundle_identifier == string_replace(_bundle_identifier, "_", "-"),
"$target_name: bundle_identifier does not respect rfc1034: " +
_bundle_identifier)
}
xcode_extra_attributes = {
IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target
PRODUCT_BUNDLE_IDENTIFIER = _bundle_identifier
CODE_SIGNING_REQUIRED = "NO"
CODE_SIGNING_ALLOWED = "NO"
CODE_SIGN_IDENTITY = ""
DONT_GENERATE_INFOPLIST_FILE = "YES"
# 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),
]
foreach(mobileprovision, ios_mobileprovision_files) {
code_signing_args +=
[ "-m=" + rebase_path(mobileprovision, root_build_dir) ]
}
code_signing_sources += ios_mobileprovision_files
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 || use_remoteexec)) {
_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 = [
"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",
]
if (defined(invoker.extra_substitutions)) {
extra_substitutions += invoker.extra_substitutions
}
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.
#
# bundle_identifier:
# (optional) string, value of CFBundleIdentifier in the application
# Info.plist, defaults to "$ios_app_bundle_id_prefix.$output_name"
# if omitted. Will be used to set BUNDLE_IDENTIFIER when generating
# the application Info.plist
#
# 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
}
assert(
!defined(invoker.bundle_extension),
"bundle_extension must not be set for ios_app_bundle template for $target_name")
if (defined(invoker.bundle_identifier)) {
_bundle_identifier = invoker.bundle_identifier
assert(_bundle_identifier == string_replace(_bundle_identifier, "_", "-"),
"$target_name: bundle_identifier does not respect rfc1034: " +
_bundle_identifier)
} else {
# Bundle identifier should respect rfc1034, so replace "_" with "-".
_bundle_identifier =
"$ios_app_bundle_id_prefix." + string_replace(_output_name, "_", "-")
}
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]
_executable_target = _target_name + "_executable"
_generate_entitlements_target = _target_name + "_gen_entitlements"
_generate_entitlements_output =
get_label_info(":$_generate_entitlements_target", "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(_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",
"visibility",
"xcode_extra_attributes",
])
visibility = []
foreach(_variant, _variants) {
visibility += [ ":${_variant.target_name}" ]
}
if (!defined(frameworks)) {
frameworks = []
}
frameworks += [ "UIKit.framework" ]
if (target_environment == "simulator") {
if (!defined(deps)) {
deps = []
}
deps += [ ":$_generate_entitlements_target" ]
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
}
_generate_info_plist = target_name + "_generate_info_plist"
ios_info_plist(_generate_info_plist) {
forward_variables_from(invoker,
[
"info_plist",
"info_plist_target",
])
executable_name = _output_name
extra_substitutions = [ "BUNDLE_IDENTIFIER=$_bundle_identifier" ]
if (defined(invoker.extra_substitutions)) {
extra_substitutions += invoker.extra_substitutions
}
}
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,
]
sources += ios_mobileprovision_files
outputs = [ _generate_entitlements_output ]
args = [
"generate-entitlements",
"-e=" + rebase_path(_entitlements_path, root_build_dir),
"-p=" + rebase_path(_info_plist_path, root_build_dir),
]
foreach(mobileprovision, ios_mobileprovision_files) {
args += [ "-m=" + rebase_path(mobileprovision, root_build_dir) ]
}
args += 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 = ":$_executable_target"
bundle_binary_output = _output_name
bundle_extension = _bundle_extension
product_type = _product_type
xcode_product_bundle_id = _bundle_identifier
_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 = []
}
if (build_with_chromium) {
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)
}
}
}
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 != []
_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", "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 = [
":${_shared_library_target}",
":${_target_name}_signed_bundle",
]
include_dirs = [ _header_map_filename ]
}
}
_framework_headers_config = _target_name + "_framework_headers_config"
config(_framework_headers_config) {
framework_dirs = [ root_out_dir ]
}
_framework_public_config = _target_name + "_public_config"
config(_framework_public_config) {
configs = [ ":$_framework_headers_config" ]
frameworks = [ "$_output_name.framework" ]
}
shared_library(_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",
"public_configs",
"visibility",
])
visibility = [ ":${_target_name}_signed_bundle" ]
if (!defined(ldflags)) {
ldflags = []
}
ldflags +=
[ "-Wl,-install_name,@rpath/$_output_name.framework/$_output_name" ]
if (_has_public_headers) {
configs += [ ":$_headers_map_config" ]
if (!defined(deps)) {
deps = []
}
deps += [ ":$_framework_headers_target" ]
}
output_extension = ""
output_name = _output_name
output_prefix_override = true
output_dir = target_out_dir
}
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, root_build_dir),
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" ]
}
}
# Bundle identifier should respect rfc1034, so replace "_" with "-".
_bundle_identifier =
"$ios_app_bundle_id_prefix." + string_replace(_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,
[
"info_plist",
"info_plist_target",
])
extra_substitutions = [ "BUNDLE_IDENTIFIER=$_bundle_identifier" ]
if (defined(invoker.extra_substitutions)) {
extra_substitutions += invoker.extra_substitutions
}
}
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 = ":$_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")
_target_name = target_name
_output_name = target_name
if (defined(invoker.output_name)) {
_output_name = invoker.output_name
}
_loadable_module_target = _target_name + "_loadable_module"
loadable_module(_loadable_module_target) {
forward_variables_from(invoker,
"*",
[
"host_target",
"output_dir",
"output_extension",
"output_name",
"output_prefix_override",
"product_type",
"testonly",
"visibility",
"xcode_test_application_name",
"xcode_test_application_output_name",
"xctest_bundle_principal_class",
"bundle_deps_filter",
])
testonly = true
visibility = [ ":$_target_name" ]
configs += [ "//build/config/ios:xctest_config" ]
output_dir = target_out_dir
output_name = _output_name
output_prefix_override = true
output_extension = ""
}
_info_plist_target = _target_name + "_info_plist"
_info_plist_bundle = _target_name + "_info_plist_bundle"
# Bundle identifier should respect rfc1034, so replace "_" with "-".
_bundle_identifier = "$ios_app_bundle_id_prefix.chrome." +
string_replace(_output_name, "_", "-")
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}",
"BUNDLE_IDENTIFIER=$_bundle_identifier",
]
}
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" ]
}
_xctest_bundle = _target_name + "_bundle"
create_signed_bundle(_target_name) {
forward_variables_from(invoker,
[
"bundle_id",
"data_deps",
"bundle_deps_filter",
"enable_code_signing",
"product_type",
"xcode_test_application_name",
])
testonly = true
visibility = [ ":$_xctest_bundle" ]
bundle_extension = ".xctest"
output_name = _output_name
bundle_binary_target = ":$_loadable_module_target"
bundle_binary_output = _output_name
xcode_extra_attributes = {
IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target
PRODUCT_BUNDLE_IDENTIFIER = _bundle_identifier
CODE_SIGNING_REQUIRED = "NO"
CODE_SIGNING_ALLOWED = "NO"
CODE_SIGN_IDENTITY = ""
DONT_GENERATE_INFOPLIST_FILE = "YES"
# 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}"
}
}
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",
]
}
# XCTestSupport framework is required as of Xcode 14.3 or later.
if (xcode_version_int >= 1430) {
extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestSupport.framework" ]
}
_xctest_bundle = _xctest_target + "_bundle"
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
}
# Bundle identifier should respect rfc1034, so replace "_" with "-".
_bundle_identifier = "$ios_app_bundle_id_prefix.chrome." +
string_replace(_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 || use_remoteexec)) {
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"
extra_substitutions = [ "BUNDLE_IDENTIFIER=$_bundle_identifier" ]
}
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 || use_remoteexec)) {
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",
]
}
# XCTestSupport framework is required as of Xcode 14.3 or later.
if (xcode_version_int >= 1430) {
extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestSupport.framework" ]
}
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.
#
# runner_only_bundle_deps:
# list of labels of bundle target to include in the runner and
# exclude from the test module (the use case is a framework bundle
# that is used by the test module and thus needs to be packaged in
# the runner application bundle)
#
# 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
if (defined(invoker.runner_only_bundle_deps)) {
bundle_deps_filter = invoker.runner_only_bundle_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" ])
if (defined(invoker.runner_only_bundle_deps)) {
if (!defined(bundle_deps)) {
bundle_deps = []
}
bundle_deps += invoker.runner_only_bundle_deps
}
}
}
set_defaults("ios_xcuitest_test") {
configs = default_executable_configs
}