• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import("//build/apple/apple_info_plist.gni")
6import("//build/config/apple/create_signed_bundle.gni")
7import("//build/config/apple/mobile_bundle_data.gni")
8import("//build/config/apple/symbols.gni")
9import("//build/config/compiler/compiler.gni")
10import("//build/config/ios/ios_sdk.gni")
11import("//build/toolchain/rbe.gni")
12import("//build/toolchain/siso.gni")
13import("//build/toolchain/toolchain.gni")
14import("//build_overrides/build.gni")
15
16# iOS-specific wrapper around apple_mobile_create_signed_bundle.
17#
18# See //build/config/apple/mobile_rules.gni for a description of arguments.
19template("ios_create_signed_bundle") {
20  apple_mobile_create_signed_bundle(target_name) {
21    forward_variables_from(invoker,
22                           "*",
23                           [
24                             "platform_sdk_name",
25                             "xcode_extra_attributes",
26                           ])
27    platform_sdk_name = ios_sdk_name
28    xcode_extra_attributes = {
29      IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target
30
31      # If invoker has defined extra attributes, they override the defaults.
32      if (defined(invoker.xcode_extra_attributes)) {
33        forward_variables_from(invoker.xcode_extra_attributes, "*")
34      }
35    }
36  }
37}
38
39# Expose the template under its original name, to avoid breaking dependencies.
40template("create_signed_bundle") {
41  ios_create_signed_bundle(target_name) {
42    forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)
43    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
44  }
45}
46
47# Generates Info.plist files for iOS apps and frameworks.
48#
49# Arguments
50#
51#     info_plist:
52#         (optional) string, path to the Info.plist file that will be used for
53#         the bundle.
54#
55#     info_plist_target:
56#         (optional) string, if the info_plist is generated from an action,
57#         rather than a regular source file, specify the target name in lieu
58#         of info_plist. The two arguments are mutually exclusive.
59#
60#     executable_name:
61#         string, name of the generated target used for the product
62#         and executable name as specified in the output Info.plist.
63#
64#     extra_substitutions:
65#         (optional) string array, 'key=value' pairs for extra fields which are
66#         specified in a source Info.plist template.
67template("ios_info_plist") {
68  assert(defined(invoker.info_plist) != defined(invoker.info_plist_target),
69         "Only one of info_plist or info_plist_target may be specified in " +
70             target_name)
71
72  if (defined(invoker.info_plist)) {
73    _info_plist = invoker.info_plist
74  } else {
75    _info_plist_target_output = get_target_outputs(invoker.info_plist_target)
76    _info_plist = _info_plist_target_output[0]
77  }
78
79  apple_info_plist(target_name) {
80    format = "binary1"
81    extra_substitutions = [
82      "IOS_BUNDLE_ID_PREFIX=$ios_app_bundle_id_prefix",
83      "IOS_PLATFORM_BUILD=$ios_platform_build",
84      "IOS_PLATFORM_NAME=$ios_sdk_name",
85      "IOS_PLATFORM_VERSION=$ios_sdk_version",
86      "IOS_SDK_BUILD=$ios_sdk_build",
87      "IOS_SDK_NAME=$ios_sdk_name$ios_sdk_version",
88      "IOS_SUPPORTED_PLATFORM=$ios_sdk_platform",
89      "BUILD_MACHINE_OS_BUILD=$machine_os_build",
90      "IOS_DEPLOYMENT_TARGET=$ios_deployment_target",
91      "XCODE_BUILD=$xcode_build",
92      "XCODE_VERSION=$xcode_version",
93    ]
94    if (defined(invoker.extra_substitutions)) {
95      extra_substitutions += invoker.extra_substitutions
96    }
97    plist_templates = [
98      "//build/config/ios/BuildInfo.plist",
99      _info_plist,
100    ]
101    if (defined(invoker.info_plist_target)) {
102      deps = [ invoker.info_plist_target ]
103    }
104    forward_variables_from(invoker,
105                           [
106                             "executable_name",
107                             "output_name",
108                             "visibility",
109                             "testonly",
110                           ])
111  }
112}
113
114# Template to build an application bundle for iOS.
115#
116# This should be used instead of "executable" built-in target type on iOS.
117# As the template forward the generation of the application executable to
118# an "executable" target, all arguments supported by "executable" targets
119# are also supported by this template.
120#
121# Arguments
122#
123#   output_name:
124#       (optional) string, name of the generated application, if omitted,
125#       defaults to the target_name.
126#
127#   extra_substitutions:
128#       (optional) list of string in "key=value" format, each value will
129#       be used as an additional variable substitution rule when generating
130#       the application Info.plist
131#
132#   info_plist:
133#       (optional) string, path to the Info.plist file that will be used for
134#       the bundle.
135#
136#   info_plist_target:
137#       (optional) string, if the info_plist is generated from an action,
138#       rather than a regular source file, specify the target name in lieu
139#       of info_plist. The two arguments are mutually exclusive.
140#
141#   entitlements_path:
142#       (optional) path to the template to use to generate the application
143#       entitlements by performing variable substitutions, defaults to
144#       //build/config/ios/entitlements.plist.
145#
146#   entitlements_target:
147#       (optional) label of the target generating the application
148#       entitlements (must generate a single file as output); cannot be
149#       defined if entitlements_path is set.
150#
151#   product_type
152#       (optional) string, product type for the generated Xcode project,
153#       default to "com.apple.product-type.application". Should only be
154#       overriden when building application extension.
155#
156#   enable_code_signing
157#       (optional) boolean, control whether code signing is enabled or not,
158#       default to ios_enable_code_signing if not defined.
159#
160#   variants
161#       (optional) list of scopes, each scope needs to define the attributes
162#       "name" and "bundle_deps"; if defined and non-empty, then one bundle
163#       named $target_out_dir/$variant/$output_name will be created for each
164#       variant with the same binary but the correct bundle_deps, the bundle
165#       at $target_out_dir/$output_name will be a copy of the first variant.
166#
167#   bundle_identifier:
168#       (optional) string, value of CFBundleIdentifier in the application
169#       Info.plist, defaults to "$ios_app_bundle_id_prefix.$output_name"
170#       if omitted. Will be used to set BUNDLE_IDENTIFIER when generating
171#       the application Info.plist
172#
173#   orderfile_path:
174#       (optional) string, path to an orderfile passed to the linker in order
175#       to improve application launch performance.
176#
177#   intents_target:
178#       (optional) string, label of the target defining the intents for the
179#       application. If defined, it must corresponds to a `swift_source_set`
180#       target configured with `generate_intents = true`.
181#
182#   transparent
183#       (optional) boolean, whether the bundle is "transparent"; defaults to
184#       "false" if omitted; a bundle is considered "transparent" if it does
185#       not package the "bundle_data" deps but forward them to all targets
186#       the depend on it (unless the "bundle_data" target sets "product_type"
187#       to the same value as the "ios_create_signed_bundle" target).
188#
189# For more information, see "gn help executable".
190template("ios_app_bundle") {
191  _output_name = target_name
192  _target_name = target_name
193  if (defined(invoker.output_name)) {
194    _output_name = invoker.output_name
195  }
196
197  assert(
198      !defined(invoker.bundle_extension),
199      "bundle_extension must not be set for ios_app_bundle template for $target_name")
200
201  # Whether the intents metadata should be extracted (note that they are
202  # disabled when building for the catalyst environment)
203  _extract_intents_metadata = false
204  if (defined(invoker.intents_target)) {
205    _extract_intents_metadata =
206        invoker.intents_target != "" && target_environment != "catalyst"
207  }
208
209  if (defined(invoker.bundle_identifier)) {
210    _bundle_identifier = invoker.bundle_identifier
211    assert(_bundle_identifier == string_replace(_bundle_identifier, "_", "-"),
212           "$target_name: bundle_identifier does not respect rfc1034: " +
213               _bundle_identifier)
214  } else {
215    # Bundle identifier should respect rfc1034, so replace "_" with "-".
216    _bundle_identifier =
217        "$ios_app_bundle_id_prefix." + string_replace(_output_name, "_", "-")
218  }
219
220  if (defined(invoker.variants) && invoker.variants != []) {
221    _variants = []
222
223    foreach(_variant, invoker.variants) {
224      assert(defined(_variant.name) && _variant.name != "",
225             "name must be defined for all $target_name variants")
226
227      assert(defined(_variant.bundle_deps),
228             "bundle_deps must be defined for all $target_name variants")
229
230      _variants += [
231        {
232          name = _variant.name
233          bundle_deps = _variant.bundle_deps
234          target_name = "${_target_name}_variants_${_variant.name}"
235          bundle_gen_dir = "$root_out_dir/variants/${_variant.name}"
236        },
237      ]
238    }
239  } else {
240    # If no variants are passed to the template, use a fake variant with
241    # no name to avoid duplicating code. As no variant can have an empty
242    # name except this fake variant, it is possible to know if a variant
243    # is fake or not.
244    _variants = [
245      {
246        name = ""
247        bundle_deps = []
248        target_name = _target_name
249        bundle_gen_dir = root_out_dir
250      },
251    ]
252  }
253
254  _default_variant = _variants[0]
255
256  _executable_target = _target_name + "_executable"
257  _generate_entitlements_target = _target_name + "_gen_entitlements"
258  _generate_entitlements_output =
259      get_label_info(":$_generate_entitlements_target", "target_out_dir") +
260      "/$_output_name.xcent"
261
262  _product_type = apple_mobile_xcode_app_bundle_id
263  if (defined(invoker.product_type)) {
264    _product_type = invoker.product_type
265  }
266
267  if (_product_type == apple_mobile_xcode_app_bundle_id) {
268    _bundle_extension = ".app"
269  } else if (_product_type == apple_mobile_xcode_appex_bundle_id) {
270    _bundle_extension = ".appex"
271  } else {
272    assert(false, "unknown product_type \"$product_type\" for $_target_name")
273  }
274
275  _is_app_bundle = _product_type == apple_mobile_xcode_app_bundle_id
276
277  if (_extract_intents_metadata) {
278    _metadata_extraction = _target_name + "_metadata_extraction"
279    _metadata_bundledata = _target_name + "_metadata_bundledata"
280  }
281
282  executable(_executable_target) {
283    forward_variables_from(invoker,
284                           "*",
285                           [
286                             "bundle_deps",
287                             "bundle_deps_filter",
288                             "bundle_extension",
289                             "enable_code_signing",
290                             "entitlements_path",
291                             "entitlements_target",
292                             "extra_substitutions",
293                             "extra_system_frameworks",
294                             "info_plist",
295                             "info_plist_target",
296                             "output_name",
297                             "product_type",
298                             "transparent",
299                             "visibility",
300                             "xcode_extra_attributes",
301                           ])
302
303    if (!defined(deps)) {
304      deps = []
305    }
306
307    visibility = []
308    foreach(_variant, _variants) {
309      visibility += [ ":${_variant.target_name}" ]
310    }
311    if (_extract_intents_metadata) {
312      visibility += [ ":$_metadata_extraction" ]
313      deps += [ invoker.intents_target ]
314    }
315
316    if (defined(invoker.orderfile_path)) {
317      orderfile_path = invoker.orderfile_path
318      if (!defined(ldflags)) {
319        ldflags = []
320      }
321      ldflags += [
322        "-Wl,-order_file",
323        "-Wl," + rebase_path(orderfile_path, root_build_dir),
324      ]
325
326      if (!defined(inputs)) {
327        inputs = []
328      }
329      inputs += [ orderfile_path ]
330    }
331
332    if (target_environment == "simulator") {
333      deps += [ ":$_generate_entitlements_target" ]
334
335      if (!defined(inputs)) {
336        inputs = []
337      }
338      inputs += [ _generate_entitlements_output ]
339
340      if (!defined(ldflags)) {
341        ldflags = []
342      }
343      ldflags += [ "-Wl,-sectcreate,__TEXT,__entitlements," +
344                   rebase_path(_generate_entitlements_output, root_build_dir) ]
345    }
346
347    output_name = _output_name
348    output_prefix_override = true
349    output_dir = target_out_dir
350  }
351
352  if (_extract_intents_metadata) {
353    _module_info_path =
354        get_label_info(invoker.intents_target, "target_out_dir") + "/" +
355        get_label_info(invoker.intents_target, "name") + ".module_info.json"
356
357    action(_metadata_extraction) {
358      _output_dir = "$target_out_dir/$target_name"
359      _binary_path = "$target_out_dir/$_output_name"
360
361      visibility = [ ":$_metadata_bundledata" ]
362      script = "//build/config/ios/extract_metadata.py"
363      sources = [
364        _binary_path,
365        _module_info_path,
366      ]
367      outputs = [
368        "$_output_dir/Metadata.appintents/extract.actionsdata",
369        "$_output_dir/Metadata.appintents/version.json",
370      ]
371      deps = [
372        ":$_executable_target",
373        invoker.intents_target,
374      ]
375      depfile = "$target_out_dir/$target_name.d"
376      args = [
377        "--toolchain-dir",
378        rebase_path(ios_toolchains_path, root_build_dir),
379        "--sdk-root",
380        rebase_path(ios_sdk_path, root_build_dir),
381        "--deployment-target",
382        ios_deployment_target,
383        "--target-cpu",
384        current_cpu,
385        "--target-environment",
386        target_environment,
387        "--depfile",
388        rebase_path(depfile, root_build_dir),
389        "--output",
390        rebase_path(_output_dir, root_build_dir),
391        "--binary-file",
392        rebase_path(_binary_path, root_build_dir),
393        "--module-info-path",
394        rebase_path(_module_info_path, root_build_dir),
395      ]
396
397      # Starting with Xcode 15.3, appintentsmetadataprocessor requires to be
398      # passed --xcode-version as parameter (with ${xcode_build} as value),
399      # while previous versions did not recognize the parameter. So check
400      # the version before deciding whether to set the parameter or not.
401      if (xcode_version_int >= 1530) {
402        args += [
403          "--xcode-version",
404          xcode_build,
405        ]
406      }
407    }
408
409    bundle_data(_metadata_bundledata) {
410      public_deps = [ ":$_metadata_extraction" ]
411      sources = get_target_outputs(":$_metadata_extraction")
412      outputs = [ "{{bundle_resources_dir}}/" +
413                  "Metadata.appintents/{{source_file_part}}" ]
414    }
415  }
416
417  _generate_info_plist = target_name + "_generate_info_plist"
418  ios_info_plist(_generate_info_plist) {
419    forward_variables_from(invoker,
420                           [
421                             "info_plist",
422                             "info_plist_target",
423                           ])
424
425    executable_name = _output_name
426
427    extra_substitutions = [ "BUNDLE_IDENTIFIER=$_bundle_identifier" ]
428    if (defined(invoker.extra_substitutions)) {
429      extra_substitutions += invoker.extra_substitutions
430    }
431  }
432
433  if (!defined(invoker.entitlements_target)) {
434    _entitlements_path = "//build/config/ios/entitlements.plist"
435    if (defined(invoker.entitlements_path)) {
436      _entitlements_path = invoker.entitlements_path
437    }
438  } else {
439    assert(!defined(invoker.entitlements_path),
440           "Cannot define both entitlements_path and entitlements_target" +
441               "for $_target_name")
442
443    _entitlements_target_outputs =
444        get_target_outputs(invoker.entitlements_target)
445    _entitlements_path = _entitlements_target_outputs[0]
446  }
447
448  action(_generate_entitlements_target) {
449    _gen_info_plist_outputs = get_target_outputs(":$_generate_info_plist")
450    _info_plist_path = _gen_info_plist_outputs[0]
451
452    script = "//build/config/apple/codesign.py"
453    deps = [ ":$_generate_info_plist" ]
454    if (defined(invoker.entitlements_target)) {
455      deps += [ invoker.entitlements_target ]
456    }
457    sources = [
458      _entitlements_path,
459      _info_plist_path,
460    ]
461    sources += ios_mobileprovision_files
462
463    outputs = [ _generate_entitlements_output ]
464
465    args = [
466      "generate-entitlements",
467      "-e=" + rebase_path(_entitlements_path, root_build_dir),
468      "-p=" + rebase_path(_info_plist_path, root_build_dir),
469    ]
470    foreach(mobileprovision, ios_mobileprovision_files) {
471      args += [ "-m=" + rebase_path(mobileprovision, root_build_dir) ]
472    }
473    args += rebase_path(outputs, root_build_dir)
474  }
475
476  # Only write PkgInfo for real application, not application extension.
477  if (_is_app_bundle) {
478    _create_pkg_info = target_name + "_pkg_info"
479    action(_create_pkg_info) {
480      forward_variables_from(invoker, [ "testonly" ])
481      script = "//build/apple/write_pkg_info.py"
482      inputs = [ "//build/apple/plist_util.py" ]
483      sources = get_target_outputs(":$_generate_info_plist")
484      outputs = [
485        # Cannot name the output PkgInfo as the name will not be unique if
486        # multiple ios_app_bundle are defined in the same BUILD.gn file. The
487        # file is renamed in the bundle_data outputs to the correct name.
488        "$target_gen_dir/$target_name",
489      ]
490      args = [ "--plist" ] + rebase_path(sources, root_build_dir) +
491             [ "--output" ] + rebase_path(outputs, root_build_dir)
492      deps = [ ":$_generate_info_plist" ]
493    }
494
495    _bundle_data_pkg_info = target_name + "_bundle_data_pkg_info"
496    bundle_data(_bundle_data_pkg_info) {
497      forward_variables_from(invoker, [ "testonly" ])
498      sources = get_target_outputs(":$_create_pkg_info")
499      outputs = [ "{{bundle_resources_dir}}/PkgInfo" ]
500      public_deps = [ ":$_create_pkg_info" ]
501    }
502  }
503
504  foreach(_variant, _variants) {
505    ios_create_signed_bundle(_variant.target_name) {
506      forward_variables_from(invoker,
507                             [
508                               "bundle_deps",
509                               "bundle_deps_filter",
510                               "data_deps",
511                               "deps",
512                               "enable_code_signing",
513                               "entitlements_path",
514                               "entitlements_target",
515                               "extra_system_frameworks",
516                               "public_configs",
517                               "public_deps",
518                               "testonly",
519                               "transparent",
520                               "visibility",
521                               "xcasset_compiler_flags",
522                               "xcode_extra_attributes",
523                             ])
524
525      output_name = _output_name
526      bundle_gen_dir = _variant.bundle_gen_dir
527      bundle_binary_target = ":$_executable_target"
528      bundle_binary_output = _output_name
529      bundle_extension = _bundle_extension
530      product_type = _product_type
531      xcode_product_bundle_id = _bundle_identifier
532
533      _generate_info_plist_outputs =
534          get_target_outputs(":$_generate_info_plist")
535      primary_info_plist = _generate_info_plist_outputs[0]
536      partial_info_plist =
537          "$target_gen_dir/${_variant.target_name}_partial_info.plist"
538
539      if (!defined(deps)) {
540        deps = []
541      }
542      deps += [ ":$_generate_info_plist" ]
543
544      if (!defined(bundle_deps)) {
545        bundle_deps = []
546      }
547      if (_is_app_bundle) {
548        bundle_deps += [ ":$_bundle_data_pkg_info" ]
549      }
550      bundle_deps += _variant.bundle_deps
551      if (_extract_intents_metadata) {
552        bundle_deps += [ ":$_metadata_bundledata" ]
553      }
554
555      if (target_environment == "simulator") {
556        if (!defined(data_deps)) {
557          data_deps = []
558        }
559        if (build_with_chromium) {
560          data_deps += [ "//testing/iossim" ]
561        }
562      }
563    }
564  }
565
566  if (_default_variant.name != "") {
567    _bundle_short_name = "$_output_name$_bundle_extension"
568    action(_target_name) {
569      forward_variables_from(invoker, [ "testonly" ])
570
571      script = "//build/config/ios/hardlink.py"
572      public_deps = []
573      foreach(_variant, _variants) {
574        public_deps += [ ":${_variant.target_name}" ]
575      }
576
577      sources = [ "${_default_variant.bundle_gen_dir}/$_bundle_short_name" ]
578      outputs = [ "$root_out_dir/$_bundle_short_name" ]
579
580      args = [
581               "--output-dir",
582               rebase_path(root_out_dir, root_build_dir),
583               "--relative-to",
584               rebase_path(_default_variant.bundle_gen_dir, root_build_dir),
585             ] + rebase_path(sources, root_build_dir)
586    }
587  }
588}
589
590set_defaults("ios_app_bundle") {
591  configs = default_executable_configs
592}
593
594# Template to build an application extension bundle for iOS.
595#
596# This should be used instead of "executable" built-in target type on iOS.
597# As the template forward the generation of the application executable to
598# an "executable" target, all arguments supported by "executable" targets
599# are also supported by this template.
600#
601# Arguments
602#
603#   output_name:
604#       (optional) string, name of the generated application, if omitted,
605#       defaults to the target_name.
606#
607#   extra_substitutions:
608#       (optional) list of string in "key=value" format, each value will
609#       be used as an additional variable substitution rule when generating
610#       the application Info.plist
611#
612#   info_plist:
613#       (optional) string, path to the Info.plist file that will be used for
614#       the bundle.
615#
616#   info_plist_target:
617#       (optional) string, if the info_plist is generated from an action,
618#       rather than a regular source file, specify the target name in lieu
619#       of info_plist. The two arguments are mutually exclusive.
620#
621# For more information, see "gn help executable".
622template("ios_appex_bundle") {
623  assert(ios_is_app_extension,
624         "$target_name needs to be defined in app extension toolchain context")
625  ios_app_bundle(target_name) {
626    forward_variables_from(invoker,
627                           "*",
628                           [
629                             "bundle_extension",
630                             "product_type",
631                           ])
632    product_type = apple_mobile_xcode_appex_bundle_id
633  }
634}
635
636set_defaults("ios_appex_bundle") {
637  configs = [ "//build/config/ios:ios_extension_executable_flags" ]
638}
639
640# Template to package a shared library into an iOS framework bundle.
641#
642# By default, the bundle target this template generates does not link the
643# resulting framework into anything that depends on it. If a dependency wants
644# a link-time (as well as build-time) dependency on the framework bundle,
645# depend against "$target_name+link". If only the build-time dependency is
646# required (e.g., for copying into another bundle), then use "$target_name".
647#
648# Arguments
649#
650#     output_name:
651#         (optional) string, name of the generated framework without the
652#         .framework suffix. If omitted, defaults to target_name.
653#
654#     public_headers:
655#         (optional) list of paths to header file that needs to be copied
656#         into the framework bundle Headers subdirectory. If omitted or
657#         empty then the Headers subdirectory is not created.
658#
659#     sources
660#         (optional) list of files. Needs to be defined and non-empty if
661#         public_headers is defined and non-empty.
662#
663#   enable_code_signing
664#       (optional) boolean, control whether code signing is enabled or not,
665#       default to ios_enable_code_signing if not defined.
666#
667#   transparent
668#       (optional) boolean, whether the bundle is "transparent"; defaults to
669#       "false" if omitted; a bundle is considered "transparent" if it does
670#       not package the "bundle_data" deps but forward them to all targets
671#       the depend on it (unless the "bundle_data" target sets "product_type"
672#       to "com.apple.product-type.framework").
673#
674# This template provides two targets for the resulting framework bundle. The
675# link-time behavior varies depending on which of the two targets below is
676# added as a dependency:
677#   - $target_name only adds a build-time dependency. Targets that depend on
678#     it will not link against the framework.
679#   - $target_name+link adds a build-time and link-time dependency. Targets
680#     that depend on it will link against the framework.
681#
682# The build-time-only dependency is used for when a target needs to use the
683# framework either only for resources, or because the target loads it at run-
684# time, via dlopen() or NSBundle. The link-time dependency will cause the
685# dependee to have the framework loaded by dyld at launch.
686#
687# Example of build-time only dependency:
688#
689#     framework_bundle("CoreTeleportation") {
690#       sources = [ ... ]
691#     }
692#
693#     bundle_data("core_teleportation_bundle_data") {
694#       deps = [ ":CoreTeleportation" ]
695#       sources = [ "$root_out_dir/CoreTeleportation.framework" ]
696#       outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ]
697#     }
698#
699#     app_bundle("GoatTeleporter") {
700#       sources = [ ... ]
701#       deps = [
702#         ":core_teleportation_bundle_data",
703#       ]
704#     }
705#
706# The GoatTeleporter.app will not directly link against
707# CoreTeleportation.framework, but it will be included in the bundle's
708# Frameworks directory.
709#
710# Example of link-time dependency:
711#
712#     framework_bundle("CoreTeleportation") {
713#       sources = [ ... ]
714#       ldflags = [
715#         "-install_name",
716#         "@executable_path/../Frameworks/$target_name.framework"
717#       ]
718#     }
719#
720#     bundle_data("core_teleportation_bundle_data") {
721#       deps = [ ":CoreTeleportation+link" ]
722#       sources = [ "$root_out_dir/CoreTeleportation.framework" ]
723#       outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ]
724#     }
725#
726#     app_bundle("GoatTeleporter") {
727#       sources = [ ... ]
728#       deps = [
729#         ":core_teleportation_bundle_data",
730#       ]
731#     }
732#
733# Note that the framework is still copied to the app's bundle, but dyld will
734# load this library when the app is launched because it uses the "+link"
735# target as a dependency. This also requires that the framework set its
736# install_name so that dyld can locate it.
737#
738# See "gn help shared_library" for more information on arguments supported
739# by shared library target.
740template("ios_framework_bundle") {
741  _target_name = target_name
742  _output_name = target_name
743  if (defined(invoker.output_name)) {
744    _output_name = invoker.output_name
745  }
746
747  _product_type = "com.apple.product-type.framework"
748  _has_public_headers =
749      defined(invoker.public_headers) && invoker.public_headers != []
750
751  _shared_library_target = _target_name + "_shared_library"
752  _link_target_name = _target_name + "+link"
753
754  if (_has_public_headers) {
755    _default_toolchain_target_gen_dir =
756        get_label_info("$_target_name", "target_gen_dir")
757
758    _framework_headers_target = _target_name + "_framework_headers"
759
760    _headers_map_config = _target_name + "_headers_map"
761    _header_map_filename =
762        "$_default_toolchain_target_gen_dir/$_output_name.headers.hmap"
763    config(_headers_map_config) {
764      visibility = [
765        ":${_shared_library_target}",
766        ":${_target_name}_signed_bundle",
767      ]
768      include_dirs = [ _header_map_filename ]
769    }
770  }
771
772  _framework_headers_config = _target_name + "_framework_headers_config"
773  config(_framework_headers_config) {
774    framework_dirs = [ root_out_dir ]
775  }
776
777  _framework_public_config = _target_name + "_public_config"
778  config(_framework_public_config) {
779    configs = [ ":$_framework_headers_config" ]
780    frameworks = [ "$_output_name.framework" ]
781  }
782
783  shared_library(_shared_library_target) {
784    forward_variables_from(invoker,
785                           "*",
786                           [
787                             "bundle_deps",
788                             "bundle_deps_filter",
789                             "data_deps",
790                             "enable_code_signing",
791                             "extra_substitutions",
792                             "info_plist",
793                             "info_plist_target",
794                             "output_name",
795                             "public_configs",
796                             "transparent",
797                             "visibility",
798                           ])
799
800    visibility = [ ":${_target_name}_signed_bundle" ]
801
802    if (!defined(ldflags)) {
803      ldflags = []
804    }
805    ldflags +=
806        [ "-Wl,-install_name,@rpath/$_output_name.framework/$_output_name" ]
807
808    if (_has_public_headers) {
809      configs += [ ":$_headers_map_config" ]
810
811      if (!defined(deps)) {
812        deps = []
813      }
814      deps += [ ":$_framework_headers_target" ]
815    }
816
817    output_extension = ""
818    output_name = _output_name
819    output_prefix_override = true
820    output_dir = target_out_dir
821  }
822
823  if (_has_public_headers) {
824    _public_headers = invoker.public_headers
825
826    _framework_root_dir = "$root_out_dir/$_output_name.framework"
827    if (target_environment == "simulator" || target_environment == "device") {
828      _framework_contents_dir = _framework_root_dir
829    } else if (target_environment == "catalyst") {
830      _framework_contents_dir = "$_framework_root_dir/Versions/A"
831    }
832
833    _compile_headers_map_target = _target_name + "_compile_headers_map"
834    action(_compile_headers_map_target) {
835      visibility = [ ":$_framework_headers_target" ]
836      forward_variables_from(invoker,
837                             [
838                               "deps",
839                               "public_deps",
840                               "testonly",
841                             ])
842      script = "//build/config/apple/write_framework_hmap.py"
843      outputs = [ _header_map_filename ]
844
845      # The header map generation only wants the list of headers, not all of
846      # sources, so filter any non-header source files from "sources". It is
847      # less error prone that having the developer duplicate the list of all
848      # headers in addition to "sources".
849      sources = []
850      if (defined(invoker.sources)) {
851        foreach(_source, invoker.sources) {
852          if (get_path_info(_source, "extension") == "h") {
853            sources += [ _source ]
854          }
855        }
856      }
857
858      args = [
859               rebase_path(_header_map_filename, root_build_dir),
860               rebase_path(_framework_root_dir, root_build_dir),
861             ] + rebase_path(sources, root_build_dir)
862    }
863
864    _create_module_map_target = _target_name + "_module_map"
865    action(_create_module_map_target) {
866      visibility = [ ":$_framework_headers_target" ]
867      script = "//build/config/apple/write_framework_modulemap.py"
868      outputs = [ "$_framework_contents_dir/Modules/module.modulemap" ]
869      args = [
870        _output_name,
871        rebase_path("$_framework_contents_dir/Modules", root_build_dir),
872      ]
873    }
874
875    _copy_public_headers_target = _target_name + "_copy_public_headers"
876    copy(_copy_public_headers_target) {
877      forward_variables_from(invoker,
878                             [
879                               "testonly",
880                               "deps",
881                             ])
882      visibility = [ ":$_framework_headers_target" ]
883      sources = _public_headers
884      outputs = [ "$_framework_contents_dir/Headers/{{source_file_part}}" ]
885
886      # Do not use forward_variables_from for "public_deps" as
887      # we do not want to forward those dependencies.
888      if (defined(invoker.public_deps)) {
889        if (!defined(deps)) {
890          deps = []
891        }
892        deps += invoker.public_deps
893      }
894    }
895
896    group(_framework_headers_target) {
897      forward_variables_from(invoker, [ "testonly" ])
898      deps = [
899        ":$_compile_headers_map_target",
900        ":$_create_module_map_target",
901      ]
902      public_deps = [ ":$_copy_public_headers_target" ]
903    }
904  }
905
906  # Bundle identifier should respect rfc1034, so replace "_" with "-".
907  _bundle_identifier =
908      "$ios_app_bundle_id_prefix." + string_replace(_output_name, "_", "-")
909
910  _info_plist_target = _target_name + "_info_plist"
911  _info_plist_bundle = _target_name + "_info_plist_bundle"
912  ios_info_plist(_info_plist_target) {
913    visibility = [ ":$_info_plist_bundle" ]
914    executable_name = _output_name
915    forward_variables_from(invoker,
916                           [
917                             "info_plist",
918                             "info_plist_target",
919                           ])
920
921    extra_substitutions = [ "BUNDLE_IDENTIFIER=$_bundle_identifier" ]
922    if (defined(invoker.extra_substitutions)) {
923      extra_substitutions += invoker.extra_substitutions
924    }
925  }
926
927  bundle_data(_info_plist_bundle) {
928    visibility = [ ":${_target_name}_signed_bundle" ]
929    forward_variables_from(invoker, [ "testonly" ])
930    sources = get_target_outputs(":$_info_plist_target")
931    public_deps = [ ":$_info_plist_target" ]
932    product_type = _product_type
933
934    if (target_environment != "catalyst") {
935      outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
936    } else {
937      outputs = [ "{{bundle_resources_dir}}/Info.plist" ]
938    }
939  }
940
941  ios_create_signed_bundle(_target_name + "_signed_bundle") {
942    forward_variables_from(invoker,
943                           [
944                             "bundle_deps",
945                             "bundle_deps_filter",
946                             "data_deps",
947                             "deps",
948                             "enable_code_signing",
949                             "public_configs",
950                             "public_deps",
951                             "testonly",
952                             "transparent",
953                             "visibility",
954                           ])
955
956    product_type = _product_type
957    bundle_extension = ".framework"
958
959    output_name = _output_name
960    bundle_binary_target = ":$_shared_library_target"
961    bundle_binary_output = _output_name
962
963    has_public_headers = _has_public_headers
964
965    # Framework do not have entitlements nor mobileprovision because they use
966    # the one from the bundle using them (.app or .appex) as they are just
967    # dynamic library with shared code.
968    disable_entitlements = true
969    disable_embedded_mobileprovision = true
970
971    if (!defined(deps)) {
972      deps = []
973    }
974    deps += [ ":$_info_plist_bundle" ]
975  }
976
977  group(_target_name) {
978    forward_variables_from(invoker,
979                           [
980                             "public_configs",
981                             "public_deps",
982                             "testonly",
983                             "visibility",
984                           ])
985    if (!defined(public_deps)) {
986      public_deps = []
987    }
988    public_deps += [ ":${_target_name}_signed_bundle" ]
989
990    if (_has_public_headers) {
991      if (!defined(public_configs)) {
992        public_configs = []
993      }
994      public_configs += [ ":$_framework_headers_config" ]
995    }
996  }
997
998  group(_link_target_name) {
999    forward_variables_from(invoker,
1000                           [
1001                             "public_configs",
1002                             "public_deps",
1003                             "testonly",
1004                             "visibility",
1005                           ])
1006    if (!defined(public_deps)) {
1007      public_deps = []
1008    }
1009    public_deps += [ ":$_target_name" ]
1010
1011    if (!defined(all_dependent_configs)) {
1012      all_dependent_configs = []
1013    }
1014    all_dependent_configs += [ ":$_framework_public_config" ]
1015  }
1016
1017  bundle_data(_target_name + "+bundle") {
1018    forward_variables_from(invoker,
1019                           [
1020                             "testonly",
1021                             "visibility",
1022                           ])
1023    public_deps = [ ":$_target_name" ]
1024    sources = [ "$root_out_dir/$_output_name.framework" ]
1025    outputs = [ "{{bundle_contents_dir}}/Frameworks/$_output_name.framework" ]
1026  }
1027}
1028
1029set_defaults("ios_framework_bundle") {
1030  configs = default_shared_library_configs
1031}
1032
1033# Template to build a xctest bundle that contains a loadable module for iOS.
1034#
1035# Arguments
1036#
1037#   deps:
1038#       list of labels to depends on, these values are used to create the
1039#       loadable module.
1040#
1041#   product_type
1042#       string, product type for the generated Xcode project, use
1043#       "com.apple.product-type.bundle.unit-test" for unit test and
1044#       "com.apple.product-type.bundle.ui-testing" for UI testing.
1045#
1046#   host_target:
1047#       string, name of the target that depends on the generated bundle, this
1048#       value is used to restrict visibilities.
1049#
1050#   xcode_test_application_name:
1051#       string, name of the test application for Xcode unit or ui test target.
1052#
1053#   output_name
1054#       (optional) string, name of the generated application, if omitted,
1055#       defaults to the target_name.
1056#
1057# This template defines two targets, one named "${target_name}" is the xctest
1058# bundle, and the other named "${target_name}_bundle" is a bundle_data that
1059# wraps the xctest bundle and that only the "${host_target}" can depend on.
1060#
1061template("ios_xctest_bundle") {
1062  assert(defined(invoker.deps), "deps must be defined for $target_name")
1063  assert(defined(invoker.product_type),
1064         "product_type must be defined for $target_name")
1065  assert(invoker.product_type == apple_mobile_xcode_xctest_bundle_id ||
1066             invoker.product_type == apple_mobile_xcode_xcuitest_bundle_id,
1067         "product_type defined for $target_name is invalid.")
1068  assert(defined(invoker.host_target),
1069         "host_target must be defined for $target_name")
1070  assert(defined(invoker.xcode_test_application_name),
1071         "xcode_test_application_name must be defined for $target_name")
1072
1073  _target_name = target_name
1074  _output_name = target_name
1075
1076  if (defined(invoker.output_name)) {
1077    _output_name = invoker.output_name
1078  }
1079
1080  _loadable_module_target = _target_name + "_loadable_module"
1081
1082  loadable_module(_loadable_module_target) {
1083    forward_variables_from(invoker,
1084                           "*",
1085                           [
1086                             "bundle_deps",
1087                             "bundle_deps_filter",
1088                             "host_target",
1089                             "output_dir",
1090                             "output_extension",
1091                             "output_name",
1092                             "output_prefix_override",
1093                             "product_type",
1094                             "testonly",
1095                             "visibility",
1096                             "xcode_test_application_name",
1097                             "xcode_test_application_output_name",
1098                             "xctest_bundle_principal_class",
1099                           ])
1100
1101    testonly = true
1102    visibility = [ ":$_target_name" ]
1103
1104    configs += [ "//build/config/ios:xctest_config" ]
1105
1106    output_dir = target_out_dir
1107    output_name = _output_name
1108    output_prefix_override = true
1109    output_extension = ""
1110  }
1111
1112  _info_plist_target = _target_name + "_info_plist"
1113  _info_plist_bundle = _target_name + "_info_plist_bundle"
1114
1115  # Bundle identifier should respect rfc1034, so replace "_" with "-".
1116  _bundle_identifier = "$ios_app_bundle_id_prefix.chrome." +
1117                       string_replace(_output_name, "_", "-")
1118
1119  ios_info_plist(_info_plist_target) {
1120    testonly = true
1121    visibility = [ ":$_info_plist_bundle" ]
1122
1123    info_plist = "//build/config/ios/Module-Info.plist"
1124    executable_name = _output_name
1125
1126    if (defined(invoker.xctest_bundle_principal_class)) {
1127      _principal_class = invoker.xctest_bundle_principal_class
1128    } else {
1129      # Fall back to a reasonable default value.
1130      _principal_class = "NSObject"
1131    }
1132    extra_substitutions = [
1133      "XCTEST_BUNDLE_PRINCIPAL_CLASS=${_principal_class}",
1134      "BUNDLE_IDENTIFIER=$_bundle_identifier",
1135    ]
1136  }
1137
1138  bundle_data(_info_plist_bundle) {
1139    testonly = true
1140    visibility = [ ":$_target_name" ]
1141
1142    public_deps = [ ":$_info_plist_target" ]
1143
1144    sources = get_target_outputs(":$_info_plist_target")
1145    outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
1146  }
1147
1148  _xctest_bundle = _target_name + "_bundle"
1149  ios_create_signed_bundle(_target_name) {
1150    forward_variables_from(invoker,
1151                           [
1152                             "bundle_deps",
1153                             "bundle_deps_filter",
1154                             "bundle_id",
1155                             "data_deps",
1156                             "enable_code_signing",
1157                             "product_type",
1158                             "transparent",
1159                             "xcode_test_application_name",
1160                           ])
1161
1162    testonly = true
1163    visibility = [ ":$_xctest_bundle" ]
1164
1165    bundle_extension = ".xctest"
1166
1167    output_name = _output_name
1168    bundle_binary_target = ":$_loadable_module_target"
1169    bundle_binary_output = _output_name
1170
1171    xcode_extra_attributes = {
1172      IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target
1173      PRODUCT_BUNDLE_IDENTIFIER = _bundle_identifier
1174      CODE_SIGNING_REQUIRED = "NO"
1175      CODE_SIGNING_ALLOWED = "NO"
1176      CODE_SIGN_IDENTITY = ""
1177      DONT_GENERATE_INFOPLIST_FILE = "YES"
1178
1179      # For XCUITest, Xcode requires specifying the host application name
1180      # via the TEST_TARGET_NAME attribute.
1181      if (invoker.product_type == apple_mobile_xcode_xcuitest_bundle_id) {
1182        TEST_TARGET_NAME = invoker.xcode_test_application_name
1183      }
1184
1185      # For XCTest, Xcode requires specifying the host application path via
1186      # both BUNDLE_LOADER and TEST_HOST attributes.
1187      if (invoker.product_type == apple_mobile_xcode_xctest_bundle_id) {
1188        _xcode_app_name = invoker.xcode_test_application_name
1189        if (defined(invoker.xcode_test_application_output_name)) {
1190          _xcode_app_name = invoker.xcode_test_application_output_name
1191        }
1192
1193        BUNDLE_LOADER = "\$(TEST_HOST)"
1194        TEST_HOST = "\$(BUILT_PRODUCTS_DIR)/" +
1195                    "${_xcode_app_name}.app/${_xcode_app_name}"
1196      }
1197    }
1198
1199    deps = [ ":$_info_plist_bundle" ]
1200  }
1201
1202  bundle_data(_xctest_bundle) {
1203    forward_variables_from(invoker, [ "host_target" ])
1204
1205    testonly = true
1206    visibility = [ ":$host_target" ]
1207
1208    public_deps = [ ":$_target_name" ]
1209    sources = [ "$root_out_dir/$_output_name.xctest" ]
1210    outputs = [ "{{bundle_contents_dir}}/PlugIns/$_output_name.xctest" ]
1211  }
1212}
1213
1214set_defaults("ios_xctest_bundle") {
1215  configs = default_shared_library_configs
1216}
1217
1218# For Chrome on iOS we want to run XCTests for all our build configurations
1219# (Debug, Release, ...). In addition, the symbols visibility is configured to
1220# private by default. To simplify testing with those constraints, our tests are
1221# compiled in the TEST_HOST target instead of the .xctest bundle.
1222template("ios_xctest_test") {
1223  _target_name = target_name
1224  _output_name = target_name
1225  if (defined(invoker.output_name)) {
1226    _output_name = invoker.output_name
1227  }
1228
1229  _xctest_target = _target_name + "_module"
1230  _xctest_output = _output_name + "_module"
1231
1232  _host_target = _target_name
1233  _host_output = _output_name
1234
1235  # Allow invokers to specify their own target for the xctest module, but
1236  # fall back to a default (empty) module otherwise.
1237  if (defined(invoker.xctest_module_target)) {
1238    _xctest_module_target = invoker.xctest_module_target
1239  } else {
1240    _xctest_module_target_name = _xctest_target + "shell_source"
1241    _xctest_module_target = ":$_xctest_module_target_name"
1242    source_set(_xctest_module_target_name) {
1243      sources = [ "//build/config/ios/xctest_shell.mm" ]
1244
1245      configs += [ "//build/config/ios:xctest_config" ]
1246    }
1247  }
1248
1249  ios_xctest_bundle(_xctest_target) {
1250    forward_variables_from(invoker, [ "data_deps" ])
1251    output_name = _xctest_output
1252    product_type = apple_mobile_xcode_xctest_bundle_id
1253    host_target = _host_target
1254
1255    # TODO(crbug.com/40120290) The change in output name results in a mismatch
1256    # between this value and the ios_app_bundle target name. To mitigate, this
1257    # has been modified to _host_target. output_name is set to _host_output
1258    # to mitigate the naming.
1259    xcode_test_application_name = _host_target
1260    xcode_test_application_output_name = _host_output
1261
1262    deps = [ _xctest_module_target ]
1263  }
1264
1265  ios_app_bundle(_host_target) {
1266    forward_variables_from(invoker, "*", [ "testonly" ])
1267
1268    testonly = true
1269    output_name = _host_output
1270    configs += [ "//build/config/ios:xctest_config" ]
1271
1272    if (!defined(invoker.info_plist) && !defined(invoker.info_plist_target)) {
1273      info_plist = "//build/config/ios/Host-Info.plist"
1274    }
1275
1276    # Xcode needs the following frameworks installed in the application (and
1277    # signed) for the XCTest to run, so install them using
1278    # extra_system_frameworks.
1279    extra_system_frameworks = [
1280      "$ios_sdk_platform_path/Developer/Library/Frameworks/XCTest.framework",
1281      "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework",
1282      "$ios_sdk_platform_path/Developer/usr/lib/libXCTestBundleInject.dylib",
1283    ]
1284
1285    # Xcode 13 now depends on XCTestCore. To keep things future proof, copy over
1286    # everything that Xcode copies.
1287    if (xcode_version_int >= 1300) {
1288      extra_system_frameworks += [
1289        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestCore.framework",
1290        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUnit.framework",
1291        "$ios_sdk_platform_path/Developer/usr/lib/libXCTestSwiftSupport.dylib",
1292      ]
1293
1294      # Xcode 16.3 moved XCUIAutomation.framework
1295      if (xcode_version_int < 1630) {
1296        extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUIAutomation.framework" ]
1297      } else {
1298        extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/Frameworks/XCUIAutomation.framework" ]
1299      }
1300    }
1301
1302    # XCTestSupport framework is required as of Xcode 14.3 or later.
1303    if (xcode_version_int >= 1430) {
1304      extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestSupport.framework" ]
1305    }
1306
1307    _xctest_bundle = _xctest_target + "_bundle"
1308    if (!defined(bundle_deps)) {
1309      bundle_deps = []
1310    }
1311    bundle_deps += [ ":$_xctest_bundle" ]
1312  }
1313}
1314
1315set_defaults("ios_xctest_test") {
1316  configs = default_executable_configs
1317}
1318
1319# Template to build a xcuitest test runner bundle.
1320#
1321# Xcode requires a test runner application with a copy of the XCTest dynamic
1322# library bundle in it for the XCUITest to run. The test runner bundle is created
1323# by copying the system bundle XCTRunner.app from Xcode SDK with the plist file
1324# being properly tweaked, and a xctest and it needs to be code signed in order
1325# to run on devices.
1326#
1327# Arguments
1328#
1329#   xctest_bundle
1330#       string, name of the dependent xctest bundle target.
1331#
1332#   output_name
1333#       (optional) string, name of the generated application, if omitted,
1334#       defaults to the target_name.
1335#
1336template("ios_xcuitest_test_runner_bundle") {
1337  assert(defined(invoker.xctest_bundle),
1338         "xctest_bundle must be defined for $target_name")
1339
1340  _target_name = target_name
1341  _output_name = target_name
1342  if (defined(invoker.output_name)) {
1343    _output_name = invoker.output_name
1344  }
1345
1346  # Bundle identifier should respect rfc1034, so replace "_" with "-".
1347  _bundle_identifier = "$ios_app_bundle_id_prefix.chrome." +
1348                       string_replace(_output_name, "_", "-")
1349
1350  _xctrunner_path =
1351      "$ios_sdk_platform_path/Developer/Library/Xcode/Agents/XCTRunner.app"
1352
1353  _info_plist_merge_plist = _target_name + "_info_plist_merge_plist"
1354  _info_plist_target = _target_name + "_info_plist"
1355  _info_plist_bundle = _target_name + "_info_plist_bundle"
1356
1357  action(_info_plist_merge_plist) {
1358    testonly = true
1359    script = "//build/apple/plist_util.py"
1360
1361    sources = [
1362      "$_xctrunner_path/Info.plist",
1363
1364      # NOTE: The XCTRunnerAddition+Info.plist must come after the Info.plist
1365      # because it overrides the values under "CFBundleIdentifier" and
1366      # "CFBundleName".
1367      "//build/config/ios/resources/XCTRunnerAddition+Info.plist",
1368    ]
1369
1370    _output_name = "$target_gen_dir/${_target_name}_merged.plist"
1371    outputs = [ _output_name ]
1372    args = [
1373             "merge",
1374             "-f=xml1",
1375             "-x=$xcode_version",
1376             "-o=" + rebase_path(_output_name, root_build_dir),
1377           ] + rebase_path(sources, root_build_dir)
1378
1379    if (ios_use_xcode_symlinks) {
1380      deps = [ "//build/config/ios:copy_xctrunner_app" ]
1381    }
1382  }
1383
1384  ios_info_plist(_info_plist_target) {
1385    testonly = true
1386    visibility = [ ":$_info_plist_bundle" ]
1387
1388    executable_name = _output_name
1389    info_plist_target = ":$_info_plist_merge_plist"
1390    extra_substitutions = [ "BUNDLE_IDENTIFIER=$_bundle_identifier" ]
1391  }
1392
1393  bundle_data(_info_plist_bundle) {
1394    testonly = true
1395    visibility = [ ":$_target_name" ]
1396
1397    public_deps = [ ":$_info_plist_target" ]
1398
1399    sources = get_target_outputs(":$_info_plist_target")
1400    outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
1401  }
1402
1403  _pkginfo_bundle = _target_name + "_pkginfo_bundle"
1404  bundle_data(_pkginfo_bundle) {
1405    testonly = true
1406    visibility = [ ":$_target_name" ]
1407
1408    sources = [ "$_xctrunner_path/PkgInfo" ]
1409
1410    outputs = [ "{{bundle_contents_dir}}/PkgInfo" ]
1411
1412    if (ios_use_xcode_symlinks) {
1413      public_deps = [ "//build/config/ios:copy_xctrunner_app" ]
1414    }
1415  }
1416
1417  _xctest_bundle = invoker.xctest_bundle
1418  ios_create_signed_bundle(_target_name) {
1419    testonly = true
1420
1421    bundle_binary_target = "//build/config/ios:xctest_runner_without_arm64e"
1422    bundle_binary_output = "XCTRunner"
1423    bundle_extension = ".app"
1424    product_type = apple_mobile_xcode_app_bundle_id
1425
1426    output_name = _output_name
1427
1428    # Xcode needs the following frameworks installed in the application
1429    # (and signed) for the XCUITest to run, so install them using
1430    # extra_system_frameworks.
1431    extra_system_frameworks = [
1432      "$ios_sdk_platform_path/Developer/Library/Frameworks/XCTest.framework",
1433      "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework",
1434    ]
1435
1436    # Xcode 13 now depends on XCTestCore. To keep things future proof, copy over
1437    # everything that Xcode copies.
1438    if (xcode_version_int >= 1300) {
1439      extra_system_frameworks += [
1440        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestCore.framework",
1441        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUnit.framework",
1442        "$ios_sdk_platform_path/Developer/usr/lib/libXCTestSwiftSupport.dylib",
1443      ]
1444
1445      # Xcode 16.3 moved XCUIAutomation.framework
1446      if (xcode_version_int < 1630) {
1447        extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUIAutomation.framework" ]
1448      } else {
1449        extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/Frameworks/XCUIAutomation.framework" ]
1450      }
1451    }
1452
1453    # XCTestSupport framework is required as of Xcode 14.3 or later.
1454    if (xcode_version_int >= 1430) {
1455      extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestSupport.framework" ]
1456    }
1457
1458    bundle_deps = []
1459    if (defined(invoker.bundle_deps)) {
1460      bundle_deps += invoker.bundle_deps
1461    }
1462    bundle_deps += [
1463      ":$_info_plist_bundle",
1464      ":$_pkginfo_bundle",
1465      ":$_xctest_bundle",
1466    ]
1467  }
1468}
1469
1470# Template to build a XCUITest that consists of two parts: the test runner
1471# application bundle and the xctest dynamic library.
1472#
1473# Arguments
1474#
1475#   deps:
1476#       list of labels to depends on, these values are used to create the
1477#       xctest dynamic library.
1478#
1479#   xcode_test_application_name:
1480#       string, name of the test application for the ui test target.
1481#
1482#   runner_only_bundle_deps:
1483#       list of labels of bundle target to include in the runner and
1484#       exclude from the test module (the use case is a framework bundle
1485#       that is used by the test module and thus needs to be packaged in
1486#       the runner application bundle)
1487#
1488# This template defines two targets, one named "${target_name}_module" is the
1489# xctest dynamic library, and the other named "${target_name}_runner" is the
1490# test runner application bundle.
1491#
1492template("ios_xcuitest_test") {
1493  assert(defined(invoker.deps), "deps must be defined for $target_name")
1494  assert(defined(invoker.xcode_test_application_name),
1495         "xcode_test_application_name must be defined for $target_name")
1496
1497  _xcuitest_target = target_name
1498  if (defined(invoker.output_name)) {
1499    _xcuitest_target = invoker.output_name
1500  }
1501
1502  _xcuitest_runner_target = _xcuitest_target + "_runner"
1503  _xcuitest_module_target = _xcuitest_target + "_module"
1504
1505  group(target_name) {
1506    testonly = true
1507
1508    deps = [ ":$_xcuitest_runner_target" ]
1509  }
1510
1511  _xcuitest_module_output = _xcuitest_target
1512  ios_xctest_bundle(_xcuitest_module_target) {
1513    forward_variables_from(invoker,
1514                           [
1515                             "bundle_deps",
1516                             "data_deps",
1517                             "deps",
1518                             "xcode_test_application_name",
1519                             "xctest_bundle_principal_class",
1520                           ])
1521
1522    product_type = apple_mobile_xcode_xcuitest_bundle_id
1523    host_target = _xcuitest_runner_target
1524    output_name = _xcuitest_module_output
1525
1526    if (defined(invoker.runner_only_bundle_deps)) {
1527      bundle_deps_filter = invoker.runner_only_bundle_deps
1528    }
1529  }
1530
1531  _xcuitest_runner_output = _xcuitest_target + "-Runner"
1532  ios_xcuitest_test_runner_bundle(_xcuitest_runner_target) {
1533    output_name = _xcuitest_runner_output
1534    xctest_bundle = _xcuitest_module_target + "_bundle"
1535
1536    if (defined(invoker.runner_only_bundle_deps)) {
1537      if (!defined(bundle_deps)) {
1538        bundle_deps = []
1539      }
1540      bundle_deps += invoker.runner_only_bundle_deps
1541    }
1542  }
1543}
1544
1545set_defaults("ios_xcuitest_test") {
1546  configs = default_executable_configs
1547}
1548