forked from cerc-io/plugeth
build: improve cross compilation setup (#22804)
This PR cleans up the CI build system and fixes a couple of issues. - The go tool launcher code has been moved to internal/build. With the new toolchain functions, the environment of the host Go (i.e. the one that built ci.go) and the target Go (i.e. the toolchain downloaded by -dlgo) are isolated more strictly. This is important to make cross compilation and -dlgo work correctly in more cases. - The -dlgo option now skips the download and uses the host Go if the running Go version matches dlgoVersion exactly. - The 'test' command now supports -dlgo, -cc and -arch. Running unit tests with foreign GOARCH is occasionally useful. For example, it can be used to run 32-bit tests on Windows. It can also be used to run darwin/amd64 tests on darwin/arm64 using Rosetta 2. - The 'aar', 'xcode' and 'xgo' commands now use a slightly different method to install external tools. They previously used `go get`, but this comes with the annoying side effect of modifying go.mod. They now use `go install` instead, which is the recommended way of installing tools without modifying the local module. - The old build warning about outdated Go version has been removed because we're much better at keeping backwards compatibility now.
This commit is contained in:
parent
b8040a430e
commit
effaf18523
11
Makefile
11
Makefile
@ -26,7 +26,7 @@ android:
|
||||
@echo "Import \"$(GOBIN)/geth.aar\" to use the library."
|
||||
@echo "Import \"$(GOBIN)/geth-sources.jar\" to add javadocs"
|
||||
@echo "For more info see https://stackoverflow.com/questions/20994336/android-studio-how-to-attach-javadoc"
|
||||
|
||||
|
||||
ios:
|
||||
$(GORUN) build/ci.go xcode --local
|
||||
@echo "Done building."
|
||||
@ -46,12 +46,11 @@ clean:
|
||||
# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'.
|
||||
|
||||
devtools:
|
||||
env GOBIN= go get -u golang.org/x/tools/cmd/stringer
|
||||
env GOBIN= go get -u github.com/kevinburke/go-bindata/go-bindata
|
||||
env GOBIN= go get -u github.com/fjl/gencodec
|
||||
env GOBIN= go get -u github.com/golang/protobuf/protoc-gen-go
|
||||
env GOBIN= go install golang.org/x/tools/cmd/stringer@latest
|
||||
env GOBIN= go install github.com/kevinburke/go-bindata/go-bindata@latest
|
||||
env GOBIN= go install github.com/fjl/gencodec@latest
|
||||
env GOBIN= go install github.com/golang/protobuf/protoc-gen-go@latest
|
||||
env GOBIN= go install ./cmd/abigen
|
||||
@type "npm" 2> /dev/null || echo 'Please install node.js and npm'
|
||||
@type "solc" 2> /dev/null || echo 'Please install solc'
|
||||
@type "protoc" 2> /dev/null || echo 'Please install protoc'
|
||||
|
||||
|
210
build/ci.go
210
build/ci.go
@ -208,58 +208,25 @@ func doInstall(cmdline []string) {
|
||||
cc = flag.String("cc", "", "C compiler to cross build with")
|
||||
)
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
|
||||
// Configure the toolchain.
|
||||
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
|
||||
if *dlgo {
|
||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||
tc.Root = build.DownloadGo(csdb, dlgoVersion)
|
||||
}
|
||||
|
||||
// Configure the build.
|
||||
env := build.Env()
|
||||
|
||||
// Check local Go version. People regularly open issues about compilation
|
||||
// failure with outdated Go. This should save them the trouble.
|
||||
if !strings.Contains(runtime.Version(), "devel") {
|
||||
// Figure out the minor version number since we can't textually compare (1.10 < 1.9)
|
||||
var minor int
|
||||
fmt.Sscanf(strings.TrimPrefix(runtime.Version(), "go1."), "%d", &minor)
|
||||
if minor < 13 {
|
||||
log.Println("You have Go version", runtime.Version())
|
||||
log.Println("go-ethereum requires at least Go version 1.13 and cannot")
|
||||
log.Println("be compiled with an earlier version. Please upgrade your Go installation.")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Choose which go command we're going to use.
|
||||
var gobuild *exec.Cmd
|
||||
if !*dlgo {
|
||||
// Default behavior: use the go version which runs ci.go right now.
|
||||
gobuild = goTool("build")
|
||||
} else {
|
||||
// Download of Go requested. This is for build environments where the
|
||||
// installed version is too old and cannot be upgraded easily.
|
||||
cachedir := filepath.Join("build", "cache")
|
||||
goroot := downloadGo(runtime.GOARCH, runtime.GOOS, cachedir)
|
||||
gobuild = localGoTool(goroot, "build")
|
||||
}
|
||||
|
||||
// Configure environment for cross build.
|
||||
if *arch != "" || *arch != runtime.GOARCH {
|
||||
gobuild.Env = append(gobuild.Env, "CGO_ENABLED=1")
|
||||
gobuild.Env = append(gobuild.Env, "GOARCH="+*arch)
|
||||
}
|
||||
|
||||
// Configure C compiler.
|
||||
if *cc != "" {
|
||||
gobuild.Env = append(gobuild.Env, "CC="+*cc)
|
||||
} else if os.Getenv("CC") != "" {
|
||||
gobuild.Env = append(gobuild.Env, "CC="+os.Getenv("CC"))
|
||||
}
|
||||
gobuild := tc.Go("build", buildFlags(env)...)
|
||||
|
||||
// arm64 CI builders are memory-constrained and can't handle concurrent builds,
|
||||
// better disable it. This check isn't the best, it should probably
|
||||
// check for something in env instead.
|
||||
if runtime.GOARCH == "arm64" {
|
||||
if env.CI && runtime.GOARCH == "arm64" {
|
||||
gobuild.Args = append(gobuild.Args, "-p", "1")
|
||||
}
|
||||
|
||||
// Put the default settings in.
|
||||
gobuild.Args = append(gobuild.Args, buildFlags(env)...)
|
||||
|
||||
// We use -trimpath to avoid leaking local paths into the built executables.
|
||||
gobuild.Args = append(gobuild.Args, "-trimpath")
|
||||
|
||||
@ -301,53 +268,30 @@ func buildFlags(env build.Environment) (flags []string) {
|
||||
return flags
|
||||
}
|
||||
|
||||
// goTool returns the go tool. This uses the Go version which runs ci.go.
|
||||
func goTool(subcmd string, args ...string) *exec.Cmd {
|
||||
cmd := build.GoTool(subcmd, args...)
|
||||
goToolSetEnv(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// localGoTool returns the go tool from the given GOROOT.
|
||||
func localGoTool(goroot string, subcmd string, args ...string) *exec.Cmd {
|
||||
gotool := filepath.Join(goroot, "bin", "go")
|
||||
cmd := exec.Command(gotool, subcmd)
|
||||
goToolSetEnv(cmd)
|
||||
cmd.Env = append(cmd.Env, "GOROOT="+goroot)
|
||||
cmd.Args = append(cmd.Args, args...)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// goToolSetEnv forwards the build environment to the go tool.
|
||||
func goToolSetEnv(cmd *exec.Cmd) {
|
||||
cmd.Env = append(cmd.Env, "GOBIN="+GOBIN)
|
||||
for _, e := range os.Environ() {
|
||||
if strings.HasPrefix(e, "GOBIN=") || strings.HasPrefix(e, "CC=") {
|
||||
continue
|
||||
}
|
||||
cmd.Env = append(cmd.Env, e)
|
||||
}
|
||||
}
|
||||
|
||||
// Running The Tests
|
||||
//
|
||||
// "tests" also includes static analysis tools such as vet.
|
||||
|
||||
func doTest(cmdline []string) {
|
||||
coverage := flag.Bool("coverage", false, "Whether to record code coverage")
|
||||
verbose := flag.Bool("v", false, "Whether to log verbosely")
|
||||
var (
|
||||
dlgo = flag.Bool("dlgo", false, "Download Go and build with it")
|
||||
arch = flag.String("arch", "", "Run tests for given architecture")
|
||||
cc = flag.String("cc", "", "Sets C compiler binary")
|
||||
coverage = flag.Bool("coverage", false, "Whether to record code coverage")
|
||||
verbose = flag.Bool("v", false, "Whether to log verbosely")
|
||||
)
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
env := build.Env()
|
||||
|
||||
packages := []string{"./..."}
|
||||
if len(flag.CommandLine.Args()) > 0 {
|
||||
packages = flag.CommandLine.Args()
|
||||
// Configure the toolchain.
|
||||
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
|
||||
if *dlgo {
|
||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||
tc.Root = build.DownloadGo(csdb, dlgoVersion)
|
||||
}
|
||||
gotest := tc.Go("test")
|
||||
|
||||
// Run the actual tests.
|
||||
// Test a single package at a time. CI builders are slow
|
||||
// and some tests run into timeouts under load.
|
||||
gotest := goTool("test", buildFlags(env)...)
|
||||
gotest.Args = append(gotest.Args, "-p", "1")
|
||||
if *coverage {
|
||||
gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover")
|
||||
@ -356,6 +300,10 @@ func doTest(cmdline []string) {
|
||||
gotest.Args = append(gotest.Args, "-v")
|
||||
}
|
||||
|
||||
packages := []string{"./..."}
|
||||
if len(flag.CommandLine.Args()) > 0 {
|
||||
packages = flag.CommandLine.Args()
|
||||
}
|
||||
gotest.Args = append(gotest.Args, packages...)
|
||||
build.MustRun(gotest)
|
||||
}
|
||||
@ -415,8 +363,7 @@ func doArchive(cmdline []string) {
|
||||
}
|
||||
|
||||
var (
|
||||
env = build.Env()
|
||||
|
||||
env = build.Env()
|
||||
basegeth = archiveBasename(*arch, params.ArchiveVersion(env.Commit))
|
||||
geth = "geth-" + basegeth + ext
|
||||
alltools = "geth-alltools-" + basegeth + ext
|
||||
@ -492,15 +439,15 @@ func archiveUpload(archive string, blobstore string, signer string, signifyVar s
|
||||
// skips archiving for some build configurations.
|
||||
func maybeSkipArchive(env build.Environment) {
|
||||
if env.IsPullRequest {
|
||||
log.Printf("skipping because this is a PR build")
|
||||
log.Printf("skipping archive creation because this is a PR build")
|
||||
os.Exit(0)
|
||||
}
|
||||
if env.IsCronJob {
|
||||
log.Printf("skipping because this is a cron job")
|
||||
log.Printf("skipping archive creation because this is a cron job")
|
||||
os.Exit(0)
|
||||
}
|
||||
if env.Branch != "master" && !strings.HasPrefix(env.Tag, "v1.") {
|
||||
log.Printf("skipping because branch %q, tag %q is not on the whitelist", env.Branch, env.Tag)
|
||||
log.Printf("skipping archive creation because branch %q, tag %q is not on the whitelist", env.Branch, env.Tag)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
@ -518,6 +465,7 @@ func doDebianSource(cmdline []string) {
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
*workdir = makeWorkdir(*workdir)
|
||||
env := build.Env()
|
||||
tc := new(build.GoToolchain)
|
||||
maybeSkipArchive(env)
|
||||
|
||||
// Import the signing key.
|
||||
@ -531,12 +479,12 @@ func doDebianSource(cmdline []string) {
|
||||
gobundle := downloadGoSources(*cachedir)
|
||||
|
||||
// Download all the dependencies needed to build the sources and run the ci script
|
||||
srcdepfetch := goTool("mod", "download")
|
||||
srcdepfetch.Env = append(os.Environ(), "GOPATH="+filepath.Join(*workdir, "modgopath"))
|
||||
srcdepfetch := tc.Go("mod", "download")
|
||||
srcdepfetch.Env = append(srcdepfetch.Env, "GOPATH="+filepath.Join(*workdir, "modgopath"))
|
||||
build.MustRun(srcdepfetch)
|
||||
|
||||
cidepfetch := goTool("run", "./build/ci.go")
|
||||
cidepfetch.Env = append(os.Environ(), "GOPATH="+filepath.Join(*workdir, "modgopath"))
|
||||
cidepfetch := tc.Go("run", "./build/ci.go")
|
||||
cidepfetch.Env = append(cidepfetch.Env, "GOPATH="+filepath.Join(*workdir, "modgopath"))
|
||||
cidepfetch.Run() // Command fails, don't care, we only need the deps to start it
|
||||
|
||||
// Create Debian packages and upload them.
|
||||
@ -592,41 +540,6 @@ func downloadGoSources(cachedir string) string {
|
||||
return dst
|
||||
}
|
||||
|
||||
// downloadGo downloads the Go binary distribution and unpacks it into a temporary
|
||||
// directory. It returns the GOROOT of the unpacked toolchain.
|
||||
func downloadGo(goarch, goos, cachedir string) string {
|
||||
if goarch == "arm" {
|
||||
goarch = "armv6l"
|
||||
}
|
||||
|
||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||
file := fmt.Sprintf("go%s.%s-%s", dlgoVersion, goos, goarch)
|
||||
if goos == "windows" {
|
||||
file += ".zip"
|
||||
} else {
|
||||
file += ".tar.gz"
|
||||
}
|
||||
url := "https://golang.org/dl/" + file
|
||||
dst := filepath.Join(cachedir, file)
|
||||
if err := csdb.DownloadFile(url, dst); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ucache, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
godir := filepath.Join(ucache, fmt.Sprintf("geth-go-%s-%s-%s", dlgoVersion, goos, goarch))
|
||||
if err := build.ExtractArchive(dst, godir); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
goroot, err := filepath.Abs(filepath.Join(godir, "go"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return goroot
|
||||
}
|
||||
|
||||
func ppaUpload(workdir, ppa, sshUser string, files []string) {
|
||||
p := strings.Split(ppa, "/")
|
||||
if len(p) != 2 {
|
||||
@ -901,13 +814,23 @@ func doAndroidArchive(cmdline []string) {
|
||||
)
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
env := build.Env()
|
||||
tc := new(build.GoToolchain)
|
||||
|
||||
// Sanity check that the SDK and NDK are installed and set
|
||||
if os.Getenv("ANDROID_HOME") == "" {
|
||||
log.Fatal("Please ensure ANDROID_HOME points to your Android SDK")
|
||||
}
|
||||
|
||||
// Build gomobile.
|
||||
install := tc.Install(GOBIN, "golang.org/x/mobile/cmd/gomobile@latest", "golang.org/x/mobile/cmd/gobind@latest")
|
||||
install.Env = append(install.Env)
|
||||
build.MustRun(install)
|
||||
|
||||
// Ensure all dependencies are available. This is required to make
|
||||
// gomobile bind work because it expects go.sum to contain all checksums.
|
||||
build.MustRun(tc.Go("mod", "download"))
|
||||
|
||||
// Build the Android archive and Maven resources
|
||||
build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind"))
|
||||
build.MustRun(gomobileTool("bind", "-ldflags", "-s -w", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile"))
|
||||
|
||||
if *local {
|
||||
@ -1027,10 +950,16 @@ func doXCodeFramework(cmdline []string) {
|
||||
)
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
env := build.Env()
|
||||
tc := new(build.GoToolchain)
|
||||
|
||||
// Build gomobile.
|
||||
build.MustRun(tc.Install(GOBIN, "golang.org/x/mobile/cmd/gomobile@latest", "golang.org/x/mobile/cmd/gobind@latest"))
|
||||
|
||||
// Ensure all dependencies are available. This is required to make
|
||||
// gomobile bind work because it expects go.sum to contain all checksums.
|
||||
build.MustRun(tc.Go("mod", "download"))
|
||||
|
||||
// Build the iOS XCode framework
|
||||
build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind"))
|
||||
build.MustRun(gomobileTool("init"))
|
||||
bind := gomobileTool("bind", "-ldflags", "-s -w", "--target", "ios", "-v", "github.com/ethereum/go-ethereum/mobile")
|
||||
|
||||
if *local {
|
||||
@ -1039,17 +968,14 @@ func doXCodeFramework(cmdline []string) {
|
||||
build.MustRun(bind)
|
||||
return
|
||||
}
|
||||
|
||||
// Create the archive.
|
||||
maybeSkipArchive(env)
|
||||
archive := "geth-" + archiveBasename("ios", params.ArchiveVersion(env.Commit))
|
||||
if err := os.Mkdir(archive, os.ModePerm); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
bind.Dir, _ = filepath.Abs(archive)
|
||||
build.MustRun(bind)
|
||||
build.MustRunCommand("tar", "-zcvf", archive+".tar.gz", archive)
|
||||
|
||||
// Skip CocoaPods deploy and Azure upload for PR builds
|
||||
maybeSkipArchive(env)
|
||||
|
||||
// Sign and upload the framework to Azure
|
||||
if err := archiveUpload(archive+".tar.gz", *upload, *signer, *signify); err != nil {
|
||||
log.Fatal(err)
|
||||
@ -1115,10 +1041,10 @@ func doXgo(cmdline []string) {
|
||||
)
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
env := build.Env()
|
||||
var tc build.GoToolchain
|
||||
|
||||
// Make sure xgo is available for cross compilation
|
||||
gogetxgo := goTool("get", "github.com/karalabe/xgo")
|
||||
build.MustRun(gogetxgo)
|
||||
build.MustRun(tc.Install(GOBIN, "github.com/karalabe/xgo@latest"))
|
||||
|
||||
// If all tools building is requested, build everything the builder wants
|
||||
args := append(buildFlags(env), flag.Args()...)
|
||||
@ -1129,27 +1055,23 @@ func doXgo(cmdline []string) {
|
||||
if strings.HasPrefix(res, GOBIN) {
|
||||
// Binary tool found, cross build it explicitly
|
||||
args = append(args, "./"+filepath.Join("cmd", filepath.Base(res)))
|
||||
xgo := xgoTool(args)
|
||||
build.MustRun(xgo)
|
||||
build.MustRun(xgoTool(args))
|
||||
args = args[:len(args)-1]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// Otherwise xxecute the explicit cross compilation
|
||||
|
||||
// Otherwise execute the explicit cross compilation
|
||||
path := args[len(args)-1]
|
||||
args = append(args[:len(args)-1], []string{"--dest", GOBIN, path}...)
|
||||
|
||||
xgo := xgoTool(args)
|
||||
build.MustRun(xgo)
|
||||
build.MustRun(xgoTool(args))
|
||||
}
|
||||
|
||||
func xgoTool(args []string) *exec.Cmd {
|
||||
cmd := exec.Command(filepath.Join(GOBIN, "xgo"), args...)
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, []string{
|
||||
"GOBIN=" + GOBIN,
|
||||
}...)
|
||||
cmd.Env = append(cmd.Env, []string{"GOBIN=" + GOBIN}...)
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ var (
|
||||
|
||||
// Environment contains metadata provided by the build environment.
|
||||
type Environment struct {
|
||||
CI bool
|
||||
Name string // name of the environment
|
||||
Repo string // name of GitHub repo
|
||||
Commit, Date, Branch, Tag string // Git info
|
||||
@ -61,6 +62,7 @@ func Env() Environment {
|
||||
commit = os.Getenv("TRAVIS_COMMIT")
|
||||
}
|
||||
return Environment{
|
||||
CI: true,
|
||||
Name: "travis",
|
||||
Repo: os.Getenv("TRAVIS_REPO_SLUG"),
|
||||
Commit: commit,
|
||||
@ -77,6 +79,7 @@ func Env() Environment {
|
||||
commit = os.Getenv("APPVEYOR_REPO_COMMIT")
|
||||
}
|
||||
return Environment{
|
||||
CI: true,
|
||||
Name: "appveyor",
|
||||
Repo: os.Getenv("APPVEYOR_REPO_NAME"),
|
||||
Commit: commit,
|
||||
|
149
internal/build/gotool.go
Normal file
149
internal/build/gotool.go
Normal file
@ -0,0 +1,149 @@
|
||||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type GoToolchain struct {
|
||||
Root string // GOROOT
|
||||
|
||||
// Cross-compilation variables. These are set when running the go tool.
|
||||
GOARCH string
|
||||
GOOS string
|
||||
CC string
|
||||
}
|
||||
|
||||
// Go creates an invocation of the go command.
|
||||
func (g *GoToolchain) Go(command string, args ...string) *exec.Cmd {
|
||||
tool := g.goTool(command, args...)
|
||||
|
||||
// Configure environment for cross build.
|
||||
if g.GOARCH != "" && g.GOARCH != runtime.GOARCH {
|
||||
tool.Env = append(tool.Env, "CGO_ENABLED=1")
|
||||
tool.Env = append(tool.Env, "GOARCH="+g.GOARCH)
|
||||
}
|
||||
if g.GOOS != "" && g.GOOS != runtime.GOOS {
|
||||
tool.Env = append(tool.Env, "GOOS="+g.GOOS)
|
||||
}
|
||||
// Configure C compiler.
|
||||
if g.CC != "" {
|
||||
tool.Env = append(tool.Env, "CC="+g.CC)
|
||||
} else if os.Getenv("CC") != "" {
|
||||
tool.Env = append(tool.Env, "CC="+os.Getenv("CC"))
|
||||
}
|
||||
return tool
|
||||
}
|
||||
|
||||
// Install creates an invocation of 'go install'. The command is configured to output
|
||||
// executables to the given 'gobin' directory.
|
||||
//
|
||||
// This can be used to install auxiliary build tools without modifying the local go.mod and
|
||||
// go.sum files. To install tools which are not required by go.mod, ensure that all module
|
||||
// paths in 'args' contain a module version suffix (e.g. "...@latest").
|
||||
func (g *GoToolchain) Install(gobin string, args ...string) *exec.Cmd {
|
||||
if !filepath.IsAbs(gobin) {
|
||||
panic("GOBIN must be an absolute path")
|
||||
}
|
||||
tool := g.goTool("install")
|
||||
tool.Env = append(tool.Env, "GOBIN="+gobin)
|
||||
tool.Args = append(tool.Args, "-mod=readonly")
|
||||
tool.Args = append(tool.Args, args...)
|
||||
|
||||
// Ensure GOPATH is set because go install seems to absolutely require it. This uses
|
||||
// 'go env' because it resolves the default value when GOPATH is not set in the
|
||||
// environment. Ignore errors running go env and leave any complaining about GOPATH to
|
||||
// the install command.
|
||||
pathTool := g.goTool("env", "GOPATH")
|
||||
output, _ := pathTool.Output()
|
||||
tool.Env = append(tool.Env, "GOPATH="+string(output))
|
||||
return tool
|
||||
}
|
||||
|
||||
func (g *GoToolchain) goTool(command string, args ...string) *exec.Cmd {
|
||||
if g.Root == "" {
|
||||
g.Root = runtime.GOROOT()
|
||||
}
|
||||
tool := exec.Command(filepath.Join(g.Root, "bin", "go"), command)
|
||||
tool.Args = append(tool.Args, args...)
|
||||
tool.Env = append(tool.Env, "GOROOT="+g.Root)
|
||||
|
||||
// Forward environment variables to the tool, but skip compiler target settings.
|
||||
// TODO: what about GOARM?
|
||||
skip := map[string]struct{}{"GOROOT": {}, "GOARCH": {}, "GOOS": {}, "GOBIN": {}, "CC": {}}
|
||||
for _, e := range os.Environ() {
|
||||
if i := strings.IndexByte(e, '='); i >= 0 {
|
||||
if _, ok := skip[e[:i]]; ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
tool.Env = append(tool.Env, e)
|
||||
}
|
||||
return tool
|
||||
}
|
||||
|
||||
// DownloadGo downloads the Go binary distribution and unpacks it into a temporary
|
||||
// directory. It returns the GOROOT of the unpacked toolchain.
|
||||
func DownloadGo(csdb *ChecksumDB, version string) string {
|
||||
// Shortcut: if the Go version that runs this script matches the
|
||||
// requested version exactly, there is no need to download anything.
|
||||
activeGo := strings.TrimPrefix(runtime.Version(), "go")
|
||||
if activeGo == version {
|
||||
log.Printf("-dlgo version matches active Go version %s, skipping download.", activeGo)
|
||||
return runtime.GOROOT()
|
||||
}
|
||||
|
||||
ucache, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// For Arm architecture, GOARCH includes ISA version.
|
||||
os := runtime.GOOS
|
||||
arch := runtime.GOARCH
|
||||
if arch == "arm" {
|
||||
arch = "armv6l"
|
||||
}
|
||||
file := fmt.Sprintf("go%s.%s-%s", version, os, arch)
|
||||
if os == "windows" {
|
||||
file += ".zip"
|
||||
} else {
|
||||
file += ".tar.gz"
|
||||
}
|
||||
url := "https://golang.org/dl/" + file
|
||||
dst := filepath.Join(ucache, file)
|
||||
if err := csdb.DownloadFile(url, dst); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
godir := filepath.Join(ucache, fmt.Sprintf("geth-go-%s-%s-%s", version, os, arch))
|
||||
if err := ExtractArchive(dst, godir); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
goroot, err := filepath.Abs(filepath.Join(godir, "go"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return goroot
|
||||
}
|
@ -29,7 +29,6 @@ import (
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
@ -111,19 +110,6 @@ func render(tpl *template.Template, outputFile string, outputPerm os.FileMode, x
|
||||
}
|
||||
}
|
||||
|
||||
// GoTool returns the command that runs a go tool. This uses go from GOROOT instead of PATH
|
||||
// so that go commands executed by build use the same version of Go as the 'host' that runs
|
||||
// build code. e.g.
|
||||
//
|
||||
// /usr/lib/go-1.12.1/bin/go run build/ci.go ...
|
||||
//
|
||||
// runs using go 1.12.1 and invokes go 1.12.1 tools from the same GOROOT. This is also important
|
||||
// because runtime.Version checks on the host should match the tools that are run.
|
||||
func GoTool(tool string, args ...string) *exec.Cmd {
|
||||
args = append([]string{tool}, args...)
|
||||
return exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...)
|
||||
}
|
||||
|
||||
// UploadSFTP uploads files to a remote host using the sftp command line tool.
|
||||
// The destination host may be specified either as [user@]host[: or as a URI in
|
||||
// the form sftp://[user@]host[:port].
|
||||
|
Loading…
Reference in New Issue
Block a user