From a5fa7c14274e8276604fdf5059db73397d2f896e Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Wed, 13 Apr 2022 15:20:59 -0500 Subject: [PATCH 1/4] Add arm/v6 cross-compiled build Update bootstrap Go to 1.18 for strings.Cut. Add missing err checks in pack code. --- eng/_core/archive/archive.go | 129 +++++++++++++++--- eng/_core/archive/writer.go | 40 ++++-- eng/_core/cmd/build/build.go | 45 ++++-- eng/_core/cmd/pack/pack.go | 5 + eng/pipeline/jobs/builders-to-jobs.yml | 2 +- eng/pipeline/jobs/go-builder-matrix-jobs.yml | 1 + eng/pipeline/jobs/run-job.yml | 13 +- .../jobs/shorthand-builders-to-builders.yml | 7 +- eng/run.ps1 | 16 ++- eng/utilities.ps1 | 23 +++- 10 files changed, 227 insertions(+), 54 deletions(-) diff --git a/eng/_core/archive/archive.go b/eng/_core/archive/archive.go index 213c874e5ed..e514db511a2 100644 --- a/eng/_core/archive/archive.go +++ b/eng/_core/archive/archive.go @@ -67,18 +67,14 @@ func CreateFromSource(source string, output string) error { // The inclusion of some files depends on the OS/ARCH. If output is specified, its filename must // follow the pattern "*{GOOS}-{GOARCH}{extension}" so OS and ARCH can be detected. If output is not // specified, the current Go runtime's OS and ARCH are used. -func CreateFromBuild(source string, output string) error { +// +// If runtime.GOOS and runtime.GOARCH don't match the OS/ARCH of the output file name, the build +// directory is treated as a cross-compiled build of Go. +func CreateFromBuild(source, output string) error { fmt.Printf("---- Creating Go archive (zip/tarball) from '%v'...\n", source) if output == "" { - archiveVersion := getBuildID() - archiveExtension := ".tar.gz" - if runtime.GOOS == "windows" { - archiveExtension = ".zip" - } - - archiveName := fmt.Sprintf("go.%v.%v-%v%v", archiveVersion, runtime.GOOS, runtime.GOARCH, archiveExtension) - output = filepath.Join(getBinDir(source), archiveName) + output = DefaultBuildOutputPath(source, "", "") } // Ensure the target directory exists. @@ -111,6 +107,22 @@ func CreateFromBuild(source string, output string) error { filepath.Join("pkg", "tool", os+"_"+arch, "api.exe"), } + hostOS := runtime.GOOS + hostArch := runtime.GOARCH + if hostOS != os || hostArch != arch { + fmt.Printf("Handling cross-compile: %v-%v host to %v-%v target\n", hostOS, hostArch, os, arch) + skipPaths = append(skipPaths, []string{ + // Don't include binaries that were built for the host toolchain. + filepath.Join("pkg", hostOS+"_"+hostArch), + filepath.Join("pkg", "tool", hostOS+"_"+hostArch), + // Don't include the host binaries: the target binaries are in a subdir. + filepath.Join("bin", "go"), + filepath.Join("bin", "go.exe"), + filepath.Join("bin", "gofmt"), + filepath.Join("bin", "gofmt.exe"), + }...) + } + // Figure out what the race detection syso (precompiled binary) is named for the current // os/arch. We want to exclude all race syso files other than this one. targetRuntimeRaceSyso := fmt.Sprintf("race_%v_%v.syso", os, arch) @@ -119,7 +131,13 @@ func CreateFromBuild(source string, output string) error { // data the script has processed to avoid appearing unresponsive. lastProgressUpdate := time.Now() - filepath.WalkDir(source, func(path string, info fs.DirEntry, err error) error { + // Keep track of target paths added to the archive (key) and the source path that it comes from + // (value). This map ensures no target files are double-added. + addedPaths := make(map[string]string) + // Keep track of dir entries that have been added. + addedDirs := make(map[string]struct{}) + + err := filepath.WalkDir(source, func(path string, info fs.DirEntry, err error) error { if err != nil { fmt.Printf("Failure accessing a path %q: %v\n", path, err) return err @@ -127,9 +145,12 @@ func CreateFromBuild(source string, output string) error { relPath, err := filepath.Rel(source, path) if err != nil { - panic(err) + return err } + // targetPath is where relPath should be placed in the output archive, if it belongs inside. + targetPath := relPath + // Walk every dir/file in the root of the repository. if relPath == "." { // Ignore the rest of the logic in this func by returning nil early. @@ -168,6 +189,11 @@ func CreateFromBuild(source string, output string) error { } } + // Include "bin/{OS}_{ARCH}/go" as "bin/go" if this is a cross-compilation build. + if filepath.Dir(relPath) == filepath.Join("bin", os+"_"+arch) { + targetPath = filepath.Join("bin", filepath.Base(relPath)) + } + // Skip race detection syso file if it doesn't match the target runtime. // // Ignore error: the only possible error is one that says the pattern is invalid (see @@ -181,16 +207,52 @@ func CreateFromBuild(source string, output string) error { if info.IsDir() { // We want to continue the recursive search in this directory for more files, but we - // don't need to add it to the archive. Return nil to continue. + // don't necessarily want to add this dir to the archive. Return nil to continue. return nil } - // At this point, we know "path" is a file that should be included. Add it. - archiver.AddFile( - path, - // Store everything in a root "go" directory to match upstream Go archives. - filepath.Join("go", relPath), - ) + if relPath != targetPath { + fmt.Printf("Archiving %#q as %#q\n", relPath, targetPath) + } + + if otherSource, ok := addedPaths[targetPath]; ok { + return fmt.Errorf( + "adding archive file %#q from %#q, but target already added from %#q", + targetPath, relPath, otherSource) + } + addedPaths[targetPath] = relPath + + // At this point, we know "path" is a file that should be included. Add it. Store everything + // in a root "go" directory to match upstream Go archives. + goTargetPath := filepath.Join("go", targetPath) + if err := archiver.AddFile(path, goTargetPath); err != nil { + return err + } + + // Add all dirs that are ancestors of this target file. Even though explicitly added dirs + // aren't necessary to create a valid archive file, upstream does this, so we do too. This + // reduces the diff, e.g. when comparing results with tools like "tar -tf". + dir := goTargetPath + for { + dir = filepath.Dir(dir) + if dir == "." { + break + } + if dir == "/" { + return fmt.Errorf("unexpected rooted target path: %#q", goTargetPath) + } + + if _, ok := addedDirs[dir]; ok { + break + } + // Use root repository dir as a stand-in for any filesystem information. This is simpler + // than reproducing the actual dir's path based on the target path, especially + // considering the target path may not match up with a directory that actually exists. + if err := archiver.AddFile(source, dir); err != nil { + return err + } + addedDirs[dir] = struct{}{} + } // If it's been long enough, log an update on our progress. now := time.Now() @@ -204,6 +266,9 @@ func CreateFromBuild(source string, output string) error { return nil }) + if err != nil { + return err + } fmt.Printf( "Complete! %v (%v kB uncompressed data archived)\n", @@ -224,6 +289,31 @@ func CreateFromBuild(source string, output string) error { return nil } +// DefaultBuildOutputPath returns the default path to place the output archive given a built Go +// directory. Optionally takes os and arch, or detects their values from the environment and runtime +// if empty string. +func DefaultBuildOutputPath(source, os, arch string) string { + if os == "" { + os = runtime.GOOS + } + if arch == "" { + arch = runtime.GOARCH + } + // Add "v6l" suffix to "arm" arch. More robust handling would be necessary if there were + // multiple "arm" builds with GOARM=6 and GOARM=7, but there are not, and "arm64" obsoletes it. + if arch == "arm" { + arch += "v6l" + } + + ext := ".tar.gz" + if runtime.GOOS == "windows" { + ext = ".zip" + } + + archiveName := fmt.Sprintf("go.%v.%v-%v%v", getBuildID(), os, arch, ext) + return filepath.Join(getBinDir(source), archiveName) +} + // getBuildID returns BUILD_BUILDNUMBER if defined (e.g. a CI build). Otherwise, "dev". func getBuildID() string { archiveVersion := os.Getenv("BUILD_BUILDNUMBER") @@ -244,7 +334,8 @@ func getArchivePathRuntime(path string, ext string) (os string, arch string) { pathNoExt := path[0 : len(path)-len(ext)] firstRuntimeIndex := strings.LastIndex(pathNoExt, ".") + 1 osArch := strings.Split(pathNoExt[firstRuntimeIndex:], "-") - return osArch[0], osArch[1] + // "v6l" is added to the end of the "arm" arch filename, but is not part of the arch. Remove it. + return osArch[0], strings.TrimSuffix(osArch[1], "v6l") } // writeSHA256ChecksumFile reads the content of the file at the given path into a SHA256 hasher, and diff --git a/eng/_core/archive/writer.go b/eng/_core/archive/writer.go index fd2c55d5be1..3596ebdd0ce 100644 --- a/eng/_core/archive/writer.go +++ b/eng/_core/archive/writer.go @@ -88,14 +88,20 @@ func (a *tarGzArchiver) AddFile(filePath string, archivePath string) error { // tar.FileInfoHeader only takes base name, so set full path here. See FileInfoHeader doc. header.Name = archivePath + if stat.IsDir() { + header.Name += "/" + } if err := a.tarWriter.WriteHeader(header); err != nil { return err } - n, err := io.Copy(a.tarWriter, fileReader) - a.processedBytes += n - return err + if !stat.IsDir() { + n, err := io.Copy(a.tarWriter, fileReader) + a.processedBytes += n + return err + } + return nil } func (a *tarGzArchiver) Close() error { @@ -129,20 +135,36 @@ func newZipArchiver(path string) *zipArchiver { } func (a *zipArchiver) AddFile(filePath string, archivePath string) error { - archiveFileWriter, err := a.writer.Create(archivePath) + fileReader, err := os.Open(filePath) if err != nil { return err } + defer fileReader.Close() - fileReader, err := os.Open(filePath) + stat, err := fileReader.Stat() if err != nil { return err } - defer fileReader.Close() - n, err := io.Copy(archiveFileWriter, fileReader) - a.processedBytes += n - return err + // Upstream Go uses "/" for archive dir separators, even on Windows. + archivePath = filepath.ToSlash(archivePath) + + // Give dirs a trailing forward slash to indicate to the zip writer that it's a dir. + if stat.IsDir() { + archivePath += "/" + } + + archiveFileWriter, err := a.writer.Create(archivePath) + if err != nil { + return err + } + + if !stat.IsDir() { + n, err := io.Copy(archiveFileWriter, fileReader) + a.processedBytes += n + return err + } + return nil } func (a *zipArchiver) Close() error { diff --git a/eng/_core/cmd/build/build.go b/eng/_core/cmd/build/build.go index 0d7b7366e0b..c8baa51f79f 100644 --- a/eng/_core/cmd/build/build.go +++ b/eng/_core/cmd/build/build.go @@ -112,6 +112,19 @@ func build(o *options) error { } } + // Get the target platform information. If the environment variable is different from the + // runtime value, this means we're doing a cross-compiled build. These values are used for + // capability checks and to make sure that if Pack is enabled, the output archive is formatted + // correctly and uses the right filename. + targetOS, err := getEnvOrDefault("GOOS", runtime.GOOS) + if err != nil { + return err + } + targetArch, err := getEnvOrDefault("GOARCH", runtime.GOARCH) + if err != nil { + return err + } + // The upstream build scripts in {repo-root}/src require your working directory to be src, or // they instantly fail. Change the current process dir so that we can run them. if err := os.Chdir("go/src"); err != nil { @@ -149,7 +162,10 @@ func build(o *options) error { return err } - if os.Getenv("CGO_ENABLED") != "0" { + // The race runtime requires cgo. + // It isn't supported on arm. + // It's supported on arm64, but the official linux-arm64 distribution doesn't include it. + if os.Getenv("CGO_ENABLED") != "0" && targetArch != "arm" && targetArch != "arm64" { fmt.Println("---- Building race runtime...") err := runCommandLine( filepath.Join("..", "bin", "go"+executableExtension), @@ -217,7 +233,8 @@ func build(o *options) error { if o.Pack { goRootDir := filepath.Join(rootDir, "go") - if err := archive.CreateFromBuild(goRootDir, ""); err != nil { + output := archive.DefaultBuildOutputPath(goRootDir, targetOS, targetArch) + if err := archive.CreateFromBuild(goRootDir, output); err != nil { return err } } @@ -271,14 +288,9 @@ func getMaxAttemptsOrExit(varName string, defaultValue int) int { } func getEnvIntOrDefault(varName string, defaultValue int) (int, error) { - a, ok := os.LookupEnv(varName) - if !ok { - return defaultValue, nil - } - if a == "" { - return 0, fmt.Errorf( - "env var %q is set to empty string, which is not an int. To use the default value %v, unset the env var", - varName, defaultValue) + a, err := getEnvOrDefault(varName, strconv.Itoa(defaultValue)) + if err != nil { + return 0, err } i, err := strconv.Atoi(a) if err != nil { @@ -286,3 +298,16 @@ func getEnvIntOrDefault(varName string, defaultValue int) (int, error) { } return i, nil } + +func getEnvOrDefault(varName, defaultValue string) (string, error) { + v, ok := os.LookupEnv(varName) + if !ok { + return defaultValue, nil + } + if v == "" { + return "", fmt.Errorf( + "env var %q is empty, not a valid string. To use the default string %v, unset the env var", + varName, defaultValue) + } + return v, nil +} diff --git a/eng/_core/cmd/pack/pack.go b/eng/_core/cmd/pack/pack.go index 0fc9cdbb103..ed106033aa3 100644 --- a/eng/_core/cmd/pack/pack.go +++ b/eng/_core/cmd/pack/pack.go @@ -16,6 +16,11 @@ import ( const description = ` This command packs a built Go directory into an archive file and produces a checksum file for the archive. It filters out the files that aren't necessary. + +Pack does not support packing cross-compiled Go directories. Use the "-pack" +argument with the build command for this, instead. The Pack command is intended +to repackage an extracted Go archive that was already in the correct format. +To re-run pack quickly on a cross-compiled build, use "build -skipbuild -pack". ` func main() { diff --git a/eng/pipeline/jobs/builders-to-jobs.yml b/eng/pipeline/jobs/builders-to-jobs.yml index 05d7a751e9a..fe2eef7a07f 100644 --- a/eng/pipeline/jobs/builders-to-jobs.yml +++ b/eng/pipeline/jobs/builders-to-jobs.yml @@ -5,7 +5,7 @@ # This template expands a list of builders into a list of jobs. parameters: - # [] of { id, os, arch, config, distro? } + # [] of { id, os, arch, hostarch, config, distro? } builders: [] # If true, include a signing job that depends on all 'buildandpack' builder jobs finishing. This # lets us start the lengthy tasks of signing and testing in parallel. diff --git a/eng/pipeline/jobs/go-builder-matrix-jobs.yml b/eng/pipeline/jobs/go-builder-matrix-jobs.yml index e7e600aa48f..c8414a75e1b 100644 --- a/eng/pipeline/jobs/go-builder-matrix-jobs.yml +++ b/eng/pipeline/jobs/go-builder-matrix-jobs.yml @@ -27,6 +27,7 @@ jobs: - { os: windows, arch: amd64, config: buildandpack } - { os: windows, arch: amd64, config: devscript } - { os: windows, arch: amd64, config: test } + - { os: linux, arch: arm, hostArch: amd64, config: buildandpack } # Only build arm64 if we're running a signed (internal, rolling) build. Avoid contention # with other projects' builds that use the same limited-capacity pool of arm64 agents. - ${{ if eq(parameters.sign, true) }}: diff --git a/eng/pipeline/jobs/run-job.yml b/eng/pipeline/jobs/run-job.yml index 4fa209632ce..a082ecede6e 100644 --- a/eng/pipeline/jobs/run-job.yml +++ b/eng/pipeline/jobs/run-job.yml @@ -5,7 +5,7 @@ # This job runs a builder for any OS. parameters: - # { id, os, arch, config, distro? } + # { id, os, arch, hostArch, config, distro? } builder: {} createSourceArchive: false @@ -19,7 +19,7 @@ jobs: vmImage: windows-2019 ${{ if eq(parameters.builder.os, 'linux') }}: - ${{ if eq(parameters.builder.arch, 'amd64') }}: + ${{ if eq(parameters.builder.hostArch, 'amd64') }}: pool: # The VM image of the Docker host. This doesn't need to match the container image, but it may # give slightly better coverage by matching the kernel version. @@ -30,7 +30,7 @@ jobs: container: golangpublicimages.azurecr.io/go-infra-images/prereqs:cbl-mariner-1.0.20211027-20211201-0cccc22 ${{ if eq(parameters.builder.distro, 'ubuntu') }}: container: mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-20211022152710-047508b - ${{ if eq(parameters.builder.arch, 'arm64') }}: + ${{ if eq(parameters.builder.hostArch, 'arm64') }}: pool: name: Docker-Linux-Arm-Internal ${{ if not(parameters.builder.distro) }}: @@ -86,6 +86,13 @@ jobs: eng/run.ps1 submodule-refresh displayName: Apply patches + - ${{ if ne(parameters.builder.hostArch, parameters.builder.arch) }}: + - pwsh: Write-Host "##vso[task.setvariable variable=GOARCH]${{ parameters.builder.arch }}" + displayName: Set GOARCH for cross-compile + - ${{ if eq(parameters.builder.arch, 'arm') }}: + - pwsh: Write-Host "##vso[task.setvariable variable=GOARM]6" + displayName: Set GOARM for cross-compile + # Use build script directly for "buildandpack". If we used run-builder, we would need to # download its external module dependencies. - ${{ if eq(parameters.builder.config, 'buildandpack' ) }}: diff --git a/eng/pipeline/jobs/shorthand-builders-to-builders.yml b/eng/pipeline/jobs/shorthand-builders-to-builders.yml index c1f7fe7baf6..02879766776 100644 --- a/eng/pipeline/jobs/shorthand-builders-to-builders.yml +++ b/eng/pipeline/jobs/shorthand-builders-to-builders.yml @@ -11,11 +11,12 @@ # to be used by template expressions, as of writing. parameters: - # [] of { os, arch, config, distro? } + # [] of { os, arch, hostArch, config, distro? } + # If hostArch is not defined, defaults to the arch value. shorthandBuilders: [] # The inner jobs template to pass the filed-out builders into. # - # It should accept parameter "builders", [] of { id, os, arch, config, distro? } + # It should accept parameter "builders", [] of { id, os, arch, hostArch, config, distro? } jobsTemplate: "" jobsParameters: {} @@ -30,5 +31,7 @@ jobs: id: ${{ builder.os }}_${{ builder.distro }}_${{ builder.arch }}_${{ builder.config }} ${{ if not(builder.distro) }}: id: ${{ builder.os }}_${{ builder.arch }}_${{ builder.config }} + ${{ if not(builder.hostArch) }}: + hostArch: ${{ builder.arch }} diff --git a/eng/run.ps1 b/eng/run.ps1 index 237ab6cc176..a12878de720 100644 --- a/eng/run.ps1 +++ b/eng/run.ps1 @@ -99,12 +99,18 @@ try { # Use a module-local path so Go resolves imports correctly. $module_local_script_path = Join-Path "." "cmd" "$tool" - Write-Host "In '$tool_module', building '$module_local_script_path' -> $tool_output" - & (Join-Path $stage0_goroot "bin" "go") build -o $tool_output $module_local_script_path - if ($LASTEXITCODE) { - Write-Host "Failed to build tool." - exit 1 + # The caller may have passed in GOOS/GOARCH to cross-compile Go. We can't use those values here: + # we need to be able to run the tool on the host, so we must always target the host OS/ARCH. Clear + # out the GOOS/GOARCH values (empty string) to detect host OS/ARCH automatically for the tools. + Invoke-CrossGoBlock "" "" { + Write-Host "In '$tool_module', building '$module_local_script_path' -> $tool_output" + & (Join-Path $stage0_goroot "bin" "go") build -o $tool_output $module_local_script_path + if ($LASTEXITCODE) { + Write-Host "Failed to build tool." + exit 1 + } } + Write-Host "Building done." } finally { Pop-Location diff --git a/eng/utilities.ps1 b/eng/utilities.ps1 index 868b6d6b913..c09bef990c3 100644 --- a/eng/utilities.ps1 +++ b/eng/utilities.ps1 @@ -24,17 +24,17 @@ function Get-Stage0GoRoot() { # pre-installed. This CI script installs a consistent, official version of Go to a directory in # $HOME to handle this. This also makes it easier to locally repro issues in CI that involve a # specific version of Go. The downloaded copy of Go is called the "stage 0" version. - $stage0_go_version = '1.17.8' + $stage0_go_version = '1.18' $proc_arch = ([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture).ToString().ToLowerInvariant() if ($IsWindows) { switch ($proc_arch) { 'x64' { - $stage0_go_sha256 = '85ccf2608dca6ea9a46b6538c9e75e7cf2aebdf502379843b248e26b8bb110be' + $stage0_go_sha256 = '65c5c0c709a7ca1b357091b10b795b439d8b50e579d3893edab4c7e9b384f435' $stage0_go_suffix = 'windows-amd64.zip' } 'arm64' { - $stage0_go_sha256 = '4a0d960f5c0cbff1edaf54f333cf857a2779f6ae4c8e759b7872b44fde5ae43f' + $stage0_go_sha256 = '1c454eb60c64d481965a165c623ff1ed6cf32d68c6b31f36069c8768d908f093' $stage0_go_suffix = 'windows-arm64.zip' } Default { throw "Unable to match Windows '$proc_arch' to an architecture supported by the Microsoft scripts to build Go." } @@ -42,11 +42,11 @@ function Get-Stage0GoRoot() { } elseif ($IsLinux) { switch ($proc_arch) { 'x64' { - $stage0_go_sha256 = '980e65a863377e69fd9b67df9d8395fd8e93858e7a24c9f55803421e453f4f99' + $stage0_go_sha256 = 'e85278e98f57cdb150fe8409e6e5df5343ecb13cebf03a5d5ff12bd55a80264f' $stage0_go_suffix = 'linux-amd64.tar.gz' } 'arm64' { - $stage0_go_sha256 = '57a9171682e297df1a5bd287be056ed0280195ad079af90af16dcad4f64710cb' + $stage0_go_sha256 = '7ac7b396a691e588c5fb57687759e6c4db84a2a3bbebb0765f4b38e5b1c5b00e' $stage0_go_suffix = 'linux-arm64.tar.gz' } Default { throw "Unable to match Linux '$proc_arch' to an architecture supported by the Microsoft scripts to build Go." } @@ -126,6 +126,19 @@ function Invoke-WithRetry([ScriptBlock]$ScriptBlock, [int]$MaxAttempts = 3, [int } } +function Invoke-CrossGoBlock([string] $GOOS, [string] $GOARCH, [ScriptBlock] $block) { + $oldGOOS = $env:GOOS + $oldGOARCH = $env:GOARCH + + $env:GOOS = $GOOS + $env:GOARCH = $GOARCH + + & $block + + $env:GOOS = $oldGOOS + $env:GOARCH = $oldGOARCH +} + # Utility method to unzip a file to a specific path. function Extract-Zip([string] $file, [string] $destination) { Add-Type -AssemblyName System.IO.Compression.FileSystem From f33cc45cd43ebf6cc20d9ef969b855c7adebac4b Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Thu, 14 Apr 2022 14:27:17 -0500 Subject: [PATCH 2/4] Use 'finally' in Invoke-CrossGoBlock --- eng/utilities.ps1 | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/eng/utilities.ps1 b/eng/utilities.ps1 index c09bef990c3..baff8efde17 100644 --- a/eng/utilities.ps1 +++ b/eng/utilities.ps1 @@ -130,13 +130,14 @@ function Invoke-CrossGoBlock([string] $GOOS, [string] $GOARCH, [ScriptBlock] $bl $oldGOOS = $env:GOOS $oldGOARCH = $env:GOARCH - $env:GOOS = $GOOS - $env:GOARCH = $GOARCH - - & $block - - $env:GOOS = $oldGOOS - $env:GOARCH = $oldGOARCH + try { + $env:GOOS = $GOOS + $env:GOARCH = $GOARCH + & $block + } finally { + $env:GOOS = $oldGOOS + $env:GOARCH = $oldGOARCH + } } # Utility method to unzip a file to a specific path. From f3caa08cc001ef755547e0908c836f90285f16a0 Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Thu, 14 Apr 2022 14:39:07 -0500 Subject: [PATCH 3/4] Explain getEnvOrDefault error case in doc comment --- eng/_core/cmd/build/build.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/eng/_core/cmd/build/build.go b/eng/_core/cmd/build/build.go index c8baa51f79f..d9b4701c71f 100644 --- a/eng/_core/cmd/build/build.go +++ b/eng/_core/cmd/build/build.go @@ -299,6 +299,14 @@ func getEnvIntOrDefault(varName string, defaultValue int) (int, error) { return i, nil } +// getEnvOrDefault find an environment variable with name varName and returns its value. If the env +// var is not set, returns defaultValue. +// +// If the env var is found and its value is empty string, returns an error. This can't happen on +// Windows because setting an env var to empty string deletes it. However, on Linux, it is possible. +// It's likely a mistake, so we let the user know what happened with an error. For example, the env +// var might be empty string because it was set by "example=$(someCommand)" and someCommand +// encountered an error and didn't send any output to stdout. func getEnvOrDefault(varName, defaultValue string) (string, error) { v, ok := os.LookupEnv(varName) if !ok { From 8d697a9cb7c7e692e001897b0121956727029181 Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Tue, 19 Apr 2022 18:25:39 -0500 Subject: [PATCH 4/4] Update go-infra for build asset JSON arm/v6 fix --- eng/_util/go.mod | 2 +- eng/_util/go.sum | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/eng/_util/go.mod b/eng/_util/go.mod index dc95b46f4ab..8786d747cd2 100644 --- a/eng/_util/go.mod +++ b/eng/_util/go.mod @@ -7,6 +7,6 @@ module github.com/microsoft/go/_util go 1.16 require ( - github.com/microsoft/go-infra v0.0.0-20220209233812-d528ea99adb8 + github.com/microsoft/go-infra v0.0.0-20220419195018-e437e0d7a6f9 gotest.tools/gotestsum v1.6.5-0.20210515201937-ecb7c6956f6d ) diff --git a/eng/_util/go.sum b/eng/_util/go.sum index fa5aa4d8ca1..4963f26cc4f 100644 --- a/eng/_util/go.sum +++ b/eng/_util/go.sum @@ -4,6 +4,8 @@ github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -15,8 +17,8 @@ github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/microsoft/go-infra v0.0.0-20220209233812-d528ea99adb8 h1:2aNRJlGG6hOhHsQV3/5+udsetpNT24/5eeJBMWOmjDY= -github.com/microsoft/go-infra v0.0.0-20220209233812-d528ea99adb8/go.mod h1:3IVGTm7qFJldQHximiWLg2kYfmugjZMGNHnvUo5Mo5M= +github.com/microsoft/go-infra v0.0.0-20220419195018-e437e0d7a6f9 h1:TztudEX3sCZvuB9ZrKclZtxNRRE87GF5v7/U7DsLOVo= +github.com/microsoft/go-infra v0.0.0-20220419195018-e437e0d7a6f9/go.mod h1:eGPzRx36eMvtr8J3LLHYyP720NNE6kWLn78BcK/APOs= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=