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