golang.org/x/build@v0.0.0-20240506185731-218518f32b70/cmd/racebuild/racebuild.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // racebuild builds the race runtime (syso files) on all supported OSes using gomote.
     6  // Usage:
     7  //
     8  //	$ racebuild -rev <llvm_git_revision> -goroot <path_to_go_repo>
     9  package main
    10  
    11  import (
    12  	"archive/tar"
    13  	"bytes"
    14  	"compress/gzip"
    15  	"context"
    16  	"flag"
    17  	"fmt"
    18  	"io"
    19  	"log"
    20  	"os"
    21  	"os/exec"
    22  	"os/signal"
    23  	"path/filepath"
    24  	"regexp"
    25  	"strings"
    26  	"sync"
    27  
    28  	"golang.org/x/build/internal/envutil"
    29  	"golang.org/x/sync/errgroup"
    30  )
    31  
    32  var (
    33  	flagGoroot     = flag.String("goroot", "", "path to Go repository to update (required)")
    34  	flagRev        = flag.String("rev", "", "llvm-project git revision from https://github.com/llvm/llvm-project (required)")
    35  	flagCherryPick = flag.String("cherrypick", "", "go.googlesource.com CL reference to cherry-pick on top of Go repo (takes form 'refs/changes/NNN/<CL number>/<patchset number>') (optional)")
    36  	flagCheckout   = flag.String("checkout", "", "go.googlesource.com CL reference to check out on top of Go repo (takes form 'refs/changes/NNN/<CL number>/<patchset number>') (optional)")
    37  	flagCopyOnFail = flag.Bool("copyonfail", false, "Attempt to copy newly built race syso into Go repo even if script fails.")
    38  	flagGoRev      = flag.String("gorev", "HEAD", "Go repository revision to use; HEAD is relative to --goroot")
    39  	flagPlatforms  = flag.String("platforms", "all", `comma-separated platforms (such as "darwin/arm64" or "linux/amd64v1") to rebuild, or "all"`)
    40  )
    41  
    42  // goRev is the resolved commit ID of flagGoRev.
    43  var goRev string
    44  
    45  // TODO: use buildlet package instead of calling out to gomote.
    46  var platforms = []*Platform{
    47  	&Platform{
    48  		OS:      "openbsd",
    49  		Arch:    "amd64",
    50  		SubArch: "v1",
    51  		Skip:    true, // openbsd support is removed from TSAN, see issue #52090
    52  		Type:    "openbsd-amd64-72",
    53  		Script: `#!/usr/bin/env bash
    54  set -e
    55  git clone https://go.googlesource.com/go
    56  pushd go
    57  git checkout $GOREV
    58  if [ "$GOGITOP" != "" ]; then
    59    git fetch https://go.googlesource.com/go "$GOSRCREF"
    60    git $GOGITOP FETCH_HEAD
    61  fi
    62  popd
    63  curl -L -o llvm.zip https://github.com/llvm/llvm-project/archive/${REV}.zip
    64  unzip -q llvm.zip llvm-project-${REV}/compiler-rt/*
    65  (cd llvm-project-${REV}/compiler-rt/lib/tsan/go && CC=clang ./buildgo.sh)
    66  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_openbsd_amd64.syso go/src/runtime/race/internal/amd64v1/race_openbsd.syso
    67  # work around gomote gettar issue #64195
    68  mkdir outdir
    69  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_openbsd_amd64.syso outdir/race_openbsd.syso
    70  # free some disk space
    71  rm -r llvm.zip llvm-project-${REV}
    72  (cd go/src && ./race.bash)
    73  			`,
    74  	},
    75  	&Platform{
    76  		OS:      "freebsd",
    77  		Arch:    "amd64",
    78  		SubArch: "v1",
    79  		Type:    "freebsd-amd64-race",
    80  		Script: `#!/usr/bin/env bash
    81  set -e
    82  git clone https://go.googlesource.com/go
    83  pushd go
    84  git checkout $GOREV
    85  if [ "$GOGITOP" != "" ]; then
    86    git fetch https://go.googlesource.com/go "$GOSRCREF"
    87    git $GOGITOP FETCH_HEAD
    88  fi
    89  popd
    90  curl -L -o llvm.zip https://github.com/llvm/llvm-project/archive/${REV}.zip
    91  unzip -q llvm.zip llvm-project-${REV}/compiler-rt/*
    92  (cd llvm-project-${REV}/compiler-rt/lib/tsan/go && CC=clang ./buildgo.sh)
    93  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_freebsd_amd64.syso go/src/runtime/race/internal/amd64v1/race_freebsd.syso
    94  # work around gomote gettar issue #64195
    95  mkdir outdir
    96  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_freebsd_amd64.syso outdir/race_freebsd.syso
    97  # free some disk space
    98  rm -r llvm.zip llvm-project-${REV}
    99  (cd go/src && ./race.bash)
   100  			`,
   101  	},
   102  	&Platform{
   103  		OS:      "darwin",
   104  		Arch:    "amd64",
   105  		SubArch: "v1",
   106  		Type:    "darwin-amd64-12_0",
   107  		Script: `#!/usr/bin/env bash
   108  set -e
   109  git clone https://go.googlesource.com/go
   110  pushd go
   111  git checkout $GOREV
   112  if [ "$GOGITOP" != "" ]; then
   113    git fetch https://go.googlesource.com/go "$GOSRCREF"
   114    git $GOGITOP FETCH_HEAD
   115  fi
   116  popd
   117  curl -L -o llvm.zip https://github.com/llvm/llvm-project/archive/${REV}.zip
   118  unzip -q llvm.zip llvm-project-${REV}/compiler-rt/*
   119  (cd llvm-project-${REV}/compiler-rt/lib/tsan/go && CC=clang ./buildgo.sh)
   120  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_darwin_amd64.syso go/src/runtime/race/internal/amd64v1/race_darwin.syso
   121  # work around gomote gettar issue #64195
   122  mkdir outdir
   123  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_darwin_amd64.syso outdir/race_darwin.syso
   124  # free some disk space
   125  rm -r llvm.zip llvm-project-${REV}
   126  (cd go/src && ./race.bash)
   127  			`,
   128  	},
   129  	&Platform{
   130  		OS:      "darwin",
   131  		Arch:    "amd64",
   132  		SubArch: "v3",
   133  		Skip:    true,
   134  		Type:    "darwin-amd64-12_0",
   135  		Script: `#!/usr/bin/env bash
   136  set -e
   137  git clone https://go.googlesource.com/go
   138  pushd go
   139  git checkout $GOREV
   140  popd
   141  curl -L -o llvm.zip https://github.com/llvm/llvm-project/archive/${REV}.zip
   142  unzip -q llvm.zip llvm-project-${REV}/compiler-rt/*
   143  (cd llvm-project-${REV}/compiler-rt/lib/tsan/go && CC=clang GOAMD64=v3 ./buildgo.sh)
   144  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_darwin_amd64.syso go/src/runtime/race/internal/amd64v3/race_darwin.syso
   145  # work around gomote gettar issue #64195
   146  mkdir outdir
   147  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_darwin_amd64.syso outdir/race_darwin.syso
   148  # free some disk space
   149  rm -r llvm.zip llvm-project-${REV}
   150  (cd go/src && GOAMD64=v3 ./race.bash)
   151  			`,
   152  	},
   153  	&Platform{
   154  		OS:   "darwin",
   155  		Arch: "arm64",
   156  		Type: "darwin-arm64-12",
   157  		Script: `#!/usr/bin/env bash
   158  set -e
   159  git clone https://go.googlesource.com/go
   160  pushd go
   161  git checkout $GOREV
   162  if [ "$GOGITOP" != "" ]; then
   163    git fetch https://go.googlesource.com/go "$GOSRCREF"
   164    git $GOGITOP FETCH_HEAD
   165  fi
   166  popd
   167  curl -L -o llvm.zip https://github.com/llvm/llvm-project/archive/${REV}.zip
   168  unzip -q llvm.zip llvm-project-${REV}/compiler-rt/*
   169  (cd llvm-project-${REV}/compiler-rt/lib/tsan/go && CC=clang ./buildgo.sh)
   170  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_darwin_arm64.syso go/src/runtime/race
   171  # work around gomote gettar issue #64195
   172  mkdir outdir
   173  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_darwin_arm64.syso outdir/race_darwin_arm64.syso
   174  # free some disk space
   175  rm -r llvm.zip llvm-project-${REV}
   176  (cd go/src && ./race.bash)
   177  			`,
   178  	},
   179  	&Platform{
   180  		OS:      "linux",
   181  		Arch:    "amd64",
   182  		SubArch: "v1",
   183  		Type:    "linux-amd64-race",
   184  		Script: `#!/usr/bin/env bash
   185  set -e
   186  apt-get update --allow-releaseinfo-change
   187  apt-get install -y git g++ unzip
   188  git clone https://go.googlesource.com/go
   189  pushd go
   190  git checkout $GOREV
   191  if [ "$GOGITOP" != "" ]; then
   192    git fetch https://go.googlesource.com/go "$GOSRCREF"
   193    git $GOGITOP FETCH_HEAD
   194  fi
   195  popd
   196  curl -L -o llvm.zip https://github.com/llvm/llvm-project/archive/${REV}.zip
   197  unzip -q llvm.zip llvm-project-${REV}/compiler-rt/*
   198  (cd llvm-project-${REV}/compiler-rt/lib/tsan/go && ./buildgo.sh)
   199  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_linux_amd64.syso go/src/runtime/race/internal/amd64v1/race_linux.syso
   200  # work around gomote gettar issue #64195
   201  mkdir outdir
   202  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_linux_amd64.syso outdir/race_linux.syso
   203  # free some disk space
   204  rm -r llvm.zip llvm-project-${REV}
   205  (cd go/src && ./race.bash)
   206  			`,
   207  	},
   208  	&Platform{
   209  		OS:      "linux",
   210  		Arch:    "amd64",
   211  		SubArch: "v3",
   212  		Type:    "linux-amd64-race",
   213  		Script: `#!/usr/bin/env bash
   214  set -e
   215  apt-get update
   216  apt-get install -y git g++ unzip
   217  git clone https://go.googlesource.com/go
   218  pushd go
   219  git checkout $GOREV
   220  popd
   221  curl -L -o llvm.zip https://github.com/llvm/llvm-project/archive/${REV}.zip
   222  unzip -q llvm.zip llvm-project-${REV}/compiler-rt/*
   223  (cd llvm-project-${REV}/compiler-rt/lib/tsan/go && GOAMD64=v3 ./buildgo.sh)
   224  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_linux_amd64.syso go/src/runtime/race/internal/amd64v3/race_linux.syso
   225  # work around gomote gettar issue #64195
   226  mkdir outdir
   227  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_linux_amd64.syso outdir/race_linux.syso
   228  # free some disk space
   229  rm -r llvm.zip llvm-project-${REV}
   230  (cd go/src && GOAMD64=v3 ./race.bash)
   231  			`,
   232  	},
   233  	&Platform{
   234  		OS:   "linux",
   235  		Arch: "ppc64le",
   236  		Type: "linux-ppc64le-buildlet",
   237  		Script: `#!/usr/bin/env bash
   238  set -e
   239  apt-get update --allow-releaseinfo-change
   240  apt-get install -y git g++ unzip
   241  git clone https://go.googlesource.com/go
   242  pushd go
   243  git checkout $GOREV
   244  if [ "$GOGITOP" != "" ]; then
   245    git fetch https://go.googlesource.com/go "$GOSRCREF"
   246    git $GOGITOP FETCH_HEAD
   247  fi
   248  popd
   249  workdir=$(pwd)
   250  curl -L -o llvm.zip https://github.com/llvm/llvm-project/archive/${REV}.zip
   251  unzip -q llvm.zip llvm-project-${REV}/compiler-rt/*
   252  (cd llvm-project-${REV}/compiler-rt/lib/tsan/go && ./buildgo.sh)
   253  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_linux_ppc64le.syso $workdir/go/src/runtime/race
   254  # work around gomote gettar issue #64195
   255  mkdir outdir
   256  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_linux_ppc64le.syso outdir/race_linux_ppc64le.syso
   257  # TODO(#23731): Uncomment to test the syso file before accepting it.
   258  # free some disk space
   259  # rm -r llvm.zip llvm-project-${REV}
   260  # (cd go/src && ./race.bash)
   261  			`,
   262  	},
   263  	&Platform{
   264  		OS:   "linux",
   265  		Arch: "arm64",
   266  		Type: "linux-arm64-race",
   267  		Script: `#!/usr/bin/env bash
   268  set -e
   269  apt-get update --allow-releaseinfo-change
   270  apt-get install -y git g++ unzip
   271  git clone https://go.googlesource.com/go
   272  pushd go
   273  git checkout $GOREV
   274  if [ "$GOGITOP" != "" ]; then
   275    git fetch https://go.googlesource.com/go "$GOSRCREF"
   276    git $GOGITOP FETCH_HEAD
   277  fi
   278  popd
   279  curl -L -o llvm.zip https://github.com/llvm/llvm-project/archive/${REV}.zip
   280  unzip -q llvm.zip llvm-project-${REV}/compiler-rt/*
   281  (cd llvm-project-${REV}/compiler-rt/lib/tsan/go && ./buildgo.sh)
   282  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_linux_arm64.syso go/src/runtime/race
   283  # work around gomote gettar issue #64195
   284  mkdir outdir
   285  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_linux_arm64.syso outdir/race_linux_arm64.syso
   286  # free some disk space
   287  rm -r llvm.zip llvm-project-${REV}
   288  (cd go/src && ./race.bash)
   289  			`,
   290  	},
   291  	&Platform{
   292  		OS:      "netbsd",
   293  		Arch:    "amd64",
   294  		SubArch: "v1",
   295  		Type:    "netbsd-amd64-9_3",
   296  		Script: `#!/usr/bin/env bash
   297  set -e
   298  git clone https://go.googlesource.com/go
   299  pushd go
   300  git checkout $GOREV
   301  if [ "$GOGITOP" != "" ]; then
   302    git fetch https://go.googlesource.com/go "$GOSRCREF"
   303    git $GOGITOP FETCH_HEAD
   304  fi
   305  popd
   306  curl -L -o llvm.zip https://github.com/llvm/llvm-project/archive/${REV}.zip
   307  unzip -q llvm.zip llvm-project-${REV}/compiler-rt/*
   308  (cd llvm-project-${REV}/compiler-rt/lib/tsan/go && CC=clang ./buildgo.sh)
   309  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_netbsd_amd64.syso go/src/runtime/race/internal/amd64v1/race_netbsd.syso
   310  # work around gomote gettar issue #64195
   311  mkdir outdir
   312  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_netbsd_amd64.syso outdir/race_netbsd.syso
   313  # TODO(#24322): Uncomment to test the syso file before accepting it.
   314  # free some disk space
   315  # rm -r llvm.zip llvm-project-${REV}
   316  # (cd go/src && ./race.bash)
   317  			`,
   318  	},
   319  	&Platform{
   320  		OS:      "windows",
   321  		Arch:    "amd64",
   322  		SubArch: "v1",
   323  		Type:    "windows-amd64-race",
   324  		Script: `
   325  @"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "[System.net.ServicePointManager]::SecurityProtocol = 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
   326  choco install git -y
   327  if %errorlevel% neq 0 exit /b %errorlevel%
   328  call refreshenv
   329  echo adding back in compiler path
   330  set PATH=C:\go\bin;%PATH%;C:\godep\gcc64\bin
   331  rem make sure we have a working copy of gcc
   332  gcc --version
   333  if %errorlevel% neq 0 exit /b %errorlevel%
   334  git clone https://go.googlesource.com/go
   335  if %errorlevel% neq 0 exit /b %errorlevel%
   336  cd go
   337  git checkout %GOREV%
   338  if %errorlevel% neq 0 exit /b %errorlevel%
   339  if "%$GOGITOP%"=="" goto nogogitop
   340  git fetch https://go.googlesource.com/go %GOSRCREF%
   341  if %errorlevel% neq 0 exit /b %errorlevel%
   342  git %GOGITOP% FETCH_HEAD
   343  if %errorlevel% neq 0 exit /b %errorlevel%
   344  :nogogitop
   345  cd ..
   346  git clone https://github.com/llvm/llvm-project
   347  if %errorlevel% neq 0 exit /b %errorlevel%
   348  cd llvm-project
   349  git checkout %REV%
   350  if %errorlevel% neq 0 exit /b %errorlevel%
   351  cd ..
   352  cd llvm-project/compiler-rt/lib/tsan/go
   353  call build.bat
   354  if %errorlevel% neq 0 exit /b %errorlevel%
   355  cd ../../../../..
   356  xcopy llvm-project\compiler-rt\lib\tsan\go\race_windows_amd64.syso go\src\runtime\race\internal\amd64v1\race_windows.syso /Y
   357  if %errorlevel% neq 0 exit /b %errorlevel%
   358  rem work around gomote gettar issue #64195
   359  mkdir outdir
   360  if %errorlevel% neq 0 exit /b %errorlevel%
   361  copy llvm-project\compiler-rt\lib\tsan\go\race_windows_amd64.syso outdir\race_windows.syso
   362  if %errorlevel% neq 0 exit /b %errorlevel%
   363  cd go/src
   364  call race.bat
   365  if %errorlevel% neq 0 exit /b %errorlevel%
   366  			`,
   367  	},
   368  	&Platform{
   369  		OS:   "linux",
   370  		Arch: "s390x",
   371  		Type: "linux-s390x-ibm",
   372  		Script: `#!/usr/bin/env bash
   373  set -e
   374  cat /etc/os-release
   375  yum install -y gcc-c++ git golang-bin unzip
   376  git clone https://go.googlesource.com/go
   377  pushd go
   378  git checkout $GOREV
   379  if [ "$GOGITOP" != "" ]; then
   380    git fetch https://go.googlesource.com/go "$GOSRCREF"
   381    git $GOGITOP FETCH_HEAD
   382  fi
   383  popd
   384  curl -L -o llvm.zip https://github.com/llvm/llvm-project/archive/${REV}.zip
   385  unzip -q llvm.zip llvm-project-${REV}/compiler-rt/*
   386  (cd llvm-project-${REV}/compiler-rt/lib/tsan/go && ./buildgo.sh)
   387  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_linux_s390x.syso go/src/runtime/race
   388  # work around gomote gettar issue #64195
   389  mkdir outdir
   390  cp llvm-project-${REV}/compiler-rt/lib/tsan/go/race_linux_s390x.syso outdir/race_linux_s390x.syso
   391  # free some disk space
   392  rm -r llvm.zip llvm-project-${REV}
   393  (cd go/src && ./race.bash)
   394  			`,
   395  	},
   396  }
   397  
   398  func init() {
   399  	// Ensure that there are no duplicate platform entries.
   400  	seen := make(map[string]bool)
   401  	for _, p := range platforms {
   402  		if seen[p.Name()] {
   403  			log.Fatalf("Duplicate platforms entry for %s.", p.Name())
   404  		}
   405  		seen[p.Name()] = true
   406  	}
   407  }
   408  
   409  var platformEnabled = make(map[string]bool)
   410  
   411  func parsePlatformsFlag() {
   412  	if *flagPlatforms == "all" {
   413  		for _, p := range platforms {
   414  			if !p.Skip {
   415  				platformEnabled[p.Name()] = true
   416  			}
   417  		}
   418  		return
   419  	}
   420  
   421  	var invalid []string
   422  	for _, name := range strings.Split(*flagPlatforms, ",") {
   423  		for _, p := range platforms {
   424  			if name == p.Name() {
   425  				platformEnabled[name] = true
   426  				break
   427  			}
   428  		}
   429  		if !platformEnabled[name] {
   430  			invalid = append(invalid, name)
   431  		}
   432  	}
   433  
   434  	if len(invalid) > 0 {
   435  		var msg bytes.Buffer
   436  		fmt.Fprintf(&msg, "Unrecognized platforms: %q. Supported platforms are:\n", invalid)
   437  		for _, p := range platforms {
   438  			fmt.Fprintf(&msg, "\t%s\n", p.Name())
   439  		}
   440  		log.Fatal(&msg)
   441  	}
   442  }
   443  
   444  func main() {
   445  	flag.Parse()
   446  	if *flagRev == "" || *flagGoroot == "" || *flagGoRev == "" {
   447  		flag.PrintDefaults()
   448  		os.Exit(1)
   449  	}
   450  	if *flagCherryPick != "" && *flagCheckout != "" {
   451  		log.Fatalf("select at most one of -cherrypick and -checkout")
   452  	}
   453  	parsePlatformsFlag()
   454  
   455  	cmd := exec.Command("git", "rev-parse", *flagGoRev)
   456  	envutil.SetDir(cmd, *flagGoroot)
   457  	cmd.Stderr = os.Stderr
   458  	out, err := cmd.Output()
   459  	if err != nil {
   460  		log.Fatalf("%s failed: %v", strings.Join(cmd.Args, " "), err)
   461  	}
   462  	goRev = string(bytes.TrimSpace(out))
   463  	log.Printf("using Go revision: %s", goRev)
   464  
   465  	// Start build on all platforms in parallel.
   466  	// On interrupt, destroy any in-flight builders before exiting.
   467  	ctx, cancel := context.WithCancel(context.Background())
   468  	shutdown := make(chan os.Signal, 1)
   469  	signal.Notify(shutdown, os.Interrupt)
   470  	go func() {
   471  		<-shutdown
   472  		cancel()
   473  	}()
   474  
   475  	g, ctx := errgroup.WithContext(ctx)
   476  	for _, p := range platforms {
   477  		if !platformEnabled[p.Name()] {
   478  			continue
   479  		}
   480  
   481  		p := p
   482  		g.Go(func() error {
   483  			if err := p.Build(ctx); err != nil {
   484  				return fmt.Errorf("%v failed: %v", p.Name(), err)
   485  			}
   486  			return p.UpdateReadme()
   487  		})
   488  	}
   489  
   490  	if err := g.Wait(); err != nil {
   491  		log.Fatal(err)
   492  	}
   493  }
   494  
   495  type Platform struct {
   496  	OS      string
   497  	Arch    string
   498  	SubArch string
   499  	Type    string // gomote instance type
   500  	Inst    string // actual gomote instance name
   501  	Script  string
   502  	Skip    bool // disabled by default
   503  }
   504  
   505  func (p *Platform) Name() string {
   506  	if p.SubArch != "" {
   507  		return fmt.Sprintf("%v/%v%v", p.OS, p.Arch, p.SubArch)
   508  	}
   509  	return fmt.Sprintf("%v/%v", p.OS, p.Arch)
   510  }
   511  
   512  // Basename returns the name of the output file relative to src/runtime/race.
   513  func (p *Platform) Basename() string {
   514  	if p.SubArch != "" {
   515  		return fmt.Sprintf("internal/%s%s/race_%s.syso", p.Arch, p.SubArch, p.OS)
   516  	}
   517  	return fmt.Sprintf("race_%v_%s.syso", p.OS, p.Arch)
   518  }
   519  
   520  func setupForGoRepoGitOp() (string, string) {
   521  	if *flagCherryPick != "" {
   522  		return "cherry-pick -n", *flagCherryPick
   523  	} else if *flagCheckout != "" {
   524  		return "checkout", *flagCheckout
   525  	}
   526  	return "", ""
   527  }
   528  
   529  func (p *Platform) Build(ctx context.Context) error {
   530  	// Create gomote instance (or reuse an existing instance for debugging).
   531  	var lastErr error
   532  	for p.Inst == "" {
   533  		inst, err := p.Gomote(ctx, "create", "-status=false", p.Type)
   534  		if err != nil {
   535  			select {
   536  			case <-ctx.Done():
   537  				if lastErr != nil {
   538  					return lastErr
   539  				}
   540  				return err
   541  			default:
   542  				// Creation sometimes fails with transient errors like:
   543  				// "buildlet didn't come up at http://10.240.0.13 in 3m0s".
   544  				log.Printf("%v: instance creation failed, retrying", p.Name())
   545  				lastErr = err
   546  				continue
   547  			}
   548  		}
   549  		p.Inst = strings.Trim(string(inst), " \t\n")
   550  		defer p.Gomote(context.Background(), "destroy", p.Inst)
   551  	}
   552  	log.Printf("%s: using instance %v", p.Name(), p.Inst)
   553  
   554  	// putbootstrap
   555  	if _, err := p.Gomote(ctx, "putbootstrap", p.Inst); err != nil {
   556  		return err
   557  	}
   558  
   559  	// Execute the script.
   560  	script, err := os.CreateTemp("", "racebuild")
   561  	if err != nil {
   562  		return fmt.Errorf("failed to create temp file: %v", err)
   563  	}
   564  	defer func() {
   565  		script.Close()
   566  		os.Remove(script.Name())
   567  	}()
   568  	if _, err := script.Write([]byte(p.Script)); err != nil {
   569  		return fmt.Errorf("failed to write temp file: %v", err)
   570  	}
   571  	script.Close()
   572  	targetName := "script.bash"
   573  	if p.OS == "windows" {
   574  		targetName = "script.bat"
   575  	}
   576  	if _, err := p.Gomote(ctx, "put", "-mode=0700", p.Inst, script.Name(), targetName); err != nil {
   577  		return err
   578  	}
   579  	var scriptRunErr error
   580  	gogitop, gosrcref := setupForGoRepoGitOp()
   581  	if _, err := p.Gomote(ctx, "run", "-e=REV="+*flagRev, "-e=GOREV="+goRev,
   582  		"-e=GOGITOP="+gogitop, "-e=GOSRCREF="+gosrcref,
   583  		p.Inst, targetName); err != nil {
   584  		if !*flagCopyOnFail {
   585  			return err
   586  		}
   587  		log.Printf("%v: gomote script run failed, continuing...\n", p.Name())
   588  		scriptRunErr = err
   589  	}
   590  
   591  	// The script is supposed to leave updated runtime at that path. Copy it out.
   592  	syso := p.Basename()
   593  	_, err = p.Gomote(ctx, "gettar", "-dir=outdir", p.Inst)
   594  	if err != nil {
   595  		return err
   596  	}
   597  
   598  	// Untar the runtime and write it to goroot.
   599  	if err := p.WriteSyso(filepath.Join(*flagGoroot, "src", "runtime", "race", syso), p.Inst+".tar.gz"); err != nil {
   600  		return fmt.Errorf("%v", err)
   601  	}
   602  	if scriptRunErr != nil {
   603  		return err
   604  	}
   605  
   606  	log.Printf("%v: build completed", p.Name())
   607  	return nil
   608  }
   609  
   610  func (p *Platform) WriteSyso(sysof string, targz string) error {
   611  	// Ungzip.
   612  	targzf, err := os.Open(targz)
   613  	if err != nil {
   614  		return fmt.Errorf("failed to open targz file %s: %v", targz, err)
   615  	}
   616  	defer targzf.Close()
   617  	gzipr, err := gzip.NewReader(targzf)
   618  	if err != nil {
   619  		return fmt.Errorf("failed to read gzip archive: %v", err)
   620  	}
   621  	defer gzipr.Close()
   622  	tr := tar.NewReader(gzipr)
   623  	if _, err := tr.Next(); err != nil {
   624  		return fmt.Errorf("failed to read tar archive: %v", err)
   625  	}
   626  
   627  	// Copy the file.
   628  	syso, err := os.Create(sysof)
   629  	if err != nil {
   630  		return fmt.Errorf("failed to open race runtime: %v", err)
   631  	}
   632  	defer syso.Close()
   633  	if _, err := io.Copy(syso, tr); err != nil {
   634  		return fmt.Errorf("failed to write race runtime: %v", err)
   635  	}
   636  	return nil
   637  }
   638  
   639  var readmeMu sync.Mutex
   640  
   641  func (p *Platform) UpdateReadme() error {
   642  	readmeMu.Lock()
   643  	defer readmeMu.Unlock()
   644  
   645  	readmeFile := filepath.Join(*flagGoroot, "src", "runtime", "race", "README")
   646  	readme, err := os.ReadFile(readmeFile)
   647  	if err != nil {
   648  		log.Fatalf("bad -goroot? %v", err)
   649  	}
   650  
   651  	syso := p.Basename()
   652  	const (
   653  		readmeTmpl = "%s built with LLVM %s and Go %s."
   654  		commitRE   = "[0-9a-f]+"
   655  	)
   656  
   657  	// TODO(bcmills): Extract the C++ toolchain version from the .syso file and
   658  	// record it in the README.
   659  	updatedLine := fmt.Sprintf(readmeTmpl, syso, *flagRev, goRev)
   660  
   661  	lineRE, err := regexp.Compile("(?m)^" + fmt.Sprintf(readmeTmpl, regexp.QuoteMeta(syso), commitRE, commitRE) + "$")
   662  	if err != nil {
   663  		return err
   664  	}
   665  	if lineRE.Match(readme) {
   666  		readme = lineRE.ReplaceAll(readme, []byte(updatedLine))
   667  	} else {
   668  		readme = append(append(readme, []byte(updatedLine)...), '\n')
   669  	}
   670  
   671  	return os.WriteFile(readmeFile, readme, 0640)
   672  }
   673  
   674  func (p *Platform) Gomote(ctx context.Context, args ...string) ([]byte, error) {
   675  	log.Printf("%v: gomote %v", p.Name(), args)
   676  
   677  	cmd := exec.CommandContext(ctx, "gomote", args...)
   678  	outBuf := new(bytes.Buffer)
   679  	errBuf := outBuf
   680  
   681  	cmd.Stdout = outBuf
   682  	cmd.Stderr = errBuf
   683  	run := cmd.Run
   684  	if len(platformEnabled) == 1 {
   685  		// If building only one platform, stream gomote output to os.Stderr.
   686  		r, w := io.Pipe()
   687  		errTee := io.TeeReader(r, cmd.Stderr)
   688  		if cmd.Stdout == cmd.Stderr {
   689  			cmd.Stdout = w
   690  		}
   691  		cmd.Stderr = w
   692  
   693  		run = func() (err error) {
   694  			go func() {
   695  				err = cmd.Run()
   696  				w.Close()
   697  			}()
   698  			io.Copy(os.Stderr, errTee)
   699  			return
   700  		}
   701  	}
   702  
   703  	if err := run(); err != nil {
   704  		select {
   705  		case <-ctx.Done():
   706  			return nil, ctx.Err()
   707  		default:
   708  		}
   709  		log.Printf("%v: gomote %v failed:\n%s", p.Name(), args, errBuf)
   710  		return nil, err
   711  	}
   712  
   713  	if errBuf.Len() == 0 {
   714  		log.Printf("%v: gomote %v succeeded: <no output>", p.Name(), args)
   715  	} else {
   716  		log.Printf("%v: gomote %v succeeded:\n%s", p.Name(), args, errBuf)
   717  	}
   718  	return outBuf.Bytes(), nil
   719  }