blob: b246a7cb7568d0b65d3d68672f219851ed93d865 [file] [log] [blame]
# Copyright 2017 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")
import("//build/config/sysroot.gni")
import("//build/host.gni")
import("//build/sdk/sdk_host_tool.gni")
import("//build/toolchain/breakpad.gni")
import("//build/toolchain/concurrent_jobs.gni")
import("//zircon/public/sysroot/go.gni")
declare_args() {
# gocache_dir
# Directory GOCACHE environment variable will be set to. This directory
# will have build and test results cached, and is safe to be written to
# concurrently. If overridden, this directory must be a full path.
gocache_dir = rebase_path("$root_out_dir/.gocache")
# go_vet_enabled
# [bool] if false, go vet invocations are disabled for all builds.
go_vet_enabled = false
# prebuilt_go_dir
# [string] points to the directory containing the prebuilt host go
# binary. By default, this points to the //prebuilts directory.
prebuilt_go_dir = "//prebuilt/third_party/go/${host_platform}"
}
# A template for an action that builds a Go binary. Users should instead use the
# go_binary or go_test rules.
#
# Parameters
#
# gopackages (optional)
# List of packages to build. Exactly one of gopackages or library must be
# set.
#
# library (optional)
# Alternative to gopackages, a library GN label corresponding to the
# package to build.
#
# use_prebuilt_go (optional)
# If true, use a prebuilt go toolchain, rather than building the toolchain.
# If not set, defaults to false when targeting Fuchsia and true otherwise.
#
# sdk_category (optional)
# Publication level of the library in SDKs.
# See //build/sdk/sdk_atom.gni.
#
# sdk_name (optional)
# Name of the library in the SDK.
#
# deps (optional)
# List of labels representing go_library targets this target depends on.
#
# non_go_deps (optional)
# List of labels this target depends on that are not Go libraries.
#
# include_dirs (optional)
# List of directories this target depends on being in the C compiler include path.
#
# lib_dirs (optional)
# List of directories this target depends on being in the C compiler library path.
#
# skip_vet (optional)
# Whether to skip running go vet for this target. This flag should _only_
# be used for packages in the Go source tree itself that otherwise match
# whitelist entries in go vet all. Go vet is only run if go_vet_enabled is
# true.
#
# test (optional, default: false)
# Whether this target defines a test.
#
# gcflags (optional)
# List of go compiler flags to pass.
#
# ldflags (optional)
# List of go linker flags to pass.
#
# tags (optional)
# List of go build tags to include in the build.
#
# cgo (optional, default: true)
# If true, will support linking against C code. Set to false for
# pure Go code to support cross-compilation.
#
# output_name (optional)
# The name of the binary that that will be generated.
# It defaults to the target name.
#
# output_dir (optional)
# Directory that the resulting binary should be placed in.
# See: `gn help output_dir`
#
template("go_build") {
main_target_name = target_name
is_test = defined(invoker.test) && invoker.test
output_name = target_name
if (defined(invoker.output_name)) {
output_name = invoker.output_name
}
if (is_win) {
output_name = "${output_name}.exe"
}
if (defined(invoker.output_dir)) {
output_dir = invoker.output_dir
} else {
output_dir = root_out_dir
}
define_sdk_target = defined(invoker.sdk_category) &&
invoker.sdk_category != "excluded" && !is_test
# Strip target binaries and binaries that are included in the SDK.
use_strip = is_fuchsia || define_sdk_target
output_path = "${output_dir}/${output_name}"
if (use_strip) {
output_path = "${output_dir}/exe.unstripped/${output_name}"
stripped_output_path = "${output_dir}/${output_name}"
}
use_prebuilt_go = !is_fuchsia
if (defined(invoker.use_prebuilt_go)) {
use_prebuilt_go = invoker.use_prebuilt_go
}
goroot_deps = []
if (use_prebuilt_go) {
goroot = rebase_path(prebuilt_go_dir, root_build_dir)
} else {
goroot = rebase_path("$host_tools_dir/goroot", root_build_dir)
goroot_deps = [ "//third_party/go:go_runtime" ]
}
go_deps = []
if (defined(invoker.deps)) {
go_deps += invoker.deps
}
if (defined(invoker.library)) {
# It's redundant to include the `library` label in `deps` because we'll add
# it implicitly.
assert(go_deps + [ invoker.library ] - [ invoker.library ] == go_deps,
"library should not be listed in deps")
go_deps += [ invoker.library ]
}
if (go_deps != []) {
go_deps_paths = []
go_deps_inputs = []
foreach(dep, go_deps) {
gen_dir = get_label_info(dep, "target_gen_dir")
name = get_label_info(dep, "name")
path = "${gen_dir}/${name}.go_deps"
go_deps_paths += [ rebase_path(path, root_build_dir) ]
go_deps_inputs += [ path ]
}
}
hermetic_inputs_target_name = "${main_target_name}_hermetic_inputs"
hermetic_inputs_target_output =
"${target_gen_dir}/${output_name}.hermetic_inputs"
hermetic_inputs_action(hermetic_inputs_target_name) {
visibility = [ ":${main_target_name}" ]
forward_variables_from(invoker, [ "testonly" ])
script = "//build/go/gen_hermetic_inputs.py"
sources = [ "//build/go/gen_library_metadata.py" ]
outputs = [ hermetic_inputs_target_output ]
args = [
"--output",
rebase_path(hermetic_inputs_target_output, root_build_dir),
"--go-root",
goroot,
]
deps = goroot_deps
if (go_deps != []) {
deps += go_deps
args += [ "--go-dep-files" ]
args += go_deps_paths
inputs = go_deps_inputs
}
if (is_test) {
testonly = true
args += [ "--is-test=true" ]
}
}
variant_target("action") {
target_name = main_target_name
_variant_shared = false
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
forward_variables_from(concurrent_jobs.local, "*")
deps = goroot_deps
if (defined(invoker.non_go_deps)) {
deps += invoker.non_go_deps
}
if (use_strip) {
# Ensure that the 'canonical' output path of $root_out_dir/$output_name
# always first among outputs, as this path has to be reconstructed within
# go_test.gni and it can canonically compare that with outputs[0].
outputs = [
stripped_output_path,
output_path,
]
} else {
outputs = [ output_path ]
}
output_name = output_name
# NOTE: output_dir is needed for variant_target(), but not for this action().
output_dir = output_dir
not_needed([ "output_dir" ])
script = "//build/go/build.py"
sources = [ "//build/go/gen_library_metadata.py" ]
hermetic_inputs_target = ":${hermetic_inputs_target_name}"
hermetic_inputs_file = hermetic_inputs_target_output
args = [
"--root-out-dir",
rebase_path(root_out_dir, root_build_dir),
"--current-cpu",
current_cpu,
"--current-os",
current_os,
"--binname",
output_name,
"--output-path",
rebase_path(output_path, root_build_dir),
"--go-cache",
rebase_path(gocache_dir, root_build_dir),
"--go-root",
goroot,
"--cc",
"$rebased_clang_prefix/clang",
"--cxx",
"$rebased_clang_prefix/clang++",
"--objcopy",
"$rebased_clang_prefix/llvm-objcopy",
"--ar",
"$rebased_clang_prefix/llvm-ar",
"--target",
current_target_tuple,
"--golibs-dir",
rebase_path("//third_party/golibs", root_build_dir),
]
if (sysroot != "") {
if (is_fuchsia) {
# For Fuchsia binaries, use a Go-specific sysroot instead of the
# standard one which only contains empty linker stubs (see
# comments in //zircon/public/sysroot/BUILD.gn for details).
sysroot = go_sysroot_dir
}
args += [
"--sysroot",
rebase_path(sysroot, root_build_dir),
]
}
include_dirs = []
if (defined(invoker.include_dirs)) {
include_dirs = rebase_path(invoker.include_dirs, root_build_dir)
}
lib_dirs = []
if (defined(invoker.lib_dirs)) {
lib_dirs = rebase_path(invoker.lib_dirs, root_build_dir)
}
lib_dirs += [ rebase_path(
get_label_info(":anything($shlib_toolchain)", "root_out_dir"),
root_build_dir) ]
if (!defined(invoker.cgo) || invoker.cgo) {
args += [ "--cgo" ]
if (is_fuchsia) {
# Inject a dependency to libfdio.so. Note that as a special case,
# when building fuzzing binaries, this library should be built in
# a non-fuzzing variant (because the fuzzing runtime depends on it).
# So compute the correct toolchain for it directly here.
_fdio_toolchain =
string_replace(current_toolchain, "-fuzzer", "") + "-shared"
_fdio_label_with_toolchain = "//sdk/lib/fdio($_fdio_toolchain)"
deps += [
"//zircon/public/sysroot:go_binary_deps",
_fdio_label_with_toolchain,
]
if (_fdio_toolchain != current_toolchain + "-shared") {
lib_dirs += [ rebase_path(
get_label_info(_fdio_label_with_toolchain, "root_out_dir"),
root_build_dir) ]
}
include_dirs +=
[ rebase_path("//sdk/lib/fdio/include", root_build_dir) ]
# See //build/config/fuchsia:fdio_config.
lib_dirs +=
[ rebase_path(get_label_info("//build/config/fuchsia:fdio_config",
"target_gen_dir"),
root_build_dir) ]
}
}
foreach(include_dir, include_dirs) {
args += [
"--include-dir",
include_dir,
]
}
foreach(lib_dir, lib_dirs) {
args += [
"--lib-dir",
lib_dir,
]
}
if (use_strip) {
args += [
"--stripped-output-path",
rebase_path(stripped_output_path, root_build_dir),
]
}
if (defined(invoker.skip_vet) && !invoker.skip_vet && go_vet_enabled) {
args += [ "--vet" ]
}
# Go build tags
if (defined(invoker.tags)) {
foreach(tag, invoker.tags) {
args += [
"--tag",
tag,
]
}
}
# Go compiler flags
if (defined(invoker.gcflags)) {
foreach(gcflag, invoker.gcflags) {
args += [ "--gcflag=${gcflag}" ]
}
}
if (is_fuchsia) {
# TODO(phosek): drop -Wl,--pack-dyn-relocs=relr once it's the default in Clang.
extldflags = "-Wl,--pack-dyn-relocs=relr"
# TODO(fxbug.dev/53078): Eventually the default in the compler driver.
extldflags += ",-zrel"
# When building with an instrumented variant, ensure the binary embeds a reference
# to the right dynamic linker path.
if (toolchain_variant.libprefix != "") {
extldflags += ",--dynamic-linker=${toolchain_variant.libprefix}ld.so.1"
}
args += [ "--ldflag=-linkmode=external \"-extldflags=$extldflags\"" ]
}
if (defined(invoker.ldflags)) {
foreach(ldflag, invoker.ldflags) {
args += [ "--ldflag=${ldflag}" ]
}
}
inputs = []
if (is_fuchsia) {
if (output_breakpad_syms && host_os != "mac") {
args += [
"--dump-syms",
breakpad_dump_syms,
]
inputs += [ breakpad_dump_syms_prebuilt ]
}
}
# Add needed arguments for the buildidtool. We should add the stamp file
# output by buildidtool to the list of outputs for this action but because
# Ninja (and by consequence GN) limits us to one depfile where that depfile
# has only one output and we need the depfile for other things we don't
# list it as an output.
args += [
"--buildidtool",
rebase_path("//prebuilt/tools/buildidtool/${host_platform}/buildidtool",
root_build_dir),
"--build-id-dir",
".build-id",
]
if (is_test) {
testonly = true
args += [ "--is-test=true" ]
}
if (go_deps != []) {
deps += go_deps
args += [ "--go-dep-files" ]
args += go_deps_paths
inputs += go_deps_inputs
}
# TODO(fxbug.dev/58755): Delete `gopackages` in favor of `library`.
if (defined(invoker.gopackages) == defined(invoker.library)) {
assert(false, "Exactly one of gopackages or library must be set")
} else if (defined(invoker.gopackages)) {
deps += [ "//build/go:allow_gopackages" ]
gopackages = invoker.gopackages
# Multi-package support was never implemented and is no longer planned as
# `gopackages` is slated for deletion as part of https://fxbug.dev/58755.
assert(gopackages == [ gopackages[0] ],
"gopackages only supports one package")
foreach(gopackage, gopackages) {
args += [
"--package",
gopackage,
]
}
} else if (defined(invoker.library)) {
_gen_dir = get_label_info(invoker.library, "target_gen_dir")
_name = get_label_info(invoker.library, "name")
args += [
"--library-metadata",
rebase_path("${_gen_dir}/${_name}.go_deps", root_build_dir),
]
}
metadata = {
tool_paths = []
# Record metadata for the //:tool_paths build API for all non-tests.
if (!is_test) {
tool_paths = [
{
cpu = current_cpu
label = get_label_info(":$main_target_name", "label_with_toolchain")
name = output_name
os = current_os
path = rebase_path(output_path, root_build_dir)
},
]
}
binaries = [
{
type = "executable"
label = get_label_info(":$target_name", "label_with_toolchain")
cpu = current_cpu
os = current_os
debug = rebase_path(output_path, root_build_dir)
if (use_strip) {
dist = rebase_path(stripped_output_path, root_build_dir)
} else {
dist = debug
}
# TODO(fxbug.dev/27215): Update when we add linux go binaries
# to .build-id.
if (is_fuchsia) {
elf_build_id = "$dist.build-id.stamp"
}
if (output_breakpad_syms && is_fuchsia) {
breakpad = "$dist.sym"
}
},
]
# Used by the distribution_manifest template.
if (is_fuchsia) {
distribution_entries = [
{
source = rebase_path(output_path, root_build_dir)
if (use_strip) {
source = rebase_path(stripped_output_path, root_build_dir)
}
destination = "bin/$output_name"
if (is_test) {
destination = "test/$output_name"
}
label = get_label_info(":$target_name", "label_with_toolchain")
elf_runtime_dir = "lib/${toolchain_variant.libprefix}"
},
]
component_catalog = [
{
has_go = true
label = get_label_info(":$target_name", "label_with_toolchain")
},
]
# Used by the fuchsia_test_component_manifest() template.
test_component_manifest_program = [
{
program = {
binary = "bin/$output_name"
if (is_test) {
binary = "test/$output_name"
}
}
},
]
test_component_manifest_program_barrier = []
if (is_test) {
# Used by the fuchsia_test_component_manifest() template.
test_component_manifest_cml = [
{
include = [ "//src/sys/test_runners/gotests/default.shard.cml" ]
},
]
}
}
}
if (output_breakpad_syms && is_fuchsia) {
metadata_binaries = metadata.binaries
b = metadata_binaries[0]
outputs += [ root_out_dir + "/" + b.breakpad ]
}
# The binaries embed absolute paths to the source file,
# which is copied into a subdir of root_build_dir.
no_output_dir_leaks = false
}
if (define_sdk_target) {
sdk_host_tool("${target_name}_sdk") {
forward_variables_from(invoker, "*", [ "deps" ])
category = invoker.sdk_category
deps = [ ":$target_name" ]
}
}
}