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