# 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 }