xref: /aosp_15_r20/external/angle/build/config/ios/rules.gni (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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                               "xcode_extra_attributes",
522                             ])
523
524      output_name = _output_name
525      bundle_gen_dir = _variant.bundle_gen_dir
526      bundle_binary_target = ":$_executable_target"
527      bundle_binary_output = _output_name
528      bundle_extension = _bundle_extension
529      product_type = _product_type
530      xcode_product_bundle_id = _bundle_identifier
531
532      _generate_info_plist_outputs =
533          get_target_outputs(":$_generate_info_plist")
534      primary_info_plist = _generate_info_plist_outputs[0]
535      partial_info_plist =
536          "$target_gen_dir/${_variant.target_name}_partial_info.plist"
537
538      if (!defined(deps)) {
539        deps = []
540      }
541      deps += [ ":$_generate_info_plist" ]
542
543      if (!defined(bundle_deps)) {
544        bundle_deps = []
545      }
546      if (_is_app_bundle) {
547        bundle_deps += [ ":$_bundle_data_pkg_info" ]
548      }
549      bundle_deps += _variant.bundle_deps
550      if (_extract_intents_metadata) {
551        bundle_deps += [ ":$_metadata_bundledata" ]
552      }
553
554      if (target_environment == "simulator") {
555        if (!defined(data_deps)) {
556          data_deps = []
557        }
558        if (build_with_chromium) {
559          data_deps += [ "//testing/iossim" ]
560        }
561      }
562    }
563  }
564
565  if (_default_variant.name != "") {
566    _bundle_short_name = "$_output_name$_bundle_extension"
567    action(_target_name) {
568      forward_variables_from(invoker, [ "testonly" ])
569
570      script = "//build/config/ios/hardlink.py"
571      public_deps = []
572      foreach(_variant, _variants) {
573        public_deps += [ ":${_variant.target_name}" ]
574      }
575
576      sources = [ "${_default_variant.bundle_gen_dir}/$_bundle_short_name" ]
577      outputs = [ "$root_out_dir/$_bundle_short_name" ]
578
579      args = [
580               "--output-dir",
581               rebase_path(root_out_dir, root_build_dir),
582               "--relative-to",
583               rebase_path(_default_variant.bundle_gen_dir, root_build_dir),
584             ] + rebase_path(sources, root_build_dir)
585    }
586  }
587}
588
589set_defaults("ios_app_bundle") {
590  configs = default_executable_configs
591}
592
593# Template to build an application extension bundle for iOS.
594#
595# This should be used instead of "executable" built-in target type on iOS.
596# As the template forward the generation of the application executable to
597# an "executable" target, all arguments supported by "executable" targets
598# are also supported by this template.
599#
600# Arguments
601#
602#   output_name:
603#       (optional) string, name of the generated application, if omitted,
604#       defaults to the target_name.
605#
606#   extra_substitutions:
607#       (optional) list of string in "key=value" format, each value will
608#       be used as an additional variable substitution rule when generating
609#       the application Info.plist
610#
611#   info_plist:
612#       (optional) string, path to the Info.plist file that will be used for
613#       the bundle.
614#
615#   info_plist_target:
616#       (optional) string, if the info_plist is generated from an action,
617#       rather than a regular source file, specify the target name in lieu
618#       of info_plist. The two arguments are mutually exclusive.
619#
620# For more information, see "gn help executable".
621template("ios_appex_bundle") {
622  assert(ios_is_app_extension,
623         "$target_name needs to be defined in app extension toolchain context")
624  ios_app_bundle(target_name) {
625    forward_variables_from(invoker,
626                           "*",
627                           [
628                             "bundle_extension",
629                             "product_type",
630                           ])
631    product_type = apple_mobile_xcode_appex_bundle_id
632  }
633}
634
635set_defaults("ios_appex_bundle") {
636  configs = [ "//build/config/ios:ios_extension_executable_flags" ]
637}
638
639# Template to package a shared library into an iOS framework bundle.
640#
641# By default, the bundle target this template generates does not link the
642# resulting framework into anything that depends on it. If a dependency wants
643# a link-time (as well as build-time) dependency on the framework bundle,
644# depend against "$target_name+link". If only the build-time dependency is
645# required (e.g., for copying into another bundle), then use "$target_name".
646#
647# Arguments
648#
649#     output_name:
650#         (optional) string, name of the generated framework without the
651#         .framework suffix. If omitted, defaults to target_name.
652#
653#     public_headers:
654#         (optional) list of paths to header file that needs to be copied
655#         into the framework bundle Headers subdirectory. If omitted or
656#         empty then the Headers subdirectory is not created.
657#
658#     sources
659#         (optional) list of files. Needs to be defined and non-empty if
660#         public_headers is defined and non-empty.
661#
662#   enable_code_signing
663#       (optional) boolean, control whether code signing is enabled or not,
664#       default to ios_enable_code_signing if not defined.
665#
666#   transparent
667#       (optional) boolean, whether the bundle is "transparent"; defaults to
668#       "false" if omitted; a bundle is considered "transparent" if it does
669#       not package the "bundle_data" deps but forward them to all targets
670#       the depend on it (unless the "bundle_data" target sets "product_type"
671#       to "com.apple.product-type.framework").
672#
673# This template provides two targets for the resulting framework bundle. The
674# link-time behavior varies depending on which of the two targets below is
675# added as a dependency:
676#   - $target_name only adds a build-time dependency. Targets that depend on
677#     it will not link against the framework.
678#   - $target_name+link adds a build-time and link-time dependency. Targets
679#     that depend on it will link against the framework.
680#
681# The build-time-only dependency is used for when a target needs to use the
682# framework either only for resources, or because the target loads it at run-
683# time, via dlopen() or NSBundle. The link-time dependency will cause the
684# dependee to have the framework loaded by dyld at launch.
685#
686# Example of build-time only dependency:
687#
688#     framework_bundle("CoreTeleportation") {
689#       sources = [ ... ]
690#     }
691#
692#     bundle_data("core_teleportation_bundle_data") {
693#       deps = [ ":CoreTeleportation" ]
694#       sources = [ "$root_out_dir/CoreTeleportation.framework" ]
695#       outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ]
696#     }
697#
698#     app_bundle("GoatTeleporter") {
699#       sources = [ ... ]
700#       deps = [
701#         ":core_teleportation_bundle_data",
702#       ]
703#     }
704#
705# The GoatTeleporter.app will not directly link against
706# CoreTeleportation.framework, but it will be included in the bundle's
707# Frameworks directory.
708#
709# Example of link-time dependency:
710#
711#     framework_bundle("CoreTeleportation") {
712#       sources = [ ... ]
713#       ldflags = [
714#         "-install_name",
715#         "@executable_path/../Frameworks/$target_name.framework"
716#       ]
717#     }
718#
719#     bundle_data("core_teleportation_bundle_data") {
720#       deps = [ ":CoreTeleportation+link" ]
721#       sources = [ "$root_out_dir/CoreTeleportation.framework" ]
722#       outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ]
723#     }
724#
725#     app_bundle("GoatTeleporter") {
726#       sources = [ ... ]
727#       deps = [
728#         ":core_teleportation_bundle_data",
729#       ]
730#     }
731#
732# Note that the framework is still copied to the app's bundle, but dyld will
733# load this library when the app is launched because it uses the "+link"
734# target as a dependency. This also requires that the framework set its
735# install_name so that dyld can locate it.
736#
737# See "gn help shared_library" for more information on arguments supported
738# by shared library target.
739template("ios_framework_bundle") {
740  _target_name = target_name
741  _output_name = target_name
742  if (defined(invoker.output_name)) {
743    _output_name = invoker.output_name
744  }
745
746  _product_type = "com.apple.product-type.framework"
747  _has_public_headers =
748      defined(invoker.public_headers) && invoker.public_headers != []
749
750  _shared_library_target = _target_name + "_shared_library"
751  _link_target_name = _target_name + "+link"
752
753  if (_has_public_headers) {
754    _default_toolchain_target_gen_dir =
755        get_label_info("$_target_name", "target_gen_dir")
756
757    _framework_headers_target = _target_name + "_framework_headers"
758
759    _headers_map_config = _target_name + "_headers_map"
760    _header_map_filename =
761        "$_default_toolchain_target_gen_dir/$_output_name.headers.hmap"
762    config(_headers_map_config) {
763      visibility = [
764        ":${_shared_library_target}",
765        ":${_target_name}_signed_bundle",
766      ]
767      include_dirs = [ _header_map_filename ]
768    }
769  }
770
771  _framework_headers_config = _target_name + "_framework_headers_config"
772  config(_framework_headers_config) {
773    framework_dirs = [ root_out_dir ]
774  }
775
776  _framework_public_config = _target_name + "_public_config"
777  config(_framework_public_config) {
778    configs = [ ":$_framework_headers_config" ]
779    frameworks = [ "$_output_name.framework" ]
780  }
781
782  shared_library(_shared_library_target) {
783    forward_variables_from(invoker,
784                           "*",
785                           [
786                             "bundle_deps",
787                             "bundle_deps_filter",
788                             "data_deps",
789                             "enable_code_signing",
790                             "extra_substitutions",
791                             "info_plist",
792                             "info_plist_target",
793                             "output_name",
794                             "public_configs",
795                             "transparent",
796                             "visibility",
797                           ])
798
799    visibility = [ ":${_target_name}_signed_bundle" ]
800
801    if (!defined(ldflags)) {
802      ldflags = []
803    }
804    ldflags +=
805        [ "-Wl,-install_name,@rpath/$_output_name.framework/$_output_name" ]
806
807    if (_has_public_headers) {
808      configs += [ ":$_headers_map_config" ]
809
810      if (!defined(deps)) {
811        deps = []
812      }
813      deps += [ ":$_framework_headers_target" ]
814    }
815
816    output_extension = ""
817    output_name = _output_name
818    output_prefix_override = true
819    output_dir = target_out_dir
820  }
821
822  if (_has_public_headers) {
823    _public_headers = invoker.public_headers
824
825    _framework_root_dir = "$root_out_dir/$_output_name.framework"
826    if (target_environment == "simulator" || target_environment == "device") {
827      _framework_contents_dir = _framework_root_dir
828    } else if (target_environment == "catalyst") {
829      _framework_contents_dir = "$_framework_root_dir/Versions/A"
830    }
831
832    _compile_headers_map_target = _target_name + "_compile_headers_map"
833    action(_compile_headers_map_target) {
834      visibility = [ ":$_framework_headers_target" ]
835      forward_variables_from(invoker,
836                             [
837                               "deps",
838                               "public_deps",
839                               "testonly",
840                             ])
841      script = "//build/config/apple/write_framework_hmap.py"
842      outputs = [ _header_map_filename ]
843
844      # The header map generation only wants the list of headers, not all of
845      # sources, so filter any non-header source files from "sources". It is
846      # less error prone that having the developer duplicate the list of all
847      # headers in addition to "sources".
848      sources = []
849      if (defined(invoker.sources)) {
850        foreach(_source, invoker.sources) {
851          if (get_path_info(_source, "extension") == "h") {
852            sources += [ _source ]
853          }
854        }
855      }
856
857      args = [
858               rebase_path(_header_map_filename, root_build_dir),
859               rebase_path(_framework_root_dir, root_build_dir),
860             ] + rebase_path(sources, root_build_dir)
861    }
862
863    _create_module_map_target = _target_name + "_module_map"
864    action(_create_module_map_target) {
865      visibility = [ ":$_framework_headers_target" ]
866      script = "//build/config/apple/write_framework_modulemap.py"
867      outputs = [ "$_framework_contents_dir/Modules/module.modulemap" ]
868      args = [
869        _output_name,
870        rebase_path("$_framework_contents_dir/Modules", root_build_dir),
871      ]
872    }
873
874    _copy_public_headers_target = _target_name + "_copy_public_headers"
875    copy(_copy_public_headers_target) {
876      forward_variables_from(invoker,
877                             [
878                               "testonly",
879                               "deps",
880                             ])
881      visibility = [ ":$_framework_headers_target" ]
882      sources = _public_headers
883      outputs = [ "$_framework_contents_dir/Headers/{{source_file_part}}" ]
884
885      # Do not use forward_variables_from for "public_deps" as
886      # we do not want to forward those dependencies.
887      if (defined(invoker.public_deps)) {
888        if (!defined(deps)) {
889          deps = []
890        }
891        deps += invoker.public_deps
892      }
893    }
894
895    group(_framework_headers_target) {
896      forward_variables_from(invoker, [ "testonly" ])
897      deps = [
898        ":$_compile_headers_map_target",
899        ":$_create_module_map_target",
900      ]
901      public_deps = [ ":$_copy_public_headers_target" ]
902    }
903  }
904
905  # Bundle identifier should respect rfc1034, so replace "_" with "-".
906  _bundle_identifier =
907      "$ios_app_bundle_id_prefix." + string_replace(_output_name, "_", "-")
908
909  _info_plist_target = _target_name + "_info_plist"
910  _info_plist_bundle = _target_name + "_info_plist_bundle"
911  ios_info_plist(_info_plist_target) {
912    visibility = [ ":$_info_plist_bundle" ]
913    executable_name = _output_name
914    forward_variables_from(invoker,
915                           [
916                             "info_plist",
917                             "info_plist_target",
918                           ])
919
920    extra_substitutions = [ "BUNDLE_IDENTIFIER=$_bundle_identifier" ]
921    if (defined(invoker.extra_substitutions)) {
922      extra_substitutions += invoker.extra_substitutions
923    }
924  }
925
926  bundle_data(_info_plist_bundle) {
927    visibility = [ ":${_target_name}_signed_bundle" ]
928    forward_variables_from(invoker, [ "testonly" ])
929    sources = get_target_outputs(":$_info_plist_target")
930    public_deps = [ ":$_info_plist_target" ]
931    product_type = _product_type
932
933    if (target_environment != "catalyst") {
934      outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
935    } else {
936      outputs = [ "{{bundle_resources_dir}}/Info.plist" ]
937    }
938  }
939
940  ios_create_signed_bundle(_target_name + "_signed_bundle") {
941    forward_variables_from(invoker,
942                           [
943                             "bundle_deps",
944                             "bundle_deps_filter",
945                             "data_deps",
946                             "deps",
947                             "enable_code_signing",
948                             "public_configs",
949                             "public_deps",
950                             "testonly",
951                             "transparent",
952                             "visibility",
953                           ])
954
955    product_type = _product_type
956    bundle_extension = ".framework"
957
958    output_name = _output_name
959    bundle_binary_target = ":$_shared_library_target"
960    bundle_binary_output = _output_name
961
962    has_public_headers = _has_public_headers
963
964    # Framework do not have entitlements nor mobileprovision because they use
965    # the one from the bundle using them (.app or .appex) as they are just
966    # dynamic library with shared code.
967    disable_entitlements = true
968    disable_embedded_mobileprovision = true
969
970    if (!defined(deps)) {
971      deps = []
972    }
973    deps += [ ":$_info_plist_bundle" ]
974  }
975
976  group(_target_name) {
977    forward_variables_from(invoker,
978                           [
979                             "public_configs",
980                             "public_deps",
981                             "testonly",
982                             "visibility",
983                           ])
984    if (!defined(public_deps)) {
985      public_deps = []
986    }
987    public_deps += [ ":${_target_name}_signed_bundle" ]
988
989    if (_has_public_headers) {
990      if (!defined(public_configs)) {
991        public_configs = []
992      }
993      public_configs += [ ":$_framework_headers_config" ]
994    }
995  }
996
997  group(_link_target_name) {
998    forward_variables_from(invoker,
999                           [
1000                             "public_configs",
1001                             "public_deps",
1002                             "testonly",
1003                             "visibility",
1004                           ])
1005    if (!defined(public_deps)) {
1006      public_deps = []
1007    }
1008    public_deps += [ ":$_target_name" ]
1009
1010    if (!defined(all_dependent_configs)) {
1011      all_dependent_configs = []
1012    }
1013    all_dependent_configs += [ ":$_framework_public_config" ]
1014  }
1015
1016  bundle_data(_target_name + "+bundle") {
1017    forward_variables_from(invoker,
1018                           [
1019                             "testonly",
1020                             "visibility",
1021                           ])
1022    public_deps = [ ":$_target_name" ]
1023    sources = [ "$root_out_dir/$_output_name.framework" ]
1024    outputs = [ "{{bundle_contents_dir}}/Frameworks/$_output_name.framework" ]
1025  }
1026}
1027
1028set_defaults("ios_framework_bundle") {
1029  configs = default_shared_library_configs
1030}
1031
1032# Template to build a xctest bundle that contains a loadable module for iOS.
1033#
1034# Arguments
1035#
1036#   deps:
1037#       list of labels to depends on, these values are used to create the
1038#       loadable module.
1039#
1040#   product_type
1041#       string, product type for the generated Xcode project, use
1042#       "com.apple.product-type.bundle.unit-test" for unit test and
1043#       "com.apple.product-type.bundle.ui-testing" for UI testing.
1044#
1045#   host_target:
1046#       string, name of the target that depends on the generated bundle, this
1047#       value is used to restrict visibilities.
1048#
1049#   xcode_test_application_name:
1050#       string, name of the test application for Xcode unit or ui test target.
1051#
1052#   output_name
1053#       (optional) string, name of the generated application, if omitted,
1054#       defaults to the target_name.
1055#
1056# This template defines two targets, one named "${target_name}" is the xctest
1057# bundle, and the other named "${target_name}_bundle" is a bundle_data that
1058# wraps the xctest bundle and that only the "${host_target}" can depend on.
1059#
1060template("ios_xctest_bundle") {
1061  assert(defined(invoker.deps), "deps must be defined for $target_name")
1062  assert(defined(invoker.product_type),
1063         "product_type must be defined for $target_name")
1064  assert(invoker.product_type == apple_mobile_xcode_xctest_bundle_id ||
1065             invoker.product_type == apple_mobile_xcode_xcuitest_bundle_id,
1066         "product_type defined for $target_name is invalid.")
1067  assert(defined(invoker.host_target),
1068         "host_target must be defined for $target_name")
1069  assert(defined(invoker.xcode_test_application_name),
1070         "xcode_test_application_name must be defined for $target_name")
1071
1072  _target_name = target_name
1073  _output_name = target_name
1074
1075  if (defined(invoker.output_name)) {
1076    _output_name = invoker.output_name
1077  }
1078
1079  _loadable_module_target = _target_name + "_loadable_module"
1080
1081  loadable_module(_loadable_module_target) {
1082    forward_variables_from(invoker,
1083                           "*",
1084                           [
1085                             "bundle_deps",
1086                             "bundle_deps_filter",
1087                             "host_target",
1088                             "output_dir",
1089                             "output_extension",
1090                             "output_name",
1091                             "output_prefix_override",
1092                             "product_type",
1093                             "testonly",
1094                             "visibility",
1095                             "xcode_test_application_name",
1096                             "xcode_test_application_output_name",
1097                             "xctest_bundle_principal_class",
1098                           ])
1099
1100    testonly = true
1101    visibility = [ ":$_target_name" ]
1102
1103    configs += [ "//build/config/ios:xctest_config" ]
1104
1105    output_dir = target_out_dir
1106    output_name = _output_name
1107    output_prefix_override = true
1108    output_extension = ""
1109  }
1110
1111  _info_plist_target = _target_name + "_info_plist"
1112  _info_plist_bundle = _target_name + "_info_plist_bundle"
1113
1114  # Bundle identifier should respect rfc1034, so replace "_" with "-".
1115  _bundle_identifier = "$ios_app_bundle_id_prefix.chrome." +
1116                       string_replace(_output_name, "_", "-")
1117
1118  ios_info_plist(_info_plist_target) {
1119    testonly = true
1120    visibility = [ ":$_info_plist_bundle" ]
1121
1122    info_plist = "//build/config/ios/Module-Info.plist"
1123    executable_name = _output_name
1124
1125    if (defined(invoker.xctest_bundle_principal_class)) {
1126      _principal_class = invoker.xctest_bundle_principal_class
1127    } else {
1128      # Fall back to a reasonable default value.
1129      _principal_class = "NSObject"
1130    }
1131    extra_substitutions = [
1132      "XCTEST_BUNDLE_PRINCIPAL_CLASS=${_principal_class}",
1133      "BUNDLE_IDENTIFIER=$_bundle_identifier",
1134    ]
1135  }
1136
1137  bundle_data(_info_plist_bundle) {
1138    testonly = true
1139    visibility = [ ":$_target_name" ]
1140
1141    public_deps = [ ":$_info_plist_target" ]
1142
1143    sources = get_target_outputs(":$_info_plist_target")
1144    outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
1145  }
1146
1147  _xctest_bundle = _target_name + "_bundle"
1148  ios_create_signed_bundle(_target_name) {
1149    forward_variables_from(invoker,
1150                           [
1151                             "bundle_deps",
1152                             "bundle_deps_filter",
1153                             "bundle_id",
1154                             "data_deps",
1155                             "enable_code_signing",
1156                             "product_type",
1157                             "transparent",
1158                             "xcode_test_application_name",
1159                           ])
1160
1161    testonly = true
1162    visibility = [ ":$_xctest_bundle" ]
1163
1164    bundle_extension = ".xctest"
1165
1166    output_name = _output_name
1167    bundle_binary_target = ":$_loadable_module_target"
1168    bundle_binary_output = _output_name
1169
1170    xcode_extra_attributes = {
1171      IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target
1172      PRODUCT_BUNDLE_IDENTIFIER = _bundle_identifier
1173      CODE_SIGNING_REQUIRED = "NO"
1174      CODE_SIGNING_ALLOWED = "NO"
1175      CODE_SIGN_IDENTITY = ""
1176      DONT_GENERATE_INFOPLIST_FILE = "YES"
1177
1178      # For XCUITest, Xcode requires specifying the host application name
1179      # via the TEST_TARGET_NAME attribute.
1180      if (invoker.product_type == apple_mobile_xcode_xcuitest_bundle_id) {
1181        TEST_TARGET_NAME = invoker.xcode_test_application_name
1182      }
1183
1184      # For XCTest, Xcode requires specifying the host application path via
1185      # both BUNDLE_LOADER and TEST_HOST attributes.
1186      if (invoker.product_type == apple_mobile_xcode_xctest_bundle_id) {
1187        _xcode_app_name = invoker.xcode_test_application_name
1188        if (defined(invoker.xcode_test_application_output_name)) {
1189          _xcode_app_name = invoker.xcode_test_application_output_name
1190        }
1191
1192        BUNDLE_LOADER = "\$(TEST_HOST)"
1193        TEST_HOST = "\$(BUILT_PRODUCTS_DIR)/" +
1194                    "${_xcode_app_name}.app/${_xcode_app_name}"
1195      }
1196    }
1197
1198    deps = [ ":$_info_plist_bundle" ]
1199  }
1200
1201  bundle_data(_xctest_bundle) {
1202    forward_variables_from(invoker, [ "host_target" ])
1203
1204    testonly = true
1205    visibility = [ ":$host_target" ]
1206
1207    public_deps = [ ":$_target_name" ]
1208    sources = [ "$root_out_dir/$_output_name.xctest" ]
1209    outputs = [ "{{bundle_contents_dir}}/PlugIns/$_output_name.xctest" ]
1210  }
1211}
1212
1213set_defaults("ios_xctest_bundle") {
1214  configs = default_shared_library_configs
1215}
1216
1217# For Chrome on iOS we want to run XCTests for all our build configurations
1218# (Debug, Release, ...). In addition, the symbols visibility is configured to
1219# private by default. To simplify testing with those constraints, our tests are
1220# compiled in the TEST_HOST target instead of the .xctest bundle.
1221template("ios_xctest_test") {
1222  _target_name = target_name
1223  _output_name = target_name
1224  if (defined(invoker.output_name)) {
1225    _output_name = invoker.output_name
1226  }
1227
1228  _xctest_target = _target_name + "_module"
1229  _xctest_output = _output_name + "_module"
1230
1231  _host_target = _target_name
1232  _host_output = _output_name
1233
1234  # Allow invokers to specify their own target for the xctest module, but
1235  # fall back to a default (empty) module otherwise.
1236  if (defined(invoker.xctest_module_target)) {
1237    _xctest_module_target = invoker.xctest_module_target
1238  } else {
1239    _xctest_module_target_name = _xctest_target + "shell_source"
1240    _xctest_module_target = ":$_xctest_module_target_name"
1241    source_set(_xctest_module_target_name) {
1242      sources = [ "//build/config/ios/xctest_shell.mm" ]
1243
1244      configs += [ "//build/config/ios:xctest_config" ]
1245    }
1246  }
1247
1248  ios_xctest_bundle(_xctest_target) {
1249    forward_variables_from(invoker, [ "data_deps" ])
1250    output_name = _xctest_output
1251    product_type = apple_mobile_xcode_xctest_bundle_id
1252    host_target = _host_target
1253
1254    # TODO(crbug.com/40120290) The change in output name results in a mismatch
1255    # between this value and the ios_app_bundle target name. To mitigate, this
1256    # has been modified to _host_target. output_name is set to _host_output
1257    # to mitigate the naming.
1258    xcode_test_application_name = _host_target
1259    xcode_test_application_output_name = _host_output
1260
1261    deps = [ _xctest_module_target ]
1262  }
1263
1264  ios_app_bundle(_host_target) {
1265    forward_variables_from(invoker, "*", [ "testonly" ])
1266
1267    testonly = true
1268    output_name = _host_output
1269    configs += [ "//build/config/ios:xctest_config" ]
1270
1271    if (!defined(invoker.info_plist) && !defined(invoker.info_plist_target)) {
1272      info_plist = "//build/config/ios/Host-Info.plist"
1273    }
1274
1275    # Xcode needs the following frameworks installed in the application (and
1276    # signed) for the XCTest to run, so install them using
1277    # extra_system_frameworks.
1278    extra_system_frameworks = [
1279      "$ios_sdk_platform_path/Developer/Library/Frameworks/XCTest.framework",
1280      "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework",
1281      "$ios_sdk_platform_path/Developer/usr/lib/libXCTestBundleInject.dylib",
1282    ]
1283
1284    # Xcode 13 now depends on XCTestCore. To keep things future proof, copy over
1285    # everything that Xcode copies.
1286    if (xcode_version_int >= 1300) {
1287      extra_system_frameworks += [
1288        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestCore.framework",
1289        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUIAutomation.framework",
1290        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUnit.framework",
1291        "$ios_sdk_platform_path/Developer/usr/lib/libXCTestSwiftSupport.dylib",
1292      ]
1293    }
1294
1295    # XCTestSupport framework is required as of Xcode 14.3 or later.
1296    if (xcode_version_int >= 1430) {
1297      extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestSupport.framework" ]
1298    }
1299
1300    _xctest_bundle = _xctest_target + "_bundle"
1301    if (!defined(bundle_deps)) {
1302      bundle_deps = []
1303    }
1304    bundle_deps += [ ":$_xctest_bundle" ]
1305  }
1306}
1307
1308set_defaults("ios_xctest_test") {
1309  configs = default_executable_configs
1310}
1311
1312# Template to build a xcuitest test runner bundle.
1313#
1314# Xcode requires a test runner application with a copy of the XCTest dynamic
1315# library bundle in it for the XCUITest to run. The test runner bundle is created
1316# by copying the system bundle XCTRunner.app from Xcode SDK with the plist file
1317# being properly tweaked, and a xctest and it needs to be code signed in order
1318# to run on devices.
1319#
1320# Arguments
1321#
1322#   xctest_bundle
1323#       string, name of the dependent xctest bundle target.
1324#
1325#   output_name
1326#       (optional) string, name of the generated application, if omitted,
1327#       defaults to the target_name.
1328#
1329template("ios_xcuitest_test_runner_bundle") {
1330  assert(defined(invoker.xctest_bundle),
1331         "xctest_bundle must be defined for $target_name")
1332
1333  _target_name = target_name
1334  _output_name = target_name
1335  if (defined(invoker.output_name)) {
1336    _output_name = invoker.output_name
1337  }
1338
1339  # Bundle identifier should respect rfc1034, so replace "_" with "-".
1340  _bundle_identifier = "$ios_app_bundle_id_prefix.chrome." +
1341                       string_replace(_output_name, "_", "-")
1342
1343  _xctrunner_path =
1344      "$ios_sdk_platform_path/Developer/Library/Xcode/Agents/XCTRunner.app"
1345
1346  _info_plist_merge_plist = _target_name + "_info_plist_merge_plist"
1347  _info_plist_target = _target_name + "_info_plist"
1348  _info_plist_bundle = _target_name + "_info_plist_bundle"
1349
1350  action(_info_plist_merge_plist) {
1351    testonly = true
1352    script = "//build/apple/plist_util.py"
1353
1354    sources = [
1355      "$_xctrunner_path/Info.plist",
1356
1357      # NOTE: The XCTRunnerAddition+Info.plist must come after the Info.plist
1358      # because it overrides the values under "CFBundleIdentifier" and
1359      # "CFBundleName".
1360      "//build/config/ios/resources/XCTRunnerAddition+Info.plist",
1361    ]
1362
1363    _output_name = "$target_gen_dir/${_target_name}_merged.plist"
1364    outputs = [ _output_name ]
1365    args = [
1366             "merge",
1367             "-f=xml1",
1368             "-x=$xcode_version",
1369             "-o=" + rebase_path(_output_name, root_build_dir),
1370           ] + rebase_path(sources, root_build_dir)
1371
1372    if (ios_use_xcode_symlinks) {
1373      deps = [ "//build/config/ios:copy_xctrunner_app" ]
1374    }
1375  }
1376
1377  ios_info_plist(_info_plist_target) {
1378    testonly = true
1379    visibility = [ ":$_info_plist_bundle" ]
1380
1381    executable_name = _output_name
1382    info_plist_target = ":$_info_plist_merge_plist"
1383    extra_substitutions = [ "BUNDLE_IDENTIFIER=$_bundle_identifier" ]
1384  }
1385
1386  bundle_data(_info_plist_bundle) {
1387    testonly = true
1388    visibility = [ ":$_target_name" ]
1389
1390    public_deps = [ ":$_info_plist_target" ]
1391
1392    sources = get_target_outputs(":$_info_plist_target")
1393    outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
1394  }
1395
1396  _pkginfo_bundle = _target_name + "_pkginfo_bundle"
1397  bundle_data(_pkginfo_bundle) {
1398    testonly = true
1399    visibility = [ ":$_target_name" ]
1400
1401    sources = [ "$_xctrunner_path/PkgInfo" ]
1402
1403    outputs = [ "{{bundle_contents_dir}}/PkgInfo" ]
1404
1405    if (ios_use_xcode_symlinks) {
1406      public_deps = [ "//build/config/ios:copy_xctrunner_app" ]
1407    }
1408  }
1409
1410  _xctest_bundle = invoker.xctest_bundle
1411  ios_create_signed_bundle(_target_name) {
1412    testonly = true
1413
1414    bundle_binary_target = "//build/config/ios:xctest_runner_without_arm64e"
1415    bundle_binary_output = "XCTRunner"
1416    bundle_extension = ".app"
1417    product_type = apple_mobile_xcode_app_bundle_id
1418
1419    output_name = _output_name
1420
1421    # Xcode needs the following frameworks installed in the application
1422    # (and signed) for the XCUITest to run, so install them using
1423    # extra_system_frameworks.
1424    extra_system_frameworks = [
1425      "$ios_sdk_platform_path/Developer/Library/Frameworks/XCTest.framework",
1426      "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework",
1427    ]
1428
1429    # Xcode 13 now depends on XCTestCore. To keep things future proof, copy over
1430    # everything that Xcode copies.
1431    if (xcode_version_int >= 1300) {
1432      extra_system_frameworks += [
1433        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestCore.framework",
1434        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUIAutomation.framework",
1435        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUnit.framework",
1436        "$ios_sdk_platform_path/Developer/usr/lib/libXCTestSwiftSupport.dylib",
1437      ]
1438    }
1439
1440    # XCTestSupport framework is required as of Xcode 14.3 or later.
1441    if (xcode_version_int >= 1430) {
1442      extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestSupport.framework" ]
1443    }
1444
1445    bundle_deps = []
1446    if (defined(invoker.bundle_deps)) {
1447      bundle_deps += invoker.bundle_deps
1448    }
1449    bundle_deps += [
1450      ":$_info_plist_bundle",
1451      ":$_pkginfo_bundle",
1452      ":$_xctest_bundle",
1453    ]
1454  }
1455}
1456
1457# Template to build a XCUITest that consists of two parts: the test runner
1458# application bundle and the xctest dynamic library.
1459#
1460# Arguments
1461#
1462#   deps:
1463#       list of labels to depends on, these values are used to create the
1464#       xctest dynamic library.
1465#
1466#   xcode_test_application_name:
1467#       string, name of the test application for the ui test target.
1468#
1469#   runner_only_bundle_deps:
1470#       list of labels of bundle target to include in the runner and
1471#       exclude from the test module (the use case is a framework bundle
1472#       that is used by the test module and thus needs to be packaged in
1473#       the runner application bundle)
1474#
1475# This template defines two targets, one named "${target_name}_module" is the
1476# xctest dynamic library, and the other named "${target_name}_runner" is the
1477# test runner application bundle.
1478#
1479template("ios_xcuitest_test") {
1480  assert(defined(invoker.deps), "deps must be defined for $target_name")
1481  assert(defined(invoker.xcode_test_application_name),
1482         "xcode_test_application_name must be defined for $target_name")
1483
1484  _xcuitest_target = target_name
1485  if (defined(invoker.output_name)) {
1486    _xcuitest_target = invoker.output_name
1487  }
1488
1489  _xcuitest_runner_target = _xcuitest_target + "_runner"
1490  _xcuitest_module_target = _xcuitest_target + "_module"
1491
1492  group(target_name) {
1493    testonly = true
1494
1495    deps = [ ":$_xcuitest_runner_target" ]
1496  }
1497
1498  _xcuitest_module_output = _xcuitest_target
1499  ios_xctest_bundle(_xcuitest_module_target) {
1500    forward_variables_from(invoker,
1501                           [
1502                             "bundle_deps",
1503                             "data_deps",
1504                             "deps",
1505                             "xcode_test_application_name",
1506                             "xctest_bundle_principal_class",
1507                           ])
1508
1509    product_type = apple_mobile_xcode_xcuitest_bundle_id
1510    host_target = _xcuitest_runner_target
1511    output_name = _xcuitest_module_output
1512
1513    if (defined(invoker.runner_only_bundle_deps)) {
1514      bundle_deps_filter = invoker.runner_only_bundle_deps
1515    }
1516  }
1517
1518  _xcuitest_runner_output = _xcuitest_target + "-Runner"
1519  ios_xcuitest_test_runner_bundle(_xcuitest_runner_target) {
1520    output_name = _xcuitest_runner_output
1521    xctest_bundle = _xcuitest_module_target + "_bundle"
1522
1523    if (defined(invoker.runner_only_bundle_deps)) {
1524      if (!defined(bundle_deps)) {
1525        bundle_deps = []
1526      }
1527      bundle_deps += invoker.runner_only_bundle_deps
1528    }
1529  }
1530}
1531
1532set_defaults("ios_xcuitest_test") {
1533  configs = default_executable_configs
1534}
1535