• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# Copyright (c) 2022 Huawei Device Co., Ltd.
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import("$build_root/config/clang/clang.gni")
17import("$build_root/config/sanitizers/sanitizers.gni")
18import("$build_root/toolchain/cc_wrapper.gni")
19import("$build_root/toolchain/clang_static_analyzer.gni")
20import("$build_root/toolchain/toolchain.gni")
21template("gcc_toolchain") {
22  toolchain(target_name) {
23    assert(defined(invoker.ar), "gcc_toolchain() must specify a \"ar\" value")
24    assert(defined(invoker.cc), "gcc_toolchain() must specify a \"cc\" value")
25    assert(defined(invoker.cxx), "gcc_toolchain() must specify a \"cxx\" value")
26    assert(defined(invoker.ld), "gcc_toolchain() must specify a \"ld\" value")
27
28    # This define changes when the toolchain changes, forcing a rebuild.
29    # Nothing should ever use this define.
30    if (defined(invoker.rebuild_define)) {
31      rebuild_string = "-D" + invoker.rebuild_define + " "
32    } else {
33      rebuild_string = ""
34    }
35
36    # GN's syntax can't handle more than one scope dereference at once, like
37    # "invoker.toolchain_args.foo", so make a temporary to hold the toolchain
38    # args so we can do "invoker_toolchain_args.foo".
39    assert(defined(invoker.toolchain_args),
40           "Toolchains must specify toolchain_args")
41    invoker_toolchain_args = invoker.toolchain_args
42    assert(defined(invoker_toolchain_args.current_cpu),
43           "toolchain_args must specify a current_cpu")
44    assert(defined(invoker_toolchain_args.current_os),
45           "toolchain_args must specify a current_os")
46
47    # When invoking this toolchain not as the default one, these args will be
48    # passed to the build. They are ignored when this is the default toolchain.
49    toolchain_args = {
50      # Populate toolchain args from the invoker.
51      forward_variables_from(invoker_toolchain_args, "*")
52      # The host toolchain value computed by the default toolchain's setup
53      # needs to be passed through unchanged to all secondary toolchains to
54      # ensure that it's always the same, regardless of the values that may be
55      # set on those toolchains.
56    }
57
58    if (defined(toolchain_args.cc_wrapper)) {
59      toolchain_cc_wrapper = toolchain_args.cc_wrapper
60    } else {
61      toolchain_cc_wrapper = cc_wrapper
62    }
63
64    if (is_clang && use_clang_static_analyzer &&
65        (!defined(invoker.is_clang_analysis_supported) ||
66         invoker.is_clang_analysis_supported)) {
67      compiler_prefix = "${analyzer_wrapper} "
68      asm = invoker.cc
69    } else {
70      compiler_prefix = "${toolchain_cc_wrapper} "
71    }
72
73    cc = compiler_prefix + invoker.cc
74    cxx = compiler_prefix + invoker.cxx
75    ar = invoker.ar
76    ld = invoker.ld
77    if (!defined(asm)) {
78      asm = cc
79    }
80    if (defined(invoker.readelf)) {
81      readelf = invoker.readelf
82    } else {
83      readelf = "readelf"
84    }
85    if (defined(invoker.nm)) {
86      nm = invoker.nm
87    } else {
88      nm = "nm"
89    }
90
91    if (defined(invoker.shlib_extension)) {
92      default_shlib_extension = invoker.shlib_extension
93    } else {
94      default_shlib_extension = shlib_extension
95    }
96
97    if (defined(invoker.executable_extension)) {
98      default_executable_extension = invoker.executable_extension
99    } else {
100      default_executable_extension = ""
101    }
102
103    # Bring these into our scope for string interpolation with default values.
104    if (defined(invoker.libs_section_prefix)) {
105      libs_section_prefix = invoker.libs_section_prefix
106    } else {
107      libs_section_prefix = ""
108    }
109
110    if (defined(invoker.libs_section_postfix)) {
111      libs_section_postfix = invoker.libs_section_postfix
112    } else {
113      libs_section_postfix = ""
114    }
115
116    if (defined(invoker.solink_libs_section_prefix)) {
117      solink_libs_section_prefix = invoker.solink_libs_section_prefix
118    } else {
119      solink_libs_section_prefix = ""
120    }
121
122    if (defined(invoker.solink_libs_section_postfix)) {
123      solink_libs_section_postfix = invoker.solink_libs_section_postfix
124    } else {
125      solink_libs_section_postfix = ""
126    }
127
128    if (defined(invoker.extra_cflags) && invoker.extra_cflags != "") {
129      extra_cflags = " " + invoker.extra_cflags
130    } else {
131      extra_cflags = ""
132    }
133
134    if (defined(invoker.extra_cppflags) && invoker.extra_cppflags != "") {
135      extra_cppflags = " " + invoker.extra_cppflags
136    } else {
137      extra_cppflags = ""
138    }
139
140    if (defined(invoker.extra_cxxflags) && invoker.extra_cxxflags != "") {
141      extra_cxxflags = " " + invoker.extra_cxxflags
142    } else {
143      extra_cxxflags = ""
144    }
145
146    if (defined(invoker.extra_asmflags) && invoker.extra_asmflags != "") {
147      extra_asmflags = " " + invoker.extra_asmflags
148    } else {
149      extra_asmflags = ""
150    }
151
152    if (defined(invoker.extra_ldflags) && invoker.extra_ldflags != "") {
153      extra_ldflags = " " + invoker.extra_ldflags
154    } else {
155      extra_ldflags = ""
156    }
157
158    enable_linker_map = defined(invoker.enable_linker_map) &&
159                        invoker.enable_linker_map && generate_linker_map
160
161    # These library switches can apply to all tools below.
162    lib_switch = "-l"
163    lib_dir_switch = "-L"
164
165    # Object files go in this directory.
166    object_subdir = "{{source_out_dir}}/{{label_name}}"
167
168    tool("cc") {
169      depfile = "{{output}}.d"
170      command = "$cc -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}}${extra_cppflags}${extra_cflags} -c {{source}} -o {{output}}"
171      depsformat = "gcc"
172      description = "CC {{output}}"
173      outputs = [ "$object_subdir/{{source_name_part}}.o" ]
174    }
175
176    tool("cxx") {
177      depfile = "{{output}}.d"
178      command = "$cxx -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}}${extra_cppflags}${extra_cxxflags} -c {{source}} -o {{output}}"
179      depsformat = "gcc"
180      description = "CXX {{output}}"
181      outputs = [ "$object_subdir/{{source_name_part}}.o" ]
182    }
183
184    tool("asm") {
185      # For GCC we can just use the C compiler to compile assembly.
186      depfile = "{{output}}.d"
187      command = "$asm -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{asmflags}}${extra_asmflags} -c {{source}} -o {{output}}"
188      depsformat = "gcc"
189      description = "ASM {{output}}"
190      outputs = [ "$object_subdir/{{source_name_part}}.o" ]
191    }
192
193    tool("alink") {
194      if (current_os == "aix") {
195        # AIX does not support either -D (deterministic output) or response
196        # files.
197        command = "$ar -X64 {{arflags}} -r -c -s {{output}} {{inputs}}"
198      } else {
199        rspfile = "{{output}}.rsp"
200        rspfile_content = "{{inputs}}"
201        command = "\"$ar\" {{arflags}} -r -c -s -D {{output}} @\"$rspfile\""
202      }
203
204      # Remove the output file first so that ar doesn't try to modify the
205      # existing file.
206      if (host_os == "win") {
207        tool_wrapper_path =
208            rebase_path("$build_root/toolchain/win/tool_wrapper.py",
209                        root_build_dir)
210        command = "cmd /c $python_path $tool_wrapper_path delete-file {{output}} && $command"
211      } else {
212        command = "rm -f {{output}} && $command"
213      }
214
215      # Almost all targets build with //build/config/compiler:thin_archive which
216      # adds -T to arflags.
217      description = "AR {{output}}"
218      outputs = [ "{{output_dir}}/{{target_output_name}}{{output_extension}}" ]
219
220      # Shared libraries go in the target out directory by default so we can
221      # generate different targets with the same name and not have them collide.
222      default_output_dir = "{{target_out_dir}}"
223      default_output_extension = ".a"
224      output_prefix = "lib"
225    }
226
227    tool("solink") {
228      soname = "{{target_output_name}}{{output_extension}}"  # e.g. "libfoo.so".
229      sofile = "{{output_dir}}/$soname"  # Possibly including toolchain dir.
230      rspfile = sofile + ".rsp"
231
232      is_mingw_link = false
233      if (invoker_toolchain_args.current_os == "mingw") {
234        is_mingw_link = true
235        libname = "{{target_output_name}}.lib"
236        libfile = "{{output_dir}}/$libname"
237      }
238
239      if (defined(invoker.strip)) {
240        unstripped_sofile = "{{root_out_dir}}/lib.unstripped/$sofile"
241      } else {
242        unstripped_sofile = sofile
243      }
244
245      link_command = "$ld -shared {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" @\"$rspfile\""
246      if (!is_mingw_link) {
247        link_command = "$link_command -Wl,-soname=\"$soname\""
248      } else {
249        link_command = "$link_command -Wl,--out-implib,{{root_out_dir}}/lib.unstripped/$libfile"
250      }
251
252      # Generate a map file to be used for binary size analysis.
253      # Map file adds ~10% to the link time on a z620.
254      map_switch = ""
255      if (enable_linker_map && is_official_build) {
256        map_file = "$unstripped_sofile.map.gz"
257        map_switch = " --map-file \"$map_file\""
258      }
259
260      assert(defined(readelf), "to solink you must have a readelf")
261      assert(defined(nm), "to solink you must have an nm")
262      strip_switch = ""
263      if (defined(invoker.strip)) {
264        strip_switch = "--strip=${invoker.strip} "
265      }
266
267      # This needs a Python script to avoid using a complex shell command
268      # requiring sh control structures, pipelines, and POSIX utilities.
269      # The host might not have a POSIX shell and utilities (e.g. Windows).
270      solink_wrapper =
271          rebase_path("$build_root/toolchain/gcc_solink_wrapper.py",
272                      root_build_dir)
273      command = "$python_path \"$solink_wrapper\" --readelf=\"$readelf\" --nm=\"$nm\" $strip_switch --sofile=\"$unstripped_sofile\" $map_switch --output=\"$sofile\""
274      if (is_mingw_link) {
275        command = "$command --libfile=\"$libfile\""
276      }
277      command = "$command -- $link_command"
278
279      rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive $solink_libs_section_prefix {{libs}} $solink_libs_section_postfix"
280
281      description = "SOLINK $sofile"
282
283      # Use this for {{output_extension}} expansions unless a target manually
284      # overrides it (in which case {{output_extension}} will be what the target
285      # specifies).
286      default_output_extension = default_shlib_extension
287
288      default_output_dir = "{{root_out_dir}}"
289
290      output_prefix = "lib"
291
292      # Since the above commands only updates the .TOC file when it changes, ask
293      # Ninja to check if the timestamp actually changed to know if downstream
294      # dependencies should be recompiled.
295      restat = true
296
297      # Tell GN about the output files. It will link to the sofile
298      outputs = [ sofile ]
299      if (sofile != unstripped_sofile) {
300        outputs += [ unstripped_sofile ]
301        if (defined(invoker.use_unstripped_as_runtime_outputs) &&
302            invoker.use_unstripped_as_runtime_outputs) {
303          runtime_outputs = [ unstripped_sofile ]
304        }
305      }
306      if (defined(map_file)) {
307        outputs += [ map_file ]
308      }
309
310      if (is_mingw_link) {
311        outputs += [ libfile ]
312        link_output = libfile
313        depend_output = libfile
314      } else {
315        link_output = sofile
316        depend_output = sofile
317      }
318    }
319
320    tool("solink_module") {
321      soname = "{{target_output_name}}{{output_extension}}"  # e.g. "libfoo.so".
322      sofile = "{{output_dir}}/$soname"
323      rspfile = sofile + ".rsp"
324
325      if (defined(invoker.strip)) {
326        unstripped_sofile = "{{root_out_dir}}/lib.unstripped/$sofile"
327      } else {
328        unstripped_sofile = sofile
329      }
330
331      command = "$ld -shared {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" -Wl,-soname=\"$soname\" @\"$rspfile\""
332
333      if (defined(invoker.strip)) {
334        strip_command = "${invoker.strip} -o \"$sofile\" \"$unstripped_sofile\""
335        command += " && " + strip_command
336      }
337      rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive $solink_libs_section_prefix {{libs}} $solink_libs_section_postfix"
338
339      description = "SOLINK_MODULE $sofile"
340
341      # Use this for {{output_extension}} expansions unless a target manually
342      # overrides it (in which case {{output_extension}} will be what the target
343      # specifies).
344      if (defined(invoker.loadable_module_extension)) {
345        default_output_extension = invoker.loadable_module_extension
346      } else {
347        default_output_extension = default_shlib_extension
348      }
349
350      default_output_dir = "{{root_out_dir}}"
351
352      output_prefix = "lib"
353
354      outputs = [ sofile ]
355      if (sofile != unstripped_sofile) {
356        outputs += [ unstripped_sofile ]
357        if (defined(invoker.use_unstripped_as_runtime_outputs) &&
358            invoker.use_unstripped_as_runtime_outputs) {
359          runtime_outputs = [ unstripped_sofile ]
360        }
361      }
362    }
363
364    tool("link") {
365      exename = "{{target_output_name}}{{output_extension}}"
366      outfile = "{{output_dir}}/$exename"
367      rspfile = "$outfile.rsp"
368      unstripped_outfile = outfile
369
370      # Use this for {{output_extension}} expansions unless a target manually
371      # overrides it (in which case {{output_extension}} will be what the target
372      # specifies).
373      default_output_extension = default_executable_extension
374
375      default_output_dir = "{{root_out_dir}}"
376
377      if (defined(invoker.strip)) {
378        unstripped_outfile = "{{root_out_dir}}/exe.unstripped/$outfile"
379      }
380
381      # Generate a map file to be used for binary size analysis.
382      # Map file adds ~10% to the link time on a z620.
383      map_switch = ""
384      if (enable_linker_map && is_official_build) {
385        map_file = "$unstripped_outfile.map.gz"
386        map_switch = " --map-file \"$map_file\""
387      }
388
389      start_group_flag = ""
390      end_group_flag = ""
391      if (current_os != "aix") {
392        # the "--start-group .. --end-group" feature isn't available on the aix ld.
393        start_group_flag = "-Wl,--start-group"
394        end_group_flag = "-Wl,--end-group "
395      }
396      _clang_rt_dso_full_path = ""
397      if (is_asan && invoker_toolchain_args.current_os == "ohos") {
398        if (invoker_toolchain_args.current_cpu == "arm64") {
399          _clang_rt_dso_full_path = rebase_path(
400                  "$clang_base_path/lib/clang/$clang_version/lib/aarch64-linux-ohos/libclang_rt.asan.so",
401                  root_build_dir)
402        } else {
403          _clang_rt_dso_full_path = rebase_path(
404                  "$clang_base_path/lib/clang/$clang_version/lib/arm-linux-ohos/libclang_rt.asan.so",
405                  root_build_dir)
406        }
407      }
408      link_command = "$ld {{ldflags}}${extra_ldflags} -o \"$unstripped_outfile\" $libs_section_prefix $start_group_flag $_clang_rt_dso_full_path @\"$rspfile\" {{solibs}} {{libs}} $end_group_flag $libs_section_postfix"
409
410      strip_switch = ""
411
412      if (defined(invoker.strip)) {
413        strip_switch = " --strip=\"${invoker.strip}\" --unstripped-file=\"$unstripped_outfile\""
414      }
415      if (is_asan && invoker_toolchain_args.current_os == "ohos") {
416        strip_switch =
417            "$strip_switch --clang_rt_dso_path=\"$_clang_rt_dso_full_path\""
418      }
419
420      link_wrapper = rebase_path("$build_root/toolchain/gcc_link_wrapper.py",
421                                 root_build_dir)
422      command = "$python_path \"$link_wrapper\" --output=\"$outfile\"$strip_switch$map_switch -- $link_command"
423      description = "LINK $outfile"
424      rspfile_content = "{{inputs}}"
425      outputs = [ outfile ]
426      if (outfile != unstripped_outfile) {
427        outputs += [ unstripped_outfile ]
428        if (defined(invoker.use_unstripped_as_runtime_outputs) &&
429            invoker.use_unstripped_as_runtime_outputs) {
430          runtime_outputs = [ unstripped_outfile ]
431        }
432      }
433      if (defined(invoker.link_outputs)) {
434        outputs += invoker.link_outputs
435      }
436      if (defined(map_file)) {
437        outputs += [ map_file ]
438      }
439    }
440
441    # These two are really entirely generic, but have to be repeated in
442    # each toolchain because GN doesn't allow a template to be used here.
443    # See //build/toolchain/toolchain.gni for details.
444    tool("stamp") {
445      command = stamp_command
446      description = stamp_description
447    }
448    tool("copy") {
449      command = copy_command
450      description = copy_description
451    }
452
453    forward_variables_from(invoker, [ "deps" ])
454  }
455}
456
457# This is a shorthand for gcc_toolchain instances based on the Chromium-built
458# version of Clang. Only the toolchain_cpu and toolchain_os variables need to
459# be specified by the invoker, and optionally toolprefix if it's a
460# cross-compile case. Note that for a cross-compile case this toolchain
461# requires a config to pass the appropriate -target option, or else it will
462# actually just be doing a native compile. The invoker can optionally override
463# use_gold too.
464template("clang_toolchain") {
465  if (defined(invoker.toolprefix)) {
466    toolprefix = invoker.toolprefix
467  } else {
468    toolprefix = ""
469  }
470
471  gcc_toolchain(target_name) {
472    prefix = rebase_path("$clang_base_path/bin", root_build_dir)
473    cc = "$prefix/clang"
474    cxx = "$prefix/clang++"
475    ld = cxx
476    readelf = "${toolprefix}readelf"
477    ar = "${prefix}/llvm-ar"
478    nm = "${toolprefix}nm"
479
480    forward_variables_from(invoker,
481                           [
482                             "strip",
483                             "is_clang_analysis_supported",
484                             "enable_linker_map",
485                             "use_unstripped_as_runtime_outputs",
486                           ])
487
488    toolchain_args = {
489      if (defined(invoker.toolchain_args)) {
490        forward_variables_from(invoker.toolchain_args, "*")
491      }
492      is_clang = true
493    }
494
495    if (defined(invoker.shlib_extension) && invoker.shlib_extension != "") {
496      shlib_extension = invoker.shlib_extension
497    }
498  }
499}
500