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
9
Makefile
9
Makefile
@ -46,12 +46,11 @@ clean:
|
|||||||
# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'.
|
# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'.
|
||||||
|
|
||||||
devtools:
|
devtools:
|
||||||
env GOBIN= go get -u golang.org/x/tools/cmd/stringer
|
env GOBIN= go install golang.org/x/tools/cmd/stringer@latest
|
||||||
env GOBIN= go get -u github.com/kevinburke/go-bindata/go-bindata
|
env GOBIN= go install github.com/kevinburke/go-bindata/go-bindata@latest
|
||||||
env GOBIN= go get -u github.com/fjl/gencodec
|
env GOBIN= go install github.com/fjl/gencodec@latest
|
||||||
env GOBIN= go get -u github.com/golang/protobuf/protoc-gen-go
|
env GOBIN= go install github.com/golang/protobuf/protoc-gen-go@latest
|
||||||
env GOBIN= go install ./cmd/abigen
|
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 "solc" 2> /dev/null || echo 'Please install solc'
|
||||||
@type "protoc" 2> /dev/null || echo 'Please install protoc'
|
@type "protoc" 2> /dev/null || echo 'Please install protoc'
|
||||||
|
|
||||||
|
208
build/ci.go
208
build/ci.go
@ -208,58 +208,25 @@ func doInstall(cmdline []string) {
|
|||||||
cc = flag.String("cc", "", "C compiler to cross build with")
|
cc = flag.String("cc", "", "C compiler to cross build with")
|
||||||
)
|
)
|
||||||
flag.CommandLine.Parse(cmdline)
|
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()
|
env := build.Env()
|
||||||
|
gobuild := tc.Go("build", buildFlags(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"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// arm64 CI builders are memory-constrained and can't handle concurrent builds,
|
// arm64 CI builders are memory-constrained and can't handle concurrent builds,
|
||||||
// better disable it. This check isn't the best, it should probably
|
// better disable it. This check isn't the best, it should probably
|
||||||
// check for something in env instead.
|
// check for something in env instead.
|
||||||
if runtime.GOARCH == "arm64" {
|
if env.CI && runtime.GOARCH == "arm64" {
|
||||||
gobuild.Args = append(gobuild.Args, "-p", "1")
|
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.
|
// We use -trimpath to avoid leaking local paths into the built executables.
|
||||||
gobuild.Args = append(gobuild.Args, "-trimpath")
|
gobuild.Args = append(gobuild.Args, "-trimpath")
|
||||||
|
|
||||||
@ -301,53 +268,30 @@ func buildFlags(env build.Environment) (flags []string) {
|
|||||||
return flags
|
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
|
// Running The Tests
|
||||||
//
|
//
|
||||||
// "tests" also includes static analysis tools such as vet.
|
// "tests" also includes static analysis tools such as vet.
|
||||||
|
|
||||||
func doTest(cmdline []string) {
|
func doTest(cmdline []string) {
|
||||||
coverage := flag.Bool("coverage", false, "Whether to record code coverage")
|
var (
|
||||||
verbose := flag.Bool("v", false, "Whether to log verbosely")
|
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)
|
flag.CommandLine.Parse(cmdline)
|
||||||
env := build.Env()
|
|
||||||
|
|
||||||
packages := []string{"./..."}
|
// Configure the toolchain.
|
||||||
if len(flag.CommandLine.Args()) > 0 {
|
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
|
||||||
packages = flag.CommandLine.Args()
|
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
|
// Test a single package at a time. CI builders are slow
|
||||||
// and some tests run into timeouts under load.
|
// and some tests run into timeouts under load.
|
||||||
gotest := goTool("test", buildFlags(env)...)
|
|
||||||
gotest.Args = append(gotest.Args, "-p", "1")
|
gotest.Args = append(gotest.Args, "-p", "1")
|
||||||
if *coverage {
|
if *coverage {
|
||||||
gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover")
|
gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover")
|
||||||
@ -356,6 +300,10 @@ func doTest(cmdline []string) {
|
|||||||
gotest.Args = append(gotest.Args, "-v")
|
gotest.Args = append(gotest.Args, "-v")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
packages := []string{"./..."}
|
||||||
|
if len(flag.CommandLine.Args()) > 0 {
|
||||||
|
packages = flag.CommandLine.Args()
|
||||||
|
}
|
||||||
gotest.Args = append(gotest.Args, packages...)
|
gotest.Args = append(gotest.Args, packages...)
|
||||||
build.MustRun(gotest)
|
build.MustRun(gotest)
|
||||||
}
|
}
|
||||||
@ -416,7 +364,6 @@ func doArchive(cmdline []string) {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
env = build.Env()
|
env = build.Env()
|
||||||
|
|
||||||
basegeth = archiveBasename(*arch, params.ArchiveVersion(env.Commit))
|
basegeth = archiveBasename(*arch, params.ArchiveVersion(env.Commit))
|
||||||
geth = "geth-" + basegeth + ext
|
geth = "geth-" + basegeth + ext
|
||||||
alltools = "geth-alltools-" + 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.
|
// skips archiving for some build configurations.
|
||||||
func maybeSkipArchive(env build.Environment) {
|
func maybeSkipArchive(env build.Environment) {
|
||||||
if env.IsPullRequest {
|
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)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
if env.IsCronJob {
|
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)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
if env.Branch != "master" && !strings.HasPrefix(env.Tag, "v1.") {
|
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)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -518,6 +465,7 @@ func doDebianSource(cmdline []string) {
|
|||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
*workdir = makeWorkdir(*workdir)
|
*workdir = makeWorkdir(*workdir)
|
||||||
env := build.Env()
|
env := build.Env()
|
||||||
|
tc := new(build.GoToolchain)
|
||||||
maybeSkipArchive(env)
|
maybeSkipArchive(env)
|
||||||
|
|
||||||
// Import the signing key.
|
// Import the signing key.
|
||||||
@ -531,12 +479,12 @@ func doDebianSource(cmdline []string) {
|
|||||||
gobundle := downloadGoSources(*cachedir)
|
gobundle := downloadGoSources(*cachedir)
|
||||||
|
|
||||||
// Download all the dependencies needed to build the sources and run the ci script
|
// Download all the dependencies needed to build the sources and run the ci script
|
||||||
srcdepfetch := goTool("mod", "download")
|
srcdepfetch := tc.Go("mod", "download")
|
||||||
srcdepfetch.Env = append(os.Environ(), "GOPATH="+filepath.Join(*workdir, "modgopath"))
|
srcdepfetch.Env = append(srcdepfetch.Env, "GOPATH="+filepath.Join(*workdir, "modgopath"))
|
||||||
build.MustRun(srcdepfetch)
|
build.MustRun(srcdepfetch)
|
||||||
|
|
||||||
cidepfetch := goTool("run", "./build/ci.go")
|
cidepfetch := tc.Go("run", "./build/ci.go")
|
||||||
cidepfetch.Env = append(os.Environ(), "GOPATH="+filepath.Join(*workdir, "modgopath"))
|
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
|
cidepfetch.Run() // Command fails, don't care, we only need the deps to start it
|
||||||
|
|
||||||
// Create Debian packages and upload them.
|
// Create Debian packages and upload them.
|
||||||
@ -592,41 +540,6 @@ func downloadGoSources(cachedir string) string {
|
|||||||
return dst
|
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) {
|
func ppaUpload(workdir, ppa, sshUser string, files []string) {
|
||||||
p := strings.Split(ppa, "/")
|
p := strings.Split(ppa, "/")
|
||||||
if len(p) != 2 {
|
if len(p) != 2 {
|
||||||
@ -901,13 +814,23 @@ func doAndroidArchive(cmdline []string) {
|
|||||||
)
|
)
|
||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
env := build.Env()
|
env := build.Env()
|
||||||
|
tc := new(build.GoToolchain)
|
||||||
|
|
||||||
// Sanity check that the SDK and NDK are installed and set
|
// Sanity check that the SDK and NDK are installed and set
|
||||||
if os.Getenv("ANDROID_HOME") == "" {
|
if os.Getenv("ANDROID_HOME") == "" {
|
||||||
log.Fatal("Please ensure ANDROID_HOME points to your Android SDK")
|
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 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"))
|
build.MustRun(gomobileTool("bind", "-ldflags", "-s -w", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile"))
|
||||||
|
|
||||||
if *local {
|
if *local {
|
||||||
@ -1027,10 +950,16 @@ func doXCodeFramework(cmdline []string) {
|
|||||||
)
|
)
|
||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
env := build.Env()
|
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 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")
|
bind := gomobileTool("bind", "-ldflags", "-s -w", "--target", "ios", "-v", "github.com/ethereum/go-ethereum/mobile")
|
||||||
|
|
||||||
if *local {
|
if *local {
|
||||||
@ -1039,17 +968,14 @@ func doXCodeFramework(cmdline []string) {
|
|||||||
build.MustRun(bind)
|
build.MustRun(bind)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the archive.
|
||||||
|
maybeSkipArchive(env)
|
||||||
archive := "geth-" + archiveBasename("ios", params.ArchiveVersion(env.Commit))
|
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)
|
bind.Dir, _ = filepath.Abs(archive)
|
||||||
build.MustRun(bind)
|
build.MustRun(bind)
|
||||||
build.MustRunCommand("tar", "-zcvf", archive+".tar.gz", archive)
|
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
|
// Sign and upload the framework to Azure
|
||||||
if err := archiveUpload(archive+".tar.gz", *upload, *signer, *signify); err != nil {
|
if err := archiveUpload(archive+".tar.gz", *upload, *signer, *signify); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@ -1115,10 +1041,10 @@ func doXgo(cmdline []string) {
|
|||||||
)
|
)
|
||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
env := build.Env()
|
env := build.Env()
|
||||||
|
var tc build.GoToolchain
|
||||||
|
|
||||||
// Make sure xgo is available for cross compilation
|
// Make sure xgo is available for cross compilation
|
||||||
gogetxgo := goTool("get", "github.com/karalabe/xgo")
|
build.MustRun(tc.Install(GOBIN, "github.com/karalabe/xgo@latest"))
|
||||||
build.MustRun(gogetxgo)
|
|
||||||
|
|
||||||
// If all tools building is requested, build everything the builder wants
|
// If all tools building is requested, build everything the builder wants
|
||||||
args := append(buildFlags(env), flag.Args()...)
|
args := append(buildFlags(env), flag.Args()...)
|
||||||
@ -1129,27 +1055,23 @@ func doXgo(cmdline []string) {
|
|||||||
if strings.HasPrefix(res, GOBIN) {
|
if strings.HasPrefix(res, GOBIN) {
|
||||||
// Binary tool found, cross build it explicitly
|
// Binary tool found, cross build it explicitly
|
||||||
args = append(args, "./"+filepath.Join("cmd", filepath.Base(res)))
|
args = append(args, "./"+filepath.Join("cmd", filepath.Base(res)))
|
||||||
xgo := xgoTool(args)
|
build.MustRun(xgoTool(args))
|
||||||
build.MustRun(xgo)
|
|
||||||
args = args[:len(args)-1]
|
args = args[:len(args)-1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Otherwise xxecute the explicit cross compilation
|
|
||||||
|
// Otherwise execute the explicit cross compilation
|
||||||
path := args[len(args)-1]
|
path := args[len(args)-1]
|
||||||
args = append(args[:len(args)-1], []string{"--dest", GOBIN, path}...)
|
args = append(args[:len(args)-1], []string{"--dest", GOBIN, path}...)
|
||||||
|
build.MustRun(xgoTool(args))
|
||||||
xgo := xgoTool(args)
|
|
||||||
build.MustRun(xgo)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func xgoTool(args []string) *exec.Cmd {
|
func xgoTool(args []string) *exec.Cmd {
|
||||||
cmd := exec.Command(filepath.Join(GOBIN, "xgo"), args...)
|
cmd := exec.Command(filepath.Join(GOBIN, "xgo"), args...)
|
||||||
cmd.Env = os.Environ()
|
cmd.Env = os.Environ()
|
||||||
cmd.Env = append(cmd.Env, []string{
|
cmd.Env = append(cmd.Env, []string{"GOBIN=" + GOBIN}...)
|
||||||
"GOBIN=" + GOBIN,
|
|
||||||
}...)
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ var (
|
|||||||
|
|
||||||
// Environment contains metadata provided by the build environment.
|
// Environment contains metadata provided by the build environment.
|
||||||
type Environment struct {
|
type Environment struct {
|
||||||
|
CI bool
|
||||||
Name string // name of the environment
|
Name string // name of the environment
|
||||||
Repo string // name of GitHub repo
|
Repo string // name of GitHub repo
|
||||||
Commit, Date, Branch, Tag string // Git info
|
Commit, Date, Branch, Tag string // Git info
|
||||||
@ -61,6 +62,7 @@ func Env() Environment {
|
|||||||
commit = os.Getenv("TRAVIS_COMMIT")
|
commit = os.Getenv("TRAVIS_COMMIT")
|
||||||
}
|
}
|
||||||
return Environment{
|
return Environment{
|
||||||
|
CI: true,
|
||||||
Name: "travis",
|
Name: "travis",
|
||||||
Repo: os.Getenv("TRAVIS_REPO_SLUG"),
|
Repo: os.Getenv("TRAVIS_REPO_SLUG"),
|
||||||
Commit: commit,
|
Commit: commit,
|
||||||
@ -77,6 +79,7 @@ func Env() Environment {
|
|||||||
commit = os.Getenv("APPVEYOR_REPO_COMMIT")
|
commit = os.Getenv("APPVEYOR_REPO_COMMIT")
|
||||||
}
|
}
|
||||||
return Environment{
|
return Environment{
|
||||||
|
CI: true,
|
||||||
Name: "appveyor",
|
Name: "appveyor",
|
||||||
Repo: os.Getenv("APPVEYOR_REPO_NAME"),
|
Repo: os.Getenv("APPVEYOR_REPO_NAME"),
|
||||||
Commit: commit,
|
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"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"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.
|
// 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 destination host may be specified either as [user@]host[: or as a URI in
|
||||||
// the form sftp://[user@]host[:port].
|
// the form sftp://[user@]host[:port].
|
||||||
|
Loading…
Reference in New Issue
Block a user