1# Copyright 2019 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14 15# This file provides GN build integration for Go. These templates are limited, 16# supporting only legacy GOPATH-based builds. 17 18import("exec.gni") 19import("input_group.gni") 20 21# Defines a Go package. 22# 23# A Go package consists of one or more Go files in a single directory. The 24# package can depend on other Go packages or generated Go code. 25# 26# Args: 27# sources: List of Go source files. 28# deps: Optional list of target dependencies. 29# external_deps: Optional list of Go package dependencies outside of Pigweed. 30# gopath: Root of the GOPATH in which the package is located. 31# 32# Example: 33# 34# # In //my_module/go/src/example.com/foo/BUILD.gn 35# pw_go_package("foo_package") { 36# sources = [ "main.go" ] 37# deps = [ 38# "//my_module:foo_proto_go" 39# ] 40# external_deps = [ 41# "github.com/golang/glog" 42# ] 43# gopath = "//my_module/go" 44# } 45# 46template("pw_go_package") { 47 assert(defined(invoker.sources), "pw_go_source_set requires sources") 48 assert(defined(invoker.gopath), "pw_go_source_set requires a GOPATH root") 49 50 _gopath = rebase_path(invoker.gopath) 51 52 # List the sources in an input group with GOPATH environment metadata. 53 pw_input_group(target_name) { 54 inputs = invoker.sources 55 forward_variables_from(invoker, 56 [ 57 "deps", 58 "metadata", 59 ]) 60 if (!defined(metadata)) { 61 metadata = { 62 } 63 } 64 metadata.gopath = [ "GOPATH+=${_gopath}" ] 65 66 if (defined(invoker.external_deps)) { 67 metadata.external_deps = invoker.external_deps 68 } 69 } 70} 71 72# Builds a Go executable from a Go package. 73# 74# The package must include only a main.go file labelled "package main". It may 75# depend on other Go packages defined in the build. 76# 77# Args: 78# deps: List of size one specifying the GN path to the Go package target. 79# package: Name of the Go package as resolved by the Go compiler. 80# 81# Example: 82# 83# # In //my_module/go 84# pw_go_executable("foo") { 85# deps = [ "//my_module/go/src/example.com/foo:foo_package" ] 86# package = "example.com/foo" 87# } 88# 89template("pw_go_executable") { 90 assert(defined(invoker.deps), 91 "pw_go_executable requires at least one Go package as a dependency") 92 assert(defined(invoker.package), 93 "pw_go_executable requires the name of the package to build") 94 95 _metadata_target_name = "${target_name}_pw_go_metadata" 96 _metadata_file = "$target_gen_dir/${target_name}_pw_go_env.env" 97 98 # Collect all the GOPATH metadata from pw_go_package and _pw_go_proto_library 99 # targets into a plaintext file of environment variable definitions. 100 generated_file(_metadata_target_name) { 101 deps = invoker.deps 102 data_keys = [ "gopath" ] 103 outputs = [ _metadata_file ] 104 } 105 106 # Collect all of the external dependencies of the executable and its packages. 107 _deps_metadata_target_name = "${target_name}_pw_go_deps" 108 _deps_metadata_file = "$target_gen_dir/${target_name}_pw_go_deps.txt" 109 generated_file(_deps_metadata_target_name) { 110 deps = invoker.deps 111 data_keys = [ "external_deps" ] 112 outputs = [ _deps_metadata_file ] 113 } 114 115 _default_gopath = rebase_path("$root_gen_dir/go") 116 117 # Create a target to download all external dependencies into the default 118 # GOPATH in the out directory. This is only run once; "go get" does not 119 # re-download existing packages. 120 _download_target_name = "${target_name}_pw_go_get" 121 pw_exec(_download_target_name) { 122 program = "go" 123 args = [ "get" ] 124 deps = [ ":$_deps_metadata_target_name" ] + invoker.deps 125 env = [ 126 "GO111MODULE=off", 127 "GOPATH=$_default_gopath", 128 ] 129 130 if (host_os == "mac") { 131 # TODO(frolv): Some versions of Go for MacOS have issues finding the C 132 # stdlib headers. Temporarily disable CGo on Mac. The root cause of this 133 # issue should be investigated and fixed. 134 env += [ "CGO_ENABLED=0" ] 135 } 136 137 args_file = _deps_metadata_file 138 139 # If the args file is empty, don't run the "go get" command. 140 skip_empty_args = true 141 142 # Limit download parallelization to 1. 143 pool = "$dir_pw_build/pool:go_download($default_toolchain)" 144 } 145 146 # Run a "go build" command with the environment configured from metadata. 147 pw_exec(target_name) { 148 program = "go" 149 args = [ 150 "build", 151 "-o", 152 rebase_path(target_out_dir, root_build_dir), 153 invoker.package, 154 ] 155 deps = [ 156 ":$_download_target_name", 157 ":$_metadata_target_name", 158 ] 159 env = [ 160 "GO111MODULE=off", 161 "GOPATH+=$_default_gopath", 162 ] 163 164 if (host_os == "mac") { 165 # TODO(frolv): Some versions of Go for MacOS have issues finding the C 166 # stdlib headers. Temporarily disable CGo on Mac. The root cause of this 167 # issue should be investigated and fixed. 168 env += [ "CGO_ENABLED=0" ] 169 } 170 171 env_file = _metadata_file 172 173 _binary_name = get_path_info(invoker.package, "name") 174 175 if (host_os == "win") { 176 _extension = ".exe" 177 } else { 178 _extension = "" 179 } 180 181 outputs = [ "$target_out_dir/$_binary_name$_extension" ] 182 } 183} 184