blob: 277a9997b29766e3aef28b165e186204e7cf046f [file] [log] [blame]
# Copyright 2019 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/clang/clang.gni")
# This template defines a toolchain.
#
# It requires the following variables specifying the executables to run:
# - ar
# - cc
# - cxx
# - ld
#
# Optional parameters that control the tools:
#
# - extra_cflags
# Extra flags to be appended when compiling C files (but not C++ files).
# - extra_cppflags
# Extra flags to be appended when compiling both C and C++ files. "CPP"
# stands for "C PreProcessor" in this context, although it can be
# used for non-preprocessor flags as well. Not to be confused with
# "CXX" (which follows).
# - extra_cxxflags
# Extra flags to be appended when compiling C++ files (but not C files).
# - extra_asmflags
# Extra flags to be appended when compiling assembly.
# - extra_ldflags
# Extra flags to be appended when linking
#
# - libs_section_prefix
# - libs_section_postfix
# The contents of these strings, if specified, will be placed around
# the libs section of the linker line. It allows one to inject libraries
# at the beginning and end for all targets in a toolchain.
# - solink_libs_section_prefix
# - solink_libs_section_postfix
# Same as libs_section_{pre,post}fix except used for solink instead of link.
# - link_outputs
# The content of this array, if specified, will be added to the list of
# outputs from the link command. This can be useful in conjunction with
# the post_link parameter.
# - use_unstripped_as_runtime_outputs
# When |strip| is set, mark unstripped executables as runtime deps rather
# than stripped ones.
# - post_link
# The content of this string, if specified, will be run as a separate
# command following the the link command.
# - deps
# Just forwarded to the toolchain definition.
# - executable_extension
# If this string is specified it will be used for the file extension
# for an executable, rather than using no extension; targets will
# still be able to override the extension using the output_extension
# variable.
# - rebuild_define
# The contents of this string, if specified, will be passed as a #define
# to the toolchain. It can be used to force recompiles whenever a
# toolchain is updated.
# - shlib_extension
# If this string is specified it will be used for the file extension
# for a shared library, rather than default value specified in
# toolchain.gni
# - use_strip
# Enable stripping symbols out of binaries. The pre-stripped
# artifacts will be put in lib.unstripped/ and exe.unstripped/.
template("clang_toolchain") {
declare_args() {
shlib_prefix = "lib"
shlib_extension = ".so"
}
params = {
if (defined(invoker.prefix)) {
prefix = invoker.prefix
} else {
prefix = rebase_path("$clang_base_path/bin", root_build_dir)
}
if (defined(invoker.ar)) {
ar = invoker.ar
} else {
ar = "${prefix}/llvm-ar"
}
if (defined(invoker.cc)) {
cc = invoker.cc
} else {
cc = "${prefix}/clang"
}
if (defined(invoker.cxx)) {
cxx = invoker.cxx
} else {
cxx = "${prefix}/clang++"
}
if (defined(invoker.ld)) {
ld = invoker.ld
} else {
ld = cxx
}
}
toolchain(target_name) {
assert(defined(params.ar), "clang_toolchain() must specify a \"ar\" value")
assert(defined(params.cc), "clang_toolchain() must specify a \"cc\" value")
assert(defined(params.cxx),
"clang_toolchain() must specify a \"cxx\" value")
assert(defined(params.ld), "clang_toolchain() must specify a \"ld\" value")
# This define changes when the toolchain changes, forcing a rebuild.
# Nothing should ever use this define.
if (defined(invoker.rebuild_define)) {
rebuild_string = "-D" + invoker.rebuild_define + " "
} else {
rebuild_string = ""
}
# GN's syntax can't handle more than one scope dereference at once, like
# "invoker.toolchain_args.foo", so make a temporary to hold the toolchain
# args so we can do "invoker_toolchain_args.foo".
assert(defined(invoker.toolchain_args),
"Toolchains must specify toolchain_args")
invoker_toolchain_args = invoker.toolchain_args
assert(defined(invoker_toolchain_args.current_cpu),
"toolchain_args must specify a current_cpu")
assert(defined(invoker_toolchain_args.current_os),
"toolchain_args must specify a current_os")
# When invoking this toolchain not as the default one, these args will be
# passed to the build. They are ignored when this is the default toolchain.
toolchain_args = {
# Populate toolchain args from the invoker.
forward_variables_from(invoker_toolchain_args, "*")
# The host toolchain value computed by the default toolchain's setup
# needs to be passed through unchanged to all secondary toolchains to
# ensure that it's always the same, regardless of the values that may be
# set on those toolchains.
host_toolchain = host_toolchain
}
compiler_prefix = " "
# Create a distinct variable for "asm", since analysis runs pass # a bunch
# of flags to clang/clang++ that are nonsensical on assembler runs.
asm_prefix = compiler_prefix
cc = compiler_prefix + params.cc
cxx = compiler_prefix + params.cxx
asm = asm_prefix + params.cc
ar = params.ar
ld = params.ld
if (defined(invoker.shlib_extension)) {
default_shlib_extension = invoker.shlib_extension
} else {
default_shlib_extension = shlib_extension
}
if (defined(invoker.default_shlib_subdir)) {
default_shlib_subdir = invoker.default_shlib_subdir
} else {
default_shlib_subdir = ""
}
if (defined(invoker.executable_extension)) {
default_executable_extension = invoker.executable_extension
} else {
default_executable_extension = ""
}
# Bring these into our scope for string interpolation with default values.
if (defined(invoker.libs_section_prefix)) {
libs_section_prefix = invoker.libs_section_prefix
} else {
libs_section_prefix = ""
}
if (defined(invoker.libs_section_postfix)) {
libs_section_postfix = invoker.libs_section_postfix
} else {
libs_section_postfix = ""
}
if (defined(invoker.solink_libs_section_prefix)) {
solink_libs_section_prefix = invoker.solink_libs_section_prefix
} else {
solink_libs_section_prefix = ""
}
if (defined(invoker.solink_libs_section_postfix)) {
solink_libs_section_postfix = invoker.solink_libs_section_postfix
} else {
solink_libs_section_postfix = ""
}
if (defined(invoker.extra_cflags) && invoker.extra_cflags != "") {
extra_cflags = " " + invoker.extra_cflags
} else {
extra_cflags = ""
}
if (defined(invoker.extra_cppflags) && invoker.extra_cppflags != "") {
extra_cppflags = " " + invoker.extra_cppflags
} else {
extra_cppflags = ""
}
if (defined(invoker.extra_cxxflags) && invoker.extra_cxxflags != "") {
extra_cxxflags = " " + invoker.extra_cxxflags
} else {
extra_cxxflags = ""
}
if (defined(invoker.extra_asmflags) && invoker.extra_asmflags != "") {
extra_asmflags = " " + invoker.extra_asmflags
} else {
extra_asmflags = ""
}
if (defined(invoker.extra_ldflags) && invoker.extra_ldflags != "") {
extra_ldflags = " " + invoker.extra_ldflags
} else {
extra_ldflags = ""
}
use_strip = false
if (defined(invoker.use_strip)) {
use_strip = invoker.use_strip
}
if (use_strip) {
if (toolchain_args.current_os == "mac") {
strip = "xcrun strip -Sx"
buildidtool = rebase_path("//buildtools/mac64/buildidtool", root_build_dir)
} else {
strip = "${params.prefix}/llvm-objcopy"
buildidtool = rebase_path("//buildtools/linux64/buildidtool", root_build_dir)
}
}
# These library switches can apply to all tools below.
lib_switch = "-l"
lib_dir_switch = "-L"
# Object files go in this directory.
object_subdir = "{{target_out_dir}}/{{label_name}}"
tool("cc") {
depfile = "{{output}}.d"
command = "$cc -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}}${extra_cppflags}${extra_cflags} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "CC {{output}}"
outputs = [ "$object_subdir/{{source_name_part}}.o" ]
}
tool("cxx") {
depfile = "{{output}}.d"
command = "$cxx -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}}${extra_cppflags}${extra_cxxflags} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "CXX {{output}}"
outputs = [ "$object_subdir/{{source_name_part}}.o" ]
}
tool("asm") {
# For clang we can just use the C compiler to compile assembly.
depfile = "{{output}}.d"
command = "$asm -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{asmflags}}${extra_asmflags} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "ASM {{output}}"
outputs = [ "$object_subdir/{{source_name_part}}.o" ]
}
tool("alink") {
rspfile = "{{output}}.rsp"
rspfile_content = "{{inputs}}"
command = "\"$ar\" {{arflags}} -r -c -s -D {{output}} @\"$rspfile\""
# Remove the output file first so that ar doesn't try to modify the
# existing file.
command = "rm -f {{output}} && $command"
# Almost all targets build with //build/config/compiler:thin_archive which
# adds -T to arflags.
description = "AR {{output}}"
outputs = [ "{{output_dir}}/{{target_output_name}}{{output_extension}}" ]
# Shared libraries go in the target out directory by default so we can
# generate different targets with the same name and not have them collide.
default_output_dir = "{{target_out_dir}}"
default_output_extension = ".a"
output_prefix = "lib"
}
tool("solink") {
soname = "{{target_output_name}}{{output_extension}}" # e.g. "libfoo.so".
sofile = "{{output_dir}}/$soname" # Possibly including toolchain dir.
rspfile = sofile + ".rsp"
if (defined(invoker.use_strip)) {
unstripped_sofile = "{{root_out_dir}}/lib.unstripped/$soname"
} else {
unstripped_sofile = sofile
}
# These variables are not built into GN but are helpers that
# implement (1) linking to produce a .so, (2) extracting the symbols
# from that file (3) if the extracted list differs from the existing
# .TOC file, overwrite it, otherwise, don't change it.
tocfile = sofile + ".TOC"
link_command = "$ld -shared {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" -Wl,-soname=\"$soname\" @\"$rspfile\""
command = "$link_command"
if (defined(invoker.use_strip)) {
strip_command = "$strip --strip-all \"$unstripped_sofile\" \"$sofile\""
command += " && " + strip_command
command +=
" && $buildidtool -build-id-dir .build-id" +
" -stamp \"$sofile.build-id.stamp\"" + " -entry \"=$sofile\"" +
" -entry \".debug=$unstripped_sofile\""
}
rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive $solink_libs_section_prefix {{libs}} $solink_libs_section_postfix"
description = "SOLINK $sofile"
# Use this for {{output_extension}} expansions unless a target manually
# overrides it (in which case {{output_extension}} will be what the target
# specifies).
default_output_extension = default_shlib_extension
default_output_dir = "{{root_out_dir}}${default_shlib_subdir}"
output_prefix = "lib"
# Since the above commands only updates the .TOC file when it changes, ask
# Ninja to check if the timestamp actually changed to know if downstream
# dependencies should be recompiled.
restat = true
# Tell GN about the output files. It will link to the sofile but use the
# tocfile for dependency management.
outputs = [
sofile,
tocfile,
]
if (sofile != unstripped_sofile) {
outputs += [ unstripped_sofile ]
if (defined(invoker.use_unstripped_as_runtime_outputs) &&
invoker.use_unstripped_as_runtime_outputs) {
runtime_outputs = [ unstripped_sofile ]
}
}
if (defined(map_file)) {
outputs += [ map_file ]
}
link_output = sofile
depend_output = tocfile
}
tool("solink_module") {
soname = "{{target_output_name}}{{output_extension}}" # e.g. "libfoo.so".
sofile = "{{output_dir}}/$soname"
rspfile = sofile + ".rsp"
if (defined(invoker.use_strip)) {
unstripped_sofile = "{{root_out_dir}}/lib.unstripped/$soname"
} else {
unstripped_sofile = sofile
}
command = "$ld -shared {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" -Wl,-soname=\"$soname\" @\"$rspfile\""
if (defined(invoker.use_strip)) {
strip_command =
"$strip --strip-sections \"$unstripped_sofile\" \"$sofile\""
command += " && " + strip_command
command +=
" && $buildidtool -build-id-dir .build-id" +
" -stamp \"$sofile.build-id.stamp\"" + " -entry \"=$sofile\"" +
" -entry \".debug=$unstripped_sofile\""
}
rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive $solink_libs_section_prefix {{libs}} $solink_libs_section_postfix"
description = "SOLINK_MODULE $sofile"
# Use this for {{output_extension}} expansions unless a target manually
# overrides it (in which case {{output_extension}} will be what the target
# specifies).
if (defined(invoker.loadable_module_extension)) {
default_output_extension = invoker.loadable_module_extension
} else {
default_output_extension = default_shlib_extension
}
default_output_dir = "{{root_out_dir}}${default_shlib_subdir}"
output_prefix = "lib"
outputs = [ sofile ]
if (sofile != unstripped_sofile) {
outputs += [ unstripped_sofile ]
if (defined(invoker.use_unstripped_as_runtime_outputs) &&
invoker.use_unstripped_as_runtime_outputs) {
runtime_outputs = [ unstripped_sofile ]
}
}
}
tool("link") {
exename = "{{target_output_name}}{{output_extension}}"
outfile = "{{output_dir}}/$exename"
rspfile = "$outfile.rsp"
unstripped_outfile = outfile
# Use this for {{output_extension}} expansions unless a target manually
# overrides it (in which case {{output_extension}} will be what the target
# specifies).
default_output_extension = default_executable_extension
default_output_dir = "{{root_out_dir}}"
if (defined(invoker.use_strip)) {
unstripped_outfile = "{{root_out_dir}}/exe.unstripped/$exename"
}
# Generate a map file to be used for binary size analysis.
# Map file adds ~10% to the link time on a z620.
# With target_os="android", libchrome.so.map.gz is ~20MB.
start_group_flag = "-Wl,--start-group"
end_group_flag = "-Wl,--end-group "
link_command = "$ld {{ldflags}}${extra_ldflags} -o \"$unstripped_outfile\" $start_group_flag @\"$rspfile\" {{solibs}} $end_group_flag $libs_section_prefix {{libs}} $libs_section_postfix"
command = "$link_command"
if (defined(invoker.use_strip)) {
strip_command =
"$strip --strip-sections \"$unstripped_outfile\" \"$outfile\""
command += " && " + strip_command
command +=
" && $buildidtool -build-id-dir .build-id" +
" -stamp \"$outfile.build-id.stamp\"" + " -entry \"=$outfile\"" +
" -entry \".debug=$unstripped_outfile\""
}
description = "LINK $outfile"
rspfile_content = "{{inputs}}"
outputs = [ outfile ]
if (outfile != unstripped_outfile) {
outputs += [ unstripped_outfile ]
}
if (defined(invoker.link_outputs)) {
outputs += invoker.link_outputs
}
if (defined(map_file)) {
outputs += [ map_file ]
}
}
# These two are really entirely generic, but have to be repeated in
# each toolchain because GN doesn't allow a template to be used here.
# See //build/toolchain/toolchain.gni for details.
tool("stamp") {
command = "touch {{output}}"
description = "STAMP {{output}}"
}
tool("copy") {
command = "ln -f {{source}} {{output}} 2>/dev/null || (rm -rf {{output}} && cp -af {{source}} {{output}})"
description = "COPY {{source}} {{output}}"
}
forward_variables_from(invoker, [ "deps" ])
}
}