@@ -46,12 +46,11 @@ import (
4646 "encoding/base64"
4747 "flag"
4848 "fmt"
49- "go/parser"
50- "go/token"
5149 "io/ioutil"
5250 "log"
5351 "os"
5452 "os/exec"
53+ "path"
5554 "path/filepath"
5655 "regexp"
5756 "runtime"
@@ -158,6 +157,11 @@ var (
158157 "golang-1.11" : "/usr/lib/go-1.11" ,
159158 "golang-go" : "/usr/lib/go" ,
160159 }
160+
161+ // This is the version of go that will be downloaded by
162+ //
163+ // go run ci.go install -dlgo
164+ dlgoVersion = "1.15.4"
161165)
162166
163167var GOBIN , _ = filepath .Abs (filepath .Join ("build" , "bin" ))
@@ -208,110 +212,128 @@ func main() {
208212
209213func doInstall (cmdline []string ) {
210214 var (
215+ dlgo = flag .Bool ("dlgo" , false , "Download Go and build with it" )
211216 arch = flag .String ("arch" , "" , "Architecture to cross build for" )
212217 cc = flag .String ("cc" , "" , "C compiler to cross build with" )
213218 )
214219 flag .CommandLine .Parse (cmdline )
215220 env := build .Env ()
216221
217- // Check Go version. People regularly open issues about compilation
222+ // Check local Go version. People regularly open issues about compilation
218223 // failure with outdated Go. This should save them the trouble.
219224 if ! strings .Contains (runtime .Version (), "devel" ) {
220225 // Figure out the minor version number since we can't textually compare (1.10 < 1.9)
221226 var minor int
222227 fmt .Sscanf (strings .TrimPrefix (runtime .Version (), "go1." ), "%d" , & minor )
223-
224228 if minor < 13 {
225229 log .Println ("You have Go version" , runtime .Version ())
226230 log .Println ("go-ethereum requires at least Go version 1.13 and cannot" )
227231 log .Println ("be compiled with an earlier version. Please upgrade your Go installation." )
228232 os .Exit (1 )
229233 }
230234 }
231- // Compile packages given as arguments, or everything if there are no arguments.
232- packages := []string {"./..." }
233- if flag .NArg () > 0 {
234- packages = flag .Args ()
235+
236+ // Choose which go command we're going to use.
237+ var gobuild * exec.Cmd
238+ if ! * dlgo {
239+ // Default behavior: use the go version which runs ci.go right now.
240+ gobuild = goTool ("build" )
241+ } else {
242+ // Download of Go requested. This is for build environments where the
243+ // installed version is too old and cannot be upgraded easily.
244+ cachedir := filepath .Join ("build" , "cache" )
245+ goroot := downloadGo (runtime .GOARCH , runtime .GOOS , cachedir )
246+ gobuild = localGoTool (goroot , "build" )
235247 }
236248
237- if * arch == "" || * arch == runtime .GOARCH {
238- goinstall := goTool ("install" , buildFlags (env )... )
239- if runtime .GOARCH == "arm64" {
240- goinstall .Args = append (goinstall .Args , "-p" , "1" )
241- }
242- goinstall .Args = append (goinstall .Args , "-trimpath" )
243- goinstall .Args = append (goinstall .Args , "-v" )
244- goinstall .Args = append (goinstall .Args , packages ... )
245- build .MustRun (goinstall )
246- return
249+ // Configure environment for cross build.
250+ if * arch != "" || * arch != runtime .GOARCH {
251+ gobuild .Env = append (gobuild .Env , "CGO_ENABLED=1" )
252+ gobuild .Env = append (gobuild .Env , "GOARCH=" + * arch )
247253 }
248254
249- // Seems we are cross compiling, work around forbidden GOBIN
250- goinstall := goToolArch (* arch , * cc , "install" , buildFlags (env )... )
251- goinstall .Args = append (goinstall .Args , "-trimpath" )
252- goinstall .Args = append (goinstall .Args , "-v" )
253- goinstall .Args = append (goinstall .Args , []string {"-buildmode" , "archive" }... )
254- goinstall .Args = append (goinstall .Args , packages ... )
255- build .MustRun (goinstall )
255+ // Configure C compiler.
256+ if * cc == "" {
257+ gobuild .Env = append (gobuild .Env , "CC=" + * cc )
258+ } else if os .Getenv ("CC" ) != "" {
259+ gobuild .Env = append (gobuild .Env , "CC=" + os .Getenv ("CC" ))
260+ }
256261
257- if cmds , err := ioutil .ReadDir ("cmd" ); err == nil {
258- for _ , cmd := range cmds {
259- pkgs , err := parser .ParseDir (token .NewFileSet (), filepath .Join ("." , "cmd" , cmd .Name ()), nil , parser .PackageClauseOnly )
260- if err != nil {
261- log .Fatal (err )
262- }
263- for name := range pkgs {
264- if name == "main" {
265- gobuild := goToolArch (* arch , * cc , "build" , buildFlags (env )... )
266- gobuild .Args = append (gobuild .Args , "-v" )
267- gobuild .Args = append (gobuild .Args , []string {"-o" , executablePath (cmd .Name ())}... )
268- gobuild .Args = append (gobuild .Args , "." + string (filepath .Separator )+ filepath .Join ("cmd" , cmd .Name ()))
269- build .MustRun (gobuild )
270- break
271- }
272- }
273- }
262+ // arm64 CI builders are memory-constrained and can't handle concurrent builds,
263+ // better disable it. This check isn't the best, it should probably
264+ // check for something in env instead.
265+ if runtime .GOARCH == "arm64" {
266+ gobuild .Args = append (gobuild .Args , "-p" , "1" )
267+ }
268+
269+ // Put the default settings in.
270+ gobuild .Args = append (gobuild .Args , buildFlags (env )... )
271+
272+ // Show packages during build.
273+ gobuild .Args = append (gobuild .Args , "-v" )
274+
275+ // Now we choose what we're even building.
276+ // Default: collect all 'main' packages in cmd/ and build those.
277+ packages := flag .Args ()
278+ if len (packages ) == 0 {
279+ packages = build .FindMainPackages ("./cmd" )
280+ }
281+
282+ // Do the build!
283+ for _ , pkg := range packages {
284+ args := make ([]string , len (gobuild .Args ))
285+ copy (args , gobuild .Args )
286+ args = append (args , "-o" , executablePath (path .Base (pkg )))
287+ args = append (args , pkg )
288+ build .MustRun (& exec.Cmd {Path : gobuild .Path , Args : args , Env : gobuild .Env })
274289 }
275290}
276291
292+ // buildFlags returns the go tool flags for building.
277293func buildFlags (env build.Environment ) (flags []string ) {
278294 var ld []string
279295 if env .Commit != "" {
280296 ld = append (ld , "-X" , "main.gitCommit=" + env .Commit )
281297 ld = append (ld , "-X" , "main.gitDate=" + env .Date )
282298 }
299+ // Strip DWARF on darwin. This used to be required for certain things,
300+ // and there is no downside to this, so we just keep doing it.
283301 if runtime .GOOS == "darwin" {
284302 ld = append (ld , "-s" )
285303 }
286-
287304 if len (ld ) > 0 {
288305 flags = append (flags , "-ldflags" , strings .Join (ld , " " ))
289306 }
307+ // We use -trimpath to avoid leaking local paths into the built executables.
308+ flags = append (flags , "-trimpath" )
290309 return flags
291310}
292311
312+ // goTool returns the go tool. This uses the Go version which runs ci.go.
293313func goTool (subcmd string , args ... string ) * exec.Cmd {
294- return goToolArch (runtime .GOARCH , os .Getenv ("CC" ), subcmd , args ... )
314+ cmd := build .GoTool (subcmd , args ... )
315+ goToolSetEnv (cmd )
316+ return cmd
295317}
296318
297- func goToolArch (arch string , cc string , subcmd string , args ... string ) * exec.Cmd {
298- cmd := build .GoTool (subcmd , args ... )
299- if arch == "" || arch == runtime .GOARCH {
300- cmd .Env = append (cmd .Env , "GOBIN=" + GOBIN )
301- } else {
302- cmd .Env = append (cmd .Env , "CGO_ENABLED=1" )
303- cmd .Env = append (cmd .Env , "GOARCH=" + arch )
304- }
305- if cc != "" {
306- cmd .Env = append (cmd .Env , "CC=" + cc )
307- }
319+ // localGoTool returns the go tool from the given GOROOT.
320+ func localGoTool (goroot string , subcmd string , args ... string ) * exec.Cmd {
321+ gotool := filepath .Join (goroot , "bin" , "go" )
322+ cmd := exec .Command (gotool , subcmd )
323+ goToolSetEnv (cmd )
324+ cmd .Env = append (cmd .Env , "GOROOT=" + goroot )
325+ cmd .Args = append (cmd .Args , args ... )
326+ return cmd
327+ }
328+
329+ // goToolSetEnv forwards the build environment to the go tool.
330+ func goToolSetEnv (cmd * exec.Cmd ) {
308331 for _ , e := range os .Environ () {
309- if strings .HasPrefix (e , "GOBIN=" ) {
332+ if strings .HasPrefix (e , "GOBIN=" ) || strings . HasPrefix ( e , "CC=" ) {
310333 continue
311334 }
312335 cmd .Env = append (cmd .Env , e )
313336 }
314- return cmd
315337}
316338
317339// Running The Tests
@@ -373,7 +395,7 @@ func downloadLinter(cachedir string) string {
373395 if err := csdb .DownloadFile (url , archivePath ); err != nil {
374396 log .Fatal (err )
375397 }
376- if err := build .ExtractTarballArchive (archivePath , cachedir ); err != nil {
398+ if err := build .ExtractArchive (archivePath , cachedir ); err != nil {
377399 log .Fatal (err )
378400 }
379401 return filepath .Join (cachedir , base , "golangci-lint" )
@@ -479,13 +501,12 @@ func maybeSkipArchive(env build.Environment) {
479501// Debian Packaging
480502func doDebianSource (cmdline []string ) {
481503 var (
482- goversion = flag .String ("goversion" , "" , `Go version to build with (will be included in the source package)` )
483- cachedir = flag .String ("cachedir" , "./build/cache" , `Filesystem path to cache the downloaded Go bundles at` )
484- signer = flag .String ("signer" , "" , `Signing key name, also used as package author` )
485- upload = flag .String ("upload" , "" , `Where to upload the source package (usually "ethereum/ethereum")` )
486- sshUser = flag .String ("sftp-user" , "" , `Username for SFTP upload (usually "geth-ci")` )
487- workdir = flag .String ("workdir" , "" , `Output directory for packages (uses temp dir if unset)` )
488- now = time .Now ()
504+ cachedir = flag .String ("cachedir" , "./build/cache" , `Filesystem path to cache the downloaded Go bundles at` )
505+ signer = flag .String ("signer" , "" , `Signing key name, also used as package author` )
506+ upload = flag .String ("upload" , "" , `Where to upload the source package (usually "ethereum/ethereum")` )
507+ sshUser = flag .String ("sftp-user" , "" , `Username for SFTP upload (usually "geth-ci")` )
508+ workdir = flag .String ("workdir" , "" , `Output directory for packages (uses temp dir if unset)` )
509+ now = time .Now ()
489510 )
490511 flag .CommandLine .Parse (cmdline )
491512 * workdir = makeWorkdir (* workdir )
@@ -500,7 +521,7 @@ func doDebianSource(cmdline []string) {
500521 }
501522
502523 // Download and verify the Go source package.
503- gobundle := downloadGoSources (* goversion , * cachedir )
524+ gobundle := downloadGoSources (* cachedir )
504525
505526 // Download all the dependencies needed to build the sources and run the ci script
506527 srcdepfetch := goTool ("install" , "-n" , "./..." )
@@ -519,7 +540,7 @@ func doDebianSource(cmdline []string) {
519540 pkgdir := stageDebianSource (* workdir , meta )
520541
521542 // Add Go source code
522- if err := build .ExtractTarballArchive (gobundle , pkgdir ); err != nil {
543+ if err := build .ExtractArchive (gobundle , pkgdir ); err != nil {
523544 log .Fatalf ("Failed to extract Go sources: %v" , err )
524545 }
525546 if err := os .Rename (filepath .Join (pkgdir , "go" ), filepath .Join (pkgdir , ".go" )); err != nil {
@@ -551,9 +572,10 @@ func doDebianSource(cmdline []string) {
551572 }
552573}
553574
554- func downloadGoSources (version string , cachedir string ) string {
575+ // downloadGoSources downloads the Go source tarball.
576+ func downloadGoSources (cachedir string ) string {
555577 csdb := build .MustLoadChecksums ("build/checksums.txt" )
556- file := fmt .Sprintf ("go%s.src.tar.gz" , version )
578+ file := fmt .Sprintf ("go%s.src.tar.gz" , dlgoVersion )
557579 url := "https://dl.google.com/go/" + file
558580 dst := filepath .Join (cachedir , file )
559581 if err := csdb .DownloadFile (url , dst ); err != nil {
@@ -562,6 +584,41 @@ func downloadGoSources(version string, cachedir string) string {
562584 return dst
563585}
564586
587+ // downloadGo downloads the Go binary distribution and unpacks it into a temporary
588+ // directory. It returns the GOROOT of the unpacked toolchain.
589+ func downloadGo (goarch , goos , cachedir string ) string {
590+ if goarch == "arm" {
591+ goarch = "armv6l"
592+ }
593+
594+ csdb := build .MustLoadChecksums ("build/checksums.txt" )
595+ file := fmt .Sprintf ("go%s.%s-%s" , dlgoVersion , goos , goarch )
596+ if goos == "windows" {
597+ file += ".zip"
598+ } else {
599+ file += ".tar.gz"
600+ }
601+ url := "https://golang.org/dl/" + file
602+ dst := filepath .Join (cachedir , file )
603+ if err := csdb .DownloadFile (url , dst ); err != nil {
604+ log .Fatal (err )
605+ }
606+
607+ ucache , err := os .UserCacheDir ()
608+ if err != nil {
609+ log .Fatal (err )
610+ }
611+ godir := filepath .Join (ucache , fmt .Sprintf ("geth-go-%s-%s-%s" , dlgoVersion , goos , goarch ))
612+ if err := build .ExtractArchive (dst , godir ); err != nil {
613+ log .Fatal (err )
614+ }
615+ goroot , err := filepath .Abs (filepath .Join (godir , "go" ))
616+ if err != nil {
617+ log .Fatal (err )
618+ }
619+ return goroot
620+ }
621+
565622func ppaUpload (workdir , ppa , sshUser string , files []string ) {
566623 p := strings .Split (ppa , "/" )
567624 if len (p ) != 2 {
0 commit comments