• 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/symbols.gni")
7import("//build/config/compiler/compiler.gni")
8import("//build/config/ios/ios_sdk.gni")
9import("//build/toolchain/goma.gni")
10import("//build/toolchain/rbe.gni")
11import("//build/toolchain/siso.gni")
12import("//build/toolchain/toolchain.gni")
13import("//build_overrides/build.gni")
14
15# Constants corresponding to the bundle type identifiers use application,
16# application extension, XCTest and XCUITest targets respectively.
17_ios_xcode_app_bundle_id = "com.apple.product-type.application"
18_ios_xcode_appex_bundle_id = "com.apple.product-type.app-extension"
19_ios_xcode_xctest_bundle_id = "com.apple.product-type.bundle.unit-test"
20_ios_xcode_xcuitest_bundle_id = "com.apple.product-type.bundle.ui-testing"
21
22# Wrapper around create_bundle taking care of code signature settings.
23#
24# Arguments
25#
26#   product_type
27#       string, product type for the generated Xcode project.
28#
29#   bundle_gen_dir
30#       (optional) directory where the bundle is generated; must be below
31#       root_out_dir and defaults to root_out_dir if omitted.
32#
33#   bundle_deps
34#       (optional) list of additional dependencies.
35#
36#   bundle_deps_filter
37#       (optional) list of dependencies to filter (for more information
38#       see "gn help bundle_deps_filter").
39#
40#   bundle_extension
41#       string, extension of the bundle, used to generate bundle name.
42#
43#   bundle_binary_target
44#       (optional) string, label of the target generating the bundle main
45#       binary. This target and bundle_binary_path are mutually exclusive.
46#
47#   bundle_binary_output
48#       (optional) string, base name of the binary generated by the
49#       bundle_binary_target target, defaults to the target name.
50#
51#   bundle_binary_path
52#       (optional) string, path to the bundle main binary. This target and
53#       bundle_binary_target are mutually exclusive.
54#
55#   output_name:
56#       (optional) string, name of the generated application, if omitted,
57#       defaults to the target_name.
58#
59#   extra_system_frameworks
60#       (optional) list of system framework to copy to the bundle.
61#
62#   enable_code_signing
63#       (optional) boolean, control whether code signing is enabled or not,
64#       default to ios_enable_code_signing if not defined.
65#
66#   entitlements_path:
67#       (optional) path to the template to use to generate the application
68#       entitlements by performing variable substitutions, defaults to
69#       //build/config/ios/entitlements.plist.
70#
71#   entitlements_target:
72#       (optional) label of the target generating the application
73#       entitlements (must generate a single file as output); cannot be
74#       defined if entitlements_path is set.
75#
76#   has_public_headers:
77#       (optional) boolean, defaults to false; only meaningful if the bundle
78#       is a framework bundle; if true, then the frameworks includes public
79#       headers
80#
81#   disable_entitlements
82#       (optional, defaults to false) boolean, control whether entitlements willi
83#       be embedded in the application during signature. If false and no
84#       entitlements are provided, default empty entitlements will be used.
85#
86#   disable_embedded_mobileprovision
87#       (optional, default to false) boolean, control whether mobile provisions
88#       will be embedded in the bundle. If true, the existing
89#       embedded.mobileprovision will be deleted.
90#
91#   xcode_extra_attributes
92#       (optional) scope, extra attributes for Xcode projects.
93#
94#   xcode_test_application_name:
95#       (optional) string, name of the test application for Xcode unit or ui
96#       test target.
97#
98#   xcode_product_bundle_id:
99#       (optional) string, the bundle ID that will be added in the XCode
100#       attributes to enable some features when debugging (e.g. MetricKit).
101#
102#   primary_info_plist:
103#       (optional) path to Info.plist to merge with the $partial_info_plist
104#       generated by the compilation of the asset catalog.
105#
106#   partial_info_plist:
107#       (optional) path to the partial Info.plist generated by the asset
108#       catalog compiler; if defined $primary_info_plist must also be defined.
109#
110#   transparent
111#       (optional) boolean, whether the bundle is "transparent"; defaults to
112#       "false" if ommitted; a bundle is considered "transparent" if it does
113#       not package the "bundle_data" deps but forward them to all targets
114#       the depend on it (unless the "bundle_data" target sets "product_type"
115#       to the same value as the "create_signed_bundle" target).
116#
117template("create_signed_bundle") {
118  assert(defined(invoker.product_type),
119         "product_type must be defined for $target_name")
120  assert(defined(invoker.bundle_extension),
121         "bundle_extension must be defined for $target_name")
122  assert(defined(invoker.bundle_binary_target) !=
123             defined(invoker.bundle_binary_path),
124         "Only one of bundle_binary_target or bundle_binary_path may be " +
125             "specified for $target_name")
126  assert(!defined(invoker.partial_info_plist) ||
127             defined(invoker.primary_info_plist),
128         "primary_info_plist must be defined when partial_info_plist is " +
129             "defined for $target_name")
130
131  if (defined(invoker.xcode_test_application_name)) {
132    assert(
133        invoker.product_type == _ios_xcode_xctest_bundle_id ||
134            invoker.product_type == _ios_xcode_xcuitest_bundle_id,
135        "xcode_test_application_name can be only defined for Xcode unit or ui test target.")
136  }
137
138  _target_name = target_name
139  _output_name = target_name
140  if (defined(invoker.output_name)) {
141    _output_name = invoker.output_name
142  }
143
144  if (defined(invoker.bundle_binary_path)) {
145    _bundle_binary_path = invoker.bundle_binary_path
146  } else {
147    _bundle_binary_target = invoker.bundle_binary_target
148    _bundle_binary_output = get_label_info(_bundle_binary_target, "name")
149    if (defined(invoker.bundle_binary_output)) {
150      _bundle_binary_output = invoker.bundle_binary_output
151    }
152    _bundle_binary_path =
153        get_label_info(_bundle_binary_target, "target_out_dir") +
154        "/$_bundle_binary_output"
155  }
156
157  _bundle_gen_dir = root_out_dir
158  if (defined(invoker.bundle_gen_dir)) {
159    _bundle_gen_dir = invoker.bundle_gen_dir
160  }
161
162  _bundle_extension = invoker.bundle_extension
163
164  _enable_embedded_mobileprovision = true
165  if (defined(invoker.disable_embedded_mobileprovision)) {
166    _enable_embedded_mobileprovision = !invoker.disable_embedded_mobileprovision
167  }
168
169  if (target_environment == "catalyst") {
170    _enable_embedded_mobileprovision = false
171  }
172
173  _enable_entitlements = true
174  if (defined(invoker.disable_entitlements)) {
175    _enable_entitlements = !invoker.disable_entitlements
176  }
177
178  if (_enable_entitlements) {
179    if (!defined(invoker.entitlements_target)) {
180      _entitlements_path = "//build/config/ios/entitlements.plist"
181      if (defined(invoker.entitlements_path)) {
182        _entitlements_path = invoker.entitlements_path
183      }
184    } else {
185      assert(!defined(invoker.entitlements_path),
186             "Cannot define both entitlements_path and entitlements_target " +
187                 "for $target_name")
188
189      _entitlements_target_outputs =
190          get_target_outputs(invoker.entitlements_target)
191      _entitlements_path = _entitlements_target_outputs[0]
192    }
193  }
194
195  _enable_code_signing = ios_enable_code_signing
196  if (defined(invoker.enable_code_signing)) {
197    _enable_code_signing = invoker.enable_code_signing
198  }
199
200  create_bundle(_target_name) {
201    forward_variables_from(invoker,
202                           [
203                             "bundle_deps_filter",
204                             "data_deps",
205                             "deps",
206                             "partial_info_plist",
207                             "product_type",
208                             "public_configs",
209                             "public_deps",
210                             "testonly",
211                             "transparent",
212                             "visibility",
213                             "xcode_test_application_name",
214                           ])
215
216    bundle_root_dir = "$_bundle_gen_dir/$_output_name$_bundle_extension"
217    if (target_environment == "simulator" || target_environment == "device") {
218      bundle_contents_dir = bundle_root_dir
219      bundle_resources_dir = bundle_contents_dir
220      bundle_executable_dir = bundle_contents_dir
221    } else if (target_environment == "catalyst") {
222      if (_bundle_extension != ".framework") {
223        bundle_contents_dir = "$bundle_root_dir/Contents"
224        bundle_resources_dir = "$bundle_contents_dir/Resources"
225        bundle_executable_dir = "$bundle_contents_dir/MacOS"
226      } else {
227        bundle_contents_dir = "$bundle_root_dir/Versions/A"
228        bundle_resources_dir = "$bundle_contents_dir/Resources"
229        bundle_executable_dir = bundle_contents_dir
230      }
231    }
232
233    if (!defined(public_deps)) {
234      public_deps = []
235    }
236
237    _bundle_identifier = ""
238    if (defined(invoker.xcode_product_bundle_id)) {
239      _bundle_identifier = invoker.xcode_product_bundle_id
240      assert(_bundle_identifier == string_replace(_bundle_identifier, "_", "-"),
241             "$target_name: bundle_identifier does not respect rfc1034: " +
242                 _bundle_identifier)
243    }
244
245    xcode_extra_attributes = {
246      IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target
247      PRODUCT_BUNDLE_IDENTIFIER = _bundle_identifier
248      CODE_SIGNING_REQUIRED = "NO"
249      CODE_SIGNING_ALLOWED = "NO"
250      CODE_SIGN_IDENTITY = ""
251      DONT_GENERATE_INFOPLIST_FILE = "YES"
252
253      # If invoker has defined extra attributes, they override the defaults.
254      if (defined(invoker.xcode_extra_attributes)) {
255        forward_variables_from(invoker.xcode_extra_attributes, "*")
256      }
257    }
258
259    if (defined(invoker.bundle_binary_target)) {
260      public_deps += [ invoker.bundle_binary_target ]
261    }
262
263    if (defined(invoker.bundle_deps)) {
264      if (!defined(deps)) {
265        deps = []
266      }
267      deps += invoker.bundle_deps
268    }
269    if (!defined(deps)) {
270      deps = []
271    }
272
273    code_signing_script = "//build/config/ios/codesign.py"
274    code_signing_sources = [ _bundle_binary_path ]
275    if (_enable_entitlements) {
276      if (defined(invoker.entitlements_target)) {
277        deps += [ invoker.entitlements_target ]
278      }
279      code_signing_sources += [ _entitlements_path ]
280    }
281    code_signing_outputs = [ "$bundle_executable_dir/$_output_name" ]
282    if (_enable_code_signing) {
283      code_signing_outputs +=
284          [ "$bundle_contents_dir/_CodeSignature/CodeResources" ]
285    }
286    if (ios_code_signing_identity != "" && target_environment == "device" &&
287        _enable_embedded_mobileprovision) {
288      code_signing_outputs +=
289          [ "$bundle_contents_dir/embedded.mobileprovision" ]
290    }
291    if (_bundle_extension == ".framework") {
292      if (target_environment == "catalyst") {
293        code_signing_outputs += [
294          "$bundle_root_dir/Versions/Current",
295          "$bundle_root_dir/$_output_name",
296        ]
297
298        if (defined(invoker.has_public_headers) && invoker.has_public_headers) {
299          code_signing_outputs += [
300            "$bundle_root_dir/Headers",
301            "$bundle_root_dir/Modules",
302          ]
303        }
304      } else {
305        not_needed(invoker, [ "has_public_headers" ])
306      }
307    }
308
309    if (defined(invoker.extra_system_frameworks)) {
310      foreach(_framework, invoker.extra_system_frameworks) {
311        code_signing_outputs += [ "$bundle_contents_dir/Frameworks/" +
312                                  get_path_info(_framework, "file") ]
313      }
314    }
315
316    code_signing_args = [
317      "code-sign-bundle",
318      "-t=" + ios_sdk_name,
319      "-i=" + ios_code_signing_identity,
320      "-b=" + rebase_path(_bundle_binary_path, root_build_dir),
321    ]
322    foreach(mobileprovision, ios_mobileprovision_files) {
323      code_signing_args +=
324          [ "-m=" + rebase_path(mobileprovision, root_build_dir) ]
325    }
326    code_signing_sources += ios_mobileprovision_files
327    if (_enable_entitlements) {
328      code_signing_args +=
329          [ "-e=" + rebase_path(_entitlements_path, root_build_dir) ]
330    }
331    if (!_enable_embedded_mobileprovision) {
332      code_signing_args += [ "--disable-embedded-mobileprovision" ]
333    }
334    code_signing_args += [ rebase_path(bundle_root_dir, root_build_dir) ]
335    if (!_enable_code_signing) {
336      code_signing_args += [ "--disable-code-signature" ]
337    }
338    if (defined(invoker.extra_system_frameworks)) {
339      # All framework in extra_system_frameworks are expected to be system
340      # framework and the path to be already system absolute so do not use
341      # rebase_path here unless using Goma RBE and system Xcode (as in that
342      # case the system framework are found via a symlink in root_build_dir).
343      foreach(_framework, invoker.extra_system_frameworks) {
344        if (use_system_xcode && (use_goma || use_remoteexec || use_siso)) {
345          _framework_path = rebase_path(_framework, root_build_dir)
346        } else {
347          _framework_path = _framework
348        }
349        code_signing_args += [ "-F=$_framework_path" ]
350      }
351    }
352    if (defined(invoker.partial_info_plist)) {
353      _partial_info_plists = [
354        invoker.primary_info_plist,
355        invoker.partial_info_plist,
356      ]
357
358      _plist_compiler_path = "//build/apple/plist_util.py"
359
360      code_signing_sources += _partial_info_plists
361      code_signing_sources += [ _plist_compiler_path ]
362      if (target_environment != "catalyst" ||
363          _bundle_extension != ".framework") {
364        code_signing_outputs += [ "$bundle_contents_dir/Info.plist" ]
365      } else {
366        code_signing_outputs += [ "$bundle_resources_dir/Info.plist" ]
367      }
368
369      code_signing_args +=
370          [ "-P=" + rebase_path(_plist_compiler_path, root_build_dir) ]
371      foreach(_partial_info_plist, _partial_info_plists) {
372        code_signing_args +=
373            [ "-p=" + rebase_path(_partial_info_plist, root_build_dir) ]
374      }
375    }
376  }
377}
378
379# Generates Info.plist files for iOS apps and frameworks.
380#
381# Arguments
382#
383#     info_plist:
384#         (optional) string, path to the Info.plist file that will be used for
385#         the bundle.
386#
387#     info_plist_target:
388#         (optional) string, if the info_plist is generated from an action,
389#         rather than a regular source file, specify the target name in lieu
390#         of info_plist. The two arguments are mutually exclusive.
391#
392#     executable_name:
393#         string, name of the generated target used for the product
394#         and executable name as specified in the output Info.plist.
395#
396#     extra_substitutions:
397#         (optional) string array, 'key=value' pairs for extra fields which are
398#         specified in a source Info.plist template.
399template("ios_info_plist") {
400  assert(defined(invoker.info_plist) != defined(invoker.info_plist_target),
401         "Only one of info_plist or info_plist_target may be specified in " +
402             target_name)
403
404  if (defined(invoker.info_plist)) {
405    _info_plist = invoker.info_plist
406  } else {
407    _info_plist_target_output = get_target_outputs(invoker.info_plist_target)
408    _info_plist = _info_plist_target_output[0]
409  }
410
411  apple_info_plist(target_name) {
412    format = "binary1"
413    extra_substitutions = [
414      "IOS_BUNDLE_ID_PREFIX=$ios_app_bundle_id_prefix",
415      "IOS_PLATFORM_BUILD=$ios_platform_build",
416      "IOS_PLATFORM_NAME=$ios_sdk_name",
417      "IOS_PLATFORM_VERSION=$ios_sdk_version",
418      "IOS_SDK_BUILD=$ios_sdk_build",
419      "IOS_SDK_NAME=$ios_sdk_name$ios_sdk_version",
420      "IOS_SUPPORTED_PLATFORM=$ios_sdk_platform",
421      "BUILD_MACHINE_OS_BUILD=$machine_os_build",
422      "IOS_DEPLOYMENT_TARGET=$ios_deployment_target",
423      "XCODE_BUILD=$xcode_build",
424      "XCODE_VERSION=$xcode_version",
425    ]
426    if (defined(invoker.extra_substitutions)) {
427      extra_substitutions += invoker.extra_substitutions
428    }
429    plist_templates = [
430      "//build/config/ios/BuildInfo.plist",
431      _info_plist,
432    ]
433    if (defined(invoker.info_plist_target)) {
434      deps = [ invoker.info_plist_target ]
435    }
436    forward_variables_from(invoker,
437                           [
438                             "executable_name",
439                             "output_name",
440                             "visibility",
441                             "testonly",
442                           ])
443  }
444}
445
446# Template to build an application bundle for iOS.
447#
448# This should be used instead of "executable" built-in target type on iOS.
449# As the template forward the generation of the application executable to
450# an "executable" target, all arguments supported by "executable" targets
451# are also supported by this template.
452#
453# Arguments
454#
455#   output_name:
456#       (optional) string, name of the generated application, if omitted,
457#       defaults to the target_name.
458#
459#   extra_substitutions:
460#       (optional) list of string in "key=value" format, each value will
461#       be used as an additional variable substitution rule when generating
462#       the application Info.plist
463#
464#   info_plist:
465#       (optional) string, path to the Info.plist file that will be used for
466#       the bundle.
467#
468#   info_plist_target:
469#       (optional) string, if the info_plist is generated from an action,
470#       rather than a regular source file, specify the target name in lieu
471#       of info_plist. The two arguments are mutually exclusive.
472#
473#   entitlements_path:
474#       (optional) path to the template to use to generate the application
475#       entitlements by performing variable substitutions, defaults to
476#       //build/config/ios/entitlements.plist.
477#
478#   entitlements_target:
479#       (optional) label of the target generating the application
480#       entitlements (must generate a single file as output); cannot be
481#       defined if entitlements_path is set.
482#
483#   product_type
484#       (optional) string, product type for the generated Xcode project,
485#       default to "com.apple.product-type.application". Should only be
486#       overriden when building application extension.
487#
488#   enable_code_signing
489#       (optional) boolean, control whether code signing is enabled or not,
490#       default to ios_enable_code_signing if not defined.
491#
492#   variants
493#       (optional) list of scopes, each scope needs to define the attributes
494#       "name" and "bundle_deps"; if defined and non-empty, then one bundle
495#       named $target_out_dir/$variant/$output_name will be created for each
496#       variant with the same binary but the correct bundle_deps, the bundle
497#       at $target_out_dir/$output_name will be a copy of the first variant.
498#
499#   bundle_identifier:
500#       (optional) string, value of CFBundleIdentifier in the application
501#       Info.plist, defaults to "$ios_app_bundle_id_prefix.$output_name"
502#       if omitted. Will be used to set BUNDLE_IDENTIFIER when generating
503#       the application Info.plist
504#
505# For more information, see "gn help executable".
506template("ios_app_bundle") {
507  _output_name = target_name
508  _target_name = target_name
509  if (defined(invoker.output_name)) {
510    _output_name = invoker.output_name
511  }
512
513  assert(
514      !defined(invoker.bundle_extension),
515      "bundle_extension must not be set for ios_app_bundle template for $target_name")
516
517  if (defined(invoker.bundle_identifier)) {
518    _bundle_identifier = invoker.bundle_identifier
519    assert(_bundle_identifier == string_replace(_bundle_identifier, "_", "-"),
520           "$target_name: bundle_identifier does not respect rfc1034: " +
521               _bundle_identifier)
522  } else {
523    # Bundle identifier should respect rfc1034, so replace "_" with "-".
524    _bundle_identifier =
525        "$ios_app_bundle_id_prefix." + string_replace(_output_name, "_", "-")
526  }
527
528  if (defined(invoker.variants) && invoker.variants != []) {
529    _variants = []
530
531    foreach(_variant, invoker.variants) {
532      assert(defined(_variant.name) && _variant.name != "",
533             "name must be defined for all $target_name variants")
534
535      assert(defined(_variant.bundle_deps),
536             "bundle_deps must be defined for all $target_name variants")
537
538      _variants += [
539        {
540          name = _variant.name
541          bundle_deps = _variant.bundle_deps
542          target_name = "${_target_name}_variants_${_variant.name}"
543          bundle_gen_dir = "$root_out_dir/variants/${_variant.name}"
544        },
545      ]
546    }
547  } else {
548    # If no variants are passed to the template, use a fake variant with
549    # no name to avoid duplicating code. As no variant can have an empty
550    # name except this fake variant, it is possible to know if a variant
551    # is fake or not.
552    _variants = [
553      {
554        name = ""
555        bundle_deps = []
556        target_name = _target_name
557        bundle_gen_dir = root_out_dir
558      },
559    ]
560  }
561
562  _default_variant = _variants[0]
563
564  _executable_target = _target_name + "_executable"
565  _generate_entitlements_target = _target_name + "_gen_entitlements"
566  _generate_entitlements_output =
567      get_label_info(":$_generate_entitlements_target", "target_out_dir") +
568      "/$_output_name.xcent"
569
570  _product_type = _ios_xcode_app_bundle_id
571  if (defined(invoker.product_type)) {
572    _product_type = invoker.product_type
573  }
574
575  if (_product_type == _ios_xcode_app_bundle_id) {
576    _bundle_extension = ".app"
577  } else if (_product_type == _ios_xcode_appex_bundle_id) {
578    _bundle_extension = ".appex"
579  } else {
580    assert(false, "unknown product_type \"$product_type\" for $_target_name")
581  }
582
583  _is_app_bundle = _product_type == _ios_xcode_app_bundle_id
584
585  executable(_executable_target) {
586    forward_variables_from(invoker,
587                           "*",
588                           [
589                             "bundle_deps",
590                             "bundle_deps_filter",
591                             "bundle_extension",
592                             "enable_code_signing",
593                             "entitlements_path",
594                             "entitlements_target",
595                             "extra_substitutions",
596                             "extra_system_frameworks",
597                             "info_plist",
598                             "info_plist_target",
599                             "output_name",
600                             "product_type",
601                             "visibility",
602                             "xcode_extra_attributes",
603                           ])
604
605    visibility = []
606    foreach(_variant, _variants) {
607      visibility += [ ":${_variant.target_name}" ]
608    }
609
610    if (target_environment == "simulator") {
611      if (!defined(deps)) {
612        deps = []
613      }
614      deps += [ ":$_generate_entitlements_target" ]
615
616      if (!defined(inputs)) {
617        inputs = []
618      }
619      inputs += [ _generate_entitlements_output ]
620
621      if (!defined(ldflags)) {
622        ldflags = []
623      }
624      ldflags += [ "-Wl,-sectcreate,__TEXT,__entitlements," +
625                   rebase_path(_generate_entitlements_output, root_build_dir) ]
626    }
627
628    output_name = _output_name
629    output_prefix_override = true
630    output_dir = target_out_dir
631  }
632
633  _generate_info_plist = target_name + "_generate_info_plist"
634  ios_info_plist(_generate_info_plist) {
635    forward_variables_from(invoker,
636                           [
637                             "info_plist",
638                             "info_plist_target",
639                           ])
640
641    executable_name = _output_name
642
643    extra_substitutions = [ "BUNDLE_IDENTIFIER=$_bundle_identifier" ]
644    if (defined(invoker.extra_substitutions)) {
645      extra_substitutions += invoker.extra_substitutions
646    }
647  }
648
649  if (!defined(invoker.entitlements_target)) {
650    _entitlements_path = "//build/config/ios/entitlements.plist"
651    if (defined(invoker.entitlements_path)) {
652      _entitlements_path = invoker.entitlements_path
653    }
654  } else {
655    assert(!defined(invoker.entitlements_path),
656           "Cannot define both entitlements_path and entitlements_target" +
657               "for $_target_name")
658
659    _entitlements_target_outputs =
660        get_target_outputs(invoker.entitlements_target)
661    _entitlements_path = _entitlements_target_outputs[0]
662  }
663
664  action(_generate_entitlements_target) {
665    _gen_info_plist_outputs = get_target_outputs(":$_generate_info_plist")
666    _info_plist_path = _gen_info_plist_outputs[0]
667
668    script = "//build/config/ios/codesign.py"
669    deps = [ ":$_generate_info_plist" ]
670    if (defined(invoker.entitlements_target)) {
671      deps += [ invoker.entitlements_target ]
672    }
673    sources = [
674      _entitlements_path,
675      _info_plist_path,
676    ]
677    sources += ios_mobileprovision_files
678
679    outputs = [ _generate_entitlements_output ]
680
681    args = [
682      "generate-entitlements",
683      "-e=" + rebase_path(_entitlements_path, root_build_dir),
684      "-p=" + rebase_path(_info_plist_path, root_build_dir),
685    ]
686    foreach(mobileprovision, ios_mobileprovision_files) {
687      args += [ "-m=" + rebase_path(mobileprovision, root_build_dir) ]
688    }
689    args += rebase_path(outputs, root_build_dir)
690  }
691
692  # Only write PkgInfo for real application, not application extension.
693  if (_is_app_bundle) {
694    _create_pkg_info = target_name + "_pkg_info"
695    action(_create_pkg_info) {
696      forward_variables_from(invoker, [ "testonly" ])
697      script = "//build/apple/write_pkg_info.py"
698      inputs = [ "//build/apple/plist_util.py" ]
699      sources = get_target_outputs(":$_generate_info_plist")
700      outputs = [
701        # Cannot name the output PkgInfo as the name will not be unique if
702        # multiple ios_app_bundle are defined in the same BUILD.gn file. The
703        # file is renamed in the bundle_data outputs to the correct name.
704        "$target_gen_dir/$target_name",
705      ]
706      args = [ "--plist" ] + rebase_path(sources, root_build_dir) +
707             [ "--output" ] + rebase_path(outputs, root_build_dir)
708      deps = [ ":$_generate_info_plist" ]
709    }
710
711    _bundle_data_pkg_info = target_name + "_bundle_data_pkg_info"
712    bundle_data(_bundle_data_pkg_info) {
713      forward_variables_from(invoker, [ "testonly" ])
714      sources = get_target_outputs(":$_create_pkg_info")
715      outputs = [ "{{bundle_resources_dir}}/PkgInfo" ]
716      public_deps = [ ":$_create_pkg_info" ]
717    }
718  }
719
720  foreach(_variant, _variants) {
721    create_signed_bundle(_variant.target_name) {
722      forward_variables_from(invoker,
723                             [
724                               "bundle_deps",
725                               "bundle_deps_filter",
726                               "data_deps",
727                               "deps",
728                               "enable_code_signing",
729                               "entitlements_path",
730                               "entitlements_target",
731                               "extra_system_frameworks",
732                               "public_configs",
733                               "public_deps",
734                               "testonly",
735                               "visibility",
736                               "xcode_extra_attributes",
737                             ])
738
739      output_name = _output_name
740      bundle_gen_dir = _variant.bundle_gen_dir
741      bundle_binary_target = ":$_executable_target"
742      bundle_binary_output = _output_name
743      bundle_extension = _bundle_extension
744      product_type = _product_type
745      xcode_product_bundle_id = _bundle_identifier
746
747      _generate_info_plist_outputs =
748          get_target_outputs(":$_generate_info_plist")
749      primary_info_plist = _generate_info_plist_outputs[0]
750      partial_info_plist =
751          "$target_gen_dir/${_variant.target_name}_partial_info.plist"
752
753      if (!defined(deps)) {
754        deps = []
755      }
756      deps += [ ":$_generate_info_plist" ]
757
758      if (!defined(bundle_deps)) {
759        bundle_deps = []
760      }
761      if (_is_app_bundle) {
762        bundle_deps += [ ":$_bundle_data_pkg_info" ]
763      }
764      bundle_deps += _variant.bundle_deps
765
766      if (target_environment == "simulator") {
767        if (!defined(data_deps)) {
768          data_deps = []
769        }
770        if (build_with_chromium) {
771          data_deps += [ "//testing/iossim" ]
772        }
773      }
774    }
775  }
776
777  if (_default_variant.name != "") {
778    _bundle_short_name = "$_output_name$_bundle_extension"
779    action(_target_name) {
780      forward_variables_from(invoker, [ "testonly" ])
781
782      script = "//build/config/ios/hardlink.py"
783      public_deps = []
784      foreach(_variant, _variants) {
785        public_deps += [ ":${_variant.target_name}" ]
786      }
787
788      sources = [ "${_default_variant.bundle_gen_dir}/$_bundle_short_name" ]
789      outputs = [ "$root_out_dir/$_bundle_short_name" ]
790
791      args = rebase_path(sources, root_build_dir) +
792             rebase_path(outputs, root_build_dir)
793    }
794  }
795}
796
797set_defaults("ios_app_bundle") {
798  configs = default_executable_configs
799}
800
801# Template to build an application extension bundle for iOS.
802#
803# This should be used instead of "executable" built-in target type on iOS.
804# As the template forward the generation of the application executable to
805# an "executable" target, all arguments supported by "executable" targets
806# are also supported by this template.
807#
808# Arguments
809#
810#   output_name:
811#       (optional) string, name of the generated application, if omitted,
812#       defaults to the target_name.
813#
814#   extra_substitutions:
815#       (optional) list of string in "key=value" format, each value will
816#       be used as an additional variable substitution rule when generating
817#       the application Info.plist
818#
819#   info_plist:
820#       (optional) string, path to the Info.plist file that will be used for
821#       the bundle.
822#
823#   info_plist_target:
824#       (optional) string, if the info_plist is generated from an action,
825#       rather than a regular source file, specify the target name in lieu
826#       of info_plist. The two arguments are mutually exclusive.
827#
828# For more information, see "gn help executable".
829template("ios_appex_bundle") {
830  assert(ios_is_app_extension,
831         "$target_name needs to be defined in app extension toolchain context")
832  ios_app_bundle(target_name) {
833    forward_variables_from(invoker,
834                           "*",
835                           [
836                             "bundle_extension",
837                             "product_type",
838                           ])
839    product_type = _ios_xcode_appex_bundle_id
840  }
841}
842
843set_defaults("ios_appex_bundle") {
844  configs = [ "//build/config/ios:ios_extension_executable_flags" ]
845}
846
847# Template to compile .xib and .storyboard files.
848#
849# Arguments
850#
851#     sources:
852#         list of string, sources to compile
853#
854#     ibtool_flags:
855#         (optional) list of string, additional flags to pass to the ibtool
856template("compile_ib_files") {
857  action_foreach(target_name) {
858    forward_variables_from(invoker,
859                           [
860                             "testonly",
861                             "visibility",
862                           ])
863    assert(defined(invoker.sources),
864           "sources must be specified for $target_name")
865    assert(defined(invoker.output_extension),
866           "output_extension must be specified for $target_name")
867
868    ibtool_flags = []
869    if (defined(invoker.ibtool_flags)) {
870      ibtool_flags = invoker.ibtool_flags
871    }
872
873    _output_extension = invoker.output_extension
874
875    script = "//build/config/ios/compile_ib_files.py"
876    sources = invoker.sources
877    outputs = [
878      "$target_gen_dir/$target_name/{{source_name_part}}.$_output_extension",
879    ]
880    args = [
881      "--input",
882      "{{source}}",
883      "--output",
884      rebase_path(
885          "$target_gen_dir/$target_name/{{source_name_part}}.$_output_extension",
886          root_build_dir),
887    ]
888    args += ibtool_flags
889  }
890}
891
892# Compile a xib or storyboard file and add it to a bundle_data so that it is
893# available at runtime in the bundle.
894#
895# Arguments
896#
897#   source:
898#       string, path of the xib or storyboard to compile.
899#
900# Forwards all variables to the bundle_data target.
901template("bundle_data_ib_file") {
902  assert(defined(invoker.source), "source needs to be defined for $target_name")
903
904  _source_extension = get_path_info(invoker.source, "extension")
905  assert(_source_extension == "xib" || _source_extension == "storyboard",
906         "source must be a .xib or .storyboard for $target_name")
907
908  _target_name = target_name
909  if (_source_extension == "xib") {
910    _compile_ib_file = target_name + "_compile_xib"
911    _output_extension = "nib"
912  } else {
913    _compile_ib_file = target_name + "_compile_storyboard"
914    _output_extension = "storyboardc"
915  }
916
917  compile_ib_files(_compile_ib_file) {
918    sources = [ invoker.source ]
919    output_extension = _output_extension
920    visibility = [ ":$_target_name" ]
921    ibtool_flags = [
922      "--minimum-deployment-target",
923      ios_deployment_target,
924      "--auto-activate-custom-fonts",
925      "--target-device",
926      "iphone",
927      "--target-device",
928      "ipad",
929    ]
930  }
931
932  bundle_data(_target_name) {
933    forward_variables_from(invoker, "*", [ "source" ])
934
935    if (!defined(public_deps)) {
936      public_deps = []
937    }
938    public_deps += [ ":$_compile_ib_file" ]
939
940    sources = get_target_outputs(":$_compile_ib_file")
941
942    outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
943  }
944}
945
946# Compile a strings file and add it to a bundle_data so that it is available
947# at runtime in the bundle.
948#
949# Arguments
950#
951#   source:
952#       string, path of the strings file to compile.
953#
954#   output:
955#       string, path of the compiled file in the final bundle.
956#
957# Forwards all variables to the bundle_data target.
958template("bundle_data_strings") {
959  assert(defined(invoker.source), "source needs to be defined for $target_name")
960  assert(defined(invoker.output), "output needs to be defined for $target_name")
961
962  _source_extension = get_path_info(invoker.source, "extension")
963  assert(_source_extension == "strings",
964         "source must be a .strings for $target_name")
965
966  _target_name = target_name
967  _convert_target = target_name + "_compile_strings"
968
969  convert_plist(_convert_target) {
970    visibility = [ ":$_target_name" ]
971    source = invoker.source
972    output =
973        "$target_gen_dir/$_target_name/" + get_path_info(invoker.source, "file")
974    format = "binary1"
975  }
976
977  bundle_data(_target_name) {
978    forward_variables_from(invoker,
979                           "*",
980                           [
981                             "source",
982                             "output",
983                           ])
984
985    if (!defined(public_deps)) {
986      public_deps = []
987    }
988    public_deps += [ ":$_convert_target" ]
989
990    sources = get_target_outputs(":$_convert_target")
991
992    outputs = [ invoker.output ]
993  }
994}
995
996# Template to package a shared library into an iOS framework bundle.
997#
998# By default, the bundle target this template generates does not link the
999# resulting framework into anything that depends on it. If a dependency wants
1000# a link-time (as well as build-time) dependency on the framework bundle,
1001# depend against "$target_name+link". If only the build-time dependency is
1002# required (e.g., for copying into another bundle), then use "$target_name".
1003#
1004# Arguments
1005#
1006#     output_name:
1007#         (optional) string, name of the generated framework without the
1008#         .framework suffix. If omitted, defaults to target_name.
1009#
1010#     public_headers:
1011#         (optional) list of paths to header file that needs to be copied
1012#         into the framework bundle Headers subdirectory. If omitted or
1013#         empty then the Headers subdirectory is not created.
1014#
1015#     sources
1016#         (optional) list of files. Needs to be defined and non-empty if
1017#         public_headers is defined and non-empty.
1018#
1019#   enable_code_signing
1020#       (optional) boolean, control whether code signing is enabled or not,
1021#       default to ios_enable_code_signing if not defined.
1022#
1023#   transparent
1024#       (optional) boolean, whether the bundle is "transparent"; defaults to
1025#       "false" if ommitted; a bundle is considered "transparent" if it does
1026#       not package the "bundle_data" deps but forward them to all targets
1027#       the depend on it (unless the "bundle_data" target sets "product_type"
1028#       to "com.apple.product-type.framework").
1029#
1030# This template provides two targets for the resulting framework bundle. The
1031# link-time behavior varies depending on which of the two targets below is
1032# added as a dependency:
1033#   - $target_name only adds a build-time dependency. Targets that depend on
1034#     it will not link against the framework.
1035#   - $target_name+link adds a build-time and link-time dependency. Targets
1036#     that depend on it will link against the framework.
1037#
1038# The build-time-only dependency is used for when a target needs to use the
1039# framework either only for resources, or because the target loads it at run-
1040# time, via dlopen() or NSBundle. The link-time dependency will cause the
1041# dependee to have the framework loaded by dyld at launch.
1042#
1043# Example of build-time only dependency:
1044#
1045#     framework_bundle("CoreTeleportation") {
1046#       sources = [ ... ]
1047#     }
1048#
1049#     bundle_data("core_teleportation_bundle_data") {
1050#       deps = [ ":CoreTeleportation" ]
1051#       sources = [ "$root_out_dir/CoreTeleportation.framework" ]
1052#       outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ]
1053#     }
1054#
1055#     app_bundle("GoatTeleporter") {
1056#       sources = [ ... ]
1057#       deps = [
1058#         ":core_teleportation_bundle_data",
1059#       ]
1060#     }
1061#
1062# The GoatTeleporter.app will not directly link against
1063# CoreTeleportation.framework, but it will be included in the bundle's
1064# Frameworks directory.
1065#
1066# Example of link-time dependency:
1067#
1068#     framework_bundle("CoreTeleportation") {
1069#       sources = [ ... ]
1070#       ldflags = [
1071#         "-install_name",
1072#         "@executable_path/../Frameworks/$target_name.framework"
1073#       ]
1074#     }
1075#
1076#     bundle_data("core_teleportation_bundle_data") {
1077#       deps = [ ":CoreTeleportation+link" ]
1078#       sources = [ "$root_out_dir/CoreTeleportation.framework" ]
1079#       outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ]
1080#     }
1081#
1082#     app_bundle("GoatTeleporter") {
1083#       sources = [ ... ]
1084#       deps = [
1085#         ":core_teleportation_bundle_data",
1086#       ]
1087#     }
1088#
1089# Note that the framework is still copied to the app's bundle, but dyld will
1090# load this library when the app is launched because it uses the "+link"
1091# target as a dependency. This also requires that the framework set its
1092# install_name so that dyld can locate it.
1093#
1094# See "gn help shared_library" for more information on arguments supported
1095# by shared library target.
1096template("ios_framework_bundle") {
1097  _target_name = target_name
1098  _output_name = target_name
1099  if (defined(invoker.output_name)) {
1100    _output_name = invoker.output_name
1101  }
1102
1103  _product_type = "com.apple.product-type.framework"
1104  _has_public_headers =
1105      defined(invoker.public_headers) && invoker.public_headers != []
1106
1107  _shared_library_target = _target_name + "_shared_library"
1108  _link_target_name = _target_name + "+link"
1109
1110  if (_has_public_headers) {
1111    _default_toolchain_target_gen_dir =
1112        get_label_info("$_target_name", "target_gen_dir")
1113
1114    _framework_headers_target = _target_name + "_framework_headers"
1115
1116    _headers_map_config = _target_name + "_headers_map"
1117    _header_map_filename =
1118        "$_default_toolchain_target_gen_dir/$_output_name.headers.hmap"
1119    config(_headers_map_config) {
1120      visibility = [
1121        ":${_shared_library_target}",
1122        ":${_target_name}_signed_bundle",
1123      ]
1124      include_dirs = [ _header_map_filename ]
1125    }
1126  }
1127
1128  _framework_headers_config = _target_name + "_framework_headers_config"
1129  config(_framework_headers_config) {
1130    framework_dirs = [ root_out_dir ]
1131  }
1132
1133  _framework_public_config = _target_name + "_public_config"
1134  config(_framework_public_config) {
1135    configs = [ ":$_framework_headers_config" ]
1136    frameworks = [ "$_output_name.framework" ]
1137  }
1138
1139  shared_library(_shared_library_target) {
1140    forward_variables_from(invoker,
1141                           "*",
1142                           [
1143                             "bundle_deps",
1144                             "bundle_deps_filter",
1145                             "data_deps",
1146                             "enable_code_signing",
1147                             "extra_substitutions",
1148                             "info_plist",
1149                             "info_plist_target",
1150                             "output_name",
1151                             "public_configs",
1152                             "transparent",
1153                             "visibility",
1154                           ])
1155
1156    visibility = [ ":${_target_name}_signed_bundle" ]
1157
1158    if (!defined(ldflags)) {
1159      ldflags = []
1160    }
1161    ldflags +=
1162        [ "-Wl,-install_name,@rpath/$_output_name.framework/$_output_name" ]
1163
1164    if (_has_public_headers) {
1165      configs += [ ":$_headers_map_config" ]
1166
1167      if (!defined(deps)) {
1168        deps = []
1169      }
1170      deps += [ ":$_framework_headers_target" ]
1171    }
1172
1173    output_extension = ""
1174    output_name = _output_name
1175    output_prefix_override = true
1176    output_dir = target_out_dir
1177  }
1178
1179  if (_has_public_headers) {
1180    _public_headers = invoker.public_headers
1181
1182    _framework_root_dir = "$root_out_dir/$_output_name.framework"
1183    if (target_environment == "simulator" || target_environment == "device") {
1184      _framework_contents_dir = _framework_root_dir
1185    } else if (target_environment == "catalyst") {
1186      _framework_contents_dir = "$_framework_root_dir/Versions/A"
1187    }
1188
1189    _compile_headers_map_target = _target_name + "_compile_headers_map"
1190    action(_compile_headers_map_target) {
1191      visibility = [ ":$_framework_headers_target" ]
1192      forward_variables_from(invoker,
1193                             [
1194                               "deps",
1195                               "public_deps",
1196                               "testonly",
1197                             ])
1198      script = "//build/config/ios/write_framework_hmap.py"
1199      outputs = [ _header_map_filename ]
1200
1201      # The header map generation only wants the list of headers, not all of
1202      # sources, so filter any non-header source files from "sources". It is
1203      # less error prone that having the developer duplicate the list of all
1204      # headers in addition to "sources".
1205      sources = []
1206      foreach(_source, invoker.sources) {
1207        if (get_path_info(_source, "extension") == "h") {
1208          sources += [ _source ]
1209        }
1210      }
1211
1212      args = [
1213               rebase_path(_header_map_filename, root_build_dir),
1214               rebase_path(_framework_root_dir, root_build_dir),
1215             ] + rebase_path(sources, root_build_dir)
1216    }
1217
1218    _create_module_map_target = _target_name + "_module_map"
1219    action(_create_module_map_target) {
1220      visibility = [ ":$_framework_headers_target" ]
1221      script = "//build/config/ios/write_framework_modulemap.py"
1222      outputs = [ "$_framework_contents_dir/Modules/module.modulemap" ]
1223      args = [
1224        _output_name,
1225        rebase_path("$_framework_contents_dir/Modules", root_build_dir),
1226      ]
1227    }
1228
1229    _copy_public_headers_target = _target_name + "_copy_public_headers"
1230    copy(_copy_public_headers_target) {
1231      forward_variables_from(invoker,
1232                             [
1233                               "testonly",
1234                               "deps",
1235                             ])
1236      visibility = [ ":$_framework_headers_target" ]
1237      sources = _public_headers
1238      outputs = [ "$_framework_contents_dir/Headers/{{source_file_part}}" ]
1239
1240      # Do not use forward_variables_from for "public_deps" as
1241      # we do not want to forward those dependencies.
1242      if (defined(invoker.public_deps)) {
1243        if (!defined(deps)) {
1244          deps = []
1245        }
1246        deps += invoker.public_deps
1247      }
1248    }
1249
1250    group(_framework_headers_target) {
1251      forward_variables_from(invoker, [ "testonly" ])
1252      deps = [
1253        ":$_compile_headers_map_target",
1254        ":$_create_module_map_target",
1255      ]
1256      public_deps = [ ":$_copy_public_headers_target" ]
1257    }
1258  }
1259
1260  # Bundle identifier should respect rfc1034, so replace "_" with "-".
1261  _bundle_identifier =
1262      "$ios_app_bundle_id_prefix." + string_replace(_output_name, "_", "-")
1263
1264  _info_plist_target = _target_name + "_info_plist"
1265  _info_plist_bundle = _target_name + "_info_plist_bundle"
1266  ios_info_plist(_info_plist_target) {
1267    visibility = [ ":$_info_plist_bundle" ]
1268    executable_name = _output_name
1269    forward_variables_from(invoker,
1270                           [
1271                             "info_plist",
1272                             "info_plist_target",
1273                           ])
1274
1275    extra_substitutions = [ "BUNDLE_IDENTIFIER=$_bundle_identifier" ]
1276    if (defined(invoker.extra_substitutions)) {
1277      extra_substitutions += invoker.extra_substitutions
1278    }
1279  }
1280
1281  bundle_data(_info_plist_bundle) {
1282    visibility = [ ":${_target_name}_signed_bundle" ]
1283    forward_variables_from(invoker, [ "testonly" ])
1284    sources = get_target_outputs(":$_info_plist_target")
1285    public_deps = [ ":$_info_plist_target" ]
1286    product_type = _product_type
1287
1288    if (target_environment != "catalyst") {
1289      outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
1290    } else {
1291      outputs = [ "{{bundle_resources_dir}}/Info.plist" ]
1292    }
1293  }
1294
1295  create_signed_bundle(_target_name + "_signed_bundle") {
1296    forward_variables_from(invoker,
1297                           [
1298                             "bundle_deps",
1299                             "bundle_deps_filter",
1300                             "data_deps",
1301                             "deps",
1302                             "enable_code_signing",
1303                             "public_configs",
1304                             "public_deps",
1305                             "testonly",
1306                             "transparent",
1307                             "visibility",
1308                           ])
1309
1310    product_type = _product_type
1311    bundle_extension = ".framework"
1312
1313    output_name = _output_name
1314    bundle_binary_target = ":$_shared_library_target"
1315    bundle_binary_output = _output_name
1316
1317    has_public_headers = _has_public_headers
1318
1319    # Framework do not have entitlements nor mobileprovision because they use
1320    # the one from the bundle using them (.app or .appex) as they are just
1321    # dynamic library with shared code.
1322    disable_entitlements = true
1323    disable_embedded_mobileprovision = true
1324
1325    if (!defined(deps)) {
1326      deps = []
1327    }
1328    deps += [ ":$_info_plist_bundle" ]
1329  }
1330
1331  group(_target_name) {
1332    forward_variables_from(invoker,
1333                           [
1334                             "public_configs",
1335                             "public_deps",
1336                             "testonly",
1337                             "visibility",
1338                           ])
1339    if (!defined(public_deps)) {
1340      public_deps = []
1341    }
1342    public_deps += [ ":${_target_name}_signed_bundle" ]
1343
1344    if (_has_public_headers) {
1345      if (!defined(public_configs)) {
1346        public_configs = []
1347      }
1348      public_configs += [ ":$_framework_headers_config" ]
1349    }
1350  }
1351
1352  group(_link_target_name) {
1353    forward_variables_from(invoker,
1354                           [
1355                             "public_configs",
1356                             "public_deps",
1357                             "testonly",
1358                             "visibility",
1359                           ])
1360    if (!defined(public_deps)) {
1361      public_deps = []
1362    }
1363    public_deps += [ ":$_target_name" ]
1364
1365    if (!defined(all_dependent_configs)) {
1366      all_dependent_configs = []
1367    }
1368    all_dependent_configs += [ ":$_framework_public_config" ]
1369  }
1370
1371  bundle_data(_target_name + "+bundle") {
1372    forward_variables_from(invoker,
1373                           [
1374                             "testonly",
1375                             "visibility",
1376                           ])
1377    public_deps = [ ":$_target_name" ]
1378    sources = [ "$root_out_dir/$_output_name.framework" ]
1379    outputs = [ "{{bundle_contents_dir}}/Frameworks/$_output_name.framework" ]
1380  }
1381}
1382
1383set_defaults("ios_framework_bundle") {
1384  configs = default_shared_library_configs
1385}
1386
1387# Template to build a xctest bundle that contains a loadable module for iOS.
1388#
1389# Arguments
1390#
1391#   deps:
1392#       list of labels to depends on, these values are used to create the
1393#       loadable module.
1394#
1395#   product_type
1396#       string, product type for the generated Xcode project, use
1397#       "com.apple.product-type.bundle.unit-test" for unit test and
1398#       "com.apple.product-type.bundle.ui-testing" for UI testing.
1399#
1400#   host_target:
1401#       string, name of the target that depends on the generated bundle, this
1402#       value is used to restrict visibilities.
1403#
1404#   xcode_test_application_name:
1405#       string, name of the test application for Xcode unit or ui test target.
1406#
1407#   output_name
1408#       (optional) string, name of the generated application, if omitted,
1409#       defaults to the target_name.
1410#
1411# This template defines two targets, one named "${target_name}" is the xctest
1412# bundle, and the other named "${target_name}_bundle" is a bundle_data that
1413# wraps the xctest bundle and that only the "${host_target}" can depend on.
1414#
1415template("ios_xctest_bundle") {
1416  assert(defined(invoker.deps), "deps must be defined for $target_name")
1417  assert(defined(invoker.product_type),
1418         "product_type must be defined for $target_name")
1419  assert(invoker.product_type == _ios_xcode_xctest_bundle_id ||
1420             invoker.product_type == _ios_xcode_xcuitest_bundle_id,
1421         "product_type defined for $target_name is invalid.")
1422  assert(defined(invoker.host_target),
1423         "host_target must be defined for $target_name")
1424  assert(defined(invoker.xcode_test_application_name),
1425         "xcode_test_application_name must be defined for $target_name")
1426
1427  _target_name = target_name
1428  _output_name = target_name
1429
1430  if (defined(invoker.output_name)) {
1431    _output_name = invoker.output_name
1432  }
1433
1434  _loadable_module_target = _target_name + "_loadable_module"
1435
1436  loadable_module(_loadable_module_target) {
1437    forward_variables_from(invoker,
1438                           "*",
1439                           [
1440                             "bundle_deps",
1441                             "bundle_deps_filter",
1442                             "host_target",
1443                             "output_dir",
1444                             "output_extension",
1445                             "output_name",
1446                             "output_prefix_override",
1447                             "product_type",
1448                             "testonly",
1449                             "visibility",
1450                             "xcode_test_application_name",
1451                             "xcode_test_application_output_name",
1452                             "xctest_bundle_principal_class",
1453                           ])
1454
1455    testonly = true
1456    visibility = [ ":$_target_name" ]
1457
1458    configs += [ "//build/config/ios:xctest_config" ]
1459
1460    output_dir = target_out_dir
1461    output_name = _output_name
1462    output_prefix_override = true
1463    output_extension = ""
1464  }
1465
1466  _info_plist_target = _target_name + "_info_plist"
1467  _info_plist_bundle = _target_name + "_info_plist_bundle"
1468
1469  # Bundle identifier should respect rfc1034, so replace "_" with "-".
1470  _bundle_identifier = "$ios_app_bundle_id_prefix.chrome." +
1471                       string_replace(_output_name, "_", "-")
1472
1473  ios_info_plist(_info_plist_target) {
1474    testonly = true
1475    visibility = [ ":$_info_plist_bundle" ]
1476
1477    info_plist = "//build/config/ios/Module-Info.plist"
1478    executable_name = _output_name
1479
1480    if (defined(invoker.xctest_bundle_principal_class)) {
1481      _principal_class = invoker.xctest_bundle_principal_class
1482    } else {
1483      # Fall back to a reasonable default value.
1484      _principal_class = "NSObject"
1485    }
1486    extra_substitutions = [
1487      "XCTEST_BUNDLE_PRINCIPAL_CLASS=${_principal_class}",
1488      "BUNDLE_IDENTIFIER=$_bundle_identifier",
1489    ]
1490  }
1491
1492  bundle_data(_info_plist_bundle) {
1493    testonly = true
1494    visibility = [ ":$_target_name" ]
1495
1496    public_deps = [ ":$_info_plist_target" ]
1497
1498    sources = get_target_outputs(":$_info_plist_target")
1499    outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
1500  }
1501
1502  _xctest_bundle = _target_name + "_bundle"
1503  create_signed_bundle(_target_name) {
1504    forward_variables_from(invoker,
1505                           [
1506                             "bundle_deps",
1507                             "bundle_deps_filter",
1508                             "bundle_id",
1509                             "data_deps",
1510                             "enable_code_signing",
1511                             "product_type",
1512                             "xcode_test_application_name",
1513                           ])
1514
1515    testonly = true
1516    visibility = [ ":$_xctest_bundle" ]
1517
1518    bundle_extension = ".xctest"
1519
1520    output_name = _output_name
1521    bundle_binary_target = ":$_loadable_module_target"
1522    bundle_binary_output = _output_name
1523
1524    xcode_extra_attributes = {
1525      IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target
1526      PRODUCT_BUNDLE_IDENTIFIER = _bundle_identifier
1527      CODE_SIGNING_REQUIRED = "NO"
1528      CODE_SIGNING_ALLOWED = "NO"
1529      CODE_SIGN_IDENTITY = ""
1530      DONT_GENERATE_INFOPLIST_FILE = "YES"
1531
1532      # For XCUITest, Xcode requires specifying the host application name
1533      # via the TEST_TARGET_NAME attribute.
1534      if (invoker.product_type == _ios_xcode_xcuitest_bundle_id) {
1535        TEST_TARGET_NAME = invoker.xcode_test_application_name
1536      }
1537
1538      # For XCTest, Xcode requires specifying the host application path via
1539      # both BUNDLE_LOADER and TEST_HOST attributes.
1540      if (invoker.product_type == _ios_xcode_xctest_bundle_id) {
1541        _xcode_app_name = invoker.xcode_test_application_name
1542        if (defined(invoker.xcode_test_application_output_name)) {
1543          _xcode_app_name = invoker.xcode_test_application_output_name
1544        }
1545
1546        BUNDLE_LOADER = "\$(TEST_HOST)"
1547        TEST_HOST = "\$(BUILT_PRODUCTS_DIR)/" +
1548                    "${_xcode_app_name}.app/${_xcode_app_name}"
1549      }
1550    }
1551
1552    deps = [ ":$_info_plist_bundle" ]
1553  }
1554
1555  bundle_data(_xctest_bundle) {
1556    forward_variables_from(invoker, [ "host_target" ])
1557
1558    testonly = true
1559    visibility = [ ":$host_target" ]
1560
1561    public_deps = [ ":$_target_name" ]
1562    sources = [ "$root_out_dir/$_output_name.xctest" ]
1563    outputs = [ "{{bundle_contents_dir}}/PlugIns/$_output_name.xctest" ]
1564  }
1565}
1566
1567set_defaults("ios_xctest_bundle") {
1568  configs = default_shared_library_configs
1569}
1570
1571# For Chrome on iOS we want to run XCTests for all our build configurations
1572# (Debug, Release, ...). In addition, the symbols visibility is configured to
1573# private by default. To simplify testing with those constraints, our tests are
1574# compiled in the TEST_HOST target instead of the .xctest bundle.
1575template("ios_xctest_test") {
1576  _target_name = target_name
1577  _output_name = target_name
1578  if (defined(invoker.output_name)) {
1579    _output_name = invoker.output_name
1580  }
1581
1582  _xctest_target = _target_name + "_module"
1583  _xctest_output = _output_name + "_module"
1584
1585  _host_target = _target_name
1586  _host_output = _output_name
1587
1588  # Allow invokers to specify their own target for the xctest module, but
1589  # fall back to a default (empty) module otherwise.
1590  if (defined(invoker.xctest_module_target)) {
1591    _xctest_module_target = invoker.xctest_module_target
1592  } else {
1593    _xctest_module_target_name = _xctest_target + "shell_source"
1594    _xctest_module_target = ":$_xctest_module_target_name"
1595    source_set(_xctest_module_target_name) {
1596      sources = [ "//build/config/ios/xctest_shell.mm" ]
1597
1598      configs += [ "//build/config/ios:xctest_config" ]
1599    }
1600  }
1601
1602  ios_xctest_bundle(_xctest_target) {
1603    forward_variables_from(invoker, [ "data_deps" ])
1604    output_name = _xctest_output
1605    product_type = _ios_xcode_xctest_bundle_id
1606    host_target = _host_target
1607
1608    # TODO(crbug.com/1056328) The change in output name results in a mismatch
1609    # between this value and the ios_app_bundle target name. To mitigate, this
1610    # has been modified to _host_target. output_name is set to _host_output
1611    # to mitigate the naming.
1612    xcode_test_application_name = _host_target
1613    xcode_test_application_output_name = _host_output
1614
1615    deps = [ _xctest_module_target ]
1616  }
1617
1618  ios_app_bundle(_host_target) {
1619    forward_variables_from(invoker, "*", [ "testonly" ])
1620
1621    testonly = true
1622    output_name = _host_output
1623    configs += [ "//build/config/ios:xctest_config" ]
1624
1625    if (!defined(invoker.info_plist) && !defined(invoker.info_plist_target)) {
1626      info_plist = "//build/config/ios/Host-Info.plist"
1627    }
1628
1629    # Xcode needs the following frameworks installed in the application (and
1630    # signed) for the XCTest to run, so install them using
1631    # extra_system_frameworks.
1632    extra_system_frameworks = [
1633      "$ios_sdk_platform_path/Developer/Library/Frameworks/XCTest.framework",
1634      "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework",
1635      "$ios_sdk_platform_path/Developer/usr/lib/libXCTestBundleInject.dylib",
1636    ]
1637
1638    # Xcode 13 now depends on XCTestCore. To keep things future proof, copy over
1639    # everything that Xcode copies.
1640    if (xcode_version_int >= 1300) {
1641      extra_system_frameworks += [
1642        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestCore.framework",
1643        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUIAutomation.framework",
1644        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUnit.framework",
1645        "$ios_sdk_platform_path/Developer/usr/lib/libXCTestSwiftSupport.dylib",
1646      ]
1647    }
1648
1649    # XCTestSupport framework is required as of Xcode 14.3 or later.
1650    if (xcode_version_int >= 1430) {
1651      extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestSupport.framework" ]
1652    }
1653
1654    _xctest_bundle = _xctest_target + "_bundle"
1655    if (!defined(bundle_deps)) {
1656      bundle_deps = []
1657    }
1658    bundle_deps += [ ":$_xctest_bundle" ]
1659  }
1660}
1661
1662set_defaults("ios_xctest_test") {
1663  configs = default_executable_configs
1664}
1665
1666# Template to build a xcuitest test runner bundle.
1667#
1668# Xcode requires a test runner application with a copy of the XCTest dynamic
1669# library bundle in it for the XCUITest to run. The test runner bundle is created
1670# by copying the system bundle XCTRunner.app from Xcode SDK with the plist file
1671# being properly tweaked, and a xctest and it needs to be code signed in order
1672# to run on devices.
1673#
1674# Arguments
1675#
1676#   xctest_bundle
1677#       string, name of the dependent xctest bundle target.
1678#
1679#   output_name
1680#       (optional) string, name of the generated application, if omitted,
1681#       defaults to the target_name.
1682#
1683template("ios_xcuitest_test_runner_bundle") {
1684  assert(defined(invoker.xctest_bundle),
1685         "xctest_bundle must be defined for $target_name")
1686
1687  _target_name = target_name
1688  _output_name = target_name
1689  if (defined(invoker.output_name)) {
1690    _output_name = invoker.output_name
1691  }
1692
1693  # Bundle identifier should respect rfc1034, so replace "_" with "-".
1694  _bundle_identifier = "$ios_app_bundle_id_prefix.chrome." +
1695                       string_replace(_output_name, "_", "-")
1696
1697  _xctrunner_path =
1698      "$ios_sdk_platform_path/Developer/Library/Xcode/Agents/XCTRunner.app"
1699
1700  _info_plist_merge_plist = _target_name + "_info_plist_merge_plist"
1701  _info_plist_target = _target_name + "_info_plist"
1702  _info_plist_bundle = _target_name + "_info_plist_bundle"
1703
1704  action(_info_plist_merge_plist) {
1705    testonly = true
1706    script = "//build/apple/plist_util.py"
1707
1708    sources = [
1709      "$_xctrunner_path/Info.plist",
1710
1711      # NOTE: The XCTRunnerAddition+Info.plist must come after the Info.plist
1712      # because it overrides the values under "CFBundleIdentifier" and
1713      # "CFBundleName".
1714      "//build/config/ios/resources/XCTRunnerAddition+Info.plist",
1715    ]
1716
1717    _output_name = "$target_gen_dir/${_target_name}_merged.plist"
1718    outputs = [ _output_name ]
1719    args = [
1720             "merge",
1721             "-f=xml1",
1722             "-x=$xcode_version",
1723             "-o=" + rebase_path(_output_name, root_build_dir),
1724           ] + rebase_path(sources, root_build_dir)
1725
1726    if (use_system_xcode && (use_goma || use_remoteexec || use_siso)) {
1727      deps = [ "//build/config/ios:copy_xctrunner_app" ]
1728    }
1729  }
1730
1731  ios_info_plist(_info_plist_target) {
1732    testonly = true
1733    visibility = [ ":$_info_plist_bundle" ]
1734
1735    executable_name = _output_name
1736    info_plist_target = ":$_info_plist_merge_plist"
1737    extra_substitutions = [ "BUNDLE_IDENTIFIER=$_bundle_identifier" ]
1738  }
1739
1740  bundle_data(_info_plist_bundle) {
1741    testonly = true
1742    visibility = [ ":$_target_name" ]
1743
1744    public_deps = [ ":$_info_plist_target" ]
1745
1746    sources = get_target_outputs(":$_info_plist_target")
1747    outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
1748  }
1749
1750  _pkginfo_bundle = _target_name + "_pkginfo_bundle"
1751  bundle_data(_pkginfo_bundle) {
1752    testonly = true
1753    visibility = [ ":$_target_name" ]
1754
1755    sources = [ "$_xctrunner_path/PkgInfo" ]
1756
1757    outputs = [ "{{bundle_contents_dir}}/PkgInfo" ]
1758
1759    if (use_system_xcode && (use_goma || use_remoteexec || use_siso)) {
1760      public_deps = [ "//build/config/ios:copy_xctrunner_app" ]
1761    }
1762  }
1763
1764  _xctest_bundle = invoker.xctest_bundle
1765  create_signed_bundle(_target_name) {
1766    testonly = true
1767
1768    bundle_binary_target = "//build/config/ios:xctest_runner_without_arm64e"
1769    bundle_binary_output = "XCTRunner"
1770    bundle_extension = ".app"
1771    product_type = _ios_xcode_app_bundle_id
1772
1773    output_name = _output_name
1774
1775    # Xcode needs the following frameworks installed in the application
1776    # (and signed) for the XCUITest to run, so install them using
1777    # extra_system_frameworks.
1778    extra_system_frameworks = [
1779      "$ios_sdk_platform_path/Developer/Library/Frameworks/XCTest.framework",
1780      "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework",
1781    ]
1782
1783    # Xcode 13 now depends on XCTestCore. To keep things future proof, copy over
1784    # everything that Xcode copies.
1785    if (xcode_version_int >= 1300) {
1786      extra_system_frameworks += [
1787        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestCore.framework",
1788        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUIAutomation.framework",
1789        "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUnit.framework",
1790        "$ios_sdk_platform_path/Developer/usr/lib/libXCTestSwiftSupport.dylib",
1791      ]
1792    }
1793
1794    # XCTestSupport framework is required as of Xcode 14.3 or later.
1795    if (xcode_version_int >= 1430) {
1796      extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestSupport.framework" ]
1797    }
1798
1799    bundle_deps = []
1800    if (defined(invoker.bundle_deps)) {
1801      bundle_deps += invoker.bundle_deps
1802    }
1803    bundle_deps += [
1804      ":$_info_plist_bundle",
1805      ":$_pkginfo_bundle",
1806      ":$_xctest_bundle",
1807    ]
1808  }
1809}
1810
1811# Template to build a XCUITest that consists of two parts: the test runner
1812# application bundle and the xctest dynamic library.
1813#
1814# Arguments
1815#
1816#   deps:
1817#       list of labels to depends on, these values are used to create the
1818#       xctest dynamic library.
1819#
1820#   xcode_test_application_name:
1821#       string, name of the test application for the ui test target.
1822#
1823#   runner_only_bundle_deps:
1824#       list of labels of bundle target to include in the runner and
1825#       exclude from the test module (the use case is a framework bundle
1826#       that is used by the test module and thus needs to be packaged in
1827#       the runner application bundle)
1828#
1829# This template defines two targets, one named "${target_name}_module" is the
1830# xctest dynamic library, and the other named "${target_name}_runner" is the
1831# test runner application bundle.
1832#
1833template("ios_xcuitest_test") {
1834  assert(defined(invoker.deps), "deps must be defined for $target_name")
1835  assert(defined(invoker.xcode_test_application_name),
1836         "xcode_test_application_name must be defined for $target_name")
1837
1838  _xcuitest_target = target_name
1839  if (defined(invoker.output_name)) {
1840    _xcuitest_target = invoker.output_name
1841  }
1842
1843  _xcuitest_runner_target = _xcuitest_target + "_runner"
1844  _xcuitest_module_target = _xcuitest_target + "_module"
1845
1846  group(target_name) {
1847    testonly = true
1848
1849    deps = [ ":$_xcuitest_runner_target" ]
1850  }
1851
1852  _xcuitest_module_output = _xcuitest_target
1853  ios_xctest_bundle(_xcuitest_module_target) {
1854    forward_variables_from(invoker,
1855                           [
1856                             "bundle_deps",
1857                             "data_deps",
1858                             "deps",
1859                             "xcode_test_application_name",
1860                             "xctest_bundle_principal_class",
1861                           ])
1862
1863    product_type = _ios_xcode_xcuitest_bundle_id
1864    host_target = _xcuitest_runner_target
1865    output_name = _xcuitest_module_output
1866
1867    if (defined(invoker.runner_only_bundle_deps)) {
1868      bundle_deps_filter = invoker.runner_only_bundle_deps
1869    }
1870  }
1871
1872  _xcuitest_runner_output = _xcuitest_target + "-Runner"
1873  ios_xcuitest_test_runner_bundle(_xcuitest_runner_target) {
1874    output_name = _xcuitest_runner_output
1875    xctest_bundle = _xcuitest_module_target + "_bundle"
1876
1877    if (defined(invoker.runner_only_bundle_deps)) {
1878      if (!defined(bundle_deps)) {
1879        bundle_deps = []
1880      }
1881      bundle_deps += invoker.runner_only_bundle_deps
1882    }
1883  }
1884}
1885
1886set_defaults("ios_xcuitest_test") {
1887  configs = default_executable_configs
1888}
1889