1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# Copyright (c) 2013 The Chromium Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7import("//build/config/clang/clang.gni") 8import("//build/config/compiler/compiler.gni") 9import("//build/config/sanitizers/sanitizers.gni") 10import("//build/config/v8_target_cpu.gni") 11import("//build/toolchain/cc_wrapper.gni") 12import("//build/toolchain/clang_static_analyzer.gni") 13import("//build/toolchain/toolchain.gni") 14 15if (is_nacl) { 16 # To keep NaCl variables out of builds that don't include NaCl, all 17 # variables defined in nacl/config.gni referenced here should be protected by 18 # is_nacl conditions. 19 import("//build/config/nacl/config.gni") 20} 21 22declare_args() { 23 # Enables allowlist generation for IDR_ grit defines seen by the compiler. 24 # Currently works only on ohos and enabled by default for release builds. 25 # Requires debug info, so disabled for symbol_level=0 & strip_debug_info=true. 26 enable_resource_allowlist_generation = 27 is_ohos && !is_debug && 28 # Always enable for official builds, but enable for release builds by 29 # default only when other args allow. 30 (is_official_build || 31 (!strip_debug_info && symbol_level > 0 && !is_component_build)) 32} 33 34declare_args() { 35 share_ccache = "" 36} 37 38# When the arg is set via args.gn, it applies to all toolchains. In order to not 39# hit the assert in grit_rule.gni, explicitly disable for host toolchains. 40if (is_linux && target_os == "ohos") { 41 enable_resource_allowlist_generation = false 42} 43 44# Path to the Clang static analysis wrapper script. 45# REVIEWERS: can you suggest a better location for this? 46# GN is really picky about dead stores of variables except at the global scope. 47analyzer_wrapper = 48 rebase_path("//build/toolchain/clang_static_analyzer_wrapper.py", 49 root_build_dir) + " --mode=clang" 50 51# This template defines a toolchain for something that works like gcc 52# (including clang). 53# 54# It requires the following variables specifying the executables to run: 55# - ar 56# - cc 57# - cxx 58# - ld 59# 60# Optional parameters that control the tools: 61# 62# - extra_cflags 63# Extra flags to be appended when compiling C files (but not C++ files). 64# - extra_cppflags 65# Extra flags to be appended when compiling both C and C++ files. "CPP" 66# stands for "C PreProcessor" in this context, although it can be 67# used for non-preprocessor flags as well. Not to be confused with 68# "CXX" (which follows). 69# - extra_cxxflags 70# Extra flags to be appended when compiling C++ files (but not C files). 71# - extra_asmflags 72# Extra flags to be appended when compiling assembly. 73# - extra_ldflags 74# Extra flags to be appended when linking 75# 76# - libs_section_prefix 77# - libs_section_postfix 78# The contents of these strings, if specified, will be placed around 79# the libs section of the linker line. It allows one to inject libraries 80# at the beginning and end for all targets in a toolchain. 81# - solink_libs_section_prefix 82# - solink_libs_section_postfix 83# Same as libs_section_{pre,post}fix except used for solink instead of link. 84# - link_outputs 85# The content of this array, if specified, will be added to the list of 86# outputs from the link command. This can be useful in conjunction with 87# the post_link parameter. 88# - use_unstripped_as_runtime_outputs 89# When |strip| is set, mark unstripped executables as runtime deps rather 90# than stripped ones. 91# - post_link 92# The content of this string, if specified, will be run as a separate 93# command following the the link command. 94# - deps 95# Just forwarded to the toolchain definition. 96# - executable_extension 97# If this string is specified it will be used for the file extension 98# for an executable, rather than using no extension; targets will 99# still be able to override the extension using the output_extension 100# variable. 101# - rebuild_define 102# The contents of this string, if specified, will be passed as a #define 103# to the toolchain. It can be used to force recompiles whenever a 104# toolchain is updated. 105# - shlib_extension 106# If this string is specified it will be used for the file extension 107# for a shared library, rather than default value specified in 108# toolchain.gni 109# - strip 110# Location of the strip executable. When specified, strip will be run on 111# all shared libraries and executables as they are built. The pre-stripped 112# artifacts will be put in lib.unstripped/ and exe.unstripped/. 113template("gcc_toolchain") { 114 toolchain(target_name) { 115 assert(defined(invoker.ar), "gcc_toolchain() must specify a \"ar\" value") 116 assert(defined(invoker.cc), "gcc_toolchain() must specify a \"cc\" value") 117 assert(defined(invoker.cxx), "gcc_toolchain() must specify a \"cxx\" value") 118 assert(defined(invoker.ld), "gcc_toolchain() must specify a \"ld\" value") 119 120 # This define changes when the toolchain changes, forcing a rebuild. 121 # Nothing should ever use this define. 122 if (defined(invoker.rebuild_define)) { 123 rebuild_string = "-D" + invoker.rebuild_define + " " 124 } else { 125 rebuild_string = "" 126 } 127 128 # GN's syntax can't handle more than one scope dereference at once, like 129 # "invoker.toolchain_args.foo", so make a temporary to hold the toolchain 130 # args so we can do "invoker_toolchain_args.foo". 131 assert(defined(invoker.toolchain_args), 132 "Toolchains must specify toolchain_args") 133 invoker_toolchain_args = invoker.toolchain_args 134 assert(defined(invoker_toolchain_args.current_cpu), 135 "toolchain_args must specify a current_cpu") 136 assert(defined(invoker_toolchain_args.current_os), 137 "toolchain_args must specify a current_os") 138 139 # When invoking this toolchain not as the default one, these args will be 140 # passed to the build. They are ignored when this is the default toolchain. 141 toolchain_args = { 142 # Populate toolchain args from the invoker. 143 forward_variables_from(invoker_toolchain_args, "*") 144 145 # The host toolchain value computed by the default toolchain's setup 146 # needs to be passed through unchanged to all secondary toolchains to 147 # ensure that it's always the same, regardless of the values that may be 148 # set on those toolchains. 149 host_toolchain = host_toolchain 150 151 if (!defined(invoker_toolchain_args.v8_current_cpu)) { 152 v8_current_cpu = invoker_toolchain_args.current_cpu 153 } 154 } 155 156 if (defined(toolchain_args.cc_wrapper)) { 157 toolchain_cc_wrapper = toolchain_args.cc_wrapper 158 } else { 159 toolchain_cc_wrapper = cc_wrapper 160 } 161 162 if (is_clang && use_clang_static_analyzer && 163 (!defined(invoker.is_clang_analysis_supported) || 164 invoker.is_clang_analysis_supported)) { 165 compiler_prefix = "${analyzer_wrapper} " 166 asm = invoker.cc 167 } else { 168 if (share_ccache != "") { 169 compiler_prefix = "CCACHE_DIR=" + share_ccache + 170 " CCACHE_NOHASHDIR=1 ${toolchain_cc_wrapper} " 171 } else { 172 compiler_prefix = "${toolchain_cc_wrapper} " 173 } 174 } 175 176 cc = compiler_prefix + invoker.cc 177 cxx = compiler_prefix + invoker.cxx 178 ar = invoker.ar 179 ld = invoker.ld 180 if (!defined(asm)) { 181 asm = cc 182 } 183 if (defined(invoker.readelf)) { 184 readelf = invoker.readelf 185 } else { 186 readelf = "readelf" 187 } 188 if (defined(invoker.nm)) { 189 nm = invoker.nm 190 } else { 191 nm = "nm" 192 } 193 194 if (defined(invoker.shlib_extension)) { 195 default_shlib_extension = invoker.shlib_extension 196 } else { 197 default_shlib_extension = shlib_extension 198 } 199 200 if (defined(invoker.executable_extension)) { 201 default_executable_extension = invoker.executable_extension 202 } else { 203 default_executable_extension = "" 204 } 205 206 # Bring these into our scope for string interpolation with default values. 207 if (defined(invoker.libs_section_prefix)) { 208 libs_section_prefix = invoker.libs_section_prefix 209 } else { 210 libs_section_prefix = "" 211 } 212 213 if (defined(invoker.libs_section_postfix)) { 214 libs_section_postfix = invoker.libs_section_postfix 215 } else { 216 libs_section_postfix = "" 217 } 218 219 if (defined(invoker.solink_libs_section_prefix)) { 220 solink_libs_section_prefix = invoker.solink_libs_section_prefix 221 } else { 222 solink_libs_section_prefix = "" 223 } 224 225 if (defined(invoker.solink_libs_section_postfix)) { 226 solink_libs_section_postfix = invoker.solink_libs_section_postfix 227 } else { 228 solink_libs_section_postfix = "" 229 } 230 231 if (defined(invoker.extra_cflags) && invoker.extra_cflags != "") { 232 extra_cflags = " " + invoker.extra_cflags 233 } else { 234 extra_cflags = "" 235 } 236 237 if (defined(invoker.extra_cppflags) && invoker.extra_cppflags != "") { 238 extra_cppflags = " " + invoker.extra_cppflags 239 } else { 240 extra_cppflags = "" 241 } 242 243 if (defined(invoker.extra_cxxflags) && invoker.extra_cxxflags != "") { 244 extra_cxxflags = " " + invoker.extra_cxxflags 245 } else { 246 extra_cxxflags = "" 247 } 248 249 if (defined(invoker.extra_asmflags) && invoker.extra_asmflags != "") { 250 extra_asmflags = " " + invoker.extra_asmflags 251 } else { 252 extra_asmflags = "" 253 } 254 255 if (defined(invoker.extra_ldflags) && invoker.extra_ldflags != "") { 256 extra_ldflags = " " + invoker.extra_ldflags 257 } else { 258 extra_ldflags = "" 259 } 260 261 enable_linker_map = defined(invoker.enable_linker_map) && 262 invoker.enable_linker_map && generate_linker_map 263 264 # These library switches can apply to all tools below. 265 lib_switch = "-l" 266 lib_dir_switch = "-L" 267 268 # Object files go in this directory. 269 object_subdir = "{{source_out_dir}}/{{label_name}}" 270 271 tool("cc") { 272 depfile = "{{output}}.d" 273 command = "$cc -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}}${extra_cppflags}${extra_cflags} -c {{source}} -o {{output}}" 274 depsformat = "gcc" 275 description = "CC {{output}}" 276 outputs = [ "$object_subdir/{{source_name_part}}.o" ] 277 } 278 279 tool("cxx") { 280 depfile = "{{output}}.d" 281 command = "$cxx -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}}${extra_cppflags}${extra_cxxflags} -c {{source}} -o {{output}}" 282 depsformat = "gcc" 283 description = "CXX {{output}}" 284 outputs = [ "$object_subdir/{{source_name_part}}.o" ] 285 } 286 287 tool("asm") { 288 # For GCC we can just use the C compiler to compile assembly. 289 depfile = "{{output}}.d" 290 command = "$asm -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{asmflags}}${extra_asmflags} -c {{source}} -o {{output}}" 291 depsformat = "gcc" 292 description = "ASM {{output}}" 293 outputs = [ "$object_subdir/{{source_name_part}}.o" ] 294 } 295 296 tool("alink") { 297 if (current_os == "aix") { 298 # AIX does not support either -D (deterministic output) or response 299 # files. 300 command = "$ar -X64 {{arflags}} -r -c -s {{output}} {{inputs}}" 301 } else { 302 rspfile = "{{output}}.rsp" 303 rspfile_content = "{{inputs}}" 304 command = "\"$ar\" {{arflags}} -r -c -s -D {{output}} @\"$rspfile\"" 305 } 306 307 # Remove the output file first so that ar doesn't try to modify the 308 # existing file. 309 if (host_os == "win") { 310 tool_wrapper_path = 311 rebase_path("//build/toolchain/win/tool_wrapper.py", root_build_dir) 312 command = "cmd /c $python_path $tool_wrapper_path delete-file {{output}} && $command" 313 } else { 314 command = "rm -f {{output}} && $command" 315 } 316 317 # Almost all targets build with //build/config/compiler:thin_archive which 318 # adds -T to arflags. 319 description = "AR {{output}}" 320 outputs = [ "{{output_dir}}/{{target_output_name}}{{output_extension}}" ] 321 322 # Shared libraries go in the target out directory by default so we can 323 # generate different targets with the same name and not have them collide. 324 default_output_dir = "{{target_out_dir}}" 325 default_output_extension = ".a" 326 output_prefix = "lib" 327 } 328 329 tool("solink") { 330 soname = "{{target_output_name}}{{output_extension}}" # e.g. "libfoo.so". 331 sofile = "{{output_dir}}/$soname" # Possibly including toolchain dir. 332 rspfile = sofile + ".rsp" 333 pool = "//build/toolchain:link_pool($default_toolchain)" 334 335 is_mingw_link = false 336 if (invoker_toolchain_args.current_os == "mingw") { 337 is_mingw_link = true 338 libname = "{{target_output_name}}.lib" 339 libfile = "{{output_dir}}/$libname" 340 } 341 342 if (defined(invoker.strip)) { 343 unstripped_sofile = "{{root_out_dir}}/lib.unstripped/$sofile" 344 } else { 345 unstripped_sofile = sofile 346 } 347 348 link_command = "$ld -shared {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" @\"$rspfile\"" 349 if (!is_mingw_link) { 350 link_command = "$link_command -Wl,-soname=\"$soname\"" 351 } else { 352 link_command = "$link_command -Wl,--out-implib,{{root_out_dir}}/lib.unstripped/$libfile" 353 } 354 355 # Generate a map file to be used for binary size analysis. 356 # Map file adds ~10% to the link time on a z620. 357 map_switch = "" 358 if (enable_linker_map && is_official_build) { 359 map_file = "$unstripped_sofile.map.gz" 360 map_switch = " --map-file \"$map_file\"" 361 } 362 363 assert(defined(readelf), "to solink you must have a readelf") 364 assert(defined(nm), "to solink you must have an nm") 365 strip_switch = "" 366 if (defined(invoker.strip)) { 367 strip_switch = "--strip=${invoker.strip} " 368 } 369 370 # This needs a Python script to avoid using a complex shell command 371 # requiring sh control structures, pipelines, and POSIX utilities. 372 # The host might not have a POSIX shell and utilities (e.g. Windows). 373 solink_wrapper = 374 rebase_path("//build/toolchain/gcc_solink_wrapper.py", root_build_dir) 375 command = "$python_path \"$solink_wrapper\" --readelf=\"$readelf\" --nm=\"$nm\" $strip_switch --sofile=\"$unstripped_sofile\" $map_switch --output=\"$sofile\"" 376 if (is_mingw_link) { 377 command = "$command --libfile=\"$libfile\"" 378 } 379 if (full_mini_debug && !is_debug) { 380 command = "$command --mini-debug" 381 } 382 command = "$command -- $link_command" 383 384 rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive $solink_libs_section_prefix {{libs}} $solink_libs_section_postfix" 385 386 description = "SOLINK $sofile" 387 388 # Use this for {{output_extension}} expansions unless a target manually 389 # overrides it (in which case {{output_extension}} will be what the target 390 # specifies). 391 default_output_extension = default_shlib_extension 392 393 default_output_dir = "{{root_out_dir}}" 394 395 output_prefix = "lib" 396 397 # Since the above commands only updates the .TOC file when it changes, ask 398 # Ninja to check if the timestamp actually changed to know if downstream 399 # dependencies should be recompiled. 400 restat = true 401 402 # Tell GN about the output files. It will link to the sofile 403 outputs = [ sofile ] 404 if (sofile != unstripped_sofile) { 405 outputs += [ unstripped_sofile ] 406 if (defined(invoker.use_unstripped_as_runtime_outputs) && 407 invoker.use_unstripped_as_runtime_outputs) { 408 runtime_outputs = [ unstripped_sofile ] 409 } 410 } 411 if (defined(map_file)) { 412 outputs += [ map_file ] 413 } 414 415 if (is_mingw_link) { 416 outputs += [ libfile ] 417 link_output = libfile 418 depend_output = libfile 419 } else { 420 link_output = sofile 421 depend_output = sofile 422 } 423 } 424 425 tool("solink_module") { 426 soname = "{{target_output_name}}{{output_extension}}" # e.g. "libfoo.so". 427 sofile = "{{output_dir}}/$soname" 428 rspfile = sofile + ".rsp" 429 pool = "//build/toolchain:link_pool($default_toolchain)" 430 431 if (defined(invoker.strip)) { 432 unstripped_sofile = "{{root_out_dir}}/lib.unstripped/$sofile" 433 } else { 434 unstripped_sofile = sofile 435 } 436 437 command = "$ld -shared {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" -Wl,-soname=\"$soname\" @\"$rspfile\"" 438 439 if (defined(invoker.strip)) { 440 strip_command = "${invoker.strip} -o \"$sofile\" \"$unstripped_sofile\"" 441 command += " && " + strip_command 442 } 443 rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive $solink_libs_section_prefix {{libs}} $solink_libs_section_postfix" 444 445 description = "SOLINK_MODULE $sofile" 446 447 # Use this for {{output_extension}} expansions unless a target manually 448 # overrides it (in which case {{output_extension}} will be what the target 449 # specifies). 450 if (defined(invoker.loadable_module_extension)) { 451 default_output_extension = invoker.loadable_module_extension 452 } else { 453 default_output_extension = default_shlib_extension 454 } 455 456 default_output_dir = "{{root_out_dir}}" 457 458 output_prefix = "lib" 459 460 outputs = [ sofile ] 461 if (sofile != unstripped_sofile) { 462 outputs += [ unstripped_sofile ] 463 if (defined(invoker.use_unstripped_as_runtime_outputs) && 464 invoker.use_unstripped_as_runtime_outputs) { 465 runtime_outputs = [ unstripped_sofile ] 466 } 467 } 468 } 469 470 tool("link") { 471 exename = "{{target_output_name}}{{output_extension}}" 472 outfile = "{{output_dir}}/$exename" 473 rspfile = "$outfile.rsp" 474 unstripped_outfile = outfile 475 pool = "//build/toolchain:link_pool($default_toolchain)" 476 477 # Use this for {{output_extension}} expansions unless a target manually 478 # overrides it (in which case {{output_extension}} will be what the target 479 # specifies). 480 default_output_extension = default_executable_extension 481 482 default_output_dir = "{{root_out_dir}}" 483 484 if (defined(invoker.strip)) { 485 unstripped_outfile = "{{root_out_dir}}/exe.unstripped/$outfile" 486 } 487 488 # Generate a map file to be used for binary size analysis. 489 # Map file adds ~10% to the link time on a z620. 490 map_switch = "" 491 if (enable_linker_map && is_official_build) { 492 map_file = "$unstripped_outfile.map.gz" 493 map_switch = " --map-file \"$map_file\"" 494 } 495 496 start_group_flag = "" 497 end_group_flag = "" 498 if (current_os != "aix") { 499 # the "--start-group .. --end-group" feature isn't available on the aix ld. 500 start_group_flag = "-Wl,--start-group" 501 end_group_flag = "-Wl,--end-group " 502 } 503 _clang_rt_dso_full_path = "" 504 if (is_asan && invoker_toolchain_args.current_os == "ohos") { 505 if (invoker_toolchain_args.current_cpu == "arm64") { 506 _clang_rt_dso_full_path = rebase_path( 507 "$clang_base_path/lib/clang/$clang_version/lib/aarch64-linux-ohos/libclang_rt.asan.so", 508 root_build_dir) 509 } else { 510 _clang_rt_dso_full_path = rebase_path( 511 "$clang_base_path/lib/clang/$clang_version/lib/arm-linux-ohos/libclang_rt.asan.so", 512 root_build_dir) 513 } 514 } 515 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" 516 517 strip_switch = "" 518 519 if (defined(invoker.strip)) { 520 strip_switch = " --strip=\"${invoker.strip}\" --unstripped-file=\"$unstripped_outfile\"" 521 } 522 if (is_asan && invoker_toolchain_args.current_os == "ohos") { 523 strip_switch = 524 "$strip_switch --clang_rt_dso_path=\"$_clang_rt_dso_full_path\"" 525 } 526 527 link_wrapper = 528 rebase_path("//build/toolchain/gcc_link_wrapper.py", root_build_dir) 529 command = "$python_path \"$link_wrapper\" --output=\"$outfile\"$strip_switch$map_switch " 530 if (full_mini_debug && !is_debug) { 531 command = "$command --mini-debug" 532 } 533 command = "$command -- $link_command" 534 description = "LINK $outfile" 535 rspfile_content = "{{inputs}}" 536 outputs = [ outfile ] 537 if (outfile != unstripped_outfile) { 538 outputs += [ unstripped_outfile ] 539 if (defined(invoker.use_unstripped_as_runtime_outputs) && 540 invoker.use_unstripped_as_runtime_outputs) { 541 runtime_outputs = [ unstripped_outfile ] 542 } 543 } 544 if (defined(invoker.link_outputs)) { 545 outputs += invoker.link_outputs 546 } 547 if (defined(map_file)) { 548 outputs += [ map_file ] 549 } 550 } 551 552 # These two are really entirely generic, but have to be repeated in 553 # each toolchain because GN doesn't allow a template to be used here. 554 # See //build/toolchain/toolchain.gni for details. 555 tool("stamp") { 556 command = stamp_command 557 description = stamp_description 558 } 559 tool("copy") { 560 command = copy_command 561 description = copy_description 562 } 563 564 tool("action") { 565 pool = "//build/toolchain:action_pool($default_toolchain)" 566 } 567 568 forward_variables_from(invoker, [ "deps" ]) 569 } 570} 571 572# This is a shorthand for gcc_toolchain instances based on the Chromium-built 573# version of Clang. Only the toolchain_cpu and toolchain_os variables need to 574# be specified by the invoker, and optionally toolprefix if it's a 575# cross-compile case. Note that for a cross-compile case this toolchain 576# requires a config to pass the appropriate -target option, or else it will 577# actually just be doing a native compile. The invoker can optionally override 578# use_gold too. 579template("clang_toolchain") { 580 if (defined(invoker.toolprefix)) { 581 toolprefix = invoker.toolprefix 582 } else { 583 toolprefix = "" 584 } 585 586 gcc_toolchain(target_name) { 587 prefix = rebase_path("$clang_base_path/bin", root_build_dir) 588 cc = "$prefix/clang" 589 cxx = "$prefix/clang++" 590 ld = cxx 591 readelf = "${toolprefix}readelf" 592 ar = "${prefix}/llvm-ar" 593 nm = "${toolprefix}nm" 594 595 forward_variables_from(invoker, 596 [ 597 "strip", 598 "is_clang_analysis_supported", 599 "enable_linker_map", 600 "use_unstripped_as_runtime_outputs", 601 ]) 602 603 toolchain_args = { 604 if (defined(invoker.toolchain_args)) { 605 forward_variables_from(invoker.toolchain_args, "*") 606 } 607 is_clang = true 608 } 609 610 if (defined(invoker.shlib_extension) && invoker.shlib_extension != "") { 611 shlib_extension = invoker.shlib_extension 612 } 613 } 614} 615