in cmd/release/release.go [325:697]
func (b *Build) make() error {
ctx := context.TODO()
bc, ok := dashboard.Builders[b.Builder]
if !ok {
return fmt.Errorf("unknown builder: %v", bc)
}
var hostArch string // non-empty if we're cross-compiling (s390x)
if b.SkipTests && bc.IsContainer() && (bc.GOARCH() != "amd64" && bc.GOARCH() != "386") {
hostArch = "amd64"
}
client, err := b.buildlet()
if err != nil {
return err
}
defer client.Close()
work, err := client.WorkDir(ctx)
if err != nil {
return err
}
// Push source to buildlet.
b.logf("Pushing source to buildlet.")
const (
goDir = "go"
goPath = "gopath"
go14 = "go1.4"
)
if *tarball != "" {
tarFile, err := os.Open(*tarball)
if err != nil {
b.logf("failed to open tarball %q: %v", *tarball, err)
return err
}
if err := client.PutTar(ctx, tarFile, goDir); err != nil {
b.logf("failed to put tarball %q into dir %q: %v", *tarball, goDir, err)
return err
}
tarFile.Close()
} else {
tar := "https://go.googlesource.com/go/+archive/" + *rev + ".tar.gz"
if err := client.PutTarFromURL(ctx, tar, goDir); err != nil {
b.logf("failed to put tarball %q into dir %q: %v", tar, goDir, err)
return err
}
}
if u := bc.GoBootstrapURL(buildEnv); u != "" && !b.Source {
b.logf("Installing go1.4.")
if err := client.PutTarFromURL(ctx, u, go14); err != nil {
return err
}
}
// Write out version file.
b.logf("Writing VERSION file.")
if err := client.Put(ctx, strings.NewReader(*version), "go/VERSION", 0644); err != nil {
return err
}
b.logf("Cleaning goroot (pre-build).")
if err := client.RemoveAll(ctx, addPrefix(goDir, preBuildCleanFiles)...); err != nil {
return err
}
if b.Source {
b.logf("Skipping build.")
// Remove unwanted top-level directories and verify only "go" remains:
if err := client.RemoveAll(ctx, "tmp", "gocache"); err != nil {
return err
}
if err := b.checkTopLevelDirs(ctx, client); err != nil {
return fmt.Errorf("verifying no unwanted top-level directories: %v", err)
}
if err := b.checkPerm(ctx, client); err != nil {
return fmt.Errorf("verifying file permissions: %v", err)
}
finalFilename := *version + "." + b.String() + ".tar.gz"
return b.fetchTarball(ctx, client, finalFilename)
}
// Set up build environment.
sep := "/"
if b.OS == "windows" {
sep = "\\"
}
env := append(bc.Env(),
"GOROOT_FINAL="+bc.GorootFinal(),
"GOROOT="+work+sep+goDir,
"GOPATH="+work+sep+goPath,
"GOBIN=",
)
if b.Goarm > 0 {
env = append(env, fmt.Sprintf("GOARM=%d", b.Goarm))
env = append(env, fmt.Sprintf("CGO_CFLAGS=-march=armv%d", b.Goarm))
env = append(env, fmt.Sprintf("CGO_LDFLAGS=-march=armv%d", b.Goarm))
}
// Issues #36025 #35459
if b.OS == "darwin" && b.Arch == "amd64" {
minMacVersion := minSupportedMacOSVersion(*version)
env = append(env, fmt.Sprintf("CGO_CFLAGS=-mmacosx-version-min=%s", minMacVersion))
}
// Execute build (make.bash only first).
b.logf("Building (make.bash only).")
out := new(bytes.Buffer)
var execOut io.Writer = out
if *watch && *target != "" {
execOut = io.MultiWriter(out, os.Stdout)
}
remoteErr, err := client.Exec(context.Background(), filepath.Join(goDir, bc.MakeScript()), buildlet.ExecOpts{
Output: execOut,
ExtraEnv: env,
Args: bc.MakeScriptArgs(),
})
if err != nil {
return err
}
if remoteErr != nil {
return fmt.Errorf("Build failed: %v\nOutput:\n%v", remoteErr, out)
}
if err := b.checkRelocations(client); err != nil {
return err
}
goCmd := path.Join(goDir, "bin/go")
if b.OS == "windows" {
goCmd += ".exe"
}
runGo := func(args ...string) error {
out := new(bytes.Buffer)
var execOut io.Writer = out
if *watch && *target != "" {
execOut = io.MultiWriter(out, os.Stdout)
}
cmdEnv := append([]string(nil), env...)
if len(args) > 0 && args[0] == "run" && hostArch != "" {
cmdEnv = setGOARCH(cmdEnv, hostArch)
}
remoteErr, err := client.Exec(context.Background(), goCmd, buildlet.ExecOpts{
Output: execOut,
Dir: ".", // root of buildlet work directory
Args: args,
ExtraEnv: cmdEnv,
})
if err != nil {
return err
}
if remoteErr != nil {
return fmt.Errorf("go %v: %v\n%s", strings.Join(args, " "), remoteErr, out)
}
return nil
}
if b.Race {
b.logf("Building race detector.")
if err := runGo("install", "-race", "std"); err != nil {
return err
}
}
// postBuildCleanFiles are the list of files to remove in the go/ directory
// after things have been built.
postBuildCleanFiles := []string{
"VERSION.cache",
"pkg/bootstrap",
}
// Remove race detector *.syso files for other GOOS/GOARCHes (except for the source release).
if !b.Source {
okayRace := fmt.Sprintf("race_%s_%s.syso", b.OS, b.Arch)
err := client.ListDir(ctx, ".", buildlet.ListDirOpts{Recursive: true}, func(ent buildlet.DirEntry) {
name := strings.TrimPrefix(ent.Name(), "go/")
if strings.HasPrefix(name, "src/runtime/race/race_") &&
strings.HasSuffix(name, ".syso") &&
path.Base(name) != okayRace {
postBuildCleanFiles = append(postBuildCleanFiles, name)
}
})
if err != nil {
return fmt.Errorf("enumerating files to clean race syso files: %v", err)
}
}
b.logf("Cleaning goroot (post-build).")
if err := client.RemoveAll(ctx, addPrefix(goDir, postBuildCleanFiles)...); err != nil {
return err
}
// Users don't need the api checker binary pre-built. It's
// used by tests, but all.bash builds it first.
if err := client.RemoveAll(ctx, b.toolDir()+"/api"); err != nil {
return err
}
// The oldlink tool is used for debugging differences during
// testing of the linker rewrite, and can be built by users
// if necessary. See issue 39509.
// This can be removed when oldlink is gone, likely once Go 1.16
// is no longer supported.
if err := client.RemoveAll(ctx, b.toolDir()+"/oldlink"); err != nil {
return err
}
// Remove go/pkg/${GOOS}_${GOARCH}/cmd. This saves a bunch of
// space, and users don't typically rebuild cmd/compile,
// cmd/link, etc. If they want to, they still can, but they'll
// have to pay the cost of rebuilding dependent libaries. No
// need to ship them just in case.
//
// Also remove go/pkg/${GOOS}_${GOARCH}_{dynlink,shared,testcshared_shared}
// per Issue 20038.
if err := client.RemoveAll(ctx,
b.pkgDir()+"/cmd",
b.pkgDir()+"_dynlink",
b.pkgDir()+"_shared",
b.pkgDir()+"_testcshared_shared",
); err != nil {
return err
}
b.logf("Pushing and running releaselet.")
err = client.Put(ctx, strings.NewReader(releaselet), "releaselet.go", 0666)
if err != nil {
return err
}
if err := runGo("run", "releaselet.go"); err != nil {
log.Printf("releaselet failed: %v", err)
client.ListDir(ctx, ".", buildlet.ListDirOpts{Recursive: true}, func(ent buildlet.DirEntry) {
log.Printf("remote: %v", ent)
})
return err
}
cleanFiles := []string{"releaselet.go", goPath, go14, "tmp", "gocache"}
// So far, we've run make.bash. We want to create the release archive next.
// Since the release archive hasn't been tested yet, place it in a temporary
// location. After all.bash runs successfully (or gets explicitly skipped),
// we'll move the release archive to its final location. For TestOnly builds,
// we only care whether tests passed and do not produce release artifacts.
type releaseFile struct {
Untested string // Temporary location of the file before the release has been tested.
Final string // Final location where to move the file after the release has been tested.
}
var releases []releaseFile
stagingDir := *stagingDir
if stagingDir == "" {
var err error
stagingDir, err = ioutil.TempDir("", "go-release-staging_")
if err != nil {
log.Fatal(err)
}
}
stagingFile := func(ext string) string {
return filepath.Join(stagingDir, *version+"."+b.String()+ext+".untested")
}
if !b.TestOnly && b.OS == "windows" {
untested := stagingFile(".msi")
if err := b.fetchFile(client, untested, "msi"); err != nil {
return err
}
releases = append(releases, releaseFile{
Untested: untested,
Final: *version + "." + b.String() + ".msi",
})
}
if b.OS == "windows" {
cleanFiles = append(cleanFiles, "msi")
}
if b.OS == "windows" && b.Arch == "arm64" {
// At least on windows-arm64, 'wix/winterop.dll' gets created.
// Delete the entire wix directory since it's unrelated to Go.
cleanFiles = append(cleanFiles, "wix")
}
// Need to delete everything except the final "go" directory,
// as we make the tarball relative to workdir.
b.logf("Cleaning workdir.")
if err := client.RemoveAll(ctx, cleanFiles...); err != nil {
return err
}
// And verify there's no other top-level stuff besides the "go" directory:
if err := b.checkTopLevelDirs(ctx, client); err != nil {
return fmt.Errorf("verifying no unwanted top-level directories: %v", err)
}
if err := b.checkPerm(ctx, client); err != nil {
return fmt.Errorf("verifying file permissions: %v", err)
}
switch {
case !b.TestOnly && b.OS != "windows":
untested := stagingFile(".tar.gz")
if err := b.fetchTarball(ctx, client, untested); err != nil {
return fmt.Errorf("fetching and writing tarball: %v", err)
}
releases = append(releases, releaseFile{
Untested: untested,
Final: *version + "." + b.String() + ".tar.gz",
})
case !b.TestOnly && b.OS == "windows":
untested := stagingFile(".zip")
if err := b.fetchZip(client, untested); err != nil {
return fmt.Errorf("fetching and writing zip: %v", err)
}
releases = append(releases, releaseFile{
Untested: untested,
Final: *version + "." + b.String() + ".zip",
})
case b.TestOnly:
// Use an empty .test-only file to indicate the test outcome.
// This file gets moved from its initial location in the
// release-staging directory to the final release directory
// when the test-only build passes tests successfully.
untested := stagingFile(".test-only")
if err := ioutil.WriteFile(untested, nil, 0600); err != nil {
return fmt.Errorf("writing empty test-only file: %v", err)
}
releases = append(releases, releaseFile{
Untested: untested,
Final: *version + "." + b.String() + ".test-only",
})
}
// Execute build (all.bash) if running tests.
if *skipTests || b.SkipTests {
b.logf("Skipping all.bash tests.")
} else {
if u := bc.GoBootstrapURL(buildEnv); u != "" {
b.logf("Installing go1.4 (second time, for all.bash).")
if err := client.PutTarFromURL(ctx, u, go14); err != nil {
return err
}
}
b.logf("Building (all.bash to ensure tests pass).")
out := new(bytes.Buffer)
var execOut io.Writer = out
if *watch && *target != "" {
execOut = io.MultiWriter(out, os.Stdout)
}
remoteErr, err := client.Exec(ctx, filepath.Join(goDir, bc.AllScript()), buildlet.ExecOpts{
Output: execOut,
ExtraEnv: env,
Args: bc.AllScriptArgs(),
})
if err != nil {
return err
}
if remoteErr != nil {
return fmt.Errorf("Build failed: %v\nOutput:\n%v", remoteErr, out)
}
}
// If we get this far, the all.bash tests have passed (or been skipped).
// Move untested release files to their final locations.
for _, r := range releases {
b.logf("Moving %q to %q.", r.Untested, r.Final)
if err := os.Rename(r.Untested, r.Final); err != nil {
return err
}
}
return nil
}