xref: /aosp_15_r20/external/cronet/build/toolchain/apple/toolchain.gni (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1# Copyright 2013 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
5# TODO(brettw) Use "gcc_toolchain.gni" like the Linux toolchains. This requires
6# some enhancements since the commands on Mac are slightly different than on
7# Linux.
8
9import("//build/config/apple/symbols.gni")
10import("//build/config/clang/clang.gni")
11import("//build/config/compiler/compiler.gni")
12import("//build/config/coverage/coverage.gni")
13import("//build/config/rust.gni")
14import("//build/toolchain/cc_wrapper.gni")
15import("//build/toolchain/rbe.gni")
16import("//build/toolchain/toolchain.gni")
17import("//build_overrides/build.gni")
18
19# TODO(crbug.com/1370527): This import is required to detect whether the
20# build is for the catalyst environment in order to disable the hermetic
21# swift compiler (as it does not include support for catalyst). Remove it
22# once the support is available.
23if (is_ios) {
24  import("//build/config/ios/config.gni")
25  import("//build/config/ios/ios_sdk.gni")
26}
27
28assert((target_os == "ios" && host_os == "mac") || host_os != "win")
29
30declare_args() {
31  # This controls whether whole module optimization is enabled when building
32  # Swift modules. If enabled, the compiler will compile the module as one
33  # unit, generating just one single object file. Otherwise, it will generate
34  # one object file per .swift file. If unspecified, will default to "true"
35  # for official builds, and "false" for all other builds.
36  swift_whole_module_optimization = -1
37
38  # If true, the intermediate build products of swift module compilation will
39  # be kept after the invocation of the swiftc compiler. Otherwise they will
40  # deleted between each invocation.
41  swift_keep_intermediate_files = false
42
43  # If unspecified, will use the toolchain downloaded via deps.
44  swift_toolchain_path = -1
45}
46
47# TODO(crbug.com/1370527): Remove this and replace with `build_with_chromium`
48# once the support for catalyst is available in the hermetic swift compiler.
49_can_use_hermetic_swift =
50    build_with_chromium && is_ios && target_environment != "catalyst"
51
52if (swift_toolchain_path == -1) {
53  # TODO(crbug.com/1451611) The custom swift toolchain not does currently work
54  # with Xcode 15 beta 1.
55  if (_can_use_hermetic_swift && !(is_ios && xcode_version_int >= 1500)) {
56    # Version of the hermetic compiler. Needs to be updated when a new version of
57    # the compiler is rolled to ensure that all outputs are regenerated. It must
58    # be kept in sync with the `version` of `third_party/swift-toolchain` in
59    # //DEPS.
60    swiftc_version = "swift-5.8-release"
61
62    # Use the hermetic swift toolchain.
63    swift_toolchain_path = "//third_party/swift-toolchain/"
64  } else {
65    swift_toolchain_path = ""
66  }
67}
68
69if (swift_whole_module_optimization == -1) {
70  swift_whole_module_optimization = is_official_build
71}
72
73# When implementing tools using Python scripts, a TOOL_VERSION=N env
74# variable is placed in front of the command. The N should be incremented
75# whenever the script is changed, so that the build system rebuilds all
76# edges that utilize the script. Ideally this should be changed to use
77# proper input-dirty checking, but that could be expensive. Instead, use a
78# script to get the tool scripts' modification time to use as the version.
79# This won't cause a re-generation of GN files when the tool script changes
80# but it will cause edges to be marked as dirty if the ninja files are
81# regenerated. See https://crbug.com/619083 for details. A proper fix
82# would be to have inputs to tools (https://crbug.com/621119).
83tool_versions = exec_script(
84        "get_tool_mtime.py",
85        rebase_path([
86                      "//build/toolchain/apple/filter_libtool.py",
87                      "//build/toolchain/apple/linker_driver.py",
88                      "//build/toolchain/ios/compile_xcassets.py",
89                      "//build/toolchain/ios/const_extract_protocols.json",
90                      "//build/toolchain/ios/swiftc.py",
91                    ],
92                    root_build_dir),
93        "trim scope")
94
95# Shared toolchain definition. Invocations should set current_os to set the
96# build args in this definition. This is titled "single_apple_toolchain"
97# because it makes exactly one toolchain. Callers will normally want to
98# invoke instead "apple_toolchain" which makes an additional toolchain for
99# Rust targets that are build-time artificts such as proc macros.
100template("single_apple_toolchain") {
101  toolchain(target_name) {
102    # When invoking this toolchain not as the default one, these args will be
103    # passed to the build. They are ignored when this is the default toolchain.
104    assert(defined(invoker.toolchain_args),
105           "Toolchains must declare toolchain_args")
106    toolchain_args = {
107      # Populate toolchain args from the invoker.
108      forward_variables_from(invoker.toolchain_args, "*")
109
110      # The host toolchain value computed by the default toolchain's setup
111      # needs to be passed through unchanged to all secondary toolchains to
112      # ensure that it's always the same, regardless of the values that may be
113      # set on those toolchains.
114      host_toolchain = host_toolchain
115    }
116
117    # When the invoker has explicitly overridden cc_wrapper in the
118    # toolchain args, use those values, otherwise default to the global one.
119    # This works because the only reasonable override that toolchains might
120    # supply for these values are to force-disable them.
121    if (defined(toolchain_args.use_remoteexec)) {
122      toolchain_uses_remoteexec = toolchain_args.use_remoteexec
123    } else {
124      toolchain_uses_remoteexec = use_remoteexec
125    }
126    if (defined(toolchain_args.cc_wrapper)) {
127      toolchain_cc_wrapper = toolchain_args.cc_wrapper
128    } else {
129      toolchain_cc_wrapper = cc_wrapper
130    }
131    assert(!(toolchain_cc_wrapper != "" && toolchain_uses_remoteexec),
132           "re-client and cc_wrapper can't be used together.")
133
134    if (defined(toolchain_args.use_lld)) {
135      toolchain_uses_lld = toolchain_args.use_lld
136    } else {
137      toolchain_uses_lld = use_lld
138    }
139
140    # The value of all global variables (such as `is_component_build`) is the
141    # one from the default toolchain when evaluating a secondary toolchain
142    # (see https://crbug.com/gn/286). This mean that the value may change when
143    # evaluating target/configs in the new toolchain if the variable default
144    # value depends on variable set in `toolchain_args`.
145    #
146    # For this reason, "ios" needs to override `is_component_build` as its
147    # default value depends on `current_os`. Use the overridden value if it
148    # is set in `toolchain_args`.
149    if (defined(toolchain_args.is_component_build)) {
150      toolchain_is_component_build = toolchain_args.is_component_build
151    } else {
152      toolchain_is_component_build = is_component_build
153    }
154
155    prefix = rebase_path("$clang_base_path/bin/", root_build_dir)
156    _cc = "${prefix}clang"
157    _cxx = "${prefix}clang++"
158
159    swiftmodule_switch = "-Wl,-add_ast_path,"
160
161    # Compute the compiler prefix.
162    if (toolchain_uses_remoteexec) {
163      if (defined(toolchain_args.rbe_cc_cfg_file)) {
164        toolchain_rbe_cc_cfg_file = toolchain_args.rbe_cc_cfg_file
165      } else {
166        toolchain_rbe_cc_cfg_file = rbe_cc_cfg_file
167      }
168
169      # C/C++ (clang) rewrapper prefix to use when use_remoteexec is true.
170      compiler_prefix = "${rbe_bin_dir}/rewrapper -cfg=${toolchain_rbe_cc_cfg_file}${rbe_bug_326584510_missing_inputs} -exec_root=${rbe_exec_root} "
171    } else if (toolchain_cc_wrapper != "") {
172      compiler_prefix = toolchain_cc_wrapper + " "
173    } else {
174      compiler_prefix = ""
175    }
176
177    cc = compiler_prefix + _cc
178    cxx = compiler_prefix + _cxx
179    ld = _cxx
180
181    # Set the explicit search path for clang++ so it uses the right linker
182    # binary.
183    if (!toolchain_uses_lld) {
184      ld += " -B " + invoker.bin_path
185    }
186
187    if (defined(toolchain_args.coverage_instrumentation_input_file)) {
188      toolchain_coverage_instrumentation_input_file =
189          toolchain_args.coverage_instrumentation_input_file
190    } else {
191      toolchain_coverage_instrumentation_input_file =
192          coverage_instrumentation_input_file
193    }
194    _use_clang_coverage_wrapper =
195        toolchain_coverage_instrumentation_input_file != ""
196    if (_use_clang_coverage_wrapper) {
197      _coverage_wrapper =
198          rebase_path("//build/toolchain/clang_code_coverage_wrapper.py",
199                      root_build_dir) + " --files-to-instrument=" +
200          rebase_path(toolchain_coverage_instrumentation_input_file,
201                      root_build_dir) + " --target-os=" + target_os
202      cc = "$python_path $_coverage_wrapper ${cc}"
203      cxx = "$python_path $_coverage_wrapper ${cxx}"
204    }
205
206    linker_driver_env = "TOOL_VERSION=${tool_versions.linker_driver}"
207    linker_driver =
208        rebase_path("//build/toolchain/apple/linker_driver.py", root_build_dir)
209    linker_driver_args = "-Wcrl,driver,$ld"
210
211    # Specify an explicit path for the strip binary.
212    _strippath = invoker.bin_path + "strip"
213    _installnametoolpath = "${prefix}llvm-install-name-tool"
214    linker_driver_args += " -Wcrl,strippath,${_strippath} -Wcrl,installnametoolpath,${_installnametoolpath}"
215    _enable_dsyms = enable_dsyms
216    _save_unstripped_output = save_unstripped_output
217
218    # Make these apply to all tools below.
219    lib_switch = "-l"
220    lib_dir_switch = "-L"
221
222    # Object files go in this directory. Use label_name instead of
223    # target_output_name since labels will generally have no spaces and will be
224    # unique in the directory.
225    object_subdir = "{{target_out_dir}}/{{label_name}}"
226
227    # If dSYMs are enabled, this flag will be added to the link tools.
228    if (_enable_dsyms) {
229      dsym_switch = " -Wcrl,dsym,{{root_out_dir}} "
230      dsym_switch += "-Wcrl,dsymutilpath," +
231                     rebase_path("//tools/clang/dsymutil/bin/dsymutil",
232                                 root_build_dir) + " "
233
234      dsym_output_dir =
235          "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.dSYM"
236      dsym_output = [
237        "$dsym_output_dir/Contents/Info.plist",
238        "$dsym_output_dir/Contents/Resources/DWARF/" +
239            "{{target_output_name}}{{output_extension}}",
240      ]
241    } else {
242      dsym_switch = ""
243    }
244
245    if (_save_unstripped_output) {
246      _unstripped_output = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.unstripped"
247    }
248
249    if (toolchain_has_rust) {
250      if (!defined(rust_compiler_prefix)) {
251        rust_compiler_prefix = ""
252      }
253      rust_sysroot_relative = rebase_path(rust_sysroot, root_build_dir)
254      rustc_bin = "$rust_sysroot_relative/bin/rustc"
255      rustc = "$rust_compiler_prefix${rustc_bin}"
256      rustc_wrapper =
257          rebase_path("//build/rust/rustc_wrapper.py", root_build_dir)
258
259      tool("rust_staticlib") {
260        libname = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
261        rspfile = "$libname.rsp"
262        depfile = "$libname.d"
263
264        default_output_extension = ".a"
265        output_prefix = "lib"
266
267        # Static libraries go in the target out directory by default so we can
268        # generate different targets with the same name and not have them
269        # collide.
270        default_output_dir = "{{target_out_dir}}"
271        description = "RUST(STATICLIB) {{output}}"
272        outputs = [ libname ]
273
274        # TODO(danakj): When `!toolchain_uses_lld` do we need to specify a path
275        # to libtool like the "alink" rule?
276
277        rspfile_content = "{{rustdeps}} {{externs}} SOURCES {{sources}}"
278        command = "\"$python_path\" \"$rustc_wrapper\" --rustc=$rustc --depfile=$depfile --rsp=$rspfile -- -Clinker=\"$_cxx\" $rustc_common_args --emit=dep-info=$depfile,link -o $libname LDFLAGS RUSTENV {{rustenv}}"
279        rust_sysroot = rust_sysroot_relative
280      }
281
282      tool("rust_rlib") {
283        # We must always prefix with `lib` even if the library already starts
284        # with that prefix or else our stdlib is unable to find libc.rlib (or
285        # actually liblibc.rlib).
286        rlibname =
287            "{{output_dir}}/lib{{target_output_name}}{{output_extension}}"
288        rspfile = "$rlibname.rsp"
289        depfile = "$rlibname.d"
290
291        default_output_extension = ".rlib"
292
293        # This is prefixed unconditionally in `rlibname`.
294        # output_prefix = "lib"
295
296        # Static libraries go in the target out directory by default so we can
297        # generate different targets with the same name and not have them
298        # collide.
299        default_output_dir = "{{target_out_dir}}"
300        description = "RUST {{output}}"
301        outputs = [ rlibname ]
302
303        rspfile_content = "{{rustdeps}} {{externs}} SOURCES {{sources}}"
304        command = "\"$python_path\" \"$rustc_wrapper\" --rustc=$rustc --depfile=$depfile --rsp=$rspfile -- -Clinker=\"$_cxx\" $rustc_common_args {{rustdeps}} {{externs}} --emit=dep-info=$depfile,link -o $rlibname LDFLAGS RUSTENV {{rustenv}}"
305        rust_sysroot = rust_sysroot_relative
306      }
307
308      tool("rust_bin") {
309        exename = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
310        rspfile = "$exename.rsp"
311        depfile = "$exename.d"
312        pool = "//build/toolchain:link_pool($default_toolchain)"
313
314        # TODO(danakj): solink can generate TOC files for re-exporting library
315        # symbols, and we should do the same here.
316
317        default_output_dir = "{{root_out_dir}}"
318        description = "RUST(BIN) {{output}}"
319        outputs = [ exename ]
320
321        # TODO(danakj): Support dsym_switch like C++ targets.
322        # link_command += dsym_switch
323        # if (_enable_dsyms) {
324        #   outputs += dsym_output
325        # }
326        # if (_save_unstripped_output) {
327        #   outputs += [ _unstripped_output ]
328        # }
329
330        rspfile_content = "{{rustdeps}} {{externs}} SOURCES {{sources}}"
331        command = "$linker_driver_env \"$python_path\" \"$rustc_wrapper\" --rustc=$rustc --depfile=$depfile --rsp=$rspfile -- -Clinker=\"$linker_driver\" $rustc_common_args --emit=dep-info=$depfile,link -o $exename LDFLAGS $linker_driver_args {{ldflags}} RUSTENV {{rustenv}}"
332        rust_sysroot = rust_sysroot_relative
333      }
334
335      tool("rust_cdylib") {
336        dllname = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
337        rspfile = "$dllname.rsp"
338        depfile = "$dllname.d"
339        pool = "//build/toolchain:link_pool($default_toolchain)"
340
341        # TODO(danakj): solink can generate TOC files for re-exporting library
342        # symbols, and we should do the same here.
343
344        default_output_extension = ".dylib"
345        output_prefix = "lib"
346        default_output_dir = "{{root_out_dir}}"
347        description = "RUST(CDYLIB) {{output}}"
348        outputs = [ dllname ]
349
350        # TODO(danakj): Support dsym_switch like C++ targets.
351        # link_command += dsym_switch
352        # if (_enable_dsyms) {
353        #   outputs += dsym_output
354        # }
355        # if (_save_unstripped_output) {
356        #   outputs += [ _unstripped_output ]
357        # }
358
359        rspfile_content = "{{rustdeps}} {{externs}} SOURCES {{sources}}"
360        command = "$linker_driver_env \"$python_path\" \"$rustc_wrapper\" --rustc=$rustc --depfile=$depfile --rsp=$rspfile -- -Clinker=\"$linker_driver\" $rustc_common_args --emit=dep-info=$depfile,link -o $dllname LDFLAGS $linker_driver_args {{ldflags}} RUSTENV {{rustenv}}"
361        rust_sysroot = rust_sysroot_relative
362      }
363
364      tool("rust_macro") {
365        dllname = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
366        rspfile = "$dllname.rsp"
367        depfile = "$dllname.d"
368        pool = "//build/toolchain:link_pool($default_toolchain)"
369
370        # TODO(danakj): solink can generate TOC files for re-exporting library
371        # symbols, and we should do the same here.
372
373        default_output_extension = ".dylib"
374        output_prefix = "lib"
375        default_output_dir = "{{root_out_dir}}"
376        description = "RUST(MACRO) {{output}}"
377        outputs = [ dllname ]
378
379        # TODO(danakj): Support dsym_switch like C++ targets.
380        # link_command += dsym_switch
381        # if (_enable_dsyms) {
382        #   outputs += dsym_output
383        # }
384        # if (_save_unstripped_output) {
385        #   outputs += [ _unstripped_output ]
386        # }
387
388        rspfile_content = "{{rustdeps}} {{externs}} SOURCES {{sources}}"
389        command = "\"$python_path\" \"$rustc_wrapper\" --rustc=$rustc --depfile=$depfile --rsp=$rspfile -- -Clinker=\"${_cxx}\" $rustc_common_args --emit=dep-info=$depfile,link -o $dllname LDFLAGS {{ldflags}} RUSTENV {{rustenv}}"
390        rust_sysroot = rust_sysroot_relative
391      }
392    }
393
394    tool("cc") {
395      depfile = "{{output}}.d"
396      precompiled_header_type = "gcc"
397      command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
398      depsformat = "gcc"
399      description = "CC {{output}}"
400      outputs = [ "$object_subdir/{{source_name_part}}.o" ]
401    }
402
403    tool("cxx") {
404      depfile = "{{output}}.d"
405      precompiled_header_type = "gcc"
406      command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
407      depsformat = "gcc"
408      description = "CXX {{output}}"
409      outputs = [ "$object_subdir/{{source_name_part}}.o" ]
410    }
411
412    tool("asm") {
413      # For GCC we can just use the C compiler to compile assembly.
414      depfile = "{{output}}.d"
415      command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}"
416      depsformat = "gcc"
417      description = "ASM {{output}}"
418      outputs = [ "$object_subdir/{{source_name_part}}.o" ]
419    }
420
421    tool("objc") {
422      depfile = "{{output}}.d"
423      precompiled_header_type = "gcc"
424      command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objc}} -c {{source}} -o {{output}}"
425      depsformat = "gcc"
426      description = "OBJC {{output}}"
427      outputs = [ "$object_subdir/{{source_name_part}}.o" ]
428    }
429
430    tool("objcxx") {
431      depfile = "{{output}}.d"
432      precompiled_header_type = "gcc"
433      command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objcc}} -c {{source}} -o {{output}}"
434      depsformat = "gcc"
435      description = "OBJCXX {{output}}"
436      outputs = [ "$object_subdir/{{source_name_part}}.o" ]
437    }
438
439    tool("alink") {
440      rspfile = "{{output}}.rsp"
441      rspfile_content = "{{inputs}}"
442
443      if (!toolchain_uses_lld) {
444        script = rebase_path("//build/toolchain/apple/filter_libtool.py",
445                             root_build_dir)
446
447        # Specify explicit path for libtool.
448        libtool = invoker.bin_path + "libtool"
449        command = "rm -f {{output}} && TOOL_VERSION=${tool_versions.filter_libtool} $python_path $script $libtool -static -D {{arflags}} -o {{output}} @$rspfile"
450        description = "LIBTOOL-STATIC {{output}}"
451      } else {
452        ar = "${prefix}llvm-ar"
453        command = "\"$ar\" {{arflags}} -r -c -s -D {{output}} @$rspfile"
454
455        # Remove the output file first so that ar doesn't try to modify the
456        # existing file.
457        command = "rm -f {{output}} && $command"
458        description = "AR {{output}}"
459      }
460      outputs = [ "{{output_dir}}/{{target_output_name}}{{output_extension}}" ]
461      default_output_dir = "{{target_out_dir}}"
462      default_output_extension = ".a"
463      output_prefix = "lib"
464    }
465
466    tool("solink") {
467      # E.g. "./libfoo.dylib":
468      dylib = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
469      rspfile = dylib + ".rsp"
470      pool = "//build/toolchain:link_pool($default_toolchain)"
471
472      # These variables are not built into GN but are helpers that implement
473      # (1) linking to produce a .dylib, (2) extracting the symbols from that
474      # file to a temporary file, (3) if the temporary file has differences from
475      # the existing .TOC file, overwrite it, otherwise, don't change it.
476      #
477      # As a special case, if the library reexports symbols from other dynamic
478      # libraries, we always update the .TOC and skip the temporary file and
479      # diffing steps, since that library always needs to be re-linked.
480      tocname = dylib + ".TOC"
481      temporary_tocname = dylib + ".tmp"
482
483      # Use explicit paths to binaries. The binaries present on the default
484      # search path in /usr/bin are thin wrappers around xcrun, which requires a
485      # full CommandLineTools or Xcode install, and still may not choose the
486      # appropriate binary if there are multiple installs.
487      if (host_os == "mac") {
488        nm = invoker.bin_path + "nm"
489        otool = invoker.bin_path + "otool"
490      } else {
491        nm = "${prefix}llvm-nm"
492        otool = "${prefix}llvm-otool"
493      }
494
495      does_reexport_command = "[ ! -e \"$dylib\" -o ! -e \"$tocname\" ] || $otool -l \"$dylib\" | grep -q LC_REEXPORT_DYLIB"
496
497      link_command =
498          "$linker_driver_env $linker_driver $linker_driver_args -shared "
499      if (toolchain_is_component_build) {
500        link_command += " -Wl,-install_name,@rpath/\"{{target_output_name}}{{output_extension}}\" "
501      }
502      link_command += dsym_switch
503      link_command += "{{ldflags}} -o \"$dylib\" \"@$rspfile\" {{rlibs}}"
504
505      replace_command = "if ! cmp -s \"$temporary_tocname\" \"$tocname\"; then mv \"$temporary_tocname\" \"$tocname\""
506      extract_toc_command = "{ $otool -l \"$dylib\" | grep LC_ID_DYLIB -A 5; $nm -gPp \"$dylib\" | cut -f1-2 -d' ' | grep -v U\$\$; true; }"
507
508      command = "if $does_reexport_command ; then $link_command && $extract_toc_command > \"$tocname\"; else $link_command && $extract_toc_command > \"$temporary_tocname\" && $replace_command ; fi; fi"
509
510      rspfile_content =
511          "{{inputs}} {{frameworks}} {{swiftmodules}} {{solibs}} {{libs}}"
512
513      description = "SOLINK {{output}}"
514
515      # Use this for {{output_extension}} expansions unless a target manually
516      # overrides it (in which case {{output_extension}} will be what the target
517      # specifies).
518      default_output_dir = "{{root_out_dir}}"
519      default_output_extension = ".dylib"
520
521      output_prefix = "lib"
522
523      # Since the above commands only updates the .TOC file when it changes, ask
524      # Ninja to check if the timestamp actually changed to know if downstream
525      # dependencies should be recompiled.
526      restat = true
527
528      # Tell GN about the output files. It will link to the dylib but use the
529      # tocname for dependency management.
530      outputs = [
531        dylib,
532        tocname,
533      ]
534      link_output = dylib
535      depend_output = tocname
536
537      if (_enable_dsyms) {
538        outputs += dsym_output
539      }
540      if (_save_unstripped_output) {
541        outputs += [ _unstripped_output ]
542      }
543    }
544
545    tool("solink_module") {
546      # E.g. "./libfoo.so":
547      sofile = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
548      rspfile = sofile + ".rsp"
549      pool = "//build/toolchain:link_pool($default_toolchain)"
550
551      link_command = "$linker_driver_env $linker_driver $linker_driver_args -bundle {{ldflags}} -o \"$sofile\" \"@$rspfile\" {{rlibs}}"
552      link_command += dsym_switch
553      command = link_command
554
555      rspfile_content =
556          "{{inputs}} {{frameworks}} {{swiftmodules}} {{solibs}} {{libs}}"
557
558      description = "SOLINK_MODULE {{output}}"
559
560      # Use this for {{output_extension}} expansions unless a target manually
561      # overrides it (in which case {{output_extension}} will be what the target
562      # specifies).
563      default_output_dir = "{{root_out_dir}}"
564      default_output_extension = ".so"
565
566      outputs = [ sofile ]
567
568      if (_enable_dsyms) {
569        outputs += dsym_output
570      }
571      if (_save_unstripped_output) {
572        outputs += [ _unstripped_output ]
573      }
574    }
575
576    tool("link") {
577      outfile = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
578      rspfile = "$outfile.rsp"
579      pool = "//build/toolchain:link_pool($default_toolchain)"
580
581      command = "$linker_driver_env $linker_driver $linker_driver_args $dsym_switch {{ldflags}} -o \"$outfile\" \"@$rspfile\" {{rlibs}}"
582      description = "LINK $outfile"
583      rspfile_content =
584          "{{inputs}} {{frameworks}} {{swiftmodules}} {{solibs}} {{libs}}"
585      outputs = [ outfile ]
586
587      if (_enable_dsyms) {
588        outputs += dsym_output
589      }
590      if (_save_unstripped_output) {
591        outputs += [ _unstripped_output ]
592      }
593
594      default_output_dir = "{{root_out_dir}}"
595    }
596
597    # These two are really entirely generic, but have to be repeated in
598    # each toolchain because GN doesn't allow a template to be used here.
599    # See //build/toolchain/toolchain.gni for details.
600    tool("stamp") {
601      command = stamp_command
602      description = stamp_description
603    }
604    tool("copy") {
605      command = copy_command
606      description = copy_description
607    }
608
609    tool("copy_bundle_data") {
610      # copy_command use hardlink if possible but this does not work with
611      # directories. Also when running EG2 tests from Xcode, Xcode tries to
612      # copy some files into the application bundle which fails if source
613      # and destination are hardlinked together.
614      #
615      # Instead use clonefile to copy the files which is as efficient as
616      # hardlink but ensure the file have distinct metadata (thus avoid the
617      # error with ditto, see https://crbug.com/1042182).
618      if (host_os == "mac") {
619        command = "rm -rf {{output}} && /bin/cp -Rc {{source}} {{output}}"
620      } else {
621        command = "rm -rf {{output}} && /bin/cp -Rld {{source}} {{output}}"
622      }
623      description = "COPY_BUNDLE_DATA {{source}} {{output}}"
624      pool = "//build/toolchain/apple:bundle_pool($default_toolchain)"
625    }
626
627    # Swift is only used on iOS, not macOS. We want to minimize the number
628    # of Xcode-based tools used by the macOS toolchain, so we intentionally
629    # disallow future uses of Swift on macOS. https://crbug.com/965663.
630    if (toolchain_args.current_os == "ios") {
631      tool("swift") {
632        _tool = rebase_path("//build/toolchain/ios/swiftc.py", root_build_dir)
633
634        depfile = "{{target_out_dir}}/{{module_name}}.d"
635        depsformat = "gcc"
636
637        _header_path = "{{target_gen_dir}}/{{target_output_name}}.h"
638        _output_dir = "{{target_out_dir}}/{{label_name}}"
639
640        outputs = [
641          _header_path,
642          "$_output_dir/{{module_name}}-OutputFileMap.json",
643          "$_output_dir/{{module_name}}.SwiftFileList",
644          "$_output_dir/{{module_name}}.abi.json",
645          "$_output_dir/{{module_name}}.d",
646          "$_output_dir/{{module_name}}.dia",
647          "$_output_dir/{{module_name}}.swiftdoc",
648          "$_output_dir/{{module_name}}.swiftmodule",
649          "$_output_dir/{{module_name}}.swiftsourceinfo",
650        ]
651
652        partial_outputs = [ "$_output_dir/{{source_name_part}}.o" ]
653
654        # The list of outputs and partial_outputs change whether the whole
655        # module optimization is enabled or not.
656        if (swift_whole_module_optimization) {
657          outputs += [
658            "$_output_dir/{{module_name}}.swiftconstvalues",
659            "$_output_dir/{{module_name}}.swiftdeps",
660          ]
661        } else {
662          outputs += [ "$_output_dir/{{module_name}}.priors" ]
663          partial_outputs += [
664            "$_output_dir/{{source_name_part}}.d",
665            "$_output_dir/{{source_name_part}}.dia",
666            "$_output_dir/{{source_name_part}}.swiftdeps",
667            "$_output_dir/{{source_name_part}}.swiftconstvalues",
668          ]
669        }
670
671        # If configured to keep the intermediate build files, pass the flag
672        # to the script and inform gn of the stamp file only (as the other
673        # files have names that cannot be predicted without invoking swiftc).
674        if (swift_keep_intermediate_files) {
675          _derived_data_dir = "$_output_dir/DerivedData"
676          outputs += [ "$_derived_data_dir/{{module_name}}.stamp" ]
677        }
678
679        # Additional flags passed to the wrapper script but that are only
680        # set conditionally.
681        _extra_flags = ""
682
683        # Environment variables passed to the wrapper script. Considered
684        # part of the command-line by ninja (and thus cause the build to
685        # be considered dirty if they change) without having to be parsed
686        # by the script.
687        _env_vars = "TOOL_VERSION=${tool_versions.swiftc} " +
688                    "JSON_VERSION=${tool_versions.const_extract_protocols}"
689
690        # Include the version of the compiler on the command-line. This causes
691        # `ninja` to consider all the compilation output to be dirty when the
692        # version changes.
693        if (defined(swiftc_version)) {
694          _env_vars += " SWIFTC_VERSION=$swiftc_version"
695        }
696
697        # Include the version of Xcode on the command-line (if specified via
698        # toolchain_args). This causes `ninja` to consider all the compilation
699        # outputs to be dirty when the version change.
700        #
701        # This is required because sometimes module dependency changes between
702        # different version of Xcode (e.g. when moving from Xcode 14 beta 6 to
703        # Xcode 14 RC). If the swiftmodule are not rebuilt when the version
704        # changes, they may encode dependency on now non-existing frameworks
705        # causing linker failures ultimately.
706        if (defined(toolchain_args.xcode_build)) {
707          _env_vars += " XCODE_VERSION=${toolchain_args.xcode_build}"
708        }
709
710        if (invoker.sdk_developer_dir != "") {
711          _env_vars += " DEVELOPER_DIR=${toolchain_args.sdk_developer_dir}"
712        }
713
714        if (swift_toolchain_path != "") {
715          _extra_flags += " --swift-toolchain-path " +
716                          rebase_path(swift_toolchain_path, root_build_dir)
717        }
718
719        if (swift_whole_module_optimization) {
720          _extra_flags += " --whole-module-optimization"
721        }
722
723        if (swift_keep_intermediate_files) {
724          _extra_flags += " --derived-data-dir $_derived_data_dir"
725        }
726
727        # The Swift compiler assumes that the generated header will be used by
728        # Objective-C code compiled with module support enabled (-fmodules).
729        #
730        # As Chromium code is compiled without support for modules (i.e. the
731        # code is compiled without `-fmodules`), the dependent modules are not
732        # imported from the generated header, which causes compilation failure
733        # if the client code does not first import the required modules (see
734        # https://crbug.com/1316061 for details).
735        #
736        # Secondly, the Swift compiler uses absolute path when importing other
737        # modules' generated headers or Objective-C bridging headers. This
738        # causes issues with the distributed compiler (i.e. reclient or siso)
739        # as they want all paths to be relative to the source directory.
740        #
741        # Instruct swiftc.py to rewrite the generated header use relative
742        # import and to use the old #import syntax for system frameworks.
743        _extra_flags += " --fix-generated-header"
744
745        _src_dir = rebase_path("//", root_build_dir)
746        _gen_dir = rebase_path(root_gen_dir, root_build_dir)
747        _const_gather_protocols_file =
748            rebase_path("//build/toolchain/ios/const_extract_protocols.json",
749                        root_build_dir)
750
751        command =
752            "$_env_vars $python_path $_tool --module-name {{module_name}} " +
753            "--header-path $_header_path --target-out-dir $_output_dir " +
754            "--const-gather-protocols-file $_const_gather_protocols_file " +
755            "--depfile-path $depfile --src-dir $_src_dir --gen-dir $_gen_dir " +
756            "--bridge-header {{bridge_header}} {{include_dirs}} " +
757            "{{module_dirs}} {{swiftflags}} {{inputs}}$_extra_flags"
758
759        description = "SWIFT $_output_dir/{{module_name}}.swiftmodule"
760      }
761    }
762
763    # xcassets are only used on iOS, not macOS. We want to minimize the number
764    # of Xcode-based tools used by the macOS toolchain, so we intentionally
765    # disallow future uses of xcassets on macOS. https://crbug.com/965663.
766    if (toolchain_args.current_os == "ios") {
767      tool("compile_xcassets") {
768        _tool = rebase_path("//build/toolchain/ios/compile_xcassets.py",
769                            root_build_dir)
770
771        _env_vars = "TOOL_VERSION=${tool_versions.compile_xcassets}"
772        if (invoker.sdk_developer_dir != "") {
773          _env_vars += " DEVELOPER_DIR=${toolchain_args.sdk_developer_dir}"
774        }
775
776        command =
777            "$_env_vars $python_path $_tool " +
778            "-p '${toolchain_args.current_os}' " +
779            "-e '${invoker.target_environment}' " +
780            "-t '${invoker.deployment_target}' " +
781            "-T '{{bundle_product_type}}' " +
782            "-P '{{bundle_partial_info_plist}}' " + "-o {{output}} {{inputs}}"
783
784        description = "COMPILE_XCASSETS {{output}}"
785        pool = "//build/toolchain/apple:bundle_pool($default_toolchain)"
786      }
787    }
788
789    tool("action") {
790      pool = "//build/toolchain:action_pool($default_toolchain)"
791    }
792  }
793}
794
795# Makes an Apple toolchain for the target, and an equivalent toolchain with the
796# prebuilt Rust stdlib for building proc macros (and other for-build-use
797# artifacts).
798template("apple_toolchain") {
799  single_apple_toolchain(target_name) {
800    assert(defined(invoker.toolchain_args),
801           "Toolchains must declare toolchain_args")
802    forward_variables_from(invoker,
803                           "*",
804                           [
805                             "visibility",
806                             "test_only",
807                           ])
808
809    # No need to forward visibility and test_only as they apply to targets not
810    # toolchains, but presubmit checks require that we explicitly exclude them
811  }
812
813  if (enable_rust && current_toolchain == default_toolchain) {
814    # Make an additional toolchain which is used for making tools that are run
815    # on the host machine as part of the build process (such as proc macros
816    # and Cargo build scripts). This toolchain uses the prebuilt stdlib that
817    # comes with the compiler, so it doesn't have to wait for the stdlib to be
818    # built before building other stuff. And this ensures its proc macro
819    # outputs have the right ABI to be loaded by the compiler, and it can be
820    # used to compile build scripts that are part of the stdlib that is built
821    # for the default toolchain.
822    single_apple_toolchain("${target_name}_for_rust_host_build_tools") {
823      assert(defined(invoker.toolchain_args),
824             "Toolchains must declare toolchain_args")
825      forward_variables_from(invoker,
826                             "*",
827                             [
828                               "toolchain_args",
829                               "visibility",
830                               "test_only",
831                             ])
832      toolchain_args = {
833        # Populate toolchain args from the invoker.
834        forward_variables_from(invoker.toolchain_args, "*")
835        toolchain_for_rust_host_build_tools = true
836
837        # The host build tools are static release builds to make the Chromium
838        # build faster. They do not need PGO etc, so no official builds.
839        is_debug = false
840        is_component_build = false
841        is_official_build = false
842        use_clang_coverage = false
843        use_sanitizer_coverage = false
844        generate_linker_map = false
845        use_thin_lto = false
846      }
847    }
848  }
849}
850