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