Initial commit: Go 1.23 release state

This commit is contained in:
Vorapol Rinsatitnon
2024-09-21 23:49:08 +10:00
commit 17cd57a668
13231 changed files with 3114330 additions and 0 deletions

21
src/cmd/dist/README vendored Normal file
View File

@@ -0,0 +1,21 @@
This program, dist, is the bootstrapping tool for the Go distribution.
As of Go 1.5, dist and other parts of the compiler toolchain are written
in Go, making bootstrapping a little more involved than in the past.
The approach is to build the current release of Go with an earlier one.
The process to install Go 1.x, for x ≥ 22, is:
1. Build cmd/dist with Go 1.20.6.
2. Using dist, build Go 1.x compiler toolchain with Go 1.20.6.
3. Using dist, rebuild Go 1.x compiler toolchain with itself.
4. Using dist, build Go 1.x cmd/go (as go_bootstrap) with Go 1.x compiler toolchain.
5. Using go_bootstrap, build the remaining Go 1.x standard library and commands.
Because of backward compatibility, although the steps above say Go 1.20.6,
in practice any release ≥ Go 1.20.6 but < Go 1.x will work as the bootstrap base.
Releases ≥ Go 1.x are very likely to work as well.
See https://go.dev/s/go15bootstrap for more details about the original bootstrap
and https://go.dev/issue/54265 for details about later bootstrap version bumps.

1980
src/cmd/dist/build.go vendored Normal file

File diff suppressed because it is too large Load Diff

26
src/cmd/dist/build_test.go vendored Normal file
View File

@@ -0,0 +1,26 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"internal/platform"
"testing"
)
// TestMustLinkExternal verifies that the mustLinkExternal helper
// function matches internal/platform.MustLinkExternal.
func TestMustLinkExternal(t *testing.T) {
for _, goos := range okgoos {
for _, goarch := range okgoarch {
for _, cgoEnabled := range []bool{true, false} {
got := mustLinkExternal(goos, goarch, cgoEnabled)
want := platform.MustLinkExternal(goos, goarch, cgoEnabled)
if got != want {
t.Errorf("mustLinkExternal(%q, %q, %v) = %v; want %v", goos, goarch, cgoEnabled, got, want)
}
}
}
}
}

162
src/cmd/dist/buildgo.go vendored Normal file
View File

@@ -0,0 +1,162 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
)
/*
* Helpers for building cmd/go and cmd/cgo.
*/
// generatedHeader is the string that all source files generated by dist start with.
//
// DO NOT CHANGE THIS STRING. If this string is changed then during
//
// ./make.bash
// git checkout other-rev
// ./make.bash
//
// the second make.bash will not find the files generated by the first make.bash
// and will not clean up properly.
const generatedHeader = "// Code generated by go tool dist; DO NOT EDIT.\n\n"
// writeHeader emits the standard "generated by" header for all files generated
// by dist.
func writeHeader(w io.Writer) {
fmt.Fprint(w, generatedHeader)
}
// mkzdefaultcc writes zdefaultcc.go:
//
// package main
// const defaultCC = <defaultcc>
// const defaultCXX = <defaultcxx>
// const defaultPkgConfig = <defaultpkgconfig>
//
// It is invoked to write cmd/go/internal/cfg/zdefaultcc.go
// but we also write cmd/cgo/zdefaultcc.go
func mkzdefaultcc(dir, file string) {
if strings.Contains(file, filepath.FromSlash("go/internal/cfg")) {
var buf strings.Builder
writeHeader(&buf)
fmt.Fprintf(&buf, "package cfg\n")
fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "const DefaultPkgConfig = `%s`\n", defaultpkgconfig)
buf.WriteString(defaultCCFunc("DefaultCC", defaultcc))
buf.WriteString(defaultCCFunc("DefaultCXX", defaultcxx))
writefile(buf.String(), file, writeSkipSame)
return
}
var buf strings.Builder
writeHeader(&buf)
fmt.Fprintf(&buf, "package main\n")
fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "const defaultPkgConfig = `%s`\n", defaultpkgconfig)
buf.WriteString(defaultCCFunc("defaultCC", defaultcc))
buf.WriteString(defaultCCFunc("defaultCXX", defaultcxx))
writefile(buf.String(), file, writeSkipSame)
}
func defaultCCFunc(name string, defaultcc map[string]string) string {
var buf strings.Builder
fmt.Fprintf(&buf, "func %s(goos, goarch string) string {\n", name)
fmt.Fprintf(&buf, "\tswitch goos+`/`+goarch {\n")
var keys []string
for k := range defaultcc {
if k != "" {
keys = append(keys, k)
}
}
sort.Strings(keys)
for _, k := range keys {
fmt.Fprintf(&buf, "\tcase %s:\n\t\treturn %s\n", quote(k), quote(defaultcc[k]))
}
fmt.Fprintf(&buf, "\t}\n")
if cc := defaultcc[""]; cc != "" {
fmt.Fprintf(&buf, "\treturn %s\n", quote(cc))
} else {
clang, gcc := "clang", "gcc"
if strings.HasSuffix(name, "CXX") {
clang, gcc = "clang++", "g++"
}
fmt.Fprintf(&buf, "\tswitch goos {\n")
fmt.Fprintf(&buf, "\tcase ")
for i, os := range clangos {
if i > 0 {
fmt.Fprintf(&buf, ", ")
}
fmt.Fprintf(&buf, "%s", quote(os))
}
fmt.Fprintf(&buf, ":\n")
fmt.Fprintf(&buf, "\t\treturn %s\n", quote(clang))
fmt.Fprintf(&buf, "\t}\n")
fmt.Fprintf(&buf, "\treturn %s\n", quote(gcc))
}
fmt.Fprintf(&buf, "}\n")
return buf.String()
}
// mkzcgo writes zcgo.go for the go/build package:
//
// package build
// const defaultCGO_ENABLED = <CGO_ENABLED>
//
// It is invoked to write go/build/zcgo.go.
func mkzcgo(dir, file string) {
var buf strings.Builder
writeHeader(&buf)
fmt.Fprintf(&buf, "package build\n")
fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "const defaultCGO_ENABLED = %s\n", quote(os.Getenv("CGO_ENABLED")))
writefile(buf.String(), file, writeSkipSame)
}
// mktzdata src/time/tzdata/zzipdata.go:
//
// package tzdata
// const zipdata = "PK..."
func mktzdata(dir, file string) {
zip := readfile(filepath.Join(dir, "../../../lib/time/zoneinfo.zip"))
var buf strings.Builder
writeHeader(&buf)
fmt.Fprintf(&buf, "package tzdata\n")
fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "const zipdata = %s\n", quote(zip))
writefile(buf.String(), file, writeSkipSame)
}
// quote is like strconv.Quote but simpler and has output
// that does not depend on the exact Go bootstrap version.
func quote(s string) string {
const hex = "0123456789abcdef"
var out strings.Builder
out.WriteByte('"')
for i := 0; i < len(s); i++ {
c := s[i]
if 0x20 <= c && c <= 0x7E && c != '"' && c != '\\' {
out.WriteByte(c)
} else {
out.WriteByte('\\')
out.WriteByte('x')
out.WriteByte(hex[c>>4])
out.WriteByte(hex[c&0xf])
}
}
out.WriteByte('"')
return out.String()
}

83
src/cmd/dist/buildruntime.go vendored Normal file
View File

@@ -0,0 +1,83 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"strings"
)
/*
* Helpers for building runtime.
*/
// mkzversion writes zversion.go:
//
// package sys
//
// (Nothing right now!)
func mkzversion(dir, file string) {
var buf strings.Builder
writeHeader(&buf)
fmt.Fprintf(&buf, "package sys\n")
writefile(buf.String(), file, writeSkipSame)
}
// mkbuildcfg writes internal/buildcfg/zbootstrap.go:
//
// package buildcfg
//
// const defaultGOROOT = <goroot>
// const defaultGO386 = <go386>
// ...
// const defaultGOOS = runtime.GOOS
// const defaultGOARCH = runtime.GOARCH
//
// The use of runtime.GOOS and runtime.GOARCH makes sure that
// a cross-compiled compiler expects to compile for its own target
// system. That is, if on a Mac you do:
//
// GOOS=linux GOARCH=ppc64 go build cmd/compile
//
// the resulting compiler will default to generating linux/ppc64 object files.
// This is more useful than having it default to generating objects for the
// original target (in this example, a Mac).
func mkbuildcfg(file string) {
var buf strings.Builder
writeHeader(&buf)
fmt.Fprintf(&buf, "package buildcfg\n")
fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "import \"runtime\"\n")
fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386)
fmt.Fprintf(&buf, "const defaultGOAMD64 = `%s`\n", goamd64)
fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm)
fmt.Fprintf(&buf, "const defaultGOARM64 = `%s`\n", goarm64)
fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips)
fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64)
fmt.Fprintf(&buf, "const defaultGOPPC64 = `%s`\n", goppc64)
fmt.Fprintf(&buf, "const defaultGORISCV64 = `%s`\n", goriscv64)
fmt.Fprintf(&buf, "const defaultGOEXPERIMENT = `%s`\n", goexperiment)
fmt.Fprintf(&buf, "const defaultGO_EXTLINK_ENABLED = `%s`\n", goextlinkenabled)
fmt.Fprintf(&buf, "const defaultGO_LDSO = `%s`\n", defaultldso)
fmt.Fprintf(&buf, "const version = `%s`\n", findgoversion())
fmt.Fprintf(&buf, "const defaultGOOS = runtime.GOOS\n")
fmt.Fprintf(&buf, "const defaultGOARCH = runtime.GOARCH\n")
writefile(buf.String(), file, writeSkipSame)
}
// mkobjabi writes cmd/internal/objabi/zbootstrap.go:
//
// package objabi
//
// (Nothing right now!)
func mkobjabi(file string) {
var buf strings.Builder
writeHeader(&buf)
fmt.Fprintf(&buf, "package objabi\n")
writefile(buf.String(), file, writeSkipSame)
}

133
src/cmd/dist/buildtag.go vendored Normal file
View File

@@ -0,0 +1,133 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"strings"
)
// exprParser is a //go:build expression parser and evaluator.
// The parser is a trivial precedence-based parser which is still
// almost overkill for these very simple expressions.
type exprParser struct {
x string
t exprToken // upcoming token
}
// val is the value type result of parsing.
// We don't keep a parse tree, just the value of the expression.
type val bool
// exprToken describes a single token in the input.
// Prefix operators define a prefix func that parses the
// upcoming value. Binary operators define an infix func
// that combines two values according to the operator.
// In that case, the parsing loop parses the two values.
type exprToken struct {
tok string
prec int
prefix func(*exprParser) val
infix func(val, val) val
}
var exprTokens []exprToken
func init() { // init to break init cycle
exprTokens = []exprToken{
{tok: "&&", prec: 1, infix: func(x, y val) val { return x && y }},
{tok: "||", prec: 2, infix: func(x, y val) val { return x || y }},
{tok: "!", prec: 3, prefix: (*exprParser).not},
{tok: "(", prec: 3, prefix: (*exprParser).paren},
{tok: ")"},
}
}
// matchexpr parses and evaluates the //go:build expression x.
func matchexpr(x string) (matched bool, err error) {
defer func() {
if e := recover(); e != nil {
matched = false
err = fmt.Errorf("parsing //go:build line: %v", e)
}
}()
p := &exprParser{x: x}
p.next()
v := p.parse(0)
if p.t.tok != "end of expression" {
panic("unexpected " + p.t.tok)
}
return bool(v), nil
}
// parse parses an expression, including binary operators at precedence >= prec.
func (p *exprParser) parse(prec int) val {
if p.t.prefix == nil {
panic("unexpected " + p.t.tok)
}
v := p.t.prefix(p)
for p.t.prec >= prec && p.t.infix != nil {
t := p.t
p.next()
v = t.infix(v, p.parse(t.prec+1))
}
return v
}
// not is the prefix parser for a ! token.
func (p *exprParser) not() val {
p.next()
return !p.parse(100)
}
// paren is the prefix parser for a ( token.
func (p *exprParser) paren() val {
p.next()
v := p.parse(0)
if p.t.tok != ")" {
panic("missing )")
}
p.next()
return v
}
// next advances the parser to the next token,
// leaving the token in p.t.
func (p *exprParser) next() {
p.x = strings.TrimSpace(p.x)
if p.x == "" {
p.t = exprToken{tok: "end of expression"}
return
}
for _, t := range exprTokens {
if strings.HasPrefix(p.x, t.tok) {
p.x = p.x[len(t.tok):]
p.t = t
return
}
}
i := 0
for i < len(p.x) && validtag(p.x[i]) {
i++
}
if i == 0 {
panic(fmt.Sprintf("syntax error near %#q", rune(p.x[i])))
}
tag := p.x[:i]
p.x = p.x[i:]
p.t = exprToken{
tok: "tag",
prefix: func(p *exprParser) val {
p.next()
return val(matchtag(tag))
},
}
}
func validtag(c byte) bool {
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '.' || c == '_'
}

43
src/cmd/dist/buildtag_test.go vendored Normal file
View File

@@ -0,0 +1,43 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"reflect"
"testing"
)
var buildParserTests = []struct {
x string
matched bool
err error
}{
{"gc", true, nil},
{"gccgo", false, nil},
{"!gc", false, nil},
{"gc && gccgo", false, nil},
{"gc || gccgo", true, nil},
{"gc || (gccgo && !gccgo)", true, nil},
{"gc && (gccgo || !gccgo)", true, nil},
{"!(gc && (gccgo || !gccgo))", false, nil},
{"gccgo || gc", true, nil},
{"!(!(!(gccgo || gc)))", false, nil},
{"compiler_bootstrap", false, nil},
{"cmd_go_bootstrap", true, nil},
{"syntax(error", false, fmt.Errorf("parsing //go:build line: unexpected (")},
{"(gc", false, fmt.Errorf("parsing //go:build line: missing )")},
{"gc gc", false, fmt.Errorf("parsing //go:build line: unexpected tag")},
{"(gc))", false, fmt.Errorf("parsing //go:build line: unexpected )")},
}
func TestBuildParser(t *testing.T) {
for _, tt := range buildParserTests {
matched, err := matchexpr(tt.x)
if matched != tt.matched || !reflect.DeepEqual(err, tt.err) {
t.Errorf("matchexpr(%q) = %v, %v; want %v, %v", tt.x, matched, err, tt.matched, tt.err)
}
}
}

394
src/cmd/dist/buildtool.go vendored Normal file
View File

@@ -0,0 +1,394 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Build toolchain using Go bootstrap version.
//
// The general strategy is to copy the source files we need into
// a new GOPATH workspace, adjust import paths appropriately,
// invoke the Go bootstrap toolchains go command to build those sources,
// and then copy the binaries back.
package main
import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
)
// bootstrapDirs is a list of directories holding code that must be
// compiled with the Go bootstrap toolchain to produce the bootstrapTargets.
// All directories in this list are relative to and must be below $GOROOT/src.
//
// The list has two kinds of entries: names beginning with cmd/ with
// no other slashes, which are commands, and other paths, which are packages
// supporting the commands. Packages in the standard library can be listed
// if a newer copy needs to be substituted for the Go bootstrap copy when used
// by the command packages. Paths ending with /... automatically
// include all packages within subdirectories as well.
// These will be imported during bootstrap as bootstrap/name, like bootstrap/math/big.
var bootstrapDirs = []string{
"cmp",
"cmd/asm",
"cmd/asm/internal/...",
"cmd/cgo",
"cmd/compile",
"cmd/compile/internal/...",
"cmd/internal/archive",
"cmd/internal/bio",
"cmd/internal/codesign",
"cmd/internal/dwarf",
"cmd/internal/edit",
"cmd/internal/gcprog",
"cmd/internal/goobj",
"cmd/internal/notsha256",
"cmd/internal/obj/...",
"cmd/internal/objabi",
"cmd/internal/pgo",
"cmd/internal/pkgpath",
"cmd/internal/quoted",
"cmd/internal/src",
"cmd/internal/sys",
"cmd/internal/telemetry",
"cmd/internal/telemetry/counter",
"cmd/link",
"cmd/link/internal/...",
"compress/flate",
"compress/zlib",
"container/heap",
"debug/dwarf",
"debug/elf",
"debug/macho",
"debug/pe",
"go/build/constraint",
"go/constant",
"go/version",
"internal/abi",
"internal/coverage",
"cmd/internal/cov/covcmd",
"internal/bisect",
"internal/buildcfg",
"internal/goarch",
"internal/godebugs",
"internal/goexperiment",
"internal/goroot",
"internal/gover",
"internal/goversion",
// internal/lazyregexp is provided by Go 1.17, which permits it to
// be imported by other packages in this list, but is not provided
// by the Go 1.17 version of gccgo. It's on this list only to
// support gccgo, and can be removed if we require gccgo 14 or later.
"internal/lazyregexp",
"internal/pkgbits",
"internal/platform",
"internal/profile",
"internal/race",
"internal/saferio",
"internal/syscall/unix",
"internal/types/errors",
"internal/unsafeheader",
"internal/xcoff",
"internal/zstd",
"math/bits",
"sort",
}
// File prefixes that are ignored by go/build anyway, and cause
// problems with editor generated temporary files (#18931).
var ignorePrefixes = []string{
".",
"_",
"#",
}
// File suffixes that use build tags introduced since Go 1.17.
// These must not be copied into the bootstrap build directory.
// Also ignore test files.
var ignoreSuffixes = []string{
"_test.s",
"_test.go",
// Skip PGO profile. No need to build toolchain1 compiler
// with PGO. And as it is not a text file the import path
// rewrite will break it.
".pgo",
// Skip editor backup files.
"~",
}
var tryDirs = []string{
"sdk/go1.17",
"go1.17",
}
func bootstrapBuildTools() {
goroot_bootstrap := os.Getenv("GOROOT_BOOTSTRAP")
if goroot_bootstrap == "" {
home := os.Getenv("HOME")
goroot_bootstrap = pathf("%s/go1.4", home)
for _, d := range tryDirs {
if p := pathf("%s/%s", home, d); isdir(p) {
goroot_bootstrap = p
}
}
}
xprintf("Building Go toolchain1 using %s.\n", goroot_bootstrap)
mkbuildcfg(pathf("%s/src/internal/buildcfg/zbootstrap.go", goroot))
mkobjabi(pathf("%s/src/cmd/internal/objabi/zbootstrap.go", goroot))
// Use $GOROOT/pkg/bootstrap as the bootstrap workspace root.
// We use a subdirectory of $GOROOT/pkg because that's the
// space within $GOROOT where we store all generated objects.
// We could use a temporary directory outside $GOROOT instead,
// but it is easier to debug on failure if the files are in a known location.
workspace := pathf("%s/pkg/bootstrap", goroot)
xremoveall(workspace)
xatexit(func() { xremoveall(workspace) })
base := pathf("%s/src/bootstrap", workspace)
xmkdirall(base)
// Copy source code into $GOROOT/pkg/bootstrap and rewrite import paths.
writefile("module bootstrap\ngo 1.20\n", pathf("%s/%s", base, "go.mod"), 0)
for _, dir := range bootstrapDirs {
recurse := strings.HasSuffix(dir, "/...")
dir = strings.TrimSuffix(dir, "/...")
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
fatalf("walking bootstrap dirs failed: %v: %v", path, err)
}
name := filepath.Base(path)
src := pathf("%s/src/%s", goroot, path)
dst := pathf("%s/%s", base, path)
if info.IsDir() {
if !recurse && path != dir || name == "testdata" {
return filepath.SkipDir
}
xmkdirall(dst)
if path == "cmd/cgo" {
// Write to src because we need the file both for bootstrap
// and for later in the main build.
mkzdefaultcc("", pathf("%s/zdefaultcc.go", src))
mkzdefaultcc("", pathf("%s/zdefaultcc.go", dst))
}
return nil
}
for _, pre := range ignorePrefixes {
if strings.HasPrefix(name, pre) {
return nil
}
}
for _, suf := range ignoreSuffixes {
if strings.HasSuffix(name, suf) {
return nil
}
}
text := bootstrapRewriteFile(src)
writefile(text, dst, 0)
return nil
})
}
// Set up environment for invoking Go bootstrap toolchains go command.
// GOROOT points at Go bootstrap GOROOT,
// GOPATH points at our bootstrap workspace,
// GOBIN is empty, so that binaries are installed to GOPATH/bin,
// and GOOS, GOHOSTOS, GOARCH, and GOHOSTOS are empty,
// so that Go bootstrap toolchain builds whatever kind of binary it knows how to build.
// Restore GOROOT, GOPATH, and GOBIN when done.
// Don't bother with GOOS, GOHOSTOS, GOARCH, and GOHOSTARCH,
// because setup will take care of those when bootstrapBuildTools returns.
defer os.Setenv("GOROOT", os.Getenv("GOROOT"))
os.Setenv("GOROOT", goroot_bootstrap)
defer os.Setenv("GOPATH", os.Getenv("GOPATH"))
os.Setenv("GOPATH", workspace)
defer os.Setenv("GOBIN", os.Getenv("GOBIN"))
os.Setenv("GOBIN", "")
os.Setenv("GOOS", "")
os.Setenv("GOHOSTOS", "")
os.Setenv("GOARCH", "")
os.Setenv("GOHOSTARCH", "")
// Run Go bootstrap to build binaries.
// Use the math_big_pure_go build tag to disable the assembly in math/big
// which may contain unsupported instructions.
// Use the purego build tag to disable other assembly code,
// such as in cmd/internal/notsha256.
cmd := []string{
pathf("%s/bin/go", goroot_bootstrap),
"install",
"-tags=math_big_pure_go compiler_bootstrap purego",
}
if vflag > 0 {
cmd = append(cmd, "-v")
}
if tool := os.Getenv("GOBOOTSTRAP_TOOLEXEC"); tool != "" {
cmd = append(cmd, "-toolexec="+tool)
}
cmd = append(cmd, "bootstrap/cmd/...")
run(base, ShowOutput|CheckExit, cmd...)
// Copy binaries into tool binary directory.
for _, name := range bootstrapDirs {
if !strings.HasPrefix(name, "cmd/") {
continue
}
name = name[len("cmd/"):]
if !strings.Contains(name, "/") {
copyfile(pathf("%s/%s%s", tooldir, name, exe), pathf("%s/bin/%s%s", workspace, name, exe), writeExec)
}
}
if vflag > 0 {
xprintf("\n")
}
}
var ssaRewriteFileSubstring = filepath.FromSlash("src/cmd/compile/internal/ssa/rewrite")
// isUnneededSSARewriteFile reports whether srcFile is a
// src/cmd/compile/internal/ssa/rewriteARCHNAME.go file for an
// architecture that isn't for the given GOARCH.
//
// When unneeded is true archCaps is the rewrite base filename without
// the "rewrite" prefix or ".go" suffix: AMD64, 386, ARM, ARM64, etc.
func isUnneededSSARewriteFile(srcFile, goArch string) (archCaps string, unneeded bool) {
if !strings.Contains(srcFile, ssaRewriteFileSubstring) {
return "", false
}
fileArch := strings.TrimSuffix(strings.TrimPrefix(filepath.Base(srcFile), "rewrite"), ".go")
if fileArch == "" {
return "", false
}
b := fileArch[0]
if b == '_' || ('a' <= b && b <= 'z') {
return "", false
}
archCaps = fileArch
fileArch = strings.ToLower(fileArch)
fileArch = strings.TrimSuffix(fileArch, "splitload")
fileArch = strings.TrimSuffix(fileArch, "latelower")
if fileArch == goArch {
return "", false
}
if fileArch == strings.TrimSuffix(goArch, "le") {
return "", false
}
return archCaps, true
}
func bootstrapRewriteFile(srcFile string) string {
// During bootstrap, generate dummy rewrite files for
// irrelevant architectures. We only need to build a bootstrap
// binary that works for the current gohostarch.
// This saves 6+ seconds of bootstrap.
if archCaps, ok := isUnneededSSARewriteFile(srcFile, gohostarch); ok {
return fmt.Sprintf(`%spackage ssa
func rewriteValue%s(v *Value) bool { panic("unused during bootstrap") }
func rewriteBlock%s(b *Block) bool { panic("unused during bootstrap") }
`, generatedHeader, archCaps, archCaps)
}
return bootstrapFixImports(srcFile)
}
var (
importRE = regexp.MustCompile(`\Aimport\s+(\.|[A-Za-z0-9_]+)?\s*"([^"]+)"\s*(//.*)?\n\z`)
importBlockRE = regexp.MustCompile(`\A\s*(?:(\.|[A-Za-z0-9_]+)?\s*"([^"]+)")?\s*(//.*)?\n\z`)
)
func bootstrapFixImports(srcFile string) string {
text := readfile(srcFile)
if !strings.Contains(srcFile, "/cmd/") && !strings.Contains(srcFile, `\cmd\`) {
text = regexp.MustCompile(`\bany\b`).ReplaceAllString(text, "interface{}")
}
lines := strings.SplitAfter(text, "\n")
inBlock := false
inComment := false
for i, line := range lines {
if strings.HasSuffix(line, "*/\n") {
inComment = false
}
if strings.HasSuffix(line, "/*\n") {
inComment = true
}
if inComment {
continue
}
if strings.HasPrefix(line, "import (") {
inBlock = true
continue
}
if inBlock && strings.HasPrefix(line, ")") {
inBlock = false
continue
}
var m []string
if !inBlock {
if !strings.HasPrefix(line, "import ") {
continue
}
m = importRE.FindStringSubmatch(line)
if m == nil {
fatalf("%s:%d: invalid import declaration: %q", srcFile, i+1, line)
}
} else {
m = importBlockRE.FindStringSubmatch(line)
if m == nil {
fatalf("%s:%d: invalid import block line", srcFile, i+1)
}
if m[2] == "" {
continue
}
}
path := m[2]
if strings.HasPrefix(path, "cmd/") {
path = "bootstrap/" + path
} else {
for _, dir := range bootstrapDirs {
if path == dir {
path = "bootstrap/" + dir
break
}
}
}
// Rewrite use of internal/reflectlite to be plain reflect.
if path == "internal/reflectlite" {
lines[i] = strings.ReplaceAll(line, `"reflect"`, `reflectlite "reflect"`)
continue
}
// Otherwise, reject direct imports of internal packages,
// since that implies knowledge of internal details that might
// change from one bootstrap toolchain to the next.
// There are many internal packages that are listed in
// bootstrapDirs and made into bootstrap copies based on the
// current repo's source code. Those are fine; this is catching
// references to internal packages in the older bootstrap toolchain.
if strings.HasPrefix(path, "internal/") {
fatalf("%s:%d: bootstrap-copied source file cannot import %s", srcFile, i+1, path)
}
if path != m[2] {
lines[i] = strings.ReplaceAll(line, `"`+m[2]+`"`, `"`+path+`"`)
}
}
lines[0] = generatedHeader + "// This is a bootstrap copy of " + srcFile + "\n\n//line " + srcFile + ":1\n" + lines[0]
return strings.Join(lines, "")
}

21
src/cmd/dist/doc.go vendored Normal file
View File

@@ -0,0 +1,21 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Dist helps bootstrap, build, and test the Go distribution.
//
// Usage:
//
// go tool dist [command]
//
// The commands are:
//
// banner print installation banner
// bootstrap rebuild everything
// clean deletes all built files
// env [-p] print environment (-p: include $PATH)
// install [dir] install individual directory
// list [-json] list all supported platforms
// test [-h] run Go test(s)
// version print Go version
package main

40
src/cmd/dist/exec.go vendored Normal file
View File

@@ -0,0 +1,40 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"os/exec"
"strings"
)
// setDir sets cmd.Dir to dir, and also adds PWD=dir to cmd's environment.
func setDir(cmd *exec.Cmd, dir string) {
cmd.Dir = dir
if cmd.Env != nil {
// os/exec won't set PWD automatically.
setEnv(cmd, "PWD", dir)
}
}
// setEnv sets cmd.Env so that key = value.
func setEnv(cmd *exec.Cmd, key, value string) {
cmd.Env = append(cmd.Environ(), key+"="+value)
}
// unsetEnv sets cmd.Env so that key is not present in the environment.
func unsetEnv(cmd *exec.Cmd, key string) {
cmd.Env = cmd.Environ()
prefix := key + "="
newEnv := []string{}
for _, entry := range cmd.Env {
if strings.HasPrefix(entry, prefix) {
continue
}
newEnv = append(newEnv, entry)
// key may appear multiple times, so keep going.
}
cmd.Env = newEnv
}

276
src/cmd/dist/imports.go vendored Normal file
View File

@@ -0,0 +1,276 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file is forked from go/build/read.go.
// (cmd/dist must not import go/build because we do not want it to be
// sensitive to the specific version of go/build present in $GOROOT_BOOTSTRAP.)
package main
import (
"bufio"
"errors"
"fmt"
"io"
"path"
"path/filepath"
"strconv"
"strings"
"unicode/utf8"
)
type importReader struct {
b *bufio.Reader
buf []byte
peek byte
err error
eof bool
nerr int
}
func isIdent(c byte) bool {
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf
}
var (
errSyntax = errors.New("syntax error")
errNUL = errors.New("unexpected NUL in input")
)
// syntaxError records a syntax error, but only if an I/O error has not already been recorded.
func (r *importReader) syntaxError() {
if r.err == nil {
r.err = errSyntax
}
}
// readByte reads the next byte from the input, saves it in buf, and returns it.
// If an error occurs, readByte records the error in r.err and returns 0.
func (r *importReader) readByte() byte {
c, err := r.b.ReadByte()
if err == nil {
r.buf = append(r.buf, c)
if c == 0 {
err = errNUL
}
}
if err != nil {
if err == io.EOF {
r.eof = true
} else if r.err == nil {
r.err = err
}
c = 0
}
return c
}
// peekByte returns the next byte from the input reader but does not advance beyond it.
// If skipSpace is set, peekByte skips leading spaces and comments.
func (r *importReader) peekByte(skipSpace bool) byte {
if r.err != nil {
if r.nerr++; r.nerr > 10000 {
panic("go/build: import reader looping")
}
return 0
}
// Use r.peek as first input byte.
// Don't just return r.peek here: it might have been left by peekByte(false)
// and this might be peekByte(true).
c := r.peek
if c == 0 {
c = r.readByte()
}
for r.err == nil && !r.eof {
if skipSpace {
// For the purposes of this reader, semicolons are never necessary to
// understand the input and are treated as spaces.
switch c {
case ' ', '\f', '\t', '\r', '\n', ';':
c = r.readByte()
continue
case '/':
c = r.readByte()
if c == '/' {
for c != '\n' && r.err == nil && !r.eof {
c = r.readByte()
}
} else if c == '*' {
var c1 byte
for (c != '*' || c1 != '/') && r.err == nil {
if r.eof {
r.syntaxError()
}
c, c1 = c1, r.readByte()
}
} else {
r.syntaxError()
}
c = r.readByte()
continue
}
}
break
}
r.peek = c
return r.peek
}
// nextByte is like peekByte but advances beyond the returned byte.
func (r *importReader) nextByte(skipSpace bool) byte {
c := r.peekByte(skipSpace)
r.peek = 0
return c
}
// readKeyword reads the given keyword from the input.
// If the keyword is not present, readKeyword records a syntax error.
func (r *importReader) readKeyword(kw string) {
r.peekByte(true)
for i := 0; i < len(kw); i++ {
if r.nextByte(false) != kw[i] {
r.syntaxError()
return
}
}
if isIdent(r.peekByte(false)) {
r.syntaxError()
}
}
// readIdent reads an identifier from the input.
// If an identifier is not present, readIdent records a syntax error.
func (r *importReader) readIdent() {
c := r.peekByte(true)
if !isIdent(c) {
r.syntaxError()
return
}
for isIdent(r.peekByte(false)) {
r.peek = 0
}
}
// readString reads a quoted string literal from the input.
// If an identifier is not present, readString records a syntax error.
func (r *importReader) readString(save *[]string) {
switch r.nextByte(true) {
case '`':
start := len(r.buf) - 1
for r.err == nil {
if r.nextByte(false) == '`' {
if save != nil {
*save = append(*save, string(r.buf[start:]))
}
break
}
if r.eof {
r.syntaxError()
}
}
case '"':
start := len(r.buf) - 1
for r.err == nil {
c := r.nextByte(false)
if c == '"' {
if save != nil {
*save = append(*save, string(r.buf[start:]))
}
break
}
if r.eof || c == '\n' {
r.syntaxError()
}
if c == '\\' {
r.nextByte(false)
}
}
default:
r.syntaxError()
}
}
// readImport reads an import clause - optional identifier followed by quoted string -
// from the input.
func (r *importReader) readImport(imports *[]string) {
c := r.peekByte(true)
if c == '.' {
r.peek = 0
} else if isIdent(c) {
r.readIdent()
}
r.readString(imports)
}
// readComments is like ioutil.ReadAll, except that it only reads the leading
// block of comments in the file.
func readComments(f io.Reader) ([]byte, error) {
r := &importReader{b: bufio.NewReader(f)}
r.peekByte(true)
if r.err == nil && !r.eof {
// Didn't reach EOF, so must have found a non-space byte. Remove it.
r.buf = r.buf[:len(r.buf)-1]
}
return r.buf, r.err
}
// readimports returns the imports found in the named file.
func readimports(file string) []string {
var imports []string
r := &importReader{b: bufio.NewReader(strings.NewReader(readfile(file)))}
r.readKeyword("package")
r.readIdent()
for r.peekByte(true) == 'i' {
r.readKeyword("import")
if r.peekByte(true) == '(' {
r.nextByte(false)
for r.peekByte(true) != ')' && r.err == nil {
r.readImport(&imports)
}
r.nextByte(false)
} else {
r.readImport(&imports)
}
}
for i := range imports {
unquoted, err := strconv.Unquote(imports[i])
if err != nil {
fatalf("reading imports from %s: %v", file, err)
}
imports[i] = unquoted
}
return imports
}
// resolveVendor returns a unique package path imported with the given import
// path from srcDir.
//
// resolveVendor assumes that a package is vendored if and only if its first
// path component contains a dot. If a package is vendored, its import path
// is returned with a "vendor" or "cmd/vendor" prefix, depending on srcDir.
// Otherwise, the import path is returned verbatim.
func resolveVendor(imp, srcDir string) string {
var first string
if i := strings.Index(imp, "/"); i < 0 {
first = imp
} else {
first = imp[:i]
}
isStandard := !strings.Contains(first, ".")
if isStandard {
return imp
}
if strings.HasPrefix(srcDir, filepath.Join(goroot, "src", "cmd")) {
return path.Join("cmd", "vendor", imp)
} else if strings.HasPrefix(srcDir, filepath.Join(goroot, "src")) {
return path.Join("vendor", imp)
} else {
panic(fmt.Sprintf("srcDir %q not in GOOROT/src", srcDir))
}
}

194
src/cmd/dist/main.go vendored Normal file
View File

@@ -0,0 +1,194 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"flag"
"fmt"
"os"
"runtime"
"strings"
)
func usage() {
xprintf(`usage: go tool dist [command]
Commands are:
banner print installation banner
bootstrap rebuild everything
clean deletes all built files
env [-p] print environment (-p: include $PATH)
install [dir] install individual directory
list [-json] [-broken] list all supported platforms
test [-h] run Go test(s)
version print Go version
All commands take -v flags to emit extra information.
`)
xexit(2)
}
// commands records the available commands.
var commands = map[string]func(){
"banner": cmdbanner,
"bootstrap": cmdbootstrap,
"clean": cmdclean,
"env": cmdenv,
"install": cmdinstall,
"list": cmdlist,
"test": cmdtest,
"version": cmdversion,
}
// main takes care of OS-specific startup and dispatches to xmain.
func main() {
os.Setenv("TERM", "dumb") // disable escape codes in clang errors
// provide -check-armv6k first, before checking for $GOROOT so that
// it is possible to run this check without having $GOROOT available.
if len(os.Args) > 1 && os.Args[1] == "-check-armv6k" {
useARMv6K() // might fail with SIGILL
println("ARMv6K supported.")
os.Exit(0)
}
gohostos = runtime.GOOS
switch gohostos {
case "aix":
// uname -m doesn't work under AIX
gohostarch = "ppc64"
case "plan9":
gohostarch = os.Getenv("objtype")
if gohostarch == "" {
fatalf("$objtype is unset")
}
case "solaris", "illumos":
// Solaris and illumos systems have multi-arch userlands, and
// "uname -m" reports the machine hardware name; e.g.,
// "i86pc" on both 32- and 64-bit x86 systems. Check for the
// native (widest) instruction set on the running kernel:
out := run("", CheckExit, "isainfo", "-n")
if strings.Contains(out, "amd64") {
gohostarch = "amd64"
}
if strings.Contains(out, "i386") {
gohostarch = "386"
}
case "windows":
exe = ".exe"
}
sysinit()
if gohostarch == "" {
// Default Unix system.
out := run("", CheckExit, "uname", "-m")
outAll := run("", CheckExit, "uname", "-a")
switch {
case strings.Contains(outAll, "RELEASE_ARM64"):
// MacOS prints
// Darwin p1.local 21.1.0 Darwin Kernel Version 21.1.0: Wed Oct 13 17:33:01 PDT 2021; root:xnu-8019.41.5~1/RELEASE_ARM64_T6000 x86_64
// on ARM64 laptops when there is an x86 parent in the
// process tree. Look for the RELEASE_ARM64 to avoid being
// confused into building an x86 toolchain.
gohostarch = "arm64"
case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"):
gohostarch = "amd64"
case strings.Contains(out, "86"):
gohostarch = "386"
if gohostos == "darwin" {
// Even on 64-bit platform, some versions of macOS uname -m prints i386.
// We don't support any of the OS X versions that run on 32-bit-only hardware anymore.
gohostarch = "amd64"
}
case strings.Contains(out, "aarch64"), strings.Contains(out, "arm64"):
gohostarch = "arm64"
case strings.Contains(out, "arm"):
gohostarch = "arm"
if gohostos == "netbsd" && strings.Contains(run("", CheckExit, "uname", "-p"), "aarch64") {
gohostarch = "arm64"
}
case strings.Contains(out, "ppc64le"):
gohostarch = "ppc64le"
case strings.Contains(out, "ppc64"):
gohostarch = "ppc64"
case strings.Contains(out, "mips64"):
gohostarch = "mips64"
if elfIsLittleEndian(os.Args[0]) {
gohostarch = "mips64le"
}
case strings.Contains(out, "mips"):
gohostarch = "mips"
if elfIsLittleEndian(os.Args[0]) {
gohostarch = "mipsle"
}
case strings.Contains(out, "loongarch64"):
gohostarch = "loong64"
case strings.Contains(out, "riscv64"):
gohostarch = "riscv64"
case strings.Contains(out, "s390x"):
gohostarch = "s390x"
case gohostos == "darwin", gohostos == "ios":
if strings.Contains(run("", CheckExit, "uname", "-v"), "RELEASE_ARM64_") {
gohostarch = "arm64"
}
case gohostos == "freebsd":
if strings.Contains(run("", CheckExit, "uname", "-p"), "riscv64") {
gohostarch = "riscv64"
}
case gohostos == "openbsd" && strings.Contains(out, "powerpc64"):
gohostarch = "ppc64"
case gohostos == "openbsd":
if strings.Contains(run("", CheckExit, "uname", "-p"), "mips64") {
gohostarch = "mips64"
}
default:
fatalf("unknown architecture: %s", out)
}
}
if gohostarch == "arm" || gohostarch == "mips64" || gohostarch == "mips64le" {
maxbg = min(maxbg, runtime.NumCPU())
}
// For deterministic make.bash debugging and for smallest-possible footprint,
// pay attention to GOMAXPROCS=1. This was a bad idea for 1.4 bootstrap, but
// the bootstrap version is now 1.17+ and thus this is fine.
if runtime.GOMAXPROCS(0) == 1 {
maxbg = 1
}
bginit()
if len(os.Args) > 1 && os.Args[1] == "-check-goarm" {
useVFPv1() // might fail with SIGILL
println("VFPv1 OK.")
useVFPv3() // might fail with SIGILL
println("VFPv3 OK.")
os.Exit(0)
}
xinit()
xmain()
xexit(0)
}
// The OS-specific main calls into the portable code here.
func xmain() {
if len(os.Args) < 2 {
usage()
}
cmd := os.Args[1]
os.Args = os.Args[1:] // for flag parsing during cmd
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "usage: go tool dist %s [options]\n", cmd)
flag.PrintDefaults()
os.Exit(2)
}
if f, ok := commands[cmd]; ok {
f()
} else {
xprintf("unknown command %s\n", cmd)
usage()
}
}

21
src/cmd/dist/notgo120.go vendored Normal file
View File

@@ -0,0 +1,21 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Go 1.22 and later requires Go 1.20 as the bootstrap toolchain.
// If cmd/dist is built using an earlier Go version, this file will be
// included in the build and cause an error like:
//
// % GOROOT_BOOTSTRAP=$HOME/sdk/go1.16 ./make.bash
// Building Go cmd/dist using /Users/rsc/sdk/go1.16. (go1.16 darwin/amd64)
// found packages main (build.go) and building_Go_requires_Go_1_20_6_or_later (notgo120.go) in /Users/rsc/go/src/cmd/dist
// %
//
// which is the best we can do under the circumstances.
//
// See go.dev/issue/44505 for more background on
// why Go moved on from Go 1.4 for bootstrap.
//go:build !go1.20
package building_Go_requires_Go_1_20_6_or_later

53
src/cmd/dist/quoted.go vendored Normal file
View File

@@ -0,0 +1,53 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import "fmt"
// quotedSplit is a verbatim copy from cmd/internal/quoted.go:Split and its
// dependencies (isSpaceByte). Since this package is built using the host's
// Go compiler, it cannot use `cmd/internal/...`. We also don't want to export
// it to all Go users.
//
// Please keep those in sync.
func quotedSplit(s string) ([]string, error) {
// Split fields allowing '' or "" around elements.
// Quotes further inside the string do not count.
var f []string
for len(s) > 0 {
for len(s) > 0 && isSpaceByte(s[0]) {
s = s[1:]
}
if len(s) == 0 {
break
}
// Accepted quoted string. No unescaping inside.
if s[0] == '"' || s[0] == '\'' {
quote := s[0]
s = s[1:]
i := 0
for i < len(s) && s[i] != quote {
i++
}
if i >= len(s) {
return nil, fmt.Errorf("unterminated %c string", quote)
}
f = append(f, s[:i])
s = s[i+1:]
continue
}
i := 0
for i < len(s) && !isSpaceByte(s[i]) {
i++
}
f = append(f, s[:i])
s = s[i:]
}
return f, nil
}
func isSpaceByte(c byte) bool {
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
}

48
src/cmd/dist/supported_test.go vendored Normal file
View File

@@ -0,0 +1,48 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"internal/platform"
"testing"
)
// TestSupported tests that dist and the main tools agree on
// which build modes are supported for a given target. We do things
// this way because the dist tool needs to be buildable directly by
// the bootstrap compiler, and as such can't import internal packages.
func TestSupported(t *testing.T) {
defer func(a, o string) {
goarch = a
goos = o
}(goarch, goos)
var modes = []string{
// we assume that "exe" and "archive" always work
"pie",
"c-archive",
"c-shared",
"shared",
"plugin",
}
for _, a := range okgoarch {
goarch = a
for _, o := range okgoos {
if _, ok := cgoEnabled[o+"/"+a]; !ok {
continue
}
goos = o
for _, mode := range modes {
var dt tester
dist := dt.supportedBuildmode(mode)
std := platform.BuildModeSupported("gc", mode, o, a)
if dist != std {
t.Errorf("discrepancy for %s-%s %s: dist says %t, standard library says %t", o, a, mode, dist, std)
}
}
}
}
}

10
src/cmd/dist/sys_default.go vendored Normal file
View File

@@ -0,0 +1,10 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !windows
package main
func sysinit() {
}

57
src/cmd/dist/sys_windows.go vendored Normal file
View File

@@ -0,0 +1,57 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"syscall"
"unsafe"
)
var (
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
procGetSystemInfo = modkernel32.NewProc("GetSystemInfo")
)
// see https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
type systeminfo struct {
wProcessorArchitecture uint16
wReserved uint16
dwPageSize uint32
lpMinimumApplicationAddress uintptr
lpMaximumApplicationAddress uintptr
dwActiveProcessorMask uintptr
dwNumberOfProcessors uint32
dwProcessorType uint32
dwAllocationGranularity uint32
wProcessorLevel uint16
wProcessorRevision uint16
}
// See https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
const (
PROCESSOR_ARCHITECTURE_AMD64 = 9
PROCESSOR_ARCHITECTURE_INTEL = 0
PROCESSOR_ARCHITECTURE_ARM = 5
PROCESSOR_ARCHITECTURE_ARM64 = 12
PROCESSOR_ARCHITECTURE_IA64 = 6
)
var sysinfo systeminfo
func sysinit() {
syscall.Syscall(procGetSystemInfo.Addr(), 1, uintptr(unsafe.Pointer(&sysinfo)), 0, 0)
switch sysinfo.wProcessorArchitecture {
case PROCESSOR_ARCHITECTURE_AMD64:
gohostarch = "amd64"
case PROCESSOR_ARCHITECTURE_INTEL:
gohostarch = "386"
case PROCESSOR_ARCHITECTURE_ARM:
gohostarch = "arm"
case PROCESSOR_ARCHITECTURE_ARM64:
gohostarch = "arm64"
default:
fatalf("unknown processor architecture")
}
}

1693
src/cmd/dist/test.go vendored Normal file

File diff suppressed because it is too large Load Diff

204
src/cmd/dist/testjson.go vendored Normal file
View File

@@ -0,0 +1,204 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"sync"
"time"
)
// lockedWriter serializes Write calls to an underlying Writer.
type lockedWriter struct {
lock sync.Mutex
w io.Writer
}
func (w *lockedWriter) Write(b []byte) (int, error) {
w.lock.Lock()
defer w.lock.Unlock()
return w.w.Write(b)
}
// testJSONFilter is an io.Writer filter that replaces the Package field in
// test2json output.
type testJSONFilter struct {
w io.Writer // Underlying writer
variant string // Add ":variant" to Package field
lineBuf bytes.Buffer // Buffer for incomplete lines
}
func (f *testJSONFilter) Write(b []byte) (int, error) {
bn := len(b)
// Process complete lines, and buffer any incomplete lines.
for len(b) > 0 {
nl := bytes.IndexByte(b, '\n')
if nl < 0 {
f.lineBuf.Write(b)
break
}
var line []byte
if f.lineBuf.Len() > 0 {
// We have buffered data. Add the rest of the line from b and
// process the complete line.
f.lineBuf.Write(b[:nl+1])
line = f.lineBuf.Bytes()
} else {
// Process a complete line from b.
line = b[:nl+1]
}
b = b[nl+1:]
f.process(line)
f.lineBuf.Reset()
}
return bn, nil
}
func (f *testJSONFilter) Flush() {
// Write any remaining partial line to the underlying writer.
if f.lineBuf.Len() > 0 {
f.w.Write(f.lineBuf.Bytes())
f.lineBuf.Reset()
}
}
func (f *testJSONFilter) process(line []byte) {
if len(line) > 0 && line[0] == '{' {
// Plausible test2json output. Parse it generically.
//
// We go to some effort here to preserve key order while doing this
// generically. This will stay robust to changes in the test2json
// struct, or other additions outside of it. If humans are ever looking
// at the output, it's really nice to keep field order because it
// preserves a lot of regularity in the output.
dec := json.NewDecoder(bytes.NewBuffer(line))
dec.UseNumber()
val, err := decodeJSONValue(dec)
if err == nil && val.atom == json.Delim('{') {
// Rewrite the Package field.
found := false
for i := 0; i < len(val.seq); i += 2 {
if val.seq[i].atom == "Package" {
if pkg, ok := val.seq[i+1].atom.(string); ok {
val.seq[i+1].atom = pkg + ":" + f.variant
found = true
break
}
}
}
if found {
data, err := json.Marshal(val)
if err != nil {
// Should never happen.
panic(fmt.Sprintf("failed to round-trip JSON %q: %s", line, err))
}
f.w.Write(data)
// Copy any trailing text. We expect at most a "\n" here, but
// there could be other text and we want to feed that through.
io.Copy(f.w, dec.Buffered())
return
}
}
}
// Something went wrong. Just pass the line through.
f.w.Write(line)
}
type jsonValue struct {
atom json.Token // If json.Delim, then seq will be filled
seq []jsonValue // If atom == json.Delim('{'), alternating pairs
}
var jsonPop = errors.New("end of JSON sequence")
func decodeJSONValue(dec *json.Decoder) (jsonValue, error) {
t, err := dec.Token()
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return jsonValue{}, err
}
switch t := t.(type) {
case json.Delim:
if t == '}' || t == ']' {
return jsonValue{}, jsonPop
}
var seq []jsonValue
for {
val, err := decodeJSONValue(dec)
if err == jsonPop {
break
} else if err != nil {
return jsonValue{}, err
}
seq = append(seq, val)
}
return jsonValue{t, seq}, nil
default:
return jsonValue{t, nil}, nil
}
}
func (v jsonValue) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
var marshal1 func(v jsonValue) error
marshal1 = func(v jsonValue) error {
if t, ok := v.atom.(json.Delim); ok {
buf.WriteRune(rune(t))
for i, v2 := range v.seq {
if t == '{' && i%2 == 1 {
buf.WriteByte(':')
} else if i > 0 {
buf.WriteByte(',')
}
if err := marshal1(v2); err != nil {
return err
}
}
if t == '{' {
buf.WriteByte('}')
} else {
buf.WriteByte(']')
}
return nil
}
bytes, err := json.Marshal(v.atom)
if err != nil {
return err
}
buf.Write(bytes)
return nil
}
err := marshal1(v)
return buf.Bytes(), err
}
func synthesizeSkipEvent(enc *json.Encoder, pkg, msg string) {
type event struct {
Time time.Time
Action string
Package string
Output string `json:",omitempty"`
}
ev := event{Time: time.Now(), Package: pkg, Action: "start"}
enc.Encode(ev)
ev.Action = "output"
ev.Output = msg
enc.Encode(ev)
ev.Action = "skip"
ev.Output = ""
enc.Encode(ev)
}

85
src/cmd/dist/testjson_test.go vendored Normal file
View File

@@ -0,0 +1,85 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"strings"
"testing"
)
func TestJSONFilterRewritePackage(t *testing.T) {
const in = `{"Package":"abc"}
{"Field1":"1","Package":"abc","Field3":"3"}
{"Package":123}
{}
{"Package":"abc","Unexpected":[null,true,false,99999999999999999999]}
`
want := strings.ReplaceAll(in, `"Package":"abc"`, `"Package":"abc:variant"`)
checkJSONFilter(t, in, want)
}
func TestJSONFilterMalformed(t *testing.T) {
const in = `unexpected text
{"Package":"abc"}
more text
{"Package":"abc"}trailing text
{not json}
no newline`
const want = `unexpected text
{"Package":"abc:variant"}
more text
{"Package":"abc:variant"}trailing text
{not json}
no newline`
checkJSONFilter(t, in, want)
}
func TestJSONFilterBoundaries(t *testing.T) {
const in = `{"Package":"abc"}
{"Package":"def"}
{"Package":"ghi"}
`
want := strings.ReplaceAll(in, `"}`, `:variant"}`)
// Write one bytes at a time.
t.Run("bytes", func(t *testing.T) {
checkJSONFilterWith(t, want, func(f *testJSONFilter) {
for i := 0; i < len(in); i++ {
f.Write([]byte{in[i]})
}
})
})
// Write a block containing a whole line bordered by two partial lines.
t.Run("bytes", func(t *testing.T) {
checkJSONFilterWith(t, want, func(f *testJSONFilter) {
const b1 = 5
const b2 = len(in) - 5
f.Write([]byte(in[:b1]))
f.Write([]byte(in[b1:b2]))
f.Write([]byte(in[b2:]))
})
})
}
func checkJSONFilter(t *testing.T, in, want string) {
t.Helper()
checkJSONFilterWith(t, want, func(f *testJSONFilter) {
f.Write([]byte(in))
})
}
func checkJSONFilterWith(t *testing.T, want string, write func(*testJSONFilter)) {
t.Helper()
out := new(strings.Builder)
f := &testJSONFilter{w: out, variant: "variant"}
write(f)
f.Flush()
got := out.String()
if want != got {
t.Errorf("want:\n%s\ngot:\n%s", want, got)
}
}

475
src/cmd/dist/util.go vendored Normal file
View File

@@ -0,0 +1,475 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"flag"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
"time"
)
// pathf is fmt.Sprintf for generating paths
// (on windows it turns / into \ after the printf).
func pathf(format string, args ...interface{}) string {
return filepath.Clean(fmt.Sprintf(format, args...))
}
// filter returns a slice containing the elements x from list for which f(x) == true.
func filter(list []string, f func(string) bool) []string {
var out []string
for _, x := range list {
if f(x) {
out = append(out, x)
}
}
return out
}
// uniq returns a sorted slice containing the unique elements of list.
func uniq(list []string) []string {
out := make([]string, len(list))
copy(out, list)
sort.Strings(out)
keep := out[:0]
for _, x := range out {
if len(keep) == 0 || keep[len(keep)-1] != x {
keep = append(keep, x)
}
}
return keep
}
const (
CheckExit = 1 << iota
ShowOutput
Background
)
var outputLock sync.Mutex
// run is like runEnv with no additional environment.
func run(dir string, mode int, cmd ...string) string {
return runEnv(dir, mode, nil, cmd...)
}
// runEnv runs the command line cmd in dir with additional environment env.
// If mode has ShowOutput set and Background unset, run passes cmd's output to
// stdout/stderr directly. Otherwise, run returns cmd's output as a string.
// If mode has CheckExit set and the command fails, run calls fatalf.
// If mode has Background set, this command is being run as a
// Background job. Only bgrun should use the Background mode,
// not other callers.
func runEnv(dir string, mode int, env []string, cmd ...string) string {
if vflag > 1 {
errprintf("run: %s\n", strings.Join(cmd, " "))
}
xcmd := exec.Command(cmd[0], cmd[1:]...)
if env != nil {
xcmd.Env = append(os.Environ(), env...)
}
setDir(xcmd, dir)
var data []byte
var err error
// If we want to show command output and this is not
// a background command, assume it's the only thing
// running, so we can just let it write directly stdout/stderr
// as it runs without fear of mixing the output with some
// other command's output. Not buffering lets the output
// appear as it is printed instead of once the command exits.
// This is most important for the invocation of 'go build -v bootstrap/...'.
if mode&(Background|ShowOutput) == ShowOutput {
xcmd.Stdout = os.Stdout
xcmd.Stderr = os.Stderr
err = xcmd.Run()
} else {
data, err = xcmd.CombinedOutput()
}
if err != nil && mode&CheckExit != 0 {
outputLock.Lock()
if len(data) > 0 {
xprintf("%s\n", data)
}
outputLock.Unlock()
if mode&Background != 0 {
// Prevent fatalf from waiting on our own goroutine's
// bghelper to exit:
bghelpers.Done()
}
fatalf("FAILED: %v: %v", strings.Join(cmd, " "), err)
}
if mode&ShowOutput != 0 {
outputLock.Lock()
os.Stdout.Write(data)
outputLock.Unlock()
}
if vflag > 2 {
errprintf("run: %s DONE\n", strings.Join(cmd, " "))
}
return string(data)
}
var maxbg = 4 /* maximum number of jobs to run at once */
var (
bgwork = make(chan func(), 1e5)
bghelpers sync.WaitGroup
dieOnce sync.Once // guards close of dying
dying = make(chan struct{})
)
func bginit() {
bghelpers.Add(maxbg)
for i := 0; i < maxbg; i++ {
go bghelper()
}
}
func bghelper() {
defer bghelpers.Done()
for {
select {
case <-dying:
return
case w := <-bgwork:
// Dying takes precedence over doing more work.
select {
case <-dying:
return
default:
w()
}
}
}
}
// bgrun is like run but runs the command in the background.
// CheckExit|ShowOutput mode is implied (since output cannot be returned).
// bgrun adds 1 to wg immediately, and calls Done when the work completes.
func bgrun(wg *sync.WaitGroup, dir string, cmd ...string) {
wg.Add(1)
bgwork <- func() {
defer wg.Done()
run(dir, CheckExit|ShowOutput|Background, cmd...)
}
}
// bgwait waits for pending bgruns to finish.
// bgwait must be called from only a single goroutine at a time.
func bgwait(wg *sync.WaitGroup) {
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
case <-dying:
// Don't return to the caller, to avoid reporting additional errors
// to the user.
select {}
}
}
// xgetwd returns the current directory.
func xgetwd() string {
wd, err := os.Getwd()
if err != nil {
fatalf("%s", err)
}
return wd
}
// xrealwd returns the 'real' name for the given path.
// real is defined as what xgetwd returns in that directory.
func xrealwd(path string) string {
old := xgetwd()
if err := os.Chdir(path); err != nil {
fatalf("chdir %s: %v", path, err)
}
real := xgetwd()
if err := os.Chdir(old); err != nil {
fatalf("chdir %s: %v", old, err)
}
return real
}
// isdir reports whether p names an existing directory.
func isdir(p string) bool {
fi, err := os.Stat(p)
return err == nil && fi.IsDir()
}
// isfile reports whether p names an existing file.
func isfile(p string) bool {
fi, err := os.Stat(p)
return err == nil && fi.Mode().IsRegular()
}
// mtime returns the modification time of the file p.
func mtime(p string) time.Time {
fi, err := os.Stat(p)
if err != nil {
return time.Time{}
}
return fi.ModTime()
}
// readfile returns the content of the named file.
func readfile(file string) string {
data, err := os.ReadFile(file)
if err != nil {
fatalf("%v", err)
}
return string(data)
}
const (
writeExec = 1 << iota
writeSkipSame
)
// writefile writes text to the named file, creating it if needed.
// if exec is non-zero, marks the file as executable.
// If the file already exists and has the expected content,
// it is not rewritten, to avoid changing the time stamp.
func writefile(text, file string, flag int) {
new := []byte(text)
if flag&writeSkipSame != 0 {
old, err := os.ReadFile(file)
if err == nil && bytes.Equal(old, new) {
return
}
}
mode := os.FileMode(0666)
if flag&writeExec != 0 {
mode = 0777
}
xremove(file) // in case of symlink tricks by misc/reboot test
err := os.WriteFile(file, new, mode)
if err != nil {
fatalf("%v", err)
}
}
// xmkdir creates the directory p.
func xmkdir(p string) {
err := os.Mkdir(p, 0777)
if err != nil {
fatalf("%v", err)
}
}
// xmkdirall creates the directory p and its parents, as needed.
func xmkdirall(p string) {
err := os.MkdirAll(p, 0777)
if err != nil {
fatalf("%v", err)
}
}
// xremove removes the file p.
func xremove(p string) {
if vflag > 2 {
errprintf("rm %s\n", p)
}
os.Remove(p)
}
// xremoveall removes the file or directory tree rooted at p.
func xremoveall(p string) {
if vflag > 2 {
errprintf("rm -r %s\n", p)
}
os.RemoveAll(p)
}
// xreaddir replaces dst with a list of the names of the files and subdirectories in dir.
// The names are relative to dir; they are not full paths.
func xreaddir(dir string) []string {
f, err := os.Open(dir)
if err != nil {
fatalf("%v", err)
}
defer f.Close()
names, err := f.Readdirnames(-1)
if err != nil {
fatalf("reading %s: %v", dir, err)
}
return names
}
// xworkdir creates a new temporary directory to hold object files
// and returns the name of that directory.
func xworkdir() string {
name, err := os.MkdirTemp(os.Getenv("GOTMPDIR"), "go-tool-dist-")
if err != nil {
fatalf("%v", err)
}
return name
}
// fatalf prints an error message to standard error and exits.
func fatalf(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))
dieOnce.Do(func() { close(dying) })
// Wait for background goroutines to finish,
// so that exit handler that removes the work directory
// is not fighting with active writes or open files.
bghelpers.Wait()
xexit(2)
}
var atexits []func()
// xexit exits the process with return code n.
func xexit(n int) {
for i := len(atexits) - 1; i >= 0; i-- {
atexits[i]()
}
os.Exit(n)
}
// xatexit schedules the exit-handler f to be run when the program exits.
func xatexit(f func()) {
atexits = append(atexits, f)
}
// xprintf prints a message to standard output.
func xprintf(format string, args ...interface{}) {
fmt.Printf(format, args...)
}
// errprintf prints a message to standard output.
func errprintf(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, format, args...)
}
// xsamefile reports whether f1 and f2 are the same file (or dir).
func xsamefile(f1, f2 string) bool {
fi1, err1 := os.Stat(f1)
fi2, err2 := os.Stat(f2)
if err1 != nil || err2 != nil {
return f1 == f2
}
return os.SameFile(fi1, fi2)
}
func xgetgoarm() string {
// If we're building on an actual arm system, and not building
// a cross-compiling toolchain, try to exec ourselves
// to detect whether VFP is supported and set the default GOARM.
// Windows requires ARMv7, so we can skip the check.
// We've always assumed Android is ARMv7 too.
if gohostarch == "arm" && goarch == "arm" && goos == gohostos && goos != "windows" && goos != "android" {
// Try to exec ourselves in a mode to detect VFP support.
// Seeing how far it gets determines which instructions failed.
// The test is OS-agnostic.
out := run("", 0, os.Args[0], "-check-goarm")
v1ok := strings.Contains(out, "VFPv1 OK.")
v3ok := strings.Contains(out, "VFPv3 OK.")
if v1ok && v3ok {
return "7"
}
if v1ok {
return "6"
}
return "5"
}
// Otherwise, in the absence of local information, assume GOARM=7.
//
// We used to assume GOARM=5 in certain contexts but not others,
// which produced inconsistent results. For example if you cross-compiled
// for linux/arm from a windows/amd64 machine, you got GOARM=7 binaries,
// but if you cross-compiled for linux/arm from a linux/amd64 machine,
// you got GOARM=5 binaries. Now the default is independent of the
// host operating system, for better reproducibility of builds.
return "7"
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
// elfIsLittleEndian detects if the ELF file is little endian.
func elfIsLittleEndian(fn string) bool {
// read the ELF file header to determine the endianness without using the
// debug/elf package.
file, err := os.Open(fn)
if err != nil {
fatalf("failed to open file to determine endianness: %v", err)
}
defer file.Close()
var hdr [16]byte
if _, err := io.ReadFull(file, hdr[:]); err != nil {
fatalf("failed to read ELF header to determine endianness: %v", err)
}
// hdr[5] is EI_DATA byte, 1 is ELFDATA2LSB and 2 is ELFDATA2MSB
switch hdr[5] {
default:
fatalf("unknown ELF endianness of %s: EI_DATA = %d", fn, hdr[5])
case 1:
return true
case 2:
return false
}
panic("unreachable")
}
// count is a flag.Value that is like a flag.Bool and a flag.Int.
// If used as -name, it increments the count, but -name=x sets the count.
// Used for verbose flag -v.
type count int
func (c *count) String() string {
return fmt.Sprint(int(*c))
}
func (c *count) Set(s string) error {
switch s {
case "true":
*c++
case "false":
*c = 0
default:
n, err := strconv.Atoi(s)
if err != nil {
return fmt.Errorf("invalid count %q", s)
}
*c = count(n)
}
return nil
}
func (c *count) IsBoolFlag() bool {
return true
}
func xflagparse(maxargs int) {
flag.Var((*count)(&vflag), "v", "verbosity")
flag.Parse()
if maxargs >= 0 && flag.NArg() > maxargs {
flag.Usage()
}
}

20
src/cmd/dist/util_gc.go vendored Normal file
View File

@@ -0,0 +1,20 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gc
package main
// useVFPv1 tries to execute one VFPv1 instruction on ARM.
// It will crash the current process if VFPv1 is missing.
func useVFPv1()
// useVFPv3 tries to execute one VFPv3 instruction on ARM.
// It will crash the current process if VFPv3 is missing.
func useVFPv3()
// useARMv6K tries to run ARMv6K instructions on ARM.
// It will crash the current process if it doesn't implement
// ARMv6K or above.
func useARMv6K()

13
src/cmd/dist/util_gccgo.go vendored Normal file
View File

@@ -0,0 +1,13 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gccgo
package main
func useVFPv1() {}
func useVFPv3() {}
func useARMv6K() {}

26
src/cmd/dist/vfp_arm.s vendored Normal file
View File

@@ -0,0 +1,26 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gc
#include "textflag.h"
// try to run "vmov.f64 d0, d0" instruction
TEXT ·useVFPv1(SB),NOSPLIT,$0
WORD $0xeeb00b40 // vmov.f64 d0, d0
RET
// try to run VFPv3-only "vmov.f64 d0, #112" instruction
TEXT ·useVFPv3(SB),NOSPLIT,$0
WORD $0xeeb70b00 // vmov.f64 d0, #112
RET
// try to run ARMv6K (or above) "ldrexd" instruction
TEXT ·useARMv6K(SB),NOSPLIT,$32
MOVW R13, R2
BIC $15, R13
WORD $0xe1bd0f9f // ldrexd r0, r1, [sp]
WORD $0xf57ff01f // clrex
MOVW R2, R13
RET

16
src/cmd/dist/vfp_default.s vendored Normal file
View File

@@ -0,0 +1,16 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gc && !arm
#include "textflag.h"
TEXT ·useVFPv1(SB),NOSPLIT,$0
RET
TEXT ·useVFPv3(SB),NOSPLIT,$0
RET
TEXT ·useARMv6K(SB),NOSPLIT,$0
RET