diff options
author | Ian Lance Taylor <iant@golang.org> | 2022-02-11 14:53:56 -0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2022-02-11 15:01:19 -0800 |
commit | 8dc2499aa62f768c6395c9754b8cabc1ce25c494 (patch) | |
tree | 43d7fd2bbfd7ad8c9625a718a5e8718889351994 /libgo/go/cmd | |
parent | 9a56779dbc4e2d9c15be8d31e36f2f59be7331a8 (diff) | |
download | gcc-8dc2499aa62f768c6395c9754b8cabc1ce25c494.tar.gz |
libgo: update to Go1.18beta2
gotools/
* Makefile.am (go_cmd_cgo_files): Add ast_go118.go
(check-go-tool): Copy golang.org/x/tools directories.
* Makefile.in: Regenerate.
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/384695
Diffstat (limited to 'libgo/go/cmd')
446 files changed, 13780 insertions, 3910 deletions
diff --git a/libgo/go/cmd/cgo/ast.go b/libgo/go/cmd/cgo/ast.go index a073407a961..28879e349c4 100644 --- a/libgo/go/cmd/cgo/ast.go +++ b/libgo/go/cmd/cgo/ast.go @@ -338,8 +338,7 @@ func (f *File) walk(x interface{}, context astContext, visit func(*File, interfa // everything else just recurs default: - error_(token.NoPos, "unexpected type %T in walk", x) - panic("unexpected type") + f.walkUnexpected(x, context, visit) case nil: diff --git a/libgo/go/cmd/cgo/ast_go1.go b/libgo/go/cmd/cgo/ast_go1.go new file mode 100644 index 00000000000..f52bf00d7cb --- /dev/null +++ b/libgo/go/cmd/cgo/ast_go1.go @@ -0,0 +1,17 @@ +// 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. + +//go:build compiler_bootstrap +// +build compiler_bootstrap + +package main + +import ( + "go/token" +) + +func (f *File) walkUnexpected(x interface{}, context astContext, visit func(*File, interface{}, astContext)) { + error_(token.NoPos, "unexpected type %T in walk", x) + panic("unexpected type") +} diff --git a/libgo/go/cmd/cgo/ast_go118.go b/libgo/go/cmd/cgo/ast_go118.go new file mode 100644 index 00000000000..db0108ed726 --- /dev/null +++ b/libgo/go/cmd/cgo/ast_go118.go @@ -0,0 +1,25 @@ +// 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. + +//go:build !compiler_bootstrap +// +build !compiler_bootstrap + +package main + +import ( + "go/ast" + "go/token" +) + +func (f *File) walkUnexpected(x interface{}, context astContext, visit func(*File, interface{}, astContext)) { + switch n := x.(type) { + default: + error_(token.NoPos, "unexpected type %T in walk", x) + panic("unexpected type") + + case *ast.IndexListExpr: + f.walk(&n.X, ctxExpr, visit) + f.walk(n.Indices, ctxExpr, visit) + } +} diff --git a/libgo/go/cmd/cgo/gcc.go b/libgo/go/cmd/cgo/gcc.go index de64a75f710..02391495bbe 100644 --- a/libgo/go/cmd/cgo/gcc.go +++ b/libgo/go/cmd/cgo/gcc.go @@ -23,10 +23,13 @@ import ( "internal/xcoff" "math" "os" + "os/exec" "strconv" "strings" "unicode" "unicode/utf8" + + "cmd/internal/quoted" ) var debugDefine = flag.Bool("debug-define", false, "print relevant #defines") @@ -400,7 +403,7 @@ func (p *Package) guessKinds(f *File) []*Name { stderr = p.gccErrors(b.Bytes()) } if stderr == "" { - fatalf("%s produced no output\non input:\n%s", p.gccBaseCmd()[0], b.Bytes()) + fatalf("%s produced no output\non input:\n%s", gccBaseCmd[0], b.Bytes()) } completed := false @@ -475,7 +478,7 @@ func (p *Package) guessKinds(f *File) []*Name { } if !completed { - fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", p.gccBaseCmd()[0], b.Bytes(), stderr) + fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", gccBaseCmd[0], b.Bytes(), stderr) } for i, n := range names { @@ -506,7 +509,7 @@ func (p *Package) guessKinds(f *File) []*Name { // to users debugging preamble mistakes. See issue 8442. preambleErrors := p.gccErrors([]byte(f.Preamble)) if len(preambleErrors) > 0 { - error_(token.NoPos, "\n%s errors for preamble:\n%s", p.gccBaseCmd()[0], preambleErrors) + error_(token.NoPos, "\n%s errors for preamble:\n%s", gccBaseCmd[0], preambleErrors) } fatalf("unresolved names") @@ -1521,7 +1524,7 @@ func (p *Package) rewriteName(f *File, r *Ref, addPosition bool) ast.Expr { Args: []ast.Expr{getNewIdent(name.Mangle)}, } case "type": - // Okay - might be new(T) + // Okay - might be new(T), T(x), Generic[T], etc. if r.Name.Type == nil { error_(r.Pos(), "expression C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C) } @@ -1563,20 +1566,37 @@ func gofmtPos(n ast.Expr, pos token.Pos) string { return fmt.Sprintf("/*line :%d:%d*/%s", p.Line, p.Column, s) } -// gccBaseCmd returns the start of the compiler command line. +// checkGCCBaseCmd returns the start of the compiler command line. // It uses $CC if set, or else $GCC, or else the compiler recorded // during the initial build as defaultCC. // defaultCC is defined in zdefaultcc.go, written by cmd/dist. -func (p *Package) gccBaseCmd() []string { +// +// The compiler command line is split into arguments on whitespace. Quotes +// are understood, so arguments may contain whitespace. +// +// checkGCCBaseCmd confirms that the compiler exists in PATH, returning +// an error if it does not. +func checkGCCBaseCmd() ([]string, error) { // Use $CC if set, since that's what the build uses. - if ret := strings.Fields(os.Getenv("CC")); len(ret) > 0 { - return ret + value := os.Getenv("CC") + if value == "" { + // Try $GCC if set, since that's what we used to use. + value = os.Getenv("GCC") + } + if value == "" { + value = defaultCC(goos, goarch) } - // Try $GCC if set, since that's what we used to use. - if ret := strings.Fields(os.Getenv("GCC")); len(ret) > 0 { - return ret + args, err := quoted.Split(value) + if err != nil { + return nil, err } - return strings.Fields(defaultCC(goos, goarch)) + if len(args) == 0 { + return nil, errors.New("CC not set and no default found") + } + if _, err := exec.LookPath(args[0]); err != nil { + return nil, fmt.Errorf("C compiler %q not found: %v", args[0], err) + } + return args[:len(args):len(args)], nil } // gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm". @@ -1630,7 +1650,7 @@ func gccTmp() string { // gccCmd returns the gcc command line to use for compiling // the input. func (p *Package) gccCmd() []string { - c := append(p.gccBaseCmd(), + c := append(gccBaseCmd, "-w", // no warnings "-Wno-error", // warnings are not errors "-o"+gccTmp(), // write object to tmp @@ -2030,7 +2050,7 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6 // #defines that gcc encountered while processing the input // and its included files. func (p *Package) gccDefines(stdin []byte) string { - base := append(p.gccBaseCmd(), "-E", "-dM", "-xc") + base := append(gccBaseCmd, "-E", "-dM", "-xc") base = append(base, p.gccMachine()...) stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-")) return stdout @@ -2111,6 +2131,9 @@ type typeConv struct { // Type names X for which there exists an XGetTypeID function with type func() CFTypeID. getTypeIDs map[string]bool + // badStructs contains C structs that should be marked NotInHeap. + notInHeapStructs map[string]bool + // Predeclared types. bool ast.Expr byte ast.Expr // denotes padding @@ -2122,6 +2145,7 @@ type typeConv struct { string ast.Expr goVoid ast.Expr // _Ctype_void, denotes C's void goVoidPtr ast.Expr // unsafe.Pointer or *byte + goVoidPtrNoHeap ast.Expr // *_Ctype_void_notinheap, like goVoidPtr but marked NotInHeap ptrSize int64 intSize int64 @@ -2145,6 +2169,7 @@ func (c *typeConv) Init(ptrSize, intSize int64) { c.m = make(map[string]*Type) c.ptrs = make(map[string][]*Type) c.getTypeIDs = make(map[string]bool) + c.notInHeapStructs = make(map[string]bool) c.bool = c.Ident("bool") c.byte = c.Ident("byte") c.int8 = c.Ident("int8") @@ -2163,6 +2188,7 @@ func (c *typeConv) Init(ptrSize, intSize int64) { c.void = c.Ident("void") c.string = c.Ident("string") c.goVoid = c.Ident("_Ctype_void") + c.goVoidPtrNoHeap = c.Ident("*_Ctype_void_notinheap") // Normally cgo translates void* to unsafe.Pointer, // but for historical reasons -godefs uses *byte instead. @@ -2543,6 +2569,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ tt.C = &TypeRepr{"struct %s", []interface{}{tag}} } tt.Go = g + tt.NotInHeap = c.notInHeapStructs[tag] typedef[name.Name] = &tt } @@ -2586,6 +2613,30 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ oldType.BadPointer = true } } + if c.badVoidPointerTypedef(dt) { + // Treat this typedef as a pointer to a NotInHeap void. + s := *sub + s.Go = c.goVoidPtrNoHeap + sub = &s + // Make sure we update any previously computed type. + if oldType := typedef[name.Name]; oldType != nil { + oldType.Go = sub.Go + } + } + // Check for non-pointer "struct <tag>{...}; typedef struct <tag> *<name>" + // typedefs that should be marked NotInHeap. + if ptr, ok := dt.Type.(*dwarf.PtrType); ok { + if strct, ok := ptr.Type.(*dwarf.StructType); ok { + if c.badStructPointerTypedef(dt.Name, strct) { + c.notInHeapStructs[strct.StructName] = true + // Make sure we update any previously computed type. + name := "_Ctype_struct_" + strct.StructName + if oldType := typedef[name]; oldType != nil { + oldType.NotInHeap = true + } + } + } + } t.Go = name t.BadPointer = sub.BadPointer t.NotInHeap = sub.NotInHeap @@ -3035,6 +3086,31 @@ func upper(s string) string { // so that all fields are exported. func godefsFields(fld []*ast.Field) { prefix := fieldPrefix(fld) + + // Issue 48396: check for duplicate field names. + if prefix != "" { + names := make(map[string]bool) + fldLoop: + for _, f := range fld { + for _, n := range f.Names { + name := n.Name + if name == "_" { + continue + } + if name != prefix { + name = strings.TrimPrefix(n.Name, prefix) + } + name = upper(name) + if names[name] { + // Field name conflict: don't remove prefix. + prefix = "" + break fldLoop + } + names[name] = true + } + } + } + npad := 0 for _, f := range fld { for _, n := range f.Names { @@ -3112,6 +3188,48 @@ func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool { return false } +// badVoidPointerTypedef is like badPointerTypeDef, but for "void *" typedefs that should be NotInHeap. +func (c *typeConv) badVoidPointerTypedef(dt *dwarf.TypedefType) bool { + // Match the Windows HANDLE type (#42018). + if goos != "windows" || dt.Name != "HANDLE" { + return false + } + // Check that the typedef is "typedef void *<name>". + if ptr, ok := dt.Type.(*dwarf.PtrType); ok { + if _, ok := ptr.Type.(*dwarf.VoidType); ok { + return true + } + } + return false +} + +// badStructPointerTypedef is like badVoidPointerTypedefs but for structs. +func (c *typeConv) badStructPointerTypedef(name string, dt *dwarf.StructType) bool { + // Windows handle types can all potentially contain non-pointers. + // badVoidPointerTypedef handles the "void *" HANDLE type, but other + // handles are defined as + // + // struct <name>__{int unused;}; typedef struct <name>__ *name; + // + // by the DECLARE_HANDLE macro in STRICT mode. The macro is declared in + // the Windows ntdef.h header, + // + // https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/shared/ntdef.h#L779 + if goos != "windows" { + return false + } + if len(dt.Field) != 1 { + return false + } + if dt.StructName != name+"__" { + return false + } + if f := dt.Field[0]; f.Name != "unused" || f.Type.Common().Name != "int" { + return false + } + return true +} + // baseBadPointerTypedef reports whether the base of a chain of typedefs is a bad typedef // as badPointerTypedef reports. func (c *typeConv) baseBadPointerTypedef(dt *dwarf.TypedefType) bool { diff --git a/libgo/go/cmd/cgo/main.go b/libgo/go/cmd/cgo/main.go index 40dc6d2562d..58477e47045 100644 --- a/libgo/go/cmd/cgo/main.go +++ b/libgo/go/cmd/cgo/main.go @@ -21,7 +21,6 @@ import ( "io" "io/ioutil" "os" - "os/exec" "path/filepath" "reflect" "runtime" @@ -252,6 +251,7 @@ var importSyscall = flag.Bool("import_syscall", true, "import syscall in generat var trimpath = flag.String("trimpath", "", "applies supplied rewrites or trims prefixes to recorded source file paths") var goarch, goos, gomips, gomips64 string +var gccBaseCmd []string func main() { objabi.AddVersionFlag() // -V @@ -309,10 +309,10 @@ func main() { p := newPackage(args[:i]) // We need a C compiler to be available. Check this. - gccName := p.gccBaseCmd()[0] - _, err := exec.LookPath(gccName) + var err error + gccBaseCmd, err = checkGCCBaseCmd() if err != nil { - fatalf("C compiler %q not found: %v", gccName, err) + fatalf("%v", err) os.Exit(2) } diff --git a/libgo/go/cmd/cgo/out.go b/libgo/go/cmd/cgo/out.go index 3cf16f90f98..8d1eb5b6a96 100644 --- a/libgo/go/cmd/cgo/out.go +++ b/libgo/go/cmd/cgo/out.go @@ -64,9 +64,9 @@ func (p *Package) writeDefs() { // Write C main file for using gcc to resolve imports. fmt.Fprintf(fm, "int main() { return 0; }\n") if *importRuntimeCgo { - fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*), void *a, int c, __SIZE_TYPE__ ctxt) { }\n") + fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*) __attribute__((unused)), void *a __attribute__((unused)), int c __attribute__((unused)), __SIZE_TYPE__ ctxt __attribute__((unused))) { }\n") fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done(void) { return 0; }\n") - fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__ ctxt) { }\n") + fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__ ctxt __attribute__((unused))) { }\n") fmt.Fprintf(fm, "char* _cgo_topofstack(void) { return (char*)0; }\n") } else { // If we're not importing runtime/cgo, we *are* runtime/cgo, @@ -75,8 +75,8 @@ func (p *Package) writeDefs() { fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done(void);\n") fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__);\n") } - fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n") - fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n") + fmt.Fprintf(fm, "void _cgo_allocate(void *a __attribute__((unused)), int c __attribute__((unused))) { }\n") + fmt.Fprintf(fm, "void _cgo_panic(void *a __attribute__((unused)), int c __attribute__((unused))) { }\n") fmt.Fprintf(fm, "void _cgo_reginit(void) { }\n") // Write second Go output: definitions of _C_xxx. @@ -140,6 +140,7 @@ func (p *Package) writeDefs() { fmt.Fprintf(fgo2, "%s", buf.Bytes()) fmt.Fprintf(fgo2, "\n\n") } + fmt.Fprintf(fgo2, "//go:notinheap\ntype _Ctype_void_notinheap struct{}\n\n") if *gccgo { fmt.Fprintf(fgo2, "type _Ctype_void byte\n") } else { @@ -1059,9 +1060,10 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName) + fmt.Fprintf(fgo2, "\t") + if gccResult != "void" { // Write results back to frame. - fmt.Fprintf(fgo2, "\t") forFieldList(fntype.Results, func(i int, aname string, atype ast.Expr) { if i > 0 { @@ -1463,10 +1465,10 @@ const gccProlog = ` (have a negative array count) and an inscrutable error will come out of the compiler and hopefully mention "name". */ -#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1]; +#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2UL+1UL]; /* Check at compile time that the sizes we use match our expectations. */ -#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), n, _cgo_sizeof_##t##_is_not_##n) +#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), (size_t)n, _cgo_sizeof_##t##_is_not_##n) __cgo_size_assert(char, 1) __cgo_size_assert(short, 2) diff --git a/libgo/go/cmd/go.mod b/libgo/go/cmd/go.mod index cd03968eedc..48fc888f949 100644 --- a/libgo/go/cmd/go.mod +++ b/libgo/go/cmd/go.mod @@ -1,15 +1,19 @@ module cmd -go 1.17 +go 1.18 require ( - github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a - github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 // indirect - golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e - golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e // indirect - golang.org/x/mod v0.4.3-0.20210608190319-0f08993efd8a - golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 // indirect - golang.org/x/term v0.0.0-20210503060354-a79de5458b56 - golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9 + github.com/google/pprof v0.0.0-20211104044539-f987b9c94b31 + golang.org/x/arch v0.0.0-20210923205945-b76863e36670 + golang.org/x/mod v0.6.0-dev.0.20211102181907-3a5865c02020 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 + golang.org/x/tools v0.1.9-0.20220124164225-97de9ec46646 +) + +require ( + github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d // indirect + golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect + golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect ) diff --git a/libgo/go/cmd/go/alldocs.go b/libgo/go/cmd/go/alldocs.go index 7f88d3216cf..826b0ccf19c 100644 --- a/libgo/go/cmd/go/alldocs.go +++ b/libgo/go/cmd/go/alldocs.go @@ -25,6 +25,7 @@ // install compile and install packages and dependencies // list list packages or modules // mod module maintenance +// work workspace maintenance // run compile and run Go program // test test packages // tool run specified go tool @@ -114,13 +115,16 @@ // The default is GOMAXPROCS, normally the number of CPUs available. // -race // enable data race detection. -// Supported only on linux/amd64, freebsd/amd64, darwin/amd64, windows/amd64, +// Supported only on linux/amd64, freebsd/amd64, darwin/amd64, darwin/arm64, windows/amd64, // linux/ppc64le and linux/arm64 (only for 48-bit VMA). // -msan // enable interoperation with memory sanitizer. // Supported only on linux/amd64, linux/arm64 // and only with Clang/LLVM as the host C compiler. // On linux/arm64, pie build mode will be used. +// -asan +// enable interoperation with address sanitizer. +// Supported only on linux/arm64, linux/amd64. // -v // print the names of packages as they are compiled. // -work @@ -133,6 +137,12 @@ // arguments to pass on each go tool asm invocation. // -buildmode mode // build mode to use. See 'go help buildmode' for more. +// -buildvcs +// Whether to stamp binaries with version control information. By default, +// version control information is stamped into a binary if the main package +// and the main module containing it are in the repository containing the +// current directory (if there is a repository). Use -buildvcs=false to +// omit version control information. // -compiler name // name of compiler to use, as in runtime.Compiler (gccgo or gc). // -gccgoflags '[pattern=]arg list' @@ -144,8 +154,8 @@ // in order to keep output separate from default builds. // If using the -race flag, the install suffix is automatically set to race // or, if set explicitly, has _race appended to it. Likewise for the -msan -// flag. Using a -buildmode option that requires non-default compile flags -// has a similar effect. +// and -asan flags. Using a -buildmode option that requires non-default compile +// flags has a similar effect. // -ldflags '[pattern=]arg list' // arguments to pass on each go tool link invocation. // -linkshared @@ -167,6 +177,14 @@ // directory, but it is not accessed. When -modfile is specified, an // alternate go.sum file is also used: its path is derived from the // -modfile flag by trimming the ".mod" extension and appending ".sum". +// -workfile file +// in module aware mode, use the given go.work file as a workspace file. +// By default or when -workfile is "auto", the go command searches for a +// file named go.work in the current directory and then containing directories +// until one is found. If a valid go.work file is found, the modules +// specified will collectively be used as the main modules. If -workfile +// is "off", or a go.work file is not found in "auto" mode, workspace +// mode is disabled. // -overlay file // read a JSON config file that provides an overlay for build operations. // The file is a JSON struct with a single field, named 'Replace', that @@ -191,9 +209,8 @@ // -trimpath // remove all file system paths from the resulting executable. // Instead of absolute file system paths, the recorded file names -// will begin with either "go" (for the standard library), -// or a module path@version (when using modules), -// or a plain import path (when using GOPATH). +// will begin either a module path@version (when using modules), +// or a plain import path (when using the standard library, or GOPATH). // -toolexec 'cmd args' // a program to use to invoke toolchain programs like vet and asm. // For example, instead of running asm, the go command will run @@ -284,6 +301,13 @@ // download cache, including unpacked source code of versioned // dependencies. // +// The -fuzzcache flag causes clean to remove files stored in the Go build +// cache for fuzz testing. The fuzzing engine caches files that expand +// code coverage, so removing them may make fuzzing less effective until +// new inputs are found that provide the same coverage. These files are +// distinct from those stored in testdata directory; clean does not remove +// those files. +// // For more about build flags, see 'go help build'. // // For more about specifying packages, see 'go help packages'. @@ -338,9 +362,8 @@ // path. The go tool's usual package mechanism does not apply: package path // elements like . and ... are not implemented by go doc. // -// When run with two arguments, the first must be a full package path (not just a -// suffix), and the second is a symbol, or symbol with method or struct field. -// This is similar to the syntax accepted by godoc: +// When run with two arguments, the first is a package path (full path or suffix), +// and the second is a symbol, or symbol with method or struct field: // // go doc <pkg> <sym>[.<methodOrField>] // @@ -438,14 +461,18 @@ // // Usage: // -// go fix [packages] +// go fix [-fix list] [packages] // // Fix runs the Go fix command on the packages named by the import paths. // +// The -fix flag sets a comma-separated list of fixes to run. +// The default is all known fixes. +// (Its value is passed to 'go tool fix -r'.) +// // For more about fix, see 'go doc cmd/fix'. // For more about specifying packages, see 'go help packages'. // -// To run fix with specific options, run 'go tool fix'. +// To run fix with other options, run 'go tool fix'. // // See also: go fmt, go vet. // @@ -483,7 +510,7 @@ // files. Those commands can run any process but the intent is to // create or update Go source files. // -// Go generate is never run automatically by go build, go get, go test, +// Go generate is never run automatically by go build, go test, // and so on. It must be run explicitly. // // Go generate scans the file for directives, which are lines of @@ -598,11 +625,11 @@ // // Usage: // -// go get [-d] [-t] [-u] [-v] [build flags] [packages] +// go get [-t] [-u] [-v] [build flags] [packages] // // Get resolves its command-line arguments to packages at specific module versions, -// updates go.mod to require those versions, downloads source code into the -// module cache, then builds and installs the named packages. +// updates go.mod to require those versions, and downloads source code into the +// module cache. // // To add a dependency for a package or upgrade it to its latest version: // @@ -618,17 +645,18 @@ // // See https://golang.org/ref/mod#go-get for details. // -// The 'go install' command may be used to build and install packages. When a -// version is specified, 'go install' runs in module-aware mode and ignores -// the go.mod file in the current directory. For example: +// In earlier versions of Go, 'go get' was used to build and install packages. +// Now, 'go get' is dedicated to adjusting dependencies in go.mod. 'go install' +// may be used to build and install commands instead. When a version is specified, +// 'go install' runs in module-aware mode and ignores the go.mod file in the +// current directory. For example: // // go install example.com/pkg@v1.2.3 // go install example.com/pkg@latest // // See 'go help install' or https://golang.org/ref/mod#go-install for details. // -// In addition to build flags (listed in 'go help build') 'go get' accepts the -// following flags. +// 'go get' accepts the following flags. // // The -t flag instructs get to consider modules needed to build tests of // packages specified on the command line. @@ -643,15 +671,9 @@ // When the -t and -u flags are used together, get will update // test dependencies as well. // -// The -d flag instructs get not to build or install packages. get will only -// update go.mod and download source code needed to build packages. -// -// Building and installing packages with get is deprecated. In a future release, -// the -d flag will be enabled by default, and 'go get' will be only be used to -// adjust dependencies of the current module. To install a package using -// dependencies from the current module, use 'go install'. To install a package -// ignoring the current module, use 'go install' with an @version suffix like -// "@latest" after each argument. +// The -x flag prints commands as they are executed. This is useful for +// debugging version control commands when a module is downloaded directly +// from a repository. // // For more about modules, see https://golang.org/ref/mod. // @@ -694,14 +716,17 @@ // // - All arguments must refer to packages in the same module at the same version. // +// - Package path arguments must refer to main packages. Pattern arguments +// will only match main packages. +// // - No module is considered the "main" module. If the module containing // packages named on the command line has a go.mod file, it must not contain // directives (replace and exclude) that would cause it to be interpreted // differently than if it were the main module. The module must not require // a higher version of itself. // -// - Package path arguments must refer to main packages. Pattern arguments -// will only match main packages. +// - Vendor directories are not used in any module. (Vendor directories are not +// included in the module zip files downloaded by 'go install'.) // // If the arguments don't have version suffixes, "go install" may run in // module-aware mode or GOPATH mode, depending on the GO111MODULE environment @@ -1041,8 +1066,11 @@ // // Download downloads the named modules, which can be module patterns selecting // dependencies of the main module or module queries of the form path@version. -// With no arguments, download applies to all dependencies of the main module -// (equivalent to 'go mod download all'). +// +// With no arguments, download applies to the modules needed to build and test +// the packages in the main module: the modules explicitly required by the main +// module if it is at 'go 1.17' or higher, or all transitively-required modules +// if at 'go 1.16' or lower. // // The go command will automatically download modules as needed during ordinary // execution. The "go mod download" command is useful mainly for pre-filling @@ -1260,7 +1288,7 @@ // // Usage: // -// go mod vendor [-e] [-v] +// go mod vendor [-e] [-v] [-o outdir] // // Vendor resets the main module's vendor directory to include all packages // needed to build and test all the main module's packages. @@ -1272,6 +1300,11 @@ // The -e flag causes vendor to attempt to proceed despite errors // encountered while loading packages. // +// The -o flag causes vendor to create the vendor directory at the given +// path instead of "vendor". The go command can only use a vendor directory +// named "vendor" within the module root directory, so this flag is +// primarily useful for other tools. +// // See https://golang.org/ref/mod#go-mod-vendor for more about 'go mod vendor'. // // @@ -1329,6 +1362,202 @@ // See https://golang.org/ref/mod#go-mod-why for more about 'go mod why'. // // +// Workspace maintenance +// +// Go workspace provides access to operations on workspaces. +// +// Note that support for workspaces is built into many other commands, not +// just 'go work'. +// +// See 'go help modules' for information about Go's module system of which +// workspaces are a part. +// +// A workspace is specified by a go.work file that specifies a set of +// module directories with the "use" directive. These modules are used as +// root modules by the go command for builds and related operations. A +// workspace that does not specify modules to be used cannot be used to do +// builds from local modules. +// +// go.work files are line-oriented. Each line holds a single directive, +// made up of a keyword followed by aruments. For example: +// +// go 1.18 +// +// use ../foo/bar +// use ./baz +// +// replace example.com/foo v1.2.3 => example.com/bar v1.4.5 +// +// The leading keyword can be factored out of adjacent lines to create a block, +// like in Go imports. +// +// use ( +// ../foo/bar +// ./baz +// ) +// +// The use directive specifies a module to be included in the workspace's +// set of main modules. The argument to the use directive is the directory +// containing the module's go.mod file. +// +// The go directive specifies the version of Go the file was written at. It +// is possible there may be future changes in the semantics of workspaces +// that could be controlled by this version, but for now the version +// specified has no effect. +// +// The replace directive has the same syntax as the replace directive in a +// go.mod file and takes precedence over replaces in go.mod files. It is +// primarily intended to override conflicting replaces in different workspace +// modules. +// +// To determine whether the go command is operating in workspace mode, use +// the "go env GOWORK" command. This will specify the workspace file being +// used. +// +// Usage: +// +// go work <command> [arguments] +// +// The commands are: +// +// edit edit go.work from tools or scripts +// init initialize workspace file +// sync sync workspace build list to modules +// use add modules to workspace file +// +// Use "go help work <command>" for more information about a command. +// +// Edit go.work from tools or scripts +// +// Usage: +// +// go work edit [editing flags] [go.work] +// +// Edit provides a command-line interface for editing go.work, +// for use primarily by tools or scripts. It only reads go.work; +// it does not look up information about the modules involved. +// If no file is specified, Edit looks for a go.work file in the current +// directory and its parent directories +// +// The editing flags specify a sequence of editing operations. +// +// The -fmt flag reformats the go.work file without making other changes. +// This reformatting is also implied by any other modifications that use or +// rewrite the go.mod file. The only time this flag is needed is if no other +// flags are specified, as in 'go work edit -fmt'. +// +// The -use=path and -dropuse=path flags +// add and drop a use directive from the go.work file's set of module directories. +// +// The -replace=old[@v]=new[@v] flag adds a replacement of the given +// module path and version pair. If the @v in old@v is omitted, a +// replacement without a version on the left side is added, which applies +// to all versions of the old module path. If the @v in new@v is omitted, +// the new path should be a local module root directory, not a module +// path. Note that -replace overrides any redundant replacements for old[@v], +// so omitting @v will drop existing replacements for specific versions. +// +// The -dropreplace=old[@v] flag drops a replacement of the given +// module path and version pair. If the @v is omitted, a replacement without +// a version on the left side is dropped. +// +// The -use, -dropuse, -replace, and -dropreplace, +// editing flags may be repeated, and the changes are applied in the order given. +// +// The -go=version flag sets the expected Go language version. +// +// The -print flag prints the final go.work in its text format instead of +// writing it back to go.mod. +// +// The -json flag prints the final go.work file in JSON format instead of +// writing it back to go.mod. The JSON output corresponds to these Go types: +// +// type Module struct { +// Path string +// Version string +// } +// +// type GoWork struct { +// Go string +// Directory []Directory +// Replace []Replace +// } +// +// type Use struct { +// Path string +// ModulePath string +// } +// +// type Replace struct { +// Old Module +// New Module +// } +// +// See the workspaces design proposal at +// https://go.googlesource.com/proposal/+/master/design/45713-workspace.md for +// more information. +// +// +// Initialize workspace file +// +// Usage: +// +// go work init [moddirs] +// +// Init initializes and writes a new go.work file in the +// current directory, in effect creating a new workspace at the current +// directory. +// +// go work init optionally accepts paths to the workspace modules as +// arguments. If the argument is omitted, an empty workspace with no +// modules will be created. +// +// Each argument path is added to a use directive in the go.work file. The +// current go version will also be listed in the go.work file. +// +// +// Sync workspace build list to modules +// +// Usage: +// +// go work sync +// +// Sync syncs the workspace's build list back to the +// workspace's modules +// +// The workspace's build list is the set of versions of all the +// (transitive) dependency modules used to do builds in the workspace. go +// work sync generates that build list using the Minimal Version Selection +// algorithm, and then syncs those versions back to each of modules +// specified in the workspace (with use directives). +// +// The syncing is done by sequentially upgrading each of the dependency +// modules specified in a workspace module to the version in the build list +// if the dependency module's version is not already the same as the build +// list's version. Note that Minimal Version Selection guarantees that the +// build list's version of each module is always the same or higher than +// that in each workspace module. +// +// +// Add modules to workspace file +// +// Usage: +// +// go work use [-r] [moddirs] +// +// Use provides a command-line interface for adding +// directories, optionally recursively, to a go.work file. +// +// A use directive will be added to the go.work file for each argument +// directory listed on the command line go.work file, if it exists on disk, +// or removed from the go.work file if it does not exist on disk. +// +// The -r flag searches recursively for modules in the argument +// directories, and the use command operates as if each of the directories +// were specified as arguments: namely, use directives will be added for +// directories that exist, and removed for directories that do not exist. +// +// // Compile and run Go program // // Usage: @@ -1387,8 +1616,8 @@ // // 'Go test' recompiles each package along with any files with names matching // the file pattern "*_test.go". -// These additional files can contain test functions, benchmark functions, and -// example functions. See 'go help testfunc' for more. +// These additional files can contain test functions, benchmark functions, fuzz +// tests and example functions. See 'go help testfunc' for more. // Each listed package causes the execution of a separate test binary. // Files whose names begin with "_" (including "_test.go") or "." are ignored. // @@ -1405,7 +1634,8 @@ // used. That subset is: 'atomic', 'bool', 'buildtags', 'errorsas', // 'ifaceassert', 'nilfunc', 'printf', and 'stringintconv'. You can see // the documentation for these and other vet tests via "go doc cmd/vet". -// To disable the running of go vet, use the -vet=off flag. +// To disable the running of go vet, use the -vet=off flag. To run all +// checks, use the -vet=all flag. // // All test output and summary lines are printed to the go command's // standard output, even if the test printed them to its own standard @@ -1446,16 +1676,16 @@ // The rule for a match in the cache is that the run involves the same // test binary and the flags on the command line come entirely from a // restricted set of 'cacheable' test flags, defined as -benchtime, -cpu, -// -list, -parallel, -run, -short, and -v. If a run of go test has any test -// or non-test flags outside this set, the result is not cached. To -// disable test caching, use any test flag or argument other than the -// cacheable flags. The idiomatic way to disable test caching explicitly -// is to use -count=1. Tests that open files within the package's source -// root (usually $GOPATH) or that consult environment variables only -// match future runs in which the files and environment variables are unchanged. -// A cached test result is treated as executing in no time at all, -// so a successful package test result will be cached and reused -// regardless of -timeout setting. +// -list, -parallel, -run, -short, -timeout, -failfast, and -v. +// If a run of go test has any test or non-test flags outside this set, +// the result is not cached. To disable test caching, use any test flag +// or argument other than the cacheable flags. The idiomatic way to disable +// test caching explicitly is to use -count=1. Tests that open files within +// the package's source root (usually $GOPATH) or that consult environment +// variables only match future runs in which the files and environment +// variables are unchanged. A cached test result is treated as executing +// in no time at all,so a successful package test result will be cached and +// reused regardless of -timeout setting. // // In addition to the build flags, the flags handled by 'go test' itself are: // @@ -1746,6 +1976,13 @@ // See 'go help test' for details. Running 'go clean -testcache' removes // all cached test results (but not cached build results). // +// The go command also caches values used in fuzzing with 'go test -fuzz', +// specifically, values that expanded code coverage when passed to a +// fuzz function. These values are not used for regular building and +// testing, but they're stored in a subdirectory of the build cache. +// Running 'go clean -fuzzcache' removes all cached fuzzing values. +// This may make fuzzing less effective, temporarily. +// // The GODEBUG environment variable can enable printing of debugging // information about the state of the cache: // @@ -1885,6 +2122,10 @@ // GO386 // For GOARCH=386, how to implement floating point instructions. // Valid values are sse2 (default), softfloat. +// GOAMD64 +// For GOARCH=amd64, the microarchitecture level for which to compile. +// Valid values are v1 (default), v2, v3, v4. +// See https://golang.org/wiki/MinimumRequirements#amd64 // GOMIPS // For GOARCH=mips{,le}, whether to use floating point instructions. // Valid values are hardfloat (default), softfloat. @@ -2622,9 +2863,10 @@ // (for example, -benchtime 100x). // // -count n -// Run each test and benchmark n times (default 1). +// Run each test, benchmark, and fuzz seed n times (default 1). // If -cpu is set, run n times for each GOMAXPROCS value. -// Examples are always run once. +// Examples are always run once. -count does not apply to +// fuzz tests matched by -fuzz. // // -cover // Enable coverage analysis. @@ -2651,32 +2893,67 @@ // Sets -cover. // // -cpu 1,2,4 -// Specify a list of GOMAXPROCS values for which the tests or -// benchmarks should be executed. The default is the current value -// of GOMAXPROCS. +// Specify a list of GOMAXPROCS values for which the tests, benchmarks or +// fuzz tests should be executed. The default is the current value +// of GOMAXPROCS. -cpu does not apply to fuzz tests matched by -fuzz. // // -failfast // Do not start new tests after the first test failure. // +// -fuzz regexp +// Run the fuzz test matching the regular expression. When specified, +// the command line argument must match exactly one package within the +// main module, and regexp must match exactly one fuzz test within +// that package. Fuzzing will occur after tests, benchmarks, seed corpora +// of other fuzz tests, and examples have completed. See the Fuzzing +// section of the testing package documentation for details. +// +// -fuzztime t +// Run enough iterations of the fuzz target during fuzzing to take t, +// specified as a time.Duration (for example, -fuzztime 1h30s). +// The default is to run forever. +// The special syntax Nx means to run the fuzz target N times +// (for example, -fuzztime 1000x). +// +// -fuzzminimizetime t +// Run enough iterations of the fuzz target during each minimization +// attempt to take t, as specified as a time.Duration (for example, +// -fuzzminimizetime 30s). +// The default is 60s. +// The special syntax Nx means to run the fuzz target N times +// (for example, -fuzzminimizetime 100x). +// +// -json +// Log verbose output and test results in JSON. This presents the +// same information as the -v flag in a machine-readable format. +// // -list regexp -// List tests, benchmarks, or examples matching the regular expression. -// No tests, benchmarks or examples will be run. This will only -// list top-level tests. No subtest or subbenchmarks will be shown. +// List tests, benchmarks, fuzz tests, or examples matching the regular +// expression. No tests, benchmarks, fuzz tests, or examples will be run. +// This will only list top-level tests. No subtest or subbenchmarks will be +// shown. // // -parallel n -// Allow parallel execution of test functions that call t.Parallel. +// Allow parallel execution of test functions that call t.Parallel, and +// fuzz targets that call t.Parallel when running the seed corpus. // The value of this flag is the maximum number of tests to run -// simultaneously; by default, it is set to the value of GOMAXPROCS. +// simultaneously. +// While fuzzing, the value of this flag is the maximum number of +// subprocesses that may call the fuzz function simultaneously, regardless of +// whether T.Parallel is called. +// By default, -parallel is set to the value of GOMAXPROCS. +// Setting -parallel to values higher than GOMAXPROCS may cause degraded +// performance due to CPU contention, especially when fuzzing. // Note that -parallel only applies within a single test binary. // The 'go test' command may run tests for different packages // in parallel as well, according to the setting of the -p flag // (see 'go help build'). // // -run regexp -// Run only those tests and examples matching the regular expression. -// For tests, the regular expression is split by unbracketed slash (/) -// characters into a sequence of regular expressions, and each part -// of a test's identifier must match the corresponding element in +// Run only those tests, examples, and fuzz tests matching the regular +// expression. For tests, the regular expression is split by unbracketed +// slash (/) characters into a sequence of regular expressions, and each +// part of a test's identifier must match the corresponding element in // the sequence, if any. Note that possible parents of matches are // run too, so that -run=X/Y matches and runs and reports the result // of all tests matching X, even those without sub-tests matching Y, @@ -2689,11 +2966,11 @@ // exhaustive tests. // // -shuffle off,on,N -// Randomize the execution order of tests and benchmarks. -// It is off by default. If -shuffle is set to on, then it will seed -// the randomizer using the system clock. If -shuffle is set to an -// integer N, then N will be used as the seed value. In both cases, -// the seed will be reported for reproducibility. +// Randomize the execution order of tests and benchmarks. +// It is off by default. If -shuffle is set to on, then it will seed +// the randomizer using the system clock. If -shuffle is set to an +// integer N, then N will be used as the seed value. In both cases, +// the seed will be reported for reproducibility. // // -timeout d // If a test binary runs longer than duration d, panic. @@ -2789,7 +3066,11 @@ // When 'go test' runs a test binary, it does so from within the // corresponding package's source code directory. Depending on the test, // it may be necessary to do the same when invoking a generated test -// binary directly. +// binary directly. Because that directory may be located within the +// module cache, which may be read-only and is verified by checksums, the +// test must not write to it or any other directory within the module +// unless explicitly requested by the user (such as with the -fuzz flag, +// which writes failures to testdata/fuzz). // // The command-line package list, if present, must appear before any // flag not known to the go test command. Continuing the example above, @@ -2843,6 +3124,10 @@ // // func BenchmarkXxx(b *testing.B) { ... } // +// A fuzz test is one named FuzzXxx and should have the signature, +// +// func FuzzXxx(f *testing.F) { ... } +// // An example function is similar to a test function but, instead of using // *testing.T to report success or failure, prints output to os.Stdout. // If the last comment in the function starts with "Output:" then the output @@ -2882,7 +3167,7 @@ // // The entire test file is presented as the example when it contains a single // example function, at least one other function, type, variable, or constant -// declaration, and no test or benchmark functions. +// declaration, and no tests, benchmarks, or fuzz tests. // // See the documentation of the testing package for more information. // diff --git a/libgo/go/cmd/go/go11.go b/libgo/go/cmd/go/go11.go index a1f2727825e..9faa7cba42e 100644 --- a/libgo/go/cmd/go/go11.go +++ b/libgo/go/cmd/go/go11.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.1 -// +build go1.1 package main diff --git a/libgo/go/cmd/go/go_test.go b/libgo/go/cmd/go/go_test.go index 6ce276537ba..7aaec4eb986 100644 --- a/libgo/go/cmd/go/go_test.go +++ b/libgo/go/cmd/go/go_test.go @@ -13,6 +13,7 @@ import ( "flag" "fmt" "go/format" + "internal/godebug" "internal/race" "internal/testenv" "io" @@ -31,7 +32,6 @@ import ( "cmd/go/internal/cache" "cmd/go/internal/cfg" "cmd/go/internal/robustio" - "cmd/go/internal/work" "cmd/internal/sys" ) @@ -44,9 +44,12 @@ func init() { } var ( - canRace = false // whether we can run the race detector - canCgo = false // whether we can use cgo - canMSan = false // whether we can run the memory sanitizer + canRace = false // whether we can run the race detector + canCgo = false // whether we can use cgo + canMSan = false // whether we can run the memory sanitizer + canASan = false // whether we can run the address sanitizer + canFuzz = false // whether we can search for new fuzz failures + fuzzInstrumented = false // whether fuzzing uses instrumentation ) var exeSuffix string = func() string { @@ -198,6 +201,7 @@ func TestMain(m *testing.M) { testGOCACHE = strings.TrimSpace(string(out)) canMSan = canCgo && sys.MSanSupported(runtime.GOOS, runtime.GOARCH) + canASan = canCgo && sys.ASanSupported(runtime.GOOS, runtime.GOARCH) canRace = canCgo && sys.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) // The race detector doesn't work on Alpine Linux: // golang.org/issue/14481 @@ -205,6 +209,8 @@ func TestMain(m *testing.M) { if isAlpineLinux() || runtime.Compiler == "gccgo" { canRace = false } + canFuzz = sys.FuzzSupported(runtime.GOOS, runtime.GOARCH) + fuzzInstrumented = sys.FuzzInstrumented(runtime.GOOS, runtime.GOARCH) } // Don't let these environment variables confuse the test. os.Setenv("GOENV", "off") @@ -806,7 +812,9 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) { "src/internal/abi", "src/internal/bytealg", "src/internal/cpu", + "src/internal/goarch", "src/internal/goexperiment", + "src/internal/goos", "src/math/bits", "src/unsafe", filepath.Join("pkg", runtime.GOOS+"_"+runtime.GOARCH), @@ -1120,11 +1128,11 @@ func TestGoListTest(t *testing.T) { tg.grepStdoutNot(`^testing \[sort.test\]$`, "unexpected test copy of testing") tg.grepStdoutNot(`^testing$`, "unexpected real copy of testing") - tg.run("list", "-test", "cmd/dist", "cmd/doc") - tg.grepStdout(`^cmd/dist$`, "missing cmd/dist") + tg.run("list", "-test", "cmd/buildid", "cmd/doc") + tg.grepStdout(`^cmd/buildid$`, "missing cmd/buildid") tg.grepStdout(`^cmd/doc$`, "missing cmd/doc") tg.grepStdout(`^cmd/doc\.test$`, "missing cmd/doc test") - tg.grepStdoutNot(`^cmd/dist\.test$`, "unexpected cmd/dist test") + tg.grepStdoutNot(`^cmd/buildid\.test$`, "unexpected cmd/buildid test") tg.grepStdoutNot(`^testing`, "unexpected testing") tg.run("list", "-test", "runtime/cgo") @@ -1376,7 +1384,7 @@ func TestLdFlagsLongArgumentsIssue42295(t *testing.T) { }`) testStr := "test test test test test \n\\ " var buf bytes.Buffer - for buf.Len() < work.ArgLengthForResponseFile+1 { + for buf.Len() < sys.ExecArgLengthLimit+1 { buf.WriteString(testStr) } tg.run("run", "-ldflags", fmt.Sprintf(`-X "main.extern=%s"`, buf.String()), tg.path("main.go")) @@ -2274,7 +2282,7 @@ func TestUpxCompression(t *testing.T) { func TestCacheListStale(t *testing.T) { tooSlow(t) - if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { + if godebug.Get("gocacheverify") == "1" { t.Skip("GODEBUG gocacheverify") } tg := testgo(t) @@ -2297,7 +2305,7 @@ func TestCacheListStale(t *testing.T) { func TestCacheCoverage(t *testing.T) { tooSlow(t) - if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { + if godebug.Get("gocacheverify") == "1" { t.Skip("GODEBUG gocacheverify") } @@ -2329,7 +2337,7 @@ func TestIssue22588(t *testing.T) { func TestIssue22531(t *testing.T) { tooSlow(t) - if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { + if godebug.Get("gocacheverify") == "1" { t.Skip("GODEBUG gocacheverify") } tg := testgo(t) @@ -2358,7 +2366,7 @@ func TestIssue22531(t *testing.T) { func TestIssue22596(t *testing.T) { tooSlow(t) - if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { + if godebug.Get("gocacheverify") == "1" { t.Skip("GODEBUG gocacheverify") } tg := testgo(t) @@ -2388,7 +2396,7 @@ func TestIssue22596(t *testing.T) { func TestTestCache(t *testing.T) { tooSlow(t) - if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { + if godebug.Get("gocacheverify") == "1" { t.Skip("GODEBUG gocacheverify") } tg := testgo(t) diff --git a/libgo/go/cmd/go/go_unix_test.go b/libgo/go/cmd/go/go_unix_test.go index db992b78914..29fa343d931 100644 --- a/libgo/go/cmd/go/go_unix_test.go +++ b/libgo/go/cmd/go/go_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris -// +build darwin dragonfly freebsd hurd linux netbsd openbsd solaris package main_test diff --git a/libgo/go/cmd/go/internal/base/base.go b/libgo/go/cmd/go/internal/base/base.go index 954ce47a989..c2d4e6b2588 100644 --- a/libgo/go/cmd/go/internal/base/base.go +++ b/libgo/go/cmd/go/internal/base/base.go @@ -117,12 +117,12 @@ func Exit() { os.Exit(exitStatus) } -func Fatalf(format string, args ...interface{}) { +func Fatalf(format string, args ...any) { Errorf(format, args...) Exit() } -func Errorf(format string, args ...interface{}) { +func Errorf(format string, args ...any) { log.Printf(format, args...) SetExitStatus(1) } @@ -151,7 +151,7 @@ func GetExitStatus() int { // Run runs the command, with stdout and stderr // connected to the go command's own stdout and stderr. // If the command fails, Run reports the error using Errorf. -func Run(cmdargs ...interface{}) { +func Run(cmdargs ...any) { cmdline := str.StringList(cmdargs...) if cfg.BuildN || cfg.BuildX { fmt.Printf("%s\n", strings.Join(cmdline, " ")) diff --git a/libgo/go/cmd/go/internal/base/flag.go b/libgo/go/cmd/go/internal/base/flag.go index 677f8196827..2c72c7e562b 100644 --- a/libgo/go/cmd/go/internal/base/flag.go +++ b/libgo/go/cmd/go/internal/base/flag.go @@ -9,7 +9,7 @@ import ( "cmd/go/internal/cfg" "cmd/go/internal/fsys" - "cmd/go/internal/str" + "cmd/internal/quoted" ) // A StringsFlag is a command-line flag that interprets its argument @@ -18,7 +18,7 @@ type StringsFlag []string func (v *StringsFlag) Set(s string) error { var err error - *v, err = str.SplitQuotedFields(s) + *v, err = quoted.Split(s) if *v == nil { *v = []string{} } @@ -62,6 +62,13 @@ func AddModFlag(flags *flag.FlagSet) { flags.Var(explicitStringFlag{value: &cfg.BuildMod, explicit: &cfg.BuildModExplicit}, "mod", "") } +// AddWorkfileFlag adds the workfile flag to the flag set. It enables workspace +// mode for commands that support it by resetting the cfg.WorkFile variable +// to "" (equivalent to auto) rather than off. +func AddWorkfileFlag(flags *flag.FlagSet) { + flags.Var(explicitStringFlag{value: &cfg.WorkFile, explicit: &cfg.WorkFileExplicit}, "workfile", "") +} + // AddModCommonFlags adds the module-related flags common to build commands // and 'go mod' subcommands. func AddModCommonFlags(flags *flag.FlagSet) { diff --git a/libgo/go/cmd/go/internal/base/signal_notunix.go b/libgo/go/cmd/go/internal/base/signal_notunix.go index 5cc0b0f1011..682705f9b2c 100644 --- a/libgo/go/cmd/go/internal/base/signal_notunix.go +++ b/libgo/go/cmd/go/internal/base/signal_notunix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 || windows -// +build plan9 windows package base diff --git a/libgo/go/cmd/go/internal/base/signal_unix.go b/libgo/go/cmd/go/internal/base/signal_unix.go index cdb25934c2d..0cf58eb9299 100644 --- a/libgo/go/cmd/go/internal/base/signal_unix.go +++ b/libgo/go/cmd/go/internal/base/signal_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || js || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd js linux netbsd openbsd solaris package base diff --git a/libgo/go/cmd/go/internal/base/tool.go b/libgo/go/cmd/go/internal/base/tool.go index d0da65e03ce..f9270169650 100644 --- a/libgo/go/cmd/go/internal/base/tool.go +++ b/libgo/go/cmd/go/internal/base/tool.go @@ -36,7 +36,7 @@ func Tool(toolName string) string { } // Give a nice message if there is no tool with that name. if _, err := os.Stat(toolPath); err != nil { - fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName) + fmt.Fprintf(os.Stderr, "go: no such tool %q\n", toolName) SetExitStatus(2) Exit() } diff --git a/libgo/go/cmd/go/internal/bug/bug.go b/libgo/go/cmd/go/internal/bug/bug.go index 307527c695c..702dc2a14ac 100644 --- a/libgo/go/cmd/go/internal/bug/bug.go +++ b/libgo/go/cmd/go/internal/bug/bug.go @@ -40,7 +40,7 @@ func init() { func runBug(ctx context.Context, cmd *base.Command, args []string) { if len(args) > 0 { - base.Fatalf("go bug: bug takes no arguments") + base.Fatalf("go: bug takes no arguments") } var buf bytes.Buffer buf.WriteString(bugHeader) @@ -106,8 +106,9 @@ func printGoEnv(w io.Writer) { } func printGoDetails(w io.Writer) { - printCmdOut(w, "GOROOT/bin/go version: ", filepath.Join(runtime.GOROOT(), "bin/go"), "version") - printCmdOut(w, "GOROOT/bin/go tool compile -V: ", filepath.Join(runtime.GOROOT(), "bin/go"), "tool", "compile", "-V") + gocmd := filepath.Join(runtime.GOROOT(), "bin/go") + printCmdOut(w, "GOROOT/bin/go version: ", gocmd, "version") + printCmdOut(w, "GOROOT/bin/go tool compile -V: ", gocmd, "tool", "compile", "-V") } func printOSDetails(w io.Writer) { diff --git a/libgo/go/cmd/go/internal/cache/cache.go b/libgo/go/cmd/go/internal/cache/cache.go index d592d704978..93d7c25658f 100644 --- a/libgo/go/cmd/go/internal/cache/cache.go +++ b/libgo/go/cmd/go/internal/cache/cache.go @@ -533,3 +533,15 @@ func (c *Cache) copyFile(file io.ReadSeeker, out OutputID, size int64) error { return nil } + +// FuzzDir returns a subdirectory within the cache for storing fuzzing data. +// The subdirectory may not exist. +// +// This directory is managed by the internal/fuzz package. Files in this +// directory aren't removed by the 'go clean -cache' command or by Trim. +// They may be removed with 'go clean -fuzzcache'. +// +// TODO(#48526): make Trim remove unused files from this directory. +func (c *Cache) FuzzDir() string { + return filepath.Join(c.dir, "fuzz") +} diff --git a/libgo/go/cmd/go/internal/cache/default.go b/libgo/go/cmd/go/internal/cache/default.go index 0b1c1e0c203..426dddfb978 100644 --- a/libgo/go/cmd/go/internal/cache/default.go +++ b/libgo/go/cmd/go/internal/cache/default.go @@ -30,6 +30,7 @@ var ( // README as a courtesy to explain where it came from. const cacheREADME = `This directory holds cached build artifacts from the Go build system. Run "go clean -cache" if the directory is getting too large. +Run "go clean -fuzzcache" to delete the fuzz cache. See golang.org to learn more about Go. ` diff --git a/libgo/go/cmd/go/internal/cfg/cfg.go b/libgo/go/cmd/go/internal/cfg/cfg.go index 57a3c1ff6fb..7f68d7bb628 100644 --- a/libgo/go/cmd/go/internal/cfg/cfg.go +++ b/libgo/go/cmd/go/internal/cfg/cfg.go @@ -26,6 +26,7 @@ import ( var ( BuildA bool // -a flag BuildBuildmode string // -buildmode flag + BuildBuildvcs bool // -buildvcs flag BuildContext = defaultContext() BuildMod string // -mod flag BuildModExplicit bool // whether -mod was set explicitly @@ -33,6 +34,7 @@ var ( BuildI bool // -i flag BuildLinkshared bool // -linkshared flag BuildMSan bool // -msan flag + BuildASan bool // -asan flag BuildN bool // -n flag BuildO string // -o flag BuildP = runtime.GOMAXPROCS(0) // -p flag @@ -47,17 +49,26 @@ var ( BuildWork bool // -work flag BuildX bool // -x flag - ModCacheRW bool // -modcacherw flag - ModFile string // -modfile flag + ModCacheRW bool // -modcacherw flag + ModFile string // -modfile flag + WorkFile string // -workfile flag + WorkFileExplicit bool // whether -workfile was set explicitly CmdName string // "build", "install", "list", "mod tidy", etc. DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable) DebugTrace string // -debug-trace flag + + // GoPathError is set when GOPATH is not set. it contains an + // explanation why GOPATH is unset. + GoPathError string + + GOEXPERIMENT = envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT) ) func defaultContext() build.Context { ctxt := build.Default + ctxt.JoinPath = filepath.Join // back door to say "do not use go command" ctxt.GOROOT = findGOROOT() @@ -70,7 +81,7 @@ func defaultContext() build.Context { build.ToolDir = filepath.Join(ctxt.GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH) } - ctxt.GOPATH = envOr("GOPATH", ctxt.GOPATH) + ctxt.GOPATH = envOr("GOPATH", gopath(ctxt)) // Override defaults computed in go/build with defaults // from go environment configuration file, if known. @@ -79,7 +90,7 @@ func defaultContext() build.Context { // The experiments flags are based on GOARCH, so they may // need to change. TODO: This should be cleaned up. - buildcfg.UpdateExperiments(ctxt.GOOS, ctxt.GOARCH, envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT)) + buildcfg.UpdateExperiments(ctxt.GOOS, ctxt.GOARCH, GOEXPERIMENT) ctxt.ToolTags = nil for _, exp := range buildcfg.EnabledExperiments() { ctxt.ToolTags = append(ctxt.ToolTags, "goexperiment."+exp) @@ -261,6 +272,7 @@ var ( // Used in envcmd.MkEnv and build ID computations. GOARM = envOr("GOARM", fmt.Sprint(buildcfg.GOARM)) GO386 = envOr("GO386", buildcfg.GO386) + GOAMD64 = envOr("GOAMD64", fmt.Sprintf("%s%d", "v", buildcfg.GOAMD64)) GOMIPS = envOr("GOMIPS", buildcfg.GOMIPS) GOMIPS64 = envOr("GOMIPS64", buildcfg.GOMIPS64) GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", buildcfg.GOPPC64)) @@ -287,6 +299,8 @@ func GetArchEnv() (key, val string) { return "GOARM", GOARM case "386": return "GO386", GO386 + case "amd64": + return "GOAMD64", GOAMD64 case "mips", "mipsle": return "GOMIPS", GOMIPS case "mips64", "mips64le": @@ -396,3 +410,24 @@ func gopathDir(rel string) string { } return filepath.Join(list[0], rel) } + +func gopath(ctxt build.Context) string { + if len(ctxt.GOPATH) > 0 { + return ctxt.GOPATH + } + env := "HOME" + if runtime.GOOS == "windows" { + env = "USERPROFILE" + } else if runtime.GOOS == "plan9" { + env = "home" + } + if home := os.Getenv(env); home != "" { + def := filepath.Join(home, "go") + if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) { + GoPathError = "cannot set GOROOT as GOPATH" + } + return "" + } + GoPathError = fmt.Sprintf("%s is not set", env) + return "" +} diff --git a/libgo/go/cmd/go/internal/clean/clean.go b/libgo/go/cmd/go/internal/clean/clean.go index fd4cb205591..dc93cdf5983 100644 --- a/libgo/go/cmd/go/internal/clean/clean.go +++ b/libgo/go/cmd/go/internal/clean/clean.go @@ -75,6 +75,13 @@ The -modcache flag causes clean to remove the entire module download cache, including unpacked source code of versioned dependencies. +The -fuzzcache flag causes clean to remove files stored in the Go build +cache for fuzz testing. The fuzzing engine caches files that expand +code coverage, so removing them may make fuzzing less effective until +new inputs are found that provide the same coverage. These files are +distinct from those stored in testdata directory; clean does not remove +those files. + For more about build flags, see 'go help build'. For more about specifying packages, see 'go help packages'. @@ -85,6 +92,7 @@ var ( cleanI bool // clean -i flag cleanR bool // clean -r flag cleanCache bool // clean -cache flag + cleanFuzzcache bool // clean -fuzzcache flag cleanModcache bool // clean -modcache flag cleanTestcache bool // clean -testcache flag ) @@ -96,6 +104,7 @@ func init() { CmdClean.Flag.BoolVar(&cleanI, "i", false, "") CmdClean.Flag.BoolVar(&cleanR, "r", false, "") CmdClean.Flag.BoolVar(&cleanCache, "cache", false, "") + CmdClean.Flag.BoolVar(&cleanFuzzcache, "fuzzcache", false, "") CmdClean.Flag.BoolVar(&cleanModcache, "modcache", false, "") CmdClean.Flag.BoolVar(&cleanTestcache, "testcache", false, "") @@ -112,7 +121,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { // or no other target (such as a cache) was requested to be cleaned. cleanPkg := len(args) > 0 || cleanI || cleanR if (!modload.Enabled() || modload.HasModRoot()) && - !cleanCache && !cleanModcache && !cleanTestcache { + !cleanCache && !cleanModcache && !cleanTestcache && !cleanFuzzcache { cleanPkg = true } @@ -144,7 +153,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { // This also mimics what os.RemoveAll(dir) would do. if err := os.RemoveAll(d); err != nil && !printedErrors { printedErrors = true - base.Errorf("go clean -cache: %v", err) + base.Errorf("go: %v", err) } } } @@ -157,7 +166,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { if !cfg.BuildN { if err := os.RemoveAll(logFile); err != nil && !printedErrors { printedErrors = true - base.Errorf("go clean -cache: %v", err) + base.Errorf("go: %v", err) } } } @@ -187,7 +196,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { } if err != nil { if _, statErr := os.Stat(dir); !os.IsNotExist(statErr) { - base.Errorf("go clean -testcache: %v", err) + base.Errorf("go: %v", err) } } } @@ -195,14 +204,26 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { if cleanModcache { if cfg.GOMODCACHE == "" { - base.Fatalf("go clean -modcache: no module cache") + base.Fatalf("go: cannot clean -modcache without a module cache") } if cfg.BuildN || cfg.BuildX { b.Showcmd("", "rm -rf %s", cfg.GOMODCACHE) } if !cfg.BuildN { if err := modfetch.RemoveAll(cfg.GOMODCACHE); err != nil { - base.Errorf("go clean -modcache: %v", err) + base.Errorf("go: %v", err) + } + } + } + + if cleanFuzzcache { + fuzzDir := cache.Default().FuzzDir() + if cfg.BuildN || cfg.BuildX { + b.Showcmd("", "rm -rf %s", fuzzDir) + } + if !cfg.BuildN { + if err := os.RemoveAll(fuzzDir); err != nil { + base.Errorf("go: %v", err) } } } @@ -245,7 +266,7 @@ func clean(p *load.Package) { } dirs, err := os.ReadDir(p.Dir) if err != nil { - base.Errorf("go clean %s: %v", p.Dir, err) + base.Errorf("go: %s: %v", p.Dir, err) return } @@ -334,7 +355,7 @@ func clean(p *load.Package) { } } if err := os.RemoveAll(filepath.Join(p.Dir, name)); err != nil { - base.Errorf("go clean: %v", err) + base.Errorf("go: %v", err) } } continue @@ -386,5 +407,5 @@ func removeFile(f string) { return } } - base.Errorf("go clean: %v", err) + base.Errorf("go: %v", err) } diff --git a/libgo/go/cmd/go/internal/cmdflag/flag.go b/libgo/go/cmd/go/internal/cmdflag/flag.go index 8abb7e559f5..a634bc1ab8d 100644 --- a/libgo/go/cmd/go/internal/cmdflag/flag.go +++ b/libgo/go/cmd/go/internal/cmdflag/flag.go @@ -92,7 +92,7 @@ func ParseOne(fs *flag.FlagSet, args []string) (f *flag.Flag, remainingArgs []st // Use fs.Set instead of f.Value.Set below so that any subsequent call to // fs.Visit will correctly visit the flags that have been set. - failf := func(format string, a ...interface{}) (*flag.Flag, []string, error) { + failf := func(format string, a ...any) (*flag.Flag, []string, error) { return f, args, fmt.Errorf(format, a...) } diff --git a/libgo/go/cmd/go/internal/doc/doc.go b/libgo/go/cmd/go/internal/doc/doc.go index 8580a5dc4d2..7741a9022c9 100644 --- a/libgo/go/cmd/go/internal/doc/doc.go +++ b/libgo/go/cmd/go/internal/doc/doc.go @@ -60,9 +60,8 @@ The package path must be either a qualified path or a proper suffix of a path. The go tool's usual package mechanism does not apply: package path elements like . and ... are not implemented by go doc. -When run with two arguments, the first must be a full package path (not just a -suffix), and the second is a symbol, or symbol with method or struct field. -This is similar to the syntax accepted by godoc: +When run with two arguments, the first is a package path (full path or suffix), +and the second is a symbol, or symbol with method or struct field: go doc <pkg> <sym>[.<methodOrField>] diff --git a/libgo/go/cmd/go/internal/envcmd/env.go b/libgo/go/cmd/go/internal/envcmd/env.go index 1553d263914..e56dd8223f0 100644 --- a/libgo/go/cmd/go/internal/envcmd/env.go +++ b/libgo/go/cmd/go/internal/envcmd/env.go @@ -26,6 +26,7 @@ import ( "cmd/go/internal/load" "cmd/go/internal/modload" "cmd/go/internal/work" + "cmd/internal/quoted" ) var CmdEnv = &base.Command{ @@ -104,13 +105,13 @@ func MkEnv() []cfg.EnvVar { env = append(env, cfg.EnvVar{Name: key, Value: val}) } - cc := cfg.DefaultCC(cfg.Goos, cfg.Goarch) - if env := strings.Fields(cfg.Getenv("CC")); len(env) > 0 { - cc = env[0] + cc := cfg.Getenv("CC") + if cc == "" { + cc = cfg.DefaultCC(cfg.Goos, cfg.Goarch) } - cxx := cfg.DefaultCXX(cfg.Goos, cfg.Goarch) - if env := strings.Fields(cfg.Getenv("CXX")); len(env) > 0 { - cxx = env[0] + cxx := cfg.Getenv("CXX") + if cxx == "" { + cxx = cfg.DefaultCXX(cfg.Goos, cfg.Goarch) } env = append(env, cfg.EnvVar{Name: "AR", Value: envOr("AR", "ar")}) env = append(env, cfg.EnvVar{Name: "CC", Value: cc}) @@ -145,13 +146,17 @@ func findEnv(env []cfg.EnvVar, name string) string { // ExtraEnvVars returns environment variables that should not leak into child processes. func ExtraEnvVars() []cfg.EnvVar { gomod := "" + modload.Init() if modload.HasModRoot() { - gomod = filepath.Join(modload.ModRoot(), "go.mod") + gomod = modload.ModFilePath() } else if modload.Enabled() { gomod = os.DevNull } + modload.InitWorkfile() + gowork := modload.WorkFilePath() return []cfg.EnvVar{ {Name: "GOMOD", Value: gomod}, + {Name: "GOWORK", Value: gowork}, } } @@ -191,13 +196,13 @@ func argKey(arg string) string { func runEnv(ctx context.Context, cmd *base.Command, args []string) { if *envJson && *envU { - base.Fatalf("go env: cannot use -json with -u") + base.Fatalf("go: cannot use -json with -u") } if *envJson && *envW { - base.Fatalf("go env: cannot use -json with -w") + base.Fatalf("go: cannot use -json with -w") } if *envU && *envW { - base.Fatalf("go env: cannot use -u with -w") + base.Fatalf("go: cannot use -u with -w") } // Handle 'go env -w' and 'go env -u' before calling buildcfg.Check, @@ -275,7 +280,7 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) { func runEnvW(args []string) { // Process and sanity-check command line. if len(args) == 0 { - base.Fatalf("go env -w: no KEY=VALUE arguments given") + base.Fatalf("go: no KEY=VALUE arguments given") } osEnv := make(map[string]string) for _, e := range cfg.OrigEnv { @@ -287,14 +292,14 @@ func runEnvW(args []string) { for _, arg := range args { i := strings.Index(arg, "=") if i < 0 { - base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg) + base.Fatalf("go: arguments must be KEY=VALUE: invalid argument: %s", arg) } key, val := arg[:i], arg[i+1:] if err := checkEnvWrite(key, val); err != nil { - base.Fatalf("go env -w: %v", err) + base.Fatalf("go: %v", err) } if _, ok := add[key]; ok { - base.Fatalf("go env -w: multiple values for key: %s", key) + base.Fatalf("go: multiple values for key: %s", key) } add[key] = val if osVal := osEnv[key]; osVal != "" && osVal != val { @@ -303,13 +308,13 @@ func runEnvW(args []string) { } if err := checkBuildConfig(add, nil); err != nil { - base.Fatalf("go env -w: %v", err) + base.Fatalf("go: %v", err) } gotmp, okGOTMP := add["GOTMPDIR"] if okGOTMP { if !filepath.IsAbs(gotmp) && gotmp != "" { - base.Fatalf("go env -w: GOTMPDIR must be an absolute path") + base.Fatalf("go: GOTMPDIR must be an absolute path") } } @@ -319,18 +324,18 @@ func runEnvW(args []string) { func runEnvU(args []string) { // Process and sanity-check command line. if len(args) == 0 { - base.Fatalf("go env -u: no arguments given") + base.Fatalf("go: 'go env -u' requires an argument") } del := make(map[string]bool) for _, arg := range args { if err := checkEnvWrite(arg, ""); err != nil { - base.Fatalf("go env -u: %v", err) + base.Fatalf("go: %v", err) } del[arg] = true } if err := checkBuildConfig(nil, del); err != nil { - base.Fatalf("go env -u: %v", err) + base.Fatalf("go: %v", err) } updateEnvFile(nil, del) @@ -414,7 +419,7 @@ func printEnvAsJSON(env []cfg.EnvVar) { enc := json.NewEncoder(os.Stdout) enc.SetIndent("", "\t") if err := enc.Encode(m); err != nil { - base.Fatalf("go env -json: %s", err) + base.Fatalf("go: %s", err) } } @@ -429,7 +434,7 @@ func getOrigEnv(key string) string { func checkEnvWrite(key, val string) error { switch key { - case "GOEXE", "GOGCCFLAGS", "GOHOSTARCH", "GOHOSTOS", "GOMOD", "GOTOOLDIR", "GOVERSION": + case "GOEXE", "GOGCCFLAGS", "GOHOSTARCH", "GOHOSTOS", "GOMOD", "GOWORK", "GOTOOLDIR", "GOVERSION": return fmt.Errorf("%s cannot be modified", key) case "GOENV": return fmt.Errorf("%s can only be set using the OS environment", key) @@ -457,10 +462,23 @@ func checkEnvWrite(key, val string) error { if !filepath.IsAbs(val) && val != "" { return fmt.Errorf("GOPATH entry is relative; must be absolute path: %q", val) } - // Make sure CC and CXX are absolute paths - case "CC", "CXX", "GOMODCACHE": - if !filepath.IsAbs(val) && val != "" && val != filepath.Base(val) { - return fmt.Errorf("%s entry is relative; must be absolute path: %q", key, val) + case "GOMODCACHE": + if !filepath.IsAbs(val) && val != "" { + return fmt.Errorf("GOMODCACHE entry is relative; must be absolute path: %q", val) + } + case "CC", "CXX": + if val == "" { + break + } + args, err := quoted.Split(val) + if err != nil { + return fmt.Errorf("invalid %s: %v", key, err) + } + if len(args) == 0 { + return fmt.Errorf("%s entry cannot contain only space", key) + } + if !filepath.IsAbs(args[0]) && args[0] != filepath.Base(args[0]) { + return fmt.Errorf("%s entry is relative; must be absolute path: %q", key, args[0]) } } @@ -479,11 +497,11 @@ func checkEnvWrite(key, val string) error { func updateEnvFile(add map[string]string, del map[string]bool) { file, err := cfg.EnvFile() if file == "" { - base.Fatalf("go env: cannot find go env config: %v", err) + base.Fatalf("go: cannot find go env config: %v", err) } data, err := os.ReadFile(file) if err != nil && (!os.IsNotExist(err) || len(add) == 0) { - base.Fatalf("go env: reading go env config: %v", err) + base.Fatalf("go: reading go env config: %v", err) } lines := strings.SplitAfter(string(data), "\n") @@ -541,7 +559,7 @@ func updateEnvFile(add map[string]string, del map[string]bool) { os.MkdirAll(filepath.Dir(file), 0777) err = os.WriteFile(file, data, 0666) if err != nil { - base.Fatalf("go env: writing go env config: %v", err) + base.Fatalf("go: writing go env config: %v", err) } } } diff --git a/libgo/go/cmd/go/internal/fix/fix.go b/libgo/go/cmd/go/internal/fix/fix.go index 988d45e71cc..d8ba353de65 100644 --- a/libgo/go/cmd/go/internal/fix/fix.go +++ b/libgo/go/cmd/go/internal/fix/fix.go @@ -11,27 +11,39 @@ import ( "cmd/go/internal/load" "cmd/go/internal/modload" "cmd/go/internal/str" + "cmd/go/internal/work" "context" "fmt" + "go/build" "os" ) var CmdFix = &base.Command{ - Run: runFix, - UsageLine: "go fix [packages]", + UsageLine: "go fix [-fix list] [packages]", Short: "update packages to use new APIs", Long: ` Fix runs the Go fix command on the packages named by the import paths. +The -fix flag sets a comma-separated list of fixes to run. +The default is all known fixes. +(Its value is passed to 'go tool fix -r'.) + For more about fix, see 'go doc cmd/fix'. For more about specifying packages, see 'go help packages'. -To run fix with specific options, run 'go tool fix'. +To run fix with other options, run 'go tool fix'. See also: go fmt, go vet. `, } +var fixes = CmdFix.Flag.String("fix", "", "comma-separated list of fixes to apply") + +func init() { + work.AddBuildFlags(CmdFix, work.DefaultBuildFlags) + CmdFix.Run = runFix // fix cycle +} + func runFix(ctx context.Context, cmd *base.Command, args []string) { pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{}, args) w := 0 @@ -58,6 +70,16 @@ func runFix(ctx context.Context, cmd *base.Command, args []string) { // the command only applies to this package, // not to packages in subdirectories. files := base.RelPaths(pkg.InternalAllGoFiles()) - base.Run(str.StringList(cfg.BuildToolexec, base.Tool("fix"), files)) + goVersion := "" + if pkg.Module != nil { + goVersion = "go" + pkg.Module.GoVersion + } else if pkg.Standard { + goVersion = build.Default.ReleaseTags[len(build.Default.ReleaseTags)-1] + } + var fixArg []string + if *fixes != "" { + fixArg = []string{"-r=" + *fixes} + } + base.Run(str.StringList(cfg.BuildToolexec, base.Tool("fix"), "-go="+goVersion, fixArg, files)) } } diff --git a/libgo/go/cmd/go/internal/fmtcmd/fmt.go b/libgo/go/cmd/go/internal/fmtcmd/fmt.go index 8a040087539..19656eab7fc 100644 --- a/libgo/go/cmd/go/internal/fmtcmd/fmt.go +++ b/libgo/go/cmd/go/internal/fmtcmd/fmt.go @@ -11,14 +11,12 @@ import ( "fmt" "os" "path/filepath" - "runtime" - "sync" "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/load" "cmd/go/internal/modload" - "cmd/go/internal/str" + "cmd/internal/sys" ) func init() { @@ -53,18 +51,13 @@ See also: go fix, go vet. func runFmt(ctx context.Context, cmd *base.Command, args []string) { printed := false gofmt := gofmtPath() - procs := runtime.GOMAXPROCS(0) - var wg sync.WaitGroup - wg.Add(procs) - fileC := make(chan string, 2*procs) - for i := 0; i < procs; i++ { - go func() { - defer wg.Done() - for file := range fileC { - base.Run(str.StringList(gofmt, "-l", "-w", file)) - } - }() - } + + gofmtArgs := []string{gofmt, "-l", "-w"} + gofmtArgLen := len(gofmt) + len(" -l -w") + + baseGofmtArgs := len(gofmtArgs) + baseGofmtArgLen := gofmtArgLen + for _, pkg := range load.PackagesAndErrors(ctx, load.PackageOpts{}, args) { if modload.Enabled() && pkg.Module != nil && !pkg.Module.Main { if !printed { @@ -89,11 +82,18 @@ func runFmt(ctx context.Context, cmd *base.Command, args []string) { // not to packages in subdirectories. files := base.RelPaths(pkg.InternalAllGoFiles()) for _, file := range files { - fileC <- file + gofmtArgs = append(gofmtArgs, file) + gofmtArgLen += 1 + len(file) // plus separator + if gofmtArgLen >= sys.ExecArgLengthLimit { + base.Run(gofmtArgs) + gofmtArgs = gofmtArgs[:baseGofmtArgs] + gofmtArgLen = baseGofmtArgLen + } } } - close(fileC) - wg.Wait() + if len(gofmtArgs) > baseGofmtArgs { + base.Run(gofmtArgs) + } } func gofmtPath() string { diff --git a/libgo/go/cmd/go/internal/fsys/fsys.go b/libgo/go/cmd/go/internal/fsys/fsys.go index 0b806027e64..9a1bbf890e1 100644 --- a/libgo/go/cmd/go/internal/fsys/fsys.go +++ b/libgo/go/cmd/go/internal/fsys/fsys.go @@ -499,7 +499,7 @@ func (f fakeFile) Size() int64 { return f.real.Size() } func (f fakeFile) Mode() fs.FileMode { return f.real.Mode() } func (f fakeFile) ModTime() time.Time { return f.real.ModTime() } func (f fakeFile) IsDir() bool { return f.real.IsDir() } -func (f fakeFile) Sys() interface{} { return f.real.Sys() } +func (f fakeFile) Sys() any { return f.real.Sys() } // missingFile provides an fs.FileInfo for an overlaid file where the // destination file in the overlay doesn't exist. It returns zero values @@ -512,7 +512,7 @@ func (f missingFile) Size() int64 { return 0 } func (f missingFile) Mode() fs.FileMode { return fs.ModeIrregular } func (f missingFile) ModTime() time.Time { return time.Unix(0, 0) } func (f missingFile) IsDir() bool { return false } -func (f missingFile) Sys() interface{} { return nil } +func (f missingFile) Sys() any { return nil } // fakeDir provides an fs.FileInfo implementation for directories that are // implicitly created by overlaid files. Each directory in the @@ -524,7 +524,7 @@ func (f fakeDir) Size() int64 { return 0 } func (f fakeDir) Mode() fs.FileMode { return fs.ModeDir | 0500 } func (f fakeDir) ModTime() time.Time { return time.Unix(0, 0) } func (f fakeDir) IsDir() bool { return true } -func (f fakeDir) Sys() interface{} { return nil } +func (f fakeDir) Sys() any { return nil } // Glob is like filepath.Glob but uses the overlay file system. func Glob(pattern string) (matches []string, err error) { diff --git a/libgo/go/cmd/go/internal/fsys/fsys_test.go b/libgo/go/cmd/go/internal/fsys/fsys_test.go index 7f175c70311..c080c14987c 100644 --- a/libgo/go/cmd/go/internal/fsys/fsys_test.go +++ b/libgo/go/cmd/go/internal/fsys/fsys_test.go @@ -1,7 +1,6 @@ package fsys import ( - "cmd/go/internal/txtar" "encoding/json" "errors" "fmt" @@ -12,6 +11,8 @@ import ( "path/filepath" "reflect" "testing" + + "golang.org/x/tools/txtar" ) // initOverlay resets the overlay state to reflect the config. diff --git a/libgo/go/cmd/go/internal/generate/generate.go b/libgo/go/cmd/go/internal/generate/generate.go index 80ea32b4284..54ccfe78f24 100644 --- a/libgo/go/cmd/go/internal/generate/generate.go +++ b/libgo/go/cmd/go/internal/generate/generate.go @@ -38,7 +38,7 @@ Generate runs commands described by directives within existing files. Those commands can run any process but the intent is to create or update Go source files. -Go generate is never run automatically by go build, go get, go test, +Go generate is never run automatically by go build, go test, and so on. It must be run explicitly. Go generate scans the file for directives, which are lines of @@ -408,7 +408,7 @@ var stop = fmt.Errorf("error in generation") // errorf logs an error message prefixed with the file and line number. // It then exits the program (with exit status 1) because generation stops // at the first error. -func (g *Generator) errorf(format string, args ...interface{}) { +func (g *Generator) errorf(format string, args ...any) { fmt.Fprintf(os.Stderr, "%s:%d: %s\n", base.ShortPath(g.path), g.lineNum, fmt.Sprintf(format, args...)) panic(stop) diff --git a/libgo/go/cmd/go/internal/get/get.go b/libgo/go/cmd/go/internal/get/get.go index 3c16dc3040f..8cf8fe6645f 100644 --- a/libgo/go/cmd/go/internal/get/get.go +++ b/libgo/go/cmd/go/internal/get/get.go @@ -114,16 +114,16 @@ func init() { func runGet(ctx context.Context, cmd *base.Command, args []string) { if cfg.ModulesEnabled { // Should not happen: main.go should install the separate module-enabled get code. - base.Fatalf("go get: modules not implemented") + base.Fatalf("go: modules not implemented") } work.BuildInit() if *getF && !*getU { - base.Fatalf("go get: cannot use -f flag without -u") + base.Fatalf("go: cannot use -f flag without -u") } if *getInsecure { - base.Fatalf("go get: -insecure flag is no longer supported; use GOINSECURE instead") + base.Fatalf("go: -insecure flag is no longer supported; use GOINSECURE instead") } // Disable any prompting for passwords by Git itself. @@ -214,18 +214,19 @@ func downloadPaths(patterns []string) []string { // if the argument has no slash or refers to an existing file. if strings.HasSuffix(arg, ".go") { if !strings.Contains(arg, "/") { - base.Errorf("go get %s: arguments must be package or module paths", arg) + base.Errorf("go: %s: arguments must be package or module paths", arg) continue } if fi, err := os.Stat(arg); err == nil && !fi.IsDir() { - base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", arg) + base.Errorf("go: %s exists as a file, but 'go get' requires package arguments", arg) } } } base.ExitIfErrors() var pkgs []string - for _, m := range search.ImportPathsQuiet(patterns) { + noModRoots := []string{} + for _, m := range search.ImportPathsQuiet(patterns, noModRoots) { if len(m.Pkgs) == 0 && strings.Contains(m.Pattern(), "...") { pkgs = append(pkgs, m.Pattern()) } else { @@ -315,7 +316,8 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) if wildcardOkay && strings.Contains(arg, "...") { match := search.NewMatch(arg) if match.IsLocal() { - match.MatchDirs() + noModRoots := []string{} // We're in gopath mode, so there are no modroots. + match.MatchDirs(noModRoots) args = match.Dirs } else { match.MatchPackages() @@ -415,10 +417,10 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) // to make the first copy of or update a copy of the given package. func downloadPackage(p *load.Package) error { var ( - vcsCmd *vcs.Cmd - repo, rootPath string - err error - blindRepo bool // set if the repo has unusual configuration + vcsCmd *vcs.Cmd + repo, rootPath, repoDir string + err error + blindRepo bool // set if the repo has unusual configuration ) // p can be either a real package, or a pseudo-package whose “import path” is @@ -444,10 +446,19 @@ func downloadPackage(p *load.Package) error { if p.Internal.Build.SrcRoot != "" { // Directory exists. Look for checkout along path to src. - vcsCmd, rootPath, err = vcs.FromDir(p.Dir, p.Internal.Build.SrcRoot) + const allowNesting = false + repoDir, vcsCmd, err = vcs.FromDir(p.Dir, p.Internal.Build.SrcRoot, allowNesting) if err != nil { return err } + if !str.HasFilePathPrefix(repoDir, p.Internal.Build.SrcRoot) { + panic(fmt.Sprintf("repository %q not in source root %q", repo, p.Internal.Build.SrcRoot)) + } + rootPath = str.TrimFilePathPrefix(repoDir, p.Internal.Build.SrcRoot) + if err := vcs.CheckGOVCS(vcsCmd, rootPath); err != nil { + return err + } + repo = "<local>" // should be unused; make distinctive // Double-check where it came from. diff --git a/libgo/go/cmd/go/internal/help/help.go b/libgo/go/cmd/go/internal/help/help.go index 7a730fc8eb8..2a07d2423bd 100644 --- a/libgo/go/cmd/go/internal/help/help.go +++ b/libgo/go/cmd/go/internal/help/help.go @@ -162,7 +162,7 @@ func (w *errWriter) Write(b []byte) (int, error) { } // tmpl executes the given template text on data, writing the result to w. -func tmpl(w io.Writer, text string, data interface{}) { +func tmpl(w io.Writer, text string, data any) { t := template.New("top") t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize}) template.Must(t.Parse(text)) diff --git a/libgo/go/cmd/go/internal/help/helpdoc.go b/libgo/go/cmd/go/internal/help/helpdoc.go index 490ff1fb7cf..7dc066cfbad 100644 --- a/libgo/go/cmd/go/internal/help/helpdoc.go +++ b/libgo/go/cmd/go/internal/help/helpdoc.go @@ -592,6 +592,10 @@ Architecture-specific environment variables: GO386 For GOARCH=386, how to implement floating point instructions. Valid values are sse2 (default), softfloat. + GOAMD64 + For GOARCH=amd64, the microarchitecture level for which to compile. + Valid values are v1 (default), v2, v3, v4. + See https://golang.org/wiki/MinimumRequirements#amd64 GOMIPS For GOARCH=mips{,le}, whether to use floating point instructions. Valid values are hardfloat (default), softfloat. @@ -771,6 +775,13 @@ The go command also caches successful package test results. See 'go help test' for details. Running 'go clean -testcache' removes all cached test results (but not cached build results). +The go command also caches values used in fuzzing with 'go test -fuzz', +specifically, values that expanded code coverage when passed to a +fuzz function. These values are not used for regular building and +testing, but they're stored in a subdirectory of the build cache. +Running 'go clean -fuzzcache' removes all cached fuzzing values. +This may make fuzzing less effective, temporarily. + The GODEBUG environment variable can enable printing of debugging information about the state of the cache: diff --git a/libgo/go/cmd/go/internal/imports/build.go b/libgo/go/cmd/go/internal/imports/build.go index 50aeabc578c..ff6bea6777d 100644 --- a/libgo/go/cmd/go/internal/imports/build.go +++ b/libgo/go/cmd/go/internal/imports/build.go @@ -2,17 +2,51 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Copied from Go distribution src/go/build/build.go, syslist.go +// Copied from Go distribution src/go/build/build.go, syslist.go. +// That package does not export the ability to process raw file data, +// although we could fake it with an appropriate build.Context +// and a lot of unwrapping. +// More importantly, that package does not implement the tags["*"] +// special case, in which both tag and !tag are considered to be true +// for essentially all tags (except "ignore"). +// +// If we added this API to go/build directly, we wouldn't need this +// file anymore, but this API is not terribly general-purpose and we +// don't really want to commit to any public form of it, nor do we +// want to move the core parts of go/build into a top-level internal package. +// These details change very infrequently, so the copy is fine. package imports import ( "bytes" + "errors" + "fmt" + "go/build/constraint" "strings" "unicode" ) -var slashslash = []byte("//") +var ( + bSlashSlash = []byte("//") + bStarSlash = []byte("*/") + bSlashStar = []byte("/*") + bPlusBuild = []byte("+build") + + goBuildComment = []byte("//go:build") + + errGoBuildWithoutBuild = errors.New("//go:build comment without // +build comment") + errMultipleGoBuild = errors.New("multiple //go:build comments") +) + +func isGoBuildComment(line []byte) bool { + if !bytes.HasPrefix(line, goBuildComment) { + return false + } + line = bytes.TrimSpace(line) + rest := line[len(goBuildComment):] + return len(rest) == 0 || len(bytes.TrimSpace(rest)) < len(rest) +} // ShouldBuild reports whether it is okay to use this file, // The rule is that in the file's leading run of // comments @@ -34,10 +68,61 @@ var slashslash = []byte("//") // in any build. // func ShouldBuild(content []byte, tags map[string]bool) bool { - // Pass 1. Identify leading run of // comments and blank lines, + // Identify leading run of // comments and blank lines, // which must be followed by a blank line. + // Also identify any //go:build comments. + content, goBuild, _, err := parseFileHeader(content) + if err != nil { + return false + } + + // If //go:build line is present, it controls. + // Otherwise fall back to +build processing. + var shouldBuild bool + switch { + case goBuild != nil: + x, err := constraint.Parse(string(goBuild)) + if err != nil { + return false + } + shouldBuild = eval(x, tags, true) + + default: + shouldBuild = true + p := content + for len(p) > 0 { + line := p + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, p = line[:i], p[i+1:] + } else { + p = p[len(p):] + } + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, bSlashSlash) || !bytes.Contains(line, bPlusBuild) { + continue + } + text := string(line) + if !constraint.IsPlusBuild(text) { + continue + } + if x, err := constraint.Parse(text); err == nil { + if !eval(x, tags, true) { + shouldBuild = false + } + } + } + } + + return shouldBuild +} + +func parseFileHeader(content []byte) (trimmed, goBuild []byte, sawBinaryOnly bool, err error) { end := 0 p := content + ended := false // found non-blank, non-// line, so stopped accepting // +build lines + inSlashStar := false // in /* */ comment + +Lines: for len(p) > 0 { line := p if i := bytes.IndexByte(line, '\n'); i >= 0 { @@ -46,78 +131,61 @@ func ShouldBuild(content []byte, tags map[string]bool) bool { p = p[len(p):] } line = bytes.TrimSpace(line) - if len(line) == 0 { // Blank line + if len(line) == 0 && !ended { // Blank line + // Remember position of most recent blank line. + // When we find the first non-blank, non-// line, + // this "end" position marks the latest file position + // where a // +build line can appear. + // (It must appear _before_ a blank line before the non-blank, non-// line. + // Yes, that's confusing, which is part of why we moved to //go:build lines.) + // Note that ended==false here means that inSlashStar==false, + // since seeing a /* would have set ended==true. end = len(content) - len(p) - continue + continue Lines } - if !bytes.HasPrefix(line, slashslash) { // Not comment line - break + if !bytes.HasPrefix(line, bSlashSlash) { // Not comment line + ended = true } - } - content = content[:end] - // Pass 2. Process each line in the run. - p = content - allok := true - for len(p) > 0 { - line := p - if i := bytes.IndexByte(line, '\n'); i >= 0 { - line, p = line[:i], p[i+1:] - } else { - p = p[len(p):] - } - line = bytes.TrimSpace(line) - if !bytes.HasPrefix(line, slashslash) { - continue + if !inSlashStar && isGoBuildComment(line) { + if goBuild != nil { + return nil, nil, false, errMultipleGoBuild + } + goBuild = line } - line = bytes.TrimSpace(line[len(slashslash):]) - if len(line) > 0 && line[0] == '+' { - // Looks like a comment +line. - f := strings.Fields(string(line)) - if f[0] == "+build" { - ok := false - for _, tok := range f[1:] { - if matchTags(tok, tags) { - ok = true - } - } - if !ok { - allok = false + + Comments: + for len(line) > 0 { + if inSlashStar { + if i := bytes.Index(line, bStarSlash); i >= 0 { + inSlashStar = false + line = bytes.TrimSpace(line[i+len(bStarSlash):]) + continue Comments } + continue Lines } + if bytes.HasPrefix(line, bSlashSlash) { + continue Lines + } + if bytes.HasPrefix(line, bSlashStar) { + inSlashStar = true + line = bytes.TrimSpace(line[len(bSlashStar):]) + continue Comments + } + // Found non-comment text. + break Lines } } - return allok -} - -// matchTags reports whether the name is one of: -// -// tag (if tags[tag] is true) -// !tag (if tags[tag] is false) -// a comma-separated list of any of these -// -func matchTags(name string, tags map[string]bool) bool { - if name == "" { - return false - } - if i := strings.Index(name, ","); i >= 0 { - // comma-separated list - ok1 := matchTags(name[:i], tags) - ok2 := matchTags(name[i+1:], tags) - return ok1 && ok2 - } - if strings.HasPrefix(name, "!!") { // bad syntax, reject always - return false - } - if strings.HasPrefix(name, "!") { // negation - return len(name) > 1 && matchTag(name[1:], tags, false) - } - return matchTag(name, tags, true) + return content[:end], goBuild, sawBinaryOnly, nil } -// matchTag reports whether the tag name is valid and satisfied by tags[name]==want. -func matchTag(name string, tags map[string]bool, want bool) bool { +// matchTag reports whether the tag name is valid and tags[name] is true. +// As a special case, if tags["*"] is true and name is not empty or ignore, +// then matchTag will return prefer instead of the actual answer, +// which allows the caller to pretend in that case that most tags are +// both true and false. +func matchTag(name string, tags map[string]bool, prefer bool) bool { // Tags must be letters, digits, underscores or dots. // Unlike in Go identifiers, all digits are fine (e.g., "386"). for _, c := range name { @@ -131,7 +199,7 @@ func matchTag(name string, tags map[string]bool, want bool) bool { // if we put * in the tags map then all tags // except "ignore" are considered both present and not // (so we return true no matter how 'want' is set). - return true + return prefer } have := tags[name] @@ -144,7 +212,25 @@ func matchTag(name string, tags map[string]bool, want bool) bool { if name == "darwin" { have = have || tags["ios"] } - return have == want + return have +} + +// eval is like +// x.Eval(func(tag string) bool { return matchTag(tag, tags) }) +// except that it implements the special case for tags["*"] meaning +// all tags are both true and false at the same time. +func eval(x constraint.Expr, tags map[string]bool, prefer bool) bool { + switch x := x.(type) { + case *constraint.TagExpr: + return matchTag(x.Tag, tags, prefer) + case *constraint.NotExpr: + return !eval(x.X, tags, !prefer) + case *constraint.AndExpr: + return eval(x.X, tags, prefer) && eval(x.Y, tags, prefer) + case *constraint.OrExpr: + return eval(x.X, tags, prefer) || eval(x.Y, tags, prefer) + } + panic(fmt.Sprintf("unexpected constraint expression %T", x)) } // MatchFile returns false if the name contains a $GOOS or $GOARCH diff --git a/libgo/go/cmd/go/internal/imports/scan_test.go b/libgo/go/cmd/go/internal/imports/scan_test.go index 2d245ee7872..7e69c56513a 100644 --- a/libgo/go/cmd/go/internal/imports/scan_test.go +++ b/libgo/go/cmd/go/internal/imports/scan_test.go @@ -33,7 +33,7 @@ func TestScan(t *testing.T) { } if p == "net/http" { // A test import but not an import - t.Errorf("json reported as importing encoding/binary but does not") + t.Errorf("json reported as importing net/http but does not") } } if !foundBase64 { diff --git a/libgo/go/cmd/go/internal/imports/testdata/android/e.go b/libgo/go/cmd/go/internal/imports/testdata/android/e.go index d9b2db769b5..f1b9c888c2c 100644 --- a/libgo/go/cmd/go/internal/imports/testdata/android/e.go +++ b/libgo/go/cmd/go/internal/imports/testdata/android/e.go @@ -1,3 +1,4 @@ +//go:build android // +build android package android diff --git a/libgo/go/cmd/go/internal/imports/testdata/android/f.go b/libgo/go/cmd/go/internal/imports/testdata/android/f.go index 281e4dd6b98..bb0ff7b73f6 100644 --- a/libgo/go/cmd/go/internal/imports/testdata/android/f.go +++ b/libgo/go/cmd/go/internal/imports/testdata/android/f.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package android diff --git a/libgo/go/cmd/go/internal/imports/testdata/android/g.go b/libgo/go/cmd/go/internal/imports/testdata/android/g.go index 66a789c0ada..ee19424890a 100644 --- a/libgo/go/cmd/go/internal/imports/testdata/android/g.go +++ b/libgo/go/cmd/go/internal/imports/testdata/android/g.go @@ -1,3 +1,4 @@ +//go:build !android // +build !android package android diff --git a/libgo/go/cmd/go/internal/imports/testdata/illumos/e.go b/libgo/go/cmd/go/internal/imports/testdata/illumos/e.go index 5e1ed3cb9de..fddf2c42990 100644 --- a/libgo/go/cmd/go/internal/imports/testdata/illumos/e.go +++ b/libgo/go/cmd/go/internal/imports/testdata/illumos/e.go @@ -1,3 +1,4 @@ +//go:build illumos // +build illumos package illumos diff --git a/libgo/go/cmd/go/internal/imports/testdata/illumos/f.go b/libgo/go/cmd/go/internal/imports/testdata/illumos/f.go index f3e3f728bce..4b6d528e4c2 100644 --- a/libgo/go/cmd/go/internal/imports/testdata/illumos/f.go +++ b/libgo/go/cmd/go/internal/imports/testdata/illumos/f.go @@ -1,3 +1,4 @@ +//go:build solaris // +build solaris package illumos diff --git a/libgo/go/cmd/go/internal/imports/testdata/illumos/g.go b/libgo/go/cmd/go/internal/imports/testdata/illumos/g.go index b30f1eb4037..1bf826b8151 100644 --- a/libgo/go/cmd/go/internal/imports/testdata/illumos/g.go +++ b/libgo/go/cmd/go/internal/imports/testdata/illumos/g.go @@ -1,3 +1,4 @@ +//go:build !illumos // +build !illumos package illumos diff --git a/libgo/go/cmd/go/internal/imports/testdata/star/x1.go b/libgo/go/cmd/go/internal/imports/testdata/star/x1.go index 6a9594aed03..eaaea979e9d 100644 --- a/libgo/go/cmd/go/internal/imports/testdata/star/x1.go +++ b/libgo/go/cmd/go/internal/imports/testdata/star/x1.go @@ -1,8 +1,5 @@ -// +build blahblh -// +build linux -// +build !linux -// +build windows -// +build darwin +//go:build blahblh && linux && !linux && windows && darwin +// +build blahblh,linux,!linux,windows,darwin package x diff --git a/libgo/go/cmd/go/internal/list/list.go b/libgo/go/cmd/go/internal/list/list.go index 7cb9ec6d949..d9a7078ccf2 100644 --- a/libgo/go/cmd/go/internal/list/list.go +++ b/libgo/go/cmd/go/internal/list/list.go @@ -316,6 +316,7 @@ For more about modules, see https://golang.org/ref/mod. func init() { CmdList.Run = runList // break init cycle work.AddBuildFlags(CmdList, work.DefaultBuildFlags) + base.AddWorkfileFlag(&CmdList.Flag) } var ( @@ -336,6 +337,8 @@ var ( var nl = []byte{'\n'} func runList(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() + if *listFmt != "" && *listJson == true { base.Fatalf("go list -f cannot be used with -json") } @@ -355,9 +358,9 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { } } - var do func(interface{}) + var do func(any) if *listJson { - do = func(x interface{}) { + do = func(x any) { b, err := json.MarshalIndent(x, "", "\t") if err != nil { out.Flush() @@ -383,7 +386,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { if err != nil { base.Fatalf("%s", err) } - do = func(x interface{}) { + do = func(x any) { if err := tmpl.Execute(out, x); err != nil { out.Flush() base.Fatalf("%s", err) @@ -424,12 +427,12 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { } if modload.Init(); !modload.Enabled() { - base.Fatalf("go list -m: not using modules") + base.Fatalf("go: list -m cannot be used with GO111MODULE=off") } modload.LoadModFile(ctx) // Sets cfg.BuildMod as a side-effect. if cfg.BuildMod == "vendor" { - const actionDisabledFormat = "go list -m: can't %s using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)" + const actionDisabledFormat = "go: can't %s using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)" if *listVersions { base.Fatalf(actionDisabledFormat, "determine available versions") @@ -468,11 +471,11 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { if !*listE { for _, m := range mods { if m.Error != nil { - base.Errorf("go list -m: %v", m.Error.Err) + base.Errorf("go: %v", m.Error.Err) } } if err != nil { - base.Errorf("go list -m: %v", err) + base.Errorf("go: %v", err) } base.ExitIfErrors() } @@ -708,7 +711,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { } rmods, err := modload.ListModules(ctx, args, mode) if err != nil && !*listE { - base.Errorf("go list -retracted: %v", err) + base.Errorf("go: %v", err) } for i, arg := range args { rmod := rmods[i] diff --git a/libgo/go/cmd/go/internal/load/flag.go b/libgo/go/cmd/go/internal/load/flag.go index 440cb861344..de079decdf2 100644 --- a/libgo/go/cmd/go/internal/load/flag.go +++ b/libgo/go/cmd/go/internal/load/flag.go @@ -6,7 +6,7 @@ package load import ( "cmd/go/internal/base" - "cmd/go/internal/str" + "cmd/internal/quoted" "fmt" "strings" ) @@ -22,6 +22,7 @@ var ( // that allows specifying different effective flags for different packages. // See 'go help build' for more details about per-package flags. type PerPackageFlag struct { + raw string present bool values []ppfValue } @@ -39,6 +40,7 @@ func (f *PerPackageFlag) Set(v string) error { // set is the implementation of Set, taking a cwd (current working directory) for easier testing. func (f *PerPackageFlag) set(v, cwd string) error { + f.raw = v f.present = true match := func(p *Package) bool { return p.Internal.CmdlinePkg || p.Internal.CmdlineFiles } // default predicate with no pattern // For backwards compatibility with earlier flag splitting, ignore spaces around flags. @@ -61,7 +63,7 @@ func (f *PerPackageFlag) set(v, cwd string) error { match = MatchPackage(pattern, cwd) v = v[i+1:] } - flags, err := str.SplitQuotedFields(v) + flags, err := quoted.Split(v) if err != nil { return err } @@ -72,9 +74,7 @@ func (f *PerPackageFlag) set(v, cwd string) error { return nil } -// String is required to implement flag.Value. -// It is not used, because cmd/go never calls flag.PrintDefaults. -func (f *PerPackageFlag) String() string { return "<PerPackageFlag>" } +func (f *PerPackageFlag) String() string { return f.raw } // Present reports whether the flag appeared on the command line. func (f *PerPackageFlag) Present() bool { diff --git a/libgo/go/cmd/go/internal/load/pkg.go b/libgo/go/cmd/go/internal/load/pkg.go index c085fcbbf80..67b359d19d2 100644 --- a/libgo/go/cmd/go/internal/load/pkg.go +++ b/libgo/go/cmd/go/internal/load/pkg.go @@ -21,9 +21,11 @@ import ( pathpkg "path" "path/filepath" "runtime" + "runtime/debug" "sort" "strconv" "strings" + "time" "unicode" "unicode/utf8" @@ -38,6 +40,7 @@ import ( "cmd/go/internal/search" "cmd/go/internal/str" "cmd/go/internal/trace" + "cmd/go/internal/vcs" "cmd/internal/sys" "golang.org/x/mod/modfile" @@ -203,6 +206,7 @@ type PackageInternal struct { Local bool // imported via local path (./ or ../) LocalPrefix string // interpret ./ and ../ imports relative to this prefix ExeName string // desired name for temporary executable + FuzzInstrument bool // package should be instrumented for fuzzing CoverMode string // preprocess Go source files with the coverage tool in this mode CoverVars map[string]*CoverVar // variables created by coverage analysis OmitDebug bool // tell linker not to write debug information @@ -494,7 +498,7 @@ type importError struct { err error // created with fmt.Errorf } -func ImportErrorf(path, format string, args ...interface{}) ImportPathError { +func ImportErrorf(path, format string, args ...any) ImportPathError { err := &importError{importPath: path, err: fmt.Errorf(format, args...)} if errStr := err.Error(); !strings.Contains(errStr, path) { panic(fmt.Sprintf("path %q not in error %q", path, errStr)) @@ -585,10 +589,10 @@ func ClearPackageCachePartial(args []string) { delete(packageCache, arg) } } - resolvedImportCache.DeleteIf(func(key interface{}) bool { + resolvedImportCache.DeleteIf(func(key any) bool { return shouldDelete[key.(importSpec).path] }) - packageDataCache.DeleteIf(func(key interface{}) bool { + packageDataCache.DeleteIf(func(key any) bool { return shouldDelete[key.(string)] }) } @@ -601,7 +605,7 @@ func ReloadPackageNoFlags(arg string, stk *ImportStack) *Package { p := packageCache[arg] if p != nil { delete(packageCache, arg) - resolvedImportCache.DeleteIf(func(key interface{}) bool { + resolvedImportCache.DeleteIf(func(key any) bool { return key.(importSpec).path == p.ImportPath }) packageDataCache.Delete(p.ImportPath) @@ -813,7 +817,7 @@ func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoo parentIsStd: parentIsStd, mode: mode, } - r := resolvedImportCache.Do(importKey, func() interface{} { + r := resolvedImportCache.Do(importKey, func() any { var r resolvedImport if build.IsLocalImport(path) { r.dir = filepath.Join(parentDir, path) @@ -840,7 +844,7 @@ func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoo // Load the package from its directory. If we already found the package's // directory when resolving its import path, use that. - data := packageDataCache.Do(r.path, func() interface{} { + data := packageDataCache.Do(r.path, func() any { loaded = true var data packageData if r.dir != "" { @@ -1059,7 +1063,7 @@ func cleanImport(path string) string { var isDirCache par.Cache func isDir(path string) bool { - return isDirCache.Do(path, func() interface{} { + return isDirCache.Do(path, func() any { fi, err := fsys.Stat(path) return err == nil && fi.IsDir() }).(bool) @@ -1187,7 +1191,7 @@ var ( // goModPath returns the module path in the go.mod in dir, if any. func goModPath(dir string) (path string) { - return goModPathCache.Do(dir, func() interface{} { + return goModPathCache.Do(dir, func() any { data, err := os.ReadFile(filepath.Join(dir, "go.mod")) if err != nil { return "" @@ -1456,9 +1460,9 @@ func disallowInternal(ctx context.Context, srcDir string, importer *Package, imp // The importer is a list of command-line files. // Pretend that the import path is the import path of the // directory containing them. - // If the directory is outside the main module, this will resolve to ".", + // If the directory is outside the main modules, this will resolve to ".", // which is not a prefix of any valid module. - importerPath = modload.DirImportPath(ctx, importer.Dir) + importerPath, _ = modload.MainModules.DirImportPath(ctx, importer.Dir) } parentOfInternal := p.ImportPath[:i] if str.HasPathPrefix(importerPath, parentOfInternal) { @@ -1628,6 +1632,7 @@ var cgoSyscallExclude = map[string]bool{ "runtime/cgo": true, "runtime/race": true, "runtime/msan": true, + "runtime/asan": true, } var foldPath = make(map[string]string) @@ -1683,9 +1688,10 @@ func (p *Package) DefaultExecName() string { func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk *ImportStack, importPos []token.Position, bp *build.Package, err error) { p.copyBuild(opts, bp) - // The localPrefix is the path we interpret ./ imports relative to. + // The localPrefix is the path we interpret ./ imports relative to, + // if we support them at all (not in module mode!). // Synthesized main packages sometimes override this. - if p.Internal.Local { + if p.Internal.Local && !cfg.ModulesEnabled { p.Internal.LocalPrefix = dirToImportPath(p.Dir) } @@ -1925,9 +1931,8 @@ func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk * } p.Internal.Imports = imports p.collectDeps() - - if cfg.ModulesEnabled && p.Error == nil && p.Name == "main" && len(p.DepsErrors) == 0 { - p.Internal.BuildInfo = modload.PackageBuildInfo(pkgPath, p.Deps) + if p.Error == nil && p.Name == "main" && len(p.DepsErrors) == 0 { + p.setBuildInfo() } // unsafe is a fake package. @@ -2018,13 +2023,18 @@ func resolveEmbed(pkgdir string, patterns []string) (files []string, pmap map[st for _, pattern = range patterns { pid++ + glob := pattern + all := strings.HasPrefix(pattern, "all:") + if all { + glob = pattern[len("all:"):] + } // Check pattern is valid for //go:embed. - if _, err := path.Match(pattern, ""); err != nil || !validEmbedPattern(pattern) { + if _, err := path.Match(glob, ""); err != nil || !validEmbedPattern(glob) { return nil, nil, fmt.Errorf("invalid pattern syntax") } // Glob to find matches. - match, err := fsys.Glob(pkgdir + string(filepath.Separator) + filepath.FromSlash(pattern)) + match, err := fsys.Glob(pkgdir + string(filepath.Separator) + filepath.FromSlash(glob)) if err != nil { return nil, nil, err } @@ -2087,7 +2097,7 @@ func resolveEmbed(pkgdir string, patterns []string) (files []string, pmap map[st } rel := filepath.ToSlash(path[len(pkgdir)+1:]) name := info.Name() - if path != file && (isBadEmbedName(name) || name[0] == '.' || name[0] == '_') { + if path != file && (isBadEmbedName(name) || ((name[0] == '.' || name[0] == '_') && !all)) { // Ignore bad names, assuming they won't go into modules. // Also avoid hidden files that user may not know about. // See golang.org/issue/42328. @@ -2199,6 +2209,229 @@ func (p *Package) collectDeps() { } } +// vcsStatusCache maps repository directories (string) +// to their VCS information (vcsStatusError). +var vcsStatusCache par.Cache + +// setBuildInfo gathers build information, formats it as a string to be +// embedded in the binary, then sets p.Internal.BuildInfo to that string. +// setBuildInfo should only be called on a main package with no errors. +// +// This information can be retrieved using debug.ReadBuildInfo. +// +// Note that the GoVersion field is not set here to avoid encoding it twice. +// It is stored separately in the binary, mostly for historical reasons. +func (p *Package) setBuildInfo() { + // TODO: build and vcs information is not embedded for executables in GOROOT. + // cmd/dist uses -gcflags=all= -ldflags=all= by default, which means these + // executables always appear stale unless the user sets the same flags. + // Perhaps it's safe to omit those flags when GO_GCFLAGS and GO_LDFLAGS + // are not set? + setPkgErrorf := func(format string, args ...any) { + if p.Error == nil { + p.Error = &PackageError{Err: fmt.Errorf(format, args...)} + } + } + + var debugModFromModinfo func(*modinfo.ModulePublic) *debug.Module + debugModFromModinfo = func(mi *modinfo.ModulePublic) *debug.Module { + dm := &debug.Module{ + Path: mi.Path, + Version: mi.Version, + } + if mi.Replace != nil { + dm.Replace = debugModFromModinfo(mi.Replace) + } else { + dm.Sum = modfetch.Sum(module.Version{Path: mi.Path, Version: mi.Version}) + } + return dm + } + + var main debug.Module + if p.Module != nil { + main = *debugModFromModinfo(p.Module) + } + + visited := make(map[*Package]bool) + mdeps := make(map[module.Version]*debug.Module) + var q []*Package + q = append(q, p.Internal.Imports...) + for len(q) > 0 { + p1 := q[0] + q = q[1:] + if visited[p1] { + continue + } + visited[p1] = true + if p1.Module != nil { + m := module.Version{Path: p1.Module.Path, Version: p1.Module.Version} + if p1.Module.Path != main.Path && mdeps[m] == nil { + mdeps[m] = debugModFromModinfo(p1.Module) + } + } + q = append(q, p1.Internal.Imports...) + } + sortedMods := make([]module.Version, 0, len(mdeps)) + for mod := range mdeps { + sortedMods = append(sortedMods, mod) + } + module.Sort(sortedMods) + deps := make([]*debug.Module, len(sortedMods)) + for i, mod := range sortedMods { + deps[i] = mdeps[mod] + } + + pkgPath := p.ImportPath + if p.Internal.CmdlineFiles { + pkgPath = "command-line-arguments" + } + info := &debug.BuildInfo{ + Path: pkgPath, + Main: main, + Deps: deps, + } + appendSetting := func(key, value string) { + value = strings.ReplaceAll(value, "\n", " ") // make value safe + info.Settings = append(info.Settings, debug.BuildSetting{Key: key, Value: value}) + } + + // Add command-line flags relevant to the build. + // This is informational, not an exhaustive list. + // Please keep the list sorted. + if !p.Standard { + if cfg.BuildASan { + appendSetting("-asan", "true") + } + if BuildAsmflags.present { + appendSetting("-asmflags", BuildAsmflags.String()) + } + appendSetting("-compiler", cfg.BuildContext.Compiler) + if BuildGccgoflags.present && cfg.BuildContext.Compiler == "gccgo" { + appendSetting("-gccgoflags", BuildGccgoflags.String()) + } + if BuildGcflags.present && cfg.BuildContext.Compiler == "gc" { + appendSetting("-gcflags", BuildGcflags.String()) + } + if BuildLdflags.present { + appendSetting("-ldflags", BuildLdflags.String()) + } + if cfg.BuildMSan { + appendSetting("-msan", "true") + } + if cfg.BuildRace { + appendSetting("-race", "true") + } + if tags := cfg.BuildContext.BuildTags; len(tags) > 0 { + appendSetting("-tags", strings.Join(tags, ",")) + } + cgo := "0" + if cfg.BuildContext.CgoEnabled { + cgo = "1" + } + appendSetting("CGO_ENABLED", cgo) + if cfg.BuildContext.CgoEnabled { + for _, name := range []string{"CGO_CFLAGS", "CGO_CPPFLAGS", "CGO_CXXFLAGS", "CGO_LDFLAGS"} { + appendSetting(name, cfg.Getenv(name)) + } + } + appendSetting("GOARCH", cfg.BuildContext.GOARCH) + if cfg.GOEXPERIMENT != "" { + appendSetting("GOEXPERIMENT", cfg.GOEXPERIMENT) + } + appendSetting("GOOS", cfg.BuildContext.GOOS) + if key, val := cfg.GetArchEnv(); key != "" && val != "" { + appendSetting(key, val) + } + } + + // Add VCS status if all conditions are true: + // + // - -buildvcs is enabled. + // - p is contained within a main module (there may be multiple main modules + // in a workspace, but local replacements don't count). + // - Both the current directory and p's module's root directory are contained + // in the same local repository. + // - We know the VCS commands needed to get the status. + setVCSError := func(err error) { + setPkgErrorf("error obtaining VCS status: %v\n\tUse -buildvcs=false to disable VCS stamping.", err) + } + + var repoDir string + var vcsCmd *vcs.Cmd + var err error + const allowNesting = true + if cfg.BuildBuildvcs && p.Module != nil && p.Module.Version == "" && !p.Standard { + repoDir, vcsCmd, err = vcs.FromDir(base.Cwd(), "", allowNesting) + if err != nil && !errors.Is(err, os.ErrNotExist) { + setVCSError(err) + return + } + if !str.HasFilePathPrefix(p.Module.Dir, repoDir) && + !str.HasFilePathPrefix(repoDir, p.Module.Dir) { + // The module containing the main package does not overlap with the + // repository containing the working directory. Don't include VCS info. + // If the repo contains the module or vice versa, but they are not + // the same directory, it's likely an error (see below). + repoDir, vcsCmd = "", nil + } + } + if repoDir != "" && vcsCmd.Status != nil { + // Check that the current directory, package, and module are in the same + // repository. vcs.FromDir allows nested Git repositories, but nesting + // is not allowed for other VCS tools. The current directory may be outside + // p.Module.Dir when a workspace is used. + pkgRepoDir, _, err := vcs.FromDir(p.Dir, "", allowNesting) + if err != nil { + setVCSError(err) + return + } + if pkgRepoDir != repoDir { + setVCSError(fmt.Errorf("main package is in repository %q but current directory is in repository %q", pkgRepoDir, repoDir)) + return + } + modRepoDir, _, err := vcs.FromDir(p.Module.Dir, "", allowNesting) + if err != nil { + setVCSError(err) + return + } + if modRepoDir != repoDir { + setVCSError(fmt.Errorf("main module is in repository %q but current directory is in repository %q", modRepoDir, repoDir)) + return + } + + type vcsStatusError struct { + Status vcs.Status + Err error + } + cached := vcsStatusCache.Do(repoDir, func() any { + st, err := vcsCmd.Status(vcsCmd, repoDir) + return vcsStatusError{st, err} + }).(vcsStatusError) + if err := cached.Err; err != nil { + setVCSError(err) + return + } + st := cached.Status + + appendSetting("vcs", vcsCmd.Cmd) + if st.Revision != "" { + appendSetting("vcs.revision", st.Revision) + } + if !st.CommitTime.IsZero() { + stamp := st.CommitTime.UTC().Format(time.RFC3339Nano) + appendSetting("vcs.time", stamp) + } + appendSetting("vcs.modified", strconv.FormatBool(st.Uncommitted)) + } + + text, err := info.MarshalText() + if err != nil { + setPkgErrorf("error formatting build info: %v", err) + return + } + p.Internal.BuildInfo = string(text) +} + // SafeArg reports whether arg is a "safe" command-line argument, // meaning that when it appears in a command-line, it probably // doesn't have some special meaning other than its own name. @@ -2237,6 +2470,10 @@ func LinkerDeps(p *Package) []string { if cfg.BuildMSan { deps = append(deps, "runtime/msan") } + // Using address sanitizer forces an import of runtime/asan. + if cfg.BuildASan { + deps = append(deps, "runtime/asan") + } return deps } @@ -2453,7 +2690,8 @@ func PackagesAndErrors(ctx context.Context, opts PackageOpts, patterns []string) } matches, _ = modload.LoadPackages(ctx, modOpts, patterns...) } else { - matches = search.ImportPaths(patterns) + noModRoots := []string{} + matches = search.ImportPaths(patterns, noModRoots) } var ( @@ -2679,10 +2917,7 @@ func GoFilesPackage(ctx context.Context, opts PackageOpts, gofiles []string) *Pa if fi.IsDir() { base.Fatalf("%s is a directory, should be a Go file", file) } - dir1, _ := filepath.Split(file) - if dir1 == "" { - dir1 = "./" - } + dir1 := filepath.Dir(file) if dir == "" { dir = dir1 } else if dir != dir1 { @@ -2710,7 +2945,9 @@ func GoFilesPackage(ctx context.Context, opts PackageOpts, gofiles []string) *Pa pkg.Internal.Local = true pkg.Internal.CmdlineFiles = true pkg.load(ctx, opts, "command-line-arguments", &stk, nil, bp, err) - pkg.Internal.LocalPrefix = dirToImportPath(dir) + if !cfg.ModulesEnabled { + pkg.Internal.LocalPrefix = dirToImportPath(dir) + } pkg.ImportPath = "command-line-arguments" pkg.Target = "" pkg.Match = gofiles diff --git a/libgo/go/cmd/go/internal/load/test.go b/libgo/go/cmd/go/internal/load/test.go index c8282965669..6122428c9cf 100644 --- a/libgo/go/cmd/go/internal/load/test.go +++ b/libgo/go/cmd/go/internal/load/test.go @@ -555,6 +555,7 @@ func formatTestmain(t *testFuncs) ([]byte, error) { type testFuncs struct { Tests []testFunc Benchmarks []testFunc + FuzzTargets []testFunc Examples []testFunc TestMain *testFunc Package *Package @@ -653,6 +654,13 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { } t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false}) *doImport, *seen = true, true + case isTest(name, "Fuzz"): + err := checkTestFunc(n, "F") + if err != nil { + return err + } + t.FuzzTargets = append(t.FuzzTargets, testFunc{pkg, name, "", false}) + *doImport, *seen = true, true } } ex := doc.Examples(f) @@ -670,10 +678,16 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { } func checkTestFunc(fn *ast.FuncDecl, arg string) error { + var why string if !isTestFunc(fn, arg) { - name := fn.Name.String() + why = fmt.Sprintf("must be: func %s(%s *testing.%s)", fn.Name.String(), strings.ToLower(arg), arg) + } + if fn.Type.TypeParams.NumFields() > 0 { + why = "test functions cannot have type parameters" + } + if why != "" { pos := testFileSet.Position(fn.Pos()) - return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg) + return fmt.Errorf("%s: wrong signature for %s, %s", pos, fn.Name.String(), why) } return nil } @@ -716,6 +730,12 @@ var benchmarks = []testing.InternalBenchmark{ {{end}} } +var fuzzTargets = []testing.InternalFuzzTarget{ +{{range .FuzzTargets}} + {"{{.Name}}", {{.Package}}.{{.Name}}}, +{{end}} +} + var examples = []testing.InternalExample{ {{range .Examples}} {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}}, @@ -774,7 +794,7 @@ func main() { CoveredPackages: {{printf "%q" .Covered}}, }) {{end}} - m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples) + m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples) {{with .TestMain}} {{.Package}}.{{.Name}}(m) os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int())) diff --git a/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go b/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go index e302fe7fccf..31ab5ea4002 100644 --- a/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go +++ b/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || (solaris && !illumos) -// +build aix solaris,!illumos // This code implements the filelock API using POSIX 'fcntl' locks, which attach // to an (inode, process) pair rather than a file descriptor. To avoid unlocking diff --git a/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go b/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go index c6eac302c57..4aef110bb1a 100644 --- a/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go +++ b/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !hurd && !linux && !netbsd && !openbsd && !plan9 && !solaris && !windows -// +build !aix,!darwin,!dragonfly,!freebsd,!hurd,!linux,!netbsd,!openbsd,!plan9,!solaris,!windows package filelock diff --git a/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_plan9.go b/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_plan9.go index 908afb6c8cb..54b2c946e0d 100644 --- a/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_plan9.go +++ b/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_plan9.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 package filelock diff --git a/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go b/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go index 640d4406f42..7bd7bd28f55 100644 --- a/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go +++ b/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js && !plan9 -// +build !js,!plan9 package filelock_test diff --git a/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go b/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go index b22c0bb9918..61427c7c031 100644 --- a/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go +++ b/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || hurd || illumos || linux || netbsd || openbsd -// +build darwin dragonfly freebsd hurd illumos linux netbsd openbsd package filelock diff --git a/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_windows.go b/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_windows.go index dd27ce92bd8..e2ca5383046 100644 --- a/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_windows.go +++ b/libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_windows.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package filelock diff --git a/libgo/go/cmd/go/internal/lockedfile/lockedfile_filelock.go b/libgo/go/cmd/go/internal/lockedfile/lockedfile_filelock.go index e4923f68764..1a677a7fe4a 100644 --- a/libgo/go/cmd/go/internal/lockedfile/lockedfile_filelock.go +++ b/libgo/go/cmd/go/internal/lockedfile/lockedfile_filelock.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !plan9 -// +build !plan9 package lockedfile diff --git a/libgo/go/cmd/go/internal/lockedfile/lockedfile_plan9.go b/libgo/go/cmd/go/internal/lockedfile/lockedfile_plan9.go index 979118b10ae..35669388e05 100644 --- a/libgo/go/cmd/go/internal/lockedfile/lockedfile_plan9.go +++ b/libgo/go/cmd/go/internal/lockedfile/lockedfile_plan9.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 package lockedfile diff --git a/libgo/go/cmd/go/internal/lockedfile/lockedfile_test.go b/libgo/go/cmd/go/internal/lockedfile/lockedfile_test.go index 3acc6695a74..c9907db46ce 100644 --- a/libgo/go/cmd/go/internal/lockedfile/lockedfile_test.go +++ b/libgo/go/cmd/go/internal/lockedfile/lockedfile_test.go @@ -4,7 +4,6 @@ // js does not support inter-process file locking. //go:build !js -// +build !js package lockedfile_test diff --git a/libgo/go/cmd/go/internal/lockedfile/transform_test.go b/libgo/go/cmd/go/internal/lockedfile/transform_test.go index b753346e7da..3c1caa334eb 100644 --- a/libgo/go/cmd/go/internal/lockedfile/transform_test.go +++ b/libgo/go/cmd/go/internal/lockedfile/transform_test.go @@ -4,7 +4,6 @@ // js does not support inter-process file locking. //go:build !js -// +build !js package lockedfile_test diff --git a/libgo/go/cmd/go/internal/modcmd/download.go b/libgo/go/cmd/go/internal/modcmd/download.go index 0e5af852376..6b8a010fd9d 100644 --- a/libgo/go/cmd/go/internal/modcmd/download.go +++ b/libgo/go/cmd/go/internal/modcmd/download.go @@ -16,6 +16,7 @@ import ( "cmd/go/internal/modload" "golang.org/x/mod/module" + "golang.org/x/mod/semver" ) var cmdDownload = &base.Command{ @@ -24,8 +25,11 @@ var cmdDownload = &base.Command{ Long: ` Download downloads the named modules, which can be module patterns selecting dependencies of the main module or module queries of the form path@version. -With no arguments, download applies to all dependencies of the main module -(equivalent to 'go mod download all'). + +With no arguments, download applies to the modules needed to build and test +the packages in the main module: the modules explicitly required by the main +module if it is at 'go 1.17' or higher, or all transitively-required modules +if at 'go 1.16' or lower. The go command will automatically download modules as needed during ordinary execution. The "go mod download" command is useful mainly for pre-filling @@ -66,6 +70,7 @@ func init() { // TODO(jayconrod): https://golang.org/issue/35849 Apply -x to other 'go mod' commands. cmdDownload.Flag.BoolVar(&cfg.BuildX, "x", false, "") base.AddModCommonFlags(&cmdDownload.Flag) + base.AddWorkfileFlag(&cmdDownload.Flag) } type moduleJSON struct { @@ -81,27 +86,68 @@ type moduleJSON struct { } func runDownload(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() + // Check whether modules are enabled and whether we're in a module. modload.ForceUseModules = true - if !modload.HasModRoot() && len(args) == 0 { - base.Fatalf("go mod download: no modules specified (see 'go help mod download')") - } + modload.ExplicitWriteGoMod = true haveExplicitArgs := len(args) > 0 - if !haveExplicitArgs { - args = []string{"all"} - } - if modload.HasModRoot() { - modload.LoadModFile(ctx) // to fill Target - targetAtUpgrade := modload.Target.Path + "@upgrade" - targetAtPatch := modload.Target.Path + "@patch" - for _, arg := range args { - switch arg { - case modload.Target.Path, targetAtUpgrade, targetAtPatch: - os.Stderr.WriteString("go mod download: skipping argument " + arg + " that resolves to the main module\n") + + if modload.HasModRoot() || modload.WorkFilePath() != "" { + modload.LoadModFile(ctx) // to fill MainModules + + if haveExplicitArgs { + for _, mainModule := range modload.MainModules.Versions() { + targetAtUpgrade := mainModule.Path + "@upgrade" + targetAtPatch := mainModule.Path + "@patch" + for _, arg := range args { + switch arg { + case mainModule.Path, targetAtUpgrade, targetAtPatch: + os.Stderr.WriteString("go: skipping download of " + arg + " that resolves to the main module\n") + } + } + } + } else if modload.WorkFilePath() != "" { + // TODO(#44435): Think about what the correct query is to download the + // right set of modules. Also see code review comment at + // https://go-review.googlesource.com/c/go/+/359794/comments/ce946a80_6cf53992. + args = []string{"all"} + } else { + mainModule := modload.MainModules.Versions()[0] + modFile := modload.MainModules.ModFile(mainModule) + if modFile.Go == nil || semver.Compare("v"+modFile.Go.Version, modload.ExplicitIndirectVersionV) < 0 { + if len(modFile.Require) > 0 { + args = []string{"all"} + } + } else { + // As of Go 1.17, the go.mod file explicitly requires every module + // that provides any package imported by the main module. + // 'go mod download' is typically run before testing packages in the + // main module, so by default we shouldn't download the others + // (which are presumed irrelevant to the packages in the main module). + // See https://golang.org/issue/44435. + // + // However, we also need to load the full module graph, to ensure that + // we have downloaded enough of the module graph to run 'go list all', + // 'go mod graph', and similar commands. + _ = modload.LoadModGraph(ctx, "") + + for _, m := range modFile.Require { + args = append(args, m.Mod.Path) + } } } } + if len(args) == 0 { + if modload.HasModRoot() { + os.Stderr.WriteString("go: no module dependencies to download\n") + } else { + base.Errorf("go: no modules specified (see 'go help mod download')") + } + base.Exit() + } + downloadModule := func(m *moduleJSON) { var err error m.Info, err = modfetch.InfoFile(m.Path, m.Version) @@ -140,13 +186,16 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) { if !haveExplicitArgs { // 'go mod download' is sometimes run without arguments to pre-populate the // module cache. It may fetch modules that aren't needed to build packages - // in the main mdoule. This is usually not intended, so don't save sums for - // downloaded modules (golang.org/issue/45332). - // TODO(golang.org/issue/45551): For now, in ListModules, save sums needed - // to load the build list (same as 1.15 behavior). In the future, report an - // error if go.mod or go.sum need to be updated after loading the build - // list. - modload.DisallowWriteGoMod() + // in the main module. This is usually not intended, so don't save sums for + // downloaded modules (golang.org/issue/45332). We do still fix + // inconsistencies in go.mod though. + // + // TODO(#45551): In the future, report an error if go.mod or go.sum need to + // be updated after loading the build list. This may require setting + // the mode to "mod" or "readonly" depending on haveExplicitArgs. + if err := modload.WriteGoMod(ctx); err != nil { + base.Fatalf("go: %v", err) + } } for _, info := range infos { @@ -183,7 +232,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) { for _, m := range mods { b, err := json.MarshalIndent(m, "", "\t") if err != nil { - base.Fatalf("go mod download: %v", err) + base.Fatalf("go: %v", err) } os.Stdout.Write(append(b, '\n')) if m.Error != "" { @@ -193,7 +242,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) { } else { for _, m := range mods { if m.Error != "" { - base.Errorf("go mod download: %v", m.Error) + base.Errorf("go: %v", m.Error) } } base.ExitIfErrors() @@ -206,13 +255,15 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) { // // Don't save sums for 'go mod download' without arguments; see comment above. if haveExplicitArgs { - modload.WriteGoMod(ctx) + if err := modload.WriteGoMod(ctx); err != nil { + base.Errorf("go: %v", err) + } } // If there was an error matching some of the requested packages, emit it now // (after we've written the checksums for the modules that were downloaded // successfully). if infosErr != nil { - base.Errorf("go mod download: %v", infosErr) + base.Errorf("go: %v", infosErr) } } diff --git a/libgo/go/cmd/go/internal/modcmd/edit.go b/libgo/go/cmd/go/internal/modcmd/edit.go index bb3d5210926..e5182a9590a 100644 --- a/libgo/go/cmd/go/internal/modcmd/edit.go +++ b/libgo/go/cmd/go/internal/modcmd/edit.go @@ -171,15 +171,15 @@ func runEdit(ctx context.Context, cmd *base.Command, args []string) { len(edits) > 0 if !anyFlags { - base.Fatalf("go mod edit: no flags specified (see 'go help mod edit').") + base.Fatalf("go: no flags specified (see 'go help mod edit').") } if *editJSON && *editPrint { - base.Fatalf("go mod edit: cannot use both -json and -print") + base.Fatalf("go: cannot use both -json and -print") } if len(args) > 1 { - base.Fatalf("go mod edit: too many arguments") + base.Fatalf("go: too many arguments") } var gomod string if len(args) == 1 { @@ -190,7 +190,7 @@ func runEdit(ctx context.Context, cmd *base.Command, args []string) { if *editModule != "" { if err := module.CheckImportPath(*editModule); err != nil { - base.Fatalf("go mod: invalid -module: %v", err) + base.Fatalf("go: invalid -module: %v", err) } } @@ -264,15 +264,15 @@ func runEdit(ctx context.Context, cmd *base.Command, args []string) { func parsePathVersion(flag, arg string) (path, version string) { i := strings.Index(arg, "@") if i < 0 { - base.Fatalf("go mod: -%s=%s: need path@version", flag, arg) + base.Fatalf("go: -%s=%s: need path@version", flag, arg) } path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:]) if err := module.CheckImportPath(path); err != nil { - base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err) + base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err) } if !allowedVersionArg(version) { - base.Fatalf("go mod: -%s=%s: invalid version %q", flag, arg, version) + base.Fatalf("go: -%s=%s: invalid version %q", flag, arg, version) } return path, version @@ -281,11 +281,11 @@ func parsePathVersion(flag, arg string) (path, version string) { // parsePath parses -flag=arg expecting arg to be path (not path@version). func parsePath(flag, arg string) (path string) { if strings.Contains(arg, "@") { - base.Fatalf("go mod: -%s=%s: need just path, not path@version", flag, arg) + base.Fatalf("go: -%s=%s: need just path, not path@version", flag, arg) } path = arg if err := module.CheckImportPath(path); err != nil { - base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err) + base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err) } return path } @@ -350,7 +350,7 @@ func flagRequire(arg string) { path, version := parsePathVersion("require", arg) edits = append(edits, func(f *modfile.File) { if err := f.AddRequire(path, version); err != nil { - base.Fatalf("go mod: -require=%s: %v", arg, err) + base.Fatalf("go: -require=%s: %v", arg, err) } }) } @@ -360,7 +360,7 @@ func flagDropRequire(arg string) { path := parsePath("droprequire", arg) edits = append(edits, func(f *modfile.File) { if err := f.DropRequire(path); err != nil { - base.Fatalf("go mod: -droprequire=%s: %v", arg, err) + base.Fatalf("go: -droprequire=%s: %v", arg, err) } }) } @@ -370,7 +370,7 @@ func flagExclude(arg string) { path, version := parsePathVersion("exclude", arg) edits = append(edits, func(f *modfile.File) { if err := f.AddExclude(path, version); err != nil { - base.Fatalf("go mod: -exclude=%s: %v", arg, err) + base.Fatalf("go: -exclude=%s: %v", arg, err) } }) } @@ -380,7 +380,7 @@ func flagDropExclude(arg string) { path, version := parsePathVersion("dropexclude", arg) edits = append(edits, func(f *modfile.File) { if err := f.DropExclude(path, version); err != nil { - base.Fatalf("go mod: -dropexclude=%s: %v", arg, err) + base.Fatalf("go: -dropexclude=%s: %v", arg, err) } }) } @@ -389,27 +389,27 @@ func flagDropExclude(arg string) { func flagReplace(arg string) { var i int if i = strings.Index(arg, "="); i < 0 { - base.Fatalf("go mod: -replace=%s: need old[@v]=new[@w] (missing =)", arg) + base.Fatalf("go: -replace=%s: need old[@v]=new[@w] (missing =)", arg) } old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:]) if strings.HasPrefix(new, ">") { - base.Fatalf("go mod: -replace=%s: separator between old and new is =, not =>", arg) + base.Fatalf("go: -replace=%s: separator between old and new is =, not =>", arg) } oldPath, oldVersion, err := parsePathVersionOptional("old", old, false) if err != nil { - base.Fatalf("go mod: -replace=%s: %v", arg, err) + base.Fatalf("go: -replace=%s: %v", arg, err) } newPath, newVersion, err := parsePathVersionOptional("new", new, true) if err != nil { - base.Fatalf("go mod: -replace=%s: %v", arg, err) + base.Fatalf("go: -replace=%s: %v", arg, err) } if newPath == new && !modfile.IsDirectoryPath(new) { - base.Fatalf("go mod: -replace=%s: unversioned new path must be local directory", arg) + base.Fatalf("go: -replace=%s: unversioned new path must be local directory", arg) } edits = append(edits, func(f *modfile.File) { if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil { - base.Fatalf("go mod: -replace=%s: %v", arg, err) + base.Fatalf("go: -replace=%s: %v", arg, err) } }) } @@ -418,11 +418,11 @@ func flagReplace(arg string) { func flagDropReplace(arg string) { path, version, err := parsePathVersionOptional("old", arg, true) if err != nil { - base.Fatalf("go mod: -dropreplace=%s: %v", arg, err) + base.Fatalf("go: -dropreplace=%s: %v", arg, err) } edits = append(edits, func(f *modfile.File) { if err := f.DropReplace(path, version); err != nil { - base.Fatalf("go mod: -dropreplace=%s: %v", arg, err) + base.Fatalf("go: -dropreplace=%s: %v", arg, err) } }) } @@ -431,11 +431,11 @@ func flagDropReplace(arg string) { func flagRetract(arg string) { vi, err := parseVersionInterval(arg) if err != nil { - base.Fatalf("go mod: -retract=%s: %v", arg, err) + base.Fatalf("go: -retract=%s: %v", arg, err) } edits = append(edits, func(f *modfile.File) { if err := f.AddRetract(vi, ""); err != nil { - base.Fatalf("go mod: -retract=%s: %v", arg, err) + base.Fatalf("go: -retract=%s: %v", arg, err) } }) } @@ -444,11 +444,11 @@ func flagRetract(arg string) { func flagDropRetract(arg string) { vi, err := parseVersionInterval(arg) if err != nil { - base.Fatalf("go mod: -dropretract=%s: %v", arg, err) + base.Fatalf("go: -dropretract=%s: %v", arg, err) } edits = append(edits, func(f *modfile.File) { if err := f.DropRetract(vi); err != nil { - base.Fatalf("go mod: -dropretract=%s: %v", arg, err) + base.Fatalf("go: -dropretract=%s: %v", arg, err) } }) } diff --git a/libgo/go/cmd/go/internal/modcmd/graph.go b/libgo/go/cmd/go/internal/modcmd/graph.go index ac81f26dade..9b6aa1fb14d 100644 --- a/libgo/go/cmd/go/internal/modcmd/graph.go +++ b/libgo/go/cmd/go/internal/modcmd/graph.go @@ -42,11 +42,14 @@ var ( func init() { cmdGraph.Flag.Var(&graphGo, "go", "") base.AddModCommonFlags(&cmdGraph.Flag) + base.AddWorkfileFlag(&cmdGraph.Flag) } func runGraph(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() + if len(args) > 0 { - base.Fatalf("go mod graph: graph takes no arguments") + base.Fatalf("go: 'go mod graph' accepts no arguments") } modload.ForceUseModules = true modload.RootMode = modload.NeedRoot diff --git a/libgo/go/cmd/go/internal/modcmd/init.go b/libgo/go/cmd/go/internal/modcmd/init.go index 958c3066ac1..bc4620a2a8d 100644 --- a/libgo/go/cmd/go/internal/modcmd/init.go +++ b/libgo/go/cmd/go/internal/modcmd/init.go @@ -39,7 +39,7 @@ func init() { func runInit(ctx context.Context, cmd *base.Command, args []string) { if len(args) > 1 { - base.Fatalf("go mod init: too many arguments") + base.Fatalf("go: 'go mod init' accepts at most one argument") } var modPath string if len(args) == 1 { diff --git a/libgo/go/cmd/go/internal/modcmd/tidy.go b/libgo/go/cmd/go/internal/modcmd/tidy.go index fe25507e94f..d35476eb539 100644 --- a/libgo/go/cmd/go/internal/modcmd/tidy.go +++ b/libgo/go/cmd/go/internal/modcmd/tidy.go @@ -75,8 +75,8 @@ type goVersionFlag struct { v string } -func (f *goVersionFlag) String() string { return f.v } -func (f *goVersionFlag) Get() interface{} { return f.v } +func (f *goVersionFlag) String() string { return f.v } +func (f *goVersionFlag) Get() any { return f.v } func (f *goVersionFlag) Set(s string) error { if s != "" { @@ -95,7 +95,7 @@ func (f *goVersionFlag) Set(s string) error { func runTidy(ctx context.Context, cmd *base.Command, args []string) { if len(args) > 0 { - base.Fatalf("go mod tidy: no arguments allowed") + base.Fatalf("go: 'go mod tidy' accepts no arguments") } // Tidy aims to make 'go test' reproducible for any package in 'all', so we diff --git a/libgo/go/cmd/go/internal/modcmd/vendor.go b/libgo/go/cmd/go/internal/modcmd/vendor.go index 713d5f9f3fa..ef123700aa3 100644 --- a/libgo/go/cmd/go/internal/modcmd/vendor.go +++ b/libgo/go/cmd/go/internal/modcmd/vendor.go @@ -31,7 +31,7 @@ import ( ) var cmdVendor = &base.Command{ - UsageLine: "go mod vendor [-e] [-v]", + UsageLine: "go mod vendor [-e] [-v] [-o outdir]", Short: "make vendored copy of dependencies", Long: ` Vendor resets the main module's vendor directory to include all packages @@ -44,22 +44,29 @@ modules and packages to standard error. The -e flag causes vendor to attempt to proceed despite errors encountered while loading packages. +The -o flag causes vendor to create the vendor directory at the given +path instead of "vendor". The go command can only use a vendor directory +named "vendor" within the module root directory, so this flag is +primarily useful for other tools. + See https://golang.org/ref/mod#go-mod-vendor for more about 'go mod vendor'. `, Run: runVendor, } -var vendorE bool // if true, report errors but proceed anyway +var vendorE bool // if true, report errors but proceed anyway +var vendorO string // if set, overrides the default output directory func init() { cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "") cmdVendor.Flag.BoolVar(&vendorE, "e", false, "") + cmdVendor.Flag.StringVar(&vendorO, "o", "", "") base.AddModCommonFlags(&cmdVendor.Flag) } func runVendor(ctx context.Context, cmd *base.Command, args []string) { if len(args) != 0 { - base.Fatalf("go mod vendor: vendor takes no arguments") + base.Fatalf("go: 'go mod vendor' accepts no arguments") } modload.ForceUseModules = true modload.RootMode = modload.NeedRoot @@ -74,15 +81,23 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) { } _, pkgs := modload.LoadPackages(ctx, loadOpts, "all") - vdir := filepath.Join(modload.ModRoot(), "vendor") + var vdir string + switch { + case filepath.IsAbs(vendorO): + vdir = vendorO + case vendorO != "": + vdir = filepath.Join(base.Cwd(), vendorO) + default: + vdir = filepath.Join(modload.VendorDir()) + } if err := os.RemoveAll(vdir); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } modpkgs := make(map[module.Version][]string) for _, pkg := range pkgs { m := modload.PackageModule(pkg) - if m.Path == "" || m == modload.Target { + if m.Path == "" || m.Version == "" && modload.MainModules.Contains(m.Path) { continue } modpkgs[m] = append(modpkgs[m], pkg) @@ -128,7 +143,8 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) { } for _, m := range vendorMods { - line := moduleLine(m, modload.Replacement(m)) + replacement := modload.Replacement(m) + line := moduleLine(m, replacement) io.WriteString(w, line) goVersion := "" @@ -177,11 +193,11 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) { } if err := os.MkdirAll(vdir, 0777); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if err := os.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } } @@ -242,14 +258,14 @@ func vendorPkg(vdir, pkg string) { if err != nil { if errors.As(err, &noGoError) { return // No source files in this package are built. Skip embeds in ignored files. - } else if !errors.As(err, &multiplePackageError) { // multiplePackgeErrors are okay, but others are not. + } else if !errors.As(err, &multiplePackageError) { // multiplePackageErrors are OK, but others are not. base.Fatalf("internal error: failed to find embedded files of %s: %v\n", pkg, err) } } embedPatterns := str.StringList(bp.EmbedPatterns, bp.TestEmbedPatterns, bp.XTestEmbedPatterns) embeds, err := load.ResolveEmbed(bp.Dir, embedPatterns) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } for _, embed := range embeds { embedDst := filepath.Join(dst, embed) @@ -260,21 +276,21 @@ func vendorPkg(vdir, pkg string) { // Copy the file as is done by copyDir below. r, err := os.Open(filepath.Join(src, embed)) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if err := os.MkdirAll(filepath.Dir(embedDst), 0777); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } w, err := os.Create(embedDst) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if _, err := io.Copy(w, r); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } r.Close() if err := w.Close(); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } } } @@ -353,7 +369,7 @@ func matchPotentialSourceFile(dir string, info fs.DirEntry) bool { if strings.HasSuffix(info.Name(), ".go") { f, err := fsys.Open(filepath.Join(dir, info.Name())) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } defer f.Close() @@ -375,10 +391,10 @@ func matchPotentialSourceFile(dir string, info fs.DirEntry) bool { func copyDir(dst, src string, match func(dir string, info fs.DirEntry) bool, copiedFiles map[string]bool) { files, err := os.ReadDir(src) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if err := os.MkdirAll(dst, 0777); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } for _, file := range files { if file.IsDir() || !file.Type().IsRegular() || !match(src, file) { @@ -387,20 +403,20 @@ func copyDir(dst, src string, match func(dir string, info fs.DirEntry) bool, cop copiedFiles[file.Name()] = true r, err := os.Open(filepath.Join(src, file.Name())) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } dstPath := filepath.Join(dst, file.Name()) copiedFiles[dstPath] = true w, err := os.Create(dstPath) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if _, err := io.Copy(w, r); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } r.Close() if err := w.Close(); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } } } diff --git a/libgo/go/cmd/go/internal/modcmd/verify.go b/libgo/go/cmd/go/internal/modcmd/verify.go index 5a6eca32cfb..3f0c005d5d9 100644 --- a/libgo/go/cmd/go/internal/modcmd/verify.go +++ b/libgo/go/cmd/go/internal/modcmd/verify.go @@ -39,12 +39,15 @@ See https://golang.org/ref/mod#go-mod-verify for more about 'go mod verify'. func init() { base.AddModCommonFlags(&cmdVerify.Flag) + base.AddWorkfileFlag(&cmdVerify.Flag) } func runVerify(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() + if len(args) != 0 { // NOTE(rsc): Could take a module pattern. - base.Fatalf("go mod verify: verify takes no arguments") + base.Fatalf("go: verify takes no arguments") } modload.ForceUseModules = true modload.RootMode = modload.NeedRoot diff --git a/libgo/go/cmd/go/internal/modcmd/why.go b/libgo/go/cmd/go/internal/modcmd/why.go index 3b14b27c8c7..d8355cca957 100644 --- a/libgo/go/cmd/go/internal/modcmd/why.go +++ b/libgo/go/cmd/go/internal/modcmd/why.go @@ -12,8 +12,6 @@ import ( "cmd/go/internal/base" "cmd/go/internal/imports" "cmd/go/internal/modload" - - "golang.org/x/mod/module" ) var cmdWhy = &base.Command{ @@ -61,11 +59,14 @@ var ( func init() { cmdWhy.Run = runWhy // break init cycle base.AddModCommonFlags(&cmdWhy.Flag) + base.AddWorkfileFlag(&cmdWhy.Flag) } func runWhy(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() modload.ForceUseModules = true modload.RootMode = modload.NeedRoot + modload.ExplicitWriteGoMod = true // don't write go.mod in ListModules loadOpts := modload.PackageOpts{ Tags: imports.AnyTags(), @@ -78,28 +79,28 @@ func runWhy(ctx context.Context, cmd *base.Command, args []string) { if *whyM { for _, arg := range args { if strings.Contains(arg, "@") { - base.Fatalf("go mod why: module query not allowed") + base.Fatalf("go: %s: 'go mod why' requires a module path, not a version query", arg) } } mods, err := modload.ListModules(ctx, args, 0) if err != nil { - base.Fatalf("go mod why: %v", err) + base.Fatalf("go: %v", err) } - byModule := make(map[module.Version][]string) + byModule := make(map[string][]string) _, pkgs := modload.LoadPackages(ctx, loadOpts, "all") for _, path := range pkgs { m := modload.PackageModule(path) if m.Path != "" { - byModule[m] = append(byModule[m], path) + byModule[m.Path] = append(byModule[m.Path], path) } } sep := "" for _, m := range mods { best := "" bestDepth := 1000000000 - for _, path := range byModule[module.Version{Path: m.Path, Version: m.Version}] { + for _, path := range byModule[m.Path] { d := modload.WhyDepth(path) if d > 0 && d < bestDepth { best = path diff --git a/libgo/go/cmd/go/internal/modfetch/bootstrap.go b/libgo/go/cmd/go/internal/modfetch/bootstrap.go index ed694581a7c..e23669fb00c 100644 --- a/libgo/go/cmd/go/internal/modfetch/bootstrap.go +++ b/libgo/go/cmd/go/internal/modfetch/bootstrap.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build cmd_go_bootstrap -// +build cmd_go_bootstrap package modfetch diff --git a/libgo/go/cmd/go/internal/modfetch/cache.go b/libgo/go/cmd/go/internal/modfetch/cache.go index b01b4674131..b0dae1cb3d3 100644 --- a/libgo/go/cmd/go/internal/modfetch/cache.go +++ b/libgo/go/cmd/go/internal/modfetch/cache.go @@ -204,7 +204,7 @@ func (r *cachingRepo) Versions(prefix string) ([]string, error) { list []string err error } - c := r.cache.Do("versions:"+prefix, func() interface{} { + c := r.cache.Do("versions:"+prefix, func() any { list, err := r.repo().Versions(prefix) return cached{list, err} }).(cached) @@ -221,7 +221,7 @@ type cachedInfo struct { } func (r *cachingRepo) Stat(rev string) (*RevInfo, error) { - c := r.cache.Do("stat:"+rev, func() interface{} { + c := r.cache.Do("stat:"+rev, func() any { file, info, err := readDiskStat(r.path, rev) if err == nil { return cachedInfo{info, nil} @@ -233,7 +233,7 @@ func (r *cachingRepo) Stat(rev string) (*RevInfo, error) { // then save the information under the proper version, for future use. if info.Version != rev { file, _ = CachePath(module.Version{Path: r.path, Version: info.Version}, "info") - r.cache.Do("stat:"+info.Version, func() interface{} { + r.cache.Do("stat:"+info.Version, func() any { return cachedInfo{info, err} }) } @@ -253,12 +253,12 @@ func (r *cachingRepo) Stat(rev string) (*RevInfo, error) { } func (r *cachingRepo) Latest() (*RevInfo, error) { - c := r.cache.Do("latest:", func() interface{} { + c := r.cache.Do("latest:", func() any { info, err := r.repo().Latest() // Save info for likely future Stat call. if err == nil { - r.cache.Do("stat:"+info.Version, func() interface{} { + r.cache.Do("stat:"+info.Version, func() any { return cachedInfo{info, err} }) if file, _, err := readDiskStat(r.path, info.Version); err != nil { @@ -281,7 +281,7 @@ func (r *cachingRepo) GoMod(version string) ([]byte, error) { text []byte err error } - c := r.cache.Do("gomod:"+version, func() interface{} { + c := r.cache.Do("gomod:"+version, func() any { file, text, err := readDiskGoMod(r.path, version) if err == nil { // Note: readDiskGoMod already called checkGoMod. @@ -642,7 +642,7 @@ func rewriteVersionList(dir string) (err error) { // Lock listfile when writing to it to try to avoid corruption to the file. // Under rare circumstances, for instance, if the system loses power in the // middle of a write it is possible for corrupt data to be written. This is - // not a problem for the go command itself, but may be an issue if the the + // not a problem for the go command itself, but may be an issue if the // cache is being served by a GOPROXY HTTP server. This will be corrected // the next time a new version of the module is fetched and the file is rewritten. // TODO(matloob): golang.org/issue/43313 covers adding a go mod verify @@ -720,7 +720,7 @@ func checkCacheDir() error { if cfg.GOMODCACHE == "" { // modload.Init exits if GOPATH[0] is empty, and cfg.GOMODCACHE // is set to GOPATH[0]/pkg/mod if GOMODCACHE is empty, so this should never happen. - return fmt.Errorf("internal error: cfg.GOMODCACHE not set") + return fmt.Errorf("module cache not found: neither GOMODCACHE nor GOPATH is set") } if !filepath.IsAbs(cfg.GOMODCACHE) { return fmt.Errorf("GOMODCACHE entry is relative; must be absolute path: %q.\n", cfg.GOMODCACHE) diff --git a/libgo/go/cmd/go/internal/modfetch/codehost/codehost.go b/libgo/go/cmd/go/internal/modfetch/codehost/codehost.go index 378fbae34f9..4a0e2241e50 100644 --- a/libgo/go/cmd/go/internal/modfetch/codehost/codehost.go +++ b/libgo/go/cmd/go/internal/modfetch/codehost/codehost.go @@ -55,21 +55,6 @@ type Repo interface { // os.IsNotExist(err) returns true. ReadFile(rev, file string, maxSize int64) (data []byte, err error) - // ReadFileRevs reads a single file at multiple versions. - // It should refuse to read more than maxSize bytes. - // The result is a map from each requested rev strings - // to the associated FileRev. The map must have a non-nil - // entry for every requested rev (unless ReadFileRevs returned an error). - // A file simply being missing or even corrupted in revs[i] - // should be reported only in files[revs[i]].Err, not in the error result - // from ReadFileRevs. - // The overall call should return an error (and no map) only - // in the case of a problem with obtaining the data, such as - // a network failure. - // Implementations may assume that revs only contain tags, - // not direct commit hashes. - ReadFileRevs(revs []string, file string, maxSize int64) (files map[string]*FileRev, err error) - // ReadZip downloads a zip file for the subdir subdirectory // of the given revision to a new file in a given temporary directory. // It should refuse to read more than maxSize bytes. @@ -243,7 +228,7 @@ var dirLock sync.Map // It returns the standard output and, for a non-zero exit, // a *RunError indicating the command, exit status, and standard error. // Standard error is unavailable for commands that exit successfully. -func Run(dir string, cmdline ...interface{}) ([]byte, error) { +func Run(dir string, cmdline ...any) ([]byte, error) { return RunWithStdin(dir, nil, cmdline...) } @@ -251,7 +236,7 @@ func Run(dir string, cmdline ...interface{}) ([]byte, error) { // See https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html. var bashQuoter = strings.NewReplacer(`"`, `\"`, `$`, `\$`, "`", "\\`", `\`, `\\`) -func RunWithStdin(dir string, stdin io.Reader, cmdline ...interface{}) ([]byte, error) { +func RunWithStdin(dir string, stdin io.Reader, cmdline ...any) ([]byte, error) { if dir != "" { muIface, ok := dirLock.Load(dir) if !ok { diff --git a/libgo/go/cmd/go/internal/modfetch/codehost/git.go b/libgo/go/cmd/go/internal/modfetch/codehost/git.go index 4d4964edf44..34f453c855e 100644 --- a/libgo/go/cmd/go/internal/modfetch/codehost/git.go +++ b/libgo/go/cmd/go/internal/modfetch/codehost/git.go @@ -56,7 +56,7 @@ func newGitRepoCached(remote string, localOK bool) (Repo, error) { err error } - c := gitRepoCache.Do(key{remote, localOK}, func() interface{} { + c := gitRepoCache.Do(key{remote, localOK}, func() any { repo, err := newGitRepo(remote, localOK) return cached{repo, err} }).(cached) @@ -170,59 +170,63 @@ func (r *gitRepo) loadLocalTags() { } // loadRefs loads heads and tags references from the remote into the map r.refs. -// Should only be called as r.refsOnce.Do(r.loadRefs). -func (r *gitRepo) loadRefs() { - // The git protocol sends all known refs and ls-remote filters them on the client side, - // so we might as well record both heads and tags in one shot. - // Most of the time we only care about tags but sometimes we care about heads too. - out, gitErr := Run(r.dir, "git", "ls-remote", "-q", r.remote) - if gitErr != nil { - if rerr, ok := gitErr.(*RunError); ok { - if bytes.Contains(rerr.Stderr, []byte("fatal: could not read Username")) { - rerr.HelpText = "Confirm the import path was entered correctly.\nIf this is a private repository, see https://golang.org/doc/faq#git_https for additional information." +// The result is cached in memory. +func (r *gitRepo) loadRefs() (map[string]string, error) { + r.refsOnce.Do(func() { + // The git protocol sends all known refs and ls-remote filters them on the client side, + // so we might as well record both heads and tags in one shot. + // Most of the time we only care about tags but sometimes we care about heads too. + out, gitErr := Run(r.dir, "git", "ls-remote", "-q", r.remote) + if gitErr != nil { + if rerr, ok := gitErr.(*RunError); ok { + if bytes.Contains(rerr.Stderr, []byte("fatal: could not read Username")) { + rerr.HelpText = "Confirm the import path was entered correctly.\nIf this is a private repository, see https://golang.org/doc/faq#git_https for additional information." + } } - } - // If the remote URL doesn't exist at all, ideally we should treat the whole - // repository as nonexistent by wrapping the error in a notExistError. - // For HTTP and HTTPS, that's easy to detect: we'll try to fetch the URL - // ourselves and see what code it serves. - if u, err := url.Parse(r.remoteURL); err == nil && (u.Scheme == "http" || u.Scheme == "https") { - if _, err := web.GetBytes(u); errors.Is(err, fs.ErrNotExist) { - gitErr = notExistError{gitErr} + // If the remote URL doesn't exist at all, ideally we should treat the whole + // repository as nonexistent by wrapping the error in a notExistError. + // For HTTP and HTTPS, that's easy to detect: we'll try to fetch the URL + // ourselves and see what code it serves. + if u, err := url.Parse(r.remoteURL); err == nil && (u.Scheme == "http" || u.Scheme == "https") { + if _, err := web.GetBytes(u); errors.Is(err, fs.ErrNotExist) { + gitErr = notExistError{gitErr} + } } - } - r.refsErr = gitErr - return - } - - r.refs = make(map[string]string) - for _, line := range strings.Split(string(out), "\n") { - f := strings.Fields(line) - if len(f) != 2 { - continue + r.refsErr = gitErr + return } - if f[1] == "HEAD" || strings.HasPrefix(f[1], "refs/heads/") || strings.HasPrefix(f[1], "refs/tags/") { - r.refs[f[1]] = f[0] + + refs := make(map[string]string) + for _, line := range strings.Split(string(out), "\n") { + f := strings.Fields(line) + if len(f) != 2 { + continue + } + if f[1] == "HEAD" || strings.HasPrefix(f[1], "refs/heads/") || strings.HasPrefix(f[1], "refs/tags/") { + refs[f[1]] = f[0] + } } - } - for ref, hash := range r.refs { - if strings.HasSuffix(ref, "^{}") { // record unwrapped annotated tag as value of tag - r.refs[strings.TrimSuffix(ref, "^{}")] = hash - delete(r.refs, ref) + for ref, hash := range refs { + if strings.HasSuffix(ref, "^{}") { // record unwrapped annotated tag as value of tag + refs[strings.TrimSuffix(ref, "^{}")] = hash + delete(refs, ref) + } } - } + r.refs = refs + }) + return r.refs, r.refsErr } func (r *gitRepo) Tags(prefix string) ([]string, error) { - r.refsOnce.Do(r.loadRefs) - if r.refsErr != nil { - return nil, r.refsErr + refs, err := r.loadRefs() + if err != nil { + return nil, err } tags := []string{} - for ref := range r.refs { + for ref := range refs { if !strings.HasPrefix(ref, "refs/tags/") { continue } @@ -237,14 +241,14 @@ func (r *gitRepo) Tags(prefix string) ([]string, error) { } func (r *gitRepo) Latest() (*RevInfo, error) { - r.refsOnce.Do(r.loadRefs) - if r.refsErr != nil { - return nil, r.refsErr + refs, err := r.loadRefs() + if err != nil { + return nil, err } - if r.refs["HEAD"] == "" { + if refs["HEAD"] == "" { return nil, ErrNoCommits } - return r.Stat(r.refs["HEAD"]) + return r.Stat(refs["HEAD"]) } // findRef finds some ref name for the given hash, @@ -252,8 +256,11 @@ func (r *gitRepo) Latest() (*RevInfo, error) { // There may be multiple ref names for a given hash, // in which case this returns some name - it doesn't matter which. func (r *gitRepo) findRef(hash string) (ref string, ok bool) { - r.refsOnce.Do(r.loadRefs) - for ref, h := range r.refs { + refs, err := r.loadRefs() + if err != nil { + return "", false + } + for ref, h := range refs { if h == hash { return ref, true } @@ -295,29 +302,32 @@ func (r *gitRepo) stat(rev string) (*RevInfo, error) { // Maybe rev is the name of a tag or branch on the remote server. // Or maybe it's the prefix of a hash of a named ref. // Try to resolve to both a ref (git name) and full (40-hex-digit) commit hash. - r.refsOnce.Do(r.loadRefs) + refs, err := r.loadRefs() + if err != nil { + return nil, err + } // loadRefs may return an error if git fails, for example segfaults, or // could not load a private repo, but defer checking to the else block // below, in case we already have the rev in question in the local cache. var ref, hash string - if r.refs["refs/tags/"+rev] != "" { + if refs["refs/tags/"+rev] != "" { ref = "refs/tags/" + rev - hash = r.refs[ref] + hash = refs[ref] // Keep rev as is: tags are assumed not to change meaning. - } else if r.refs["refs/heads/"+rev] != "" { + } else if refs["refs/heads/"+rev] != "" { ref = "refs/heads/" + rev - hash = r.refs[ref] + hash = refs[ref] rev = hash // Replace rev, because meaning of refs/heads/foo can change. - } else if rev == "HEAD" && r.refs["HEAD"] != "" { + } else if rev == "HEAD" && refs["HEAD"] != "" { ref = "HEAD" - hash = r.refs[ref] + hash = refs[ref] rev = hash // Replace rev, because meaning of HEAD can change. } else if len(rev) >= minHashDigits && len(rev) <= 40 && AllHex(rev) { // At the least, we have a hash prefix we can look up after the fetch below. // Maybe we can map it to a full hash using the known refs. prefix := rev // Check whether rev is prefix of known ref hash. - for k, h := range r.refs { + for k, h := range refs { if strings.HasPrefix(h, prefix) { if hash != "" && hash != h { // Hash is an ambiguous hash prefix. @@ -335,9 +345,6 @@ func (r *gitRepo) stat(rev string) (*RevInfo, error) { hash = rev } } else { - if r.refsErr != nil { - return nil, r.refsErr - } return nil, &UnknownRevisionError{Rev: rev} } @@ -496,7 +503,7 @@ func (r *gitRepo) Stat(rev string) (*RevInfo, error) { info *RevInfo err error } - c := r.statCache.Do(rev, func() interface{} { + c := r.statCache.Do(rev, func() any { info, err := r.stat(rev) return cached{info, err} }).(cached) @@ -516,140 +523,6 @@ func (r *gitRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) { return out, nil } -func (r *gitRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[string]*FileRev, error) { - // Create space to hold results. - files := make(map[string]*FileRev) - for _, rev := range revs { - f := &FileRev{Rev: rev} - files[rev] = f - } - - // Collect locally-known revs. - need, err := r.readFileRevs(revs, file, files) - if err != nil { - return nil, err - } - if len(need) == 0 { - return files, nil - } - - // Build list of known remote refs that might help. - var redo []string - r.refsOnce.Do(r.loadRefs) - if r.refsErr != nil { - return nil, r.refsErr - } - for _, tag := range need { - if r.refs["refs/tags/"+tag] != "" { - redo = append(redo, tag) - } - } - if len(redo) == 0 { - return files, nil - } - - // Protect r.fetchLevel and the "fetch more and more" sequence. - // See stat method above. - unlock, err := r.mu.Lock() - if err != nil { - return nil, err - } - defer unlock() - - if err := r.fetchRefsLocked(); err != nil { - return nil, err - } - - if _, err := r.readFileRevs(redo, file, files); err != nil { - return nil, err - } - - return files, nil -} - -func (r *gitRepo) readFileRevs(tags []string, file string, fileMap map[string]*FileRev) (missing []string, err error) { - var stdin bytes.Buffer - for _, tag := range tags { - fmt.Fprintf(&stdin, "refs/tags/%s\n", tag) - fmt.Fprintf(&stdin, "refs/tags/%s:%s\n", tag, file) - } - - data, err := RunWithStdin(r.dir, &stdin, "git", "cat-file", "--batch") - if err != nil { - return nil, err - } - - next := func() (typ string, body []byte, ok bool) { - var line string - i := bytes.IndexByte(data, '\n') - if i < 0 { - return "", nil, false - } - line, data = string(bytes.TrimSpace(data[:i])), data[i+1:] - if strings.HasSuffix(line, " missing") { - return "missing", nil, true - } - f := strings.Fields(line) - if len(f) != 3 { - return "", nil, false - } - n, err := strconv.Atoi(f[2]) - if err != nil || n > len(data) { - return "", nil, false - } - body, data = data[:n], data[n:] - if len(data) > 0 && data[0] == '\r' { - data = data[1:] - } - if len(data) > 0 && data[0] == '\n' { - data = data[1:] - } - return f[1], body, true - } - - badGit := func() ([]string, error) { - return nil, fmt.Errorf("malformed output from git cat-file --batch") - } - - for _, tag := range tags { - commitType, _, ok := next() - if !ok { - return badGit() - } - fileType, fileData, ok := next() - if !ok { - return badGit() - } - f := fileMap[tag] - f.Data = nil - f.Err = nil - switch commitType { - default: - f.Err = fmt.Errorf("unexpected non-commit type %q for rev %s", commitType, tag) - - case "missing": - // Note: f.Err must not satisfy os.IsNotExist. That's reserved for the file not existing in a valid commit. - f.Err = fmt.Errorf("no such rev %s", tag) - missing = append(missing, tag) - - case "tag", "commit": - switch fileType { - default: - f.Err = &fs.PathError{Path: tag + ":" + file, Op: "read", Err: fmt.Errorf("unexpected non-blob type %q", fileType)} - case "missing": - f.Err = &fs.PathError{Path: tag + ":" + file, Op: "read", Err: fs.ErrNotExist} - case "blob": - f.Data = fileData - } - } - } - if len(bytes.TrimSpace(data)) != 0 { - return badGit() - } - - return missing, nil -} - func (r *gitRepo) RecentTag(rev, prefix string, allowed func(string) bool) (tag string, err error) { info, err := r.Stat(rev) if err != nil { diff --git a/libgo/go/cmd/go/internal/modfetch/codehost/shell.go b/libgo/go/cmd/go/internal/modfetch/codehost/shell.go index 0e9f3819667..eaa01950b95 100644 --- a/libgo/go/cmd/go/internal/modfetch/codehost/shell.go +++ b/libgo/go/cmd/go/internal/modfetch/codehost/shell.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // Interactive debugging shell for codehost.Repo implementations. diff --git a/libgo/go/cmd/go/internal/modfetch/codehost/vcs.go b/libgo/go/cmd/go/internal/modfetch/codehost/vcs.go index c2cca084e30..de62265efc5 100644 --- a/libgo/go/cmd/go/internal/modfetch/codehost/vcs.go +++ b/libgo/go/cmd/go/internal/modfetch/codehost/vcs.go @@ -38,7 +38,7 @@ type VCSError struct { func (e *VCSError) Error() string { return e.Err.Error() } -func vcsErrorf(format string, a ...interface{}) error { +func vcsErrorf(format string, a ...any) error { return &VCSError{Err: fmt.Errorf(format, a...)} } @@ -51,7 +51,7 @@ func NewRepo(vcs, remote string) (Repo, error) { repo Repo err error } - c := vcsRepoCache.Do(key{vcs, remote}, func() interface{} { + c := vcsRepoCache.Do(key{vcs, remote}, func() any { repo, err := newVCSRepo(vcs, remote) if err != nil { err = &VCSError{err} @@ -382,19 +382,6 @@ func (r *vcsRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) { return out, nil } -func (r *vcsRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[string]*FileRev, error) { - // We don't technically need to lock here since we're returning an error - // uncondititonally, but doing so anyway will help to avoid baking in - // lock-inversion bugs. - unlock, err := r.mu.Lock() - if err != nil { - return nil, err - } - defer unlock() - - return nil, vcsErrorf("ReadFileRevs not implemented") -} - func (r *vcsRepo) RecentTag(rev, prefix string, allowed func(string) bool) (tag string, err error) { // We don't technically need to lock here since we're returning an error // uncondititonally, but doing so anyway will help to avoid baking in diff --git a/libgo/go/cmd/go/internal/modfetch/coderepo.go b/libgo/go/cmd/go/internal/modfetch/coderepo.go index dfef9f73c27..79da010809b 100644 --- a/libgo/go/cmd/go/internal/modfetch/coderepo.go +++ b/libgo/go/cmd/go/internal/modfetch/coderepo.go @@ -321,7 +321,7 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e return ok } - invalidf := func(format string, args ...interface{}) error { + invalidf := func(format string, args ...any) error { return &module.ModuleError{ Path: r.modPath, Err: &module.InvalidVersionError{ @@ -567,11 +567,11 @@ func (r *codeRepo) validatePseudoVersion(info *codehost.RevInfo, version string) if rev != info.Short { switch { case strings.HasPrefix(rev, info.Short): - return fmt.Errorf("revision is longer than canonical (%s)", info.Short) + return fmt.Errorf("revision is longer than canonical (expected %s)", info.Short) case strings.HasPrefix(info.Short, rev): - return fmt.Errorf("revision is shorter than canonical (%s)", info.Short) + return fmt.Errorf("revision is shorter than canonical (expected %s)", info.Short) default: - return fmt.Errorf("does not match short name of revision (%s)", info.Short) + return fmt.Errorf("does not match short name of revision (expected %s)", info.Short) } } @@ -1066,7 +1066,7 @@ func (fi dataFileInfo) Size() int64 { return int64(len(fi.f.data)) } func (fi dataFileInfo) Mode() fs.FileMode { return 0644 } func (fi dataFileInfo) ModTime() time.Time { return time.Time{} } func (fi dataFileInfo) IsDir() bool { return false } -func (fi dataFileInfo) Sys() interface{} { return nil } +func (fi dataFileInfo) Sys() any { return nil } // hasPathPrefix reports whether the path s begins with the // elements in prefix. diff --git a/libgo/go/cmd/go/internal/modfetch/fetch.go b/libgo/go/cmd/go/internal/modfetch/fetch.go index d3d30d970b3..f5423b48ad3 100644 --- a/libgo/go/cmd/go/internal/modfetch/fetch.go +++ b/libgo/go/cmd/go/internal/modfetch/fetch.go @@ -48,7 +48,7 @@ func Download(ctx context.Context, mod module.Version) (dir string, err error) { dir string err error } - c := downloadCache.Do(mod, func() interface{} { + c := downloadCache.Do(mod, func() any { dir, err := download(ctx, mod) if err != nil { return cached{"", err} @@ -165,7 +165,7 @@ func DownloadZip(ctx context.Context, mod module.Version) (zipfile string, err e zipfile string err error } - c := downloadZipCache.Do(mod, func() interface{} { + c := downloadZipCache.Do(mod, func() any { zipfile, err := CachePath(mod, "zip") if err != nil { return cached{"", err} @@ -384,7 +384,8 @@ func RemoveAll(dir string) error { return robustio.RemoveAll(dir) } -var GoSumFile string // path to go.sum; set by package modload +var GoSumFile string // path to go.sum; set by package modload +var WorkspaceGoSumFiles []string // path to module go.sums in workspace; set by package modload type modSum struct { mod module.Version @@ -393,16 +394,39 @@ type modSum struct { var goSum struct { mu sync.Mutex - m map[module.Version][]string // content of go.sum file - status map[modSum]modSumStatus // state of sums in m - overwrite bool // if true, overwrite go.sum without incorporating its contents - enabled bool // whether to use go.sum at all + m map[module.Version][]string // content of go.sum file + w map[string]map[module.Version][]string // sum file in workspace -> content of that sum file + status map[modSum]modSumStatus // state of sums in m + overwrite bool // if true, overwrite go.sum without incorporating its contents + enabled bool // whether to use go.sum at all } type modSumStatus struct { used, dirty bool } +// Reset resets globals in the modfetch package, so previous loads don't affect +// contents of go.sum files +func Reset() { + GoSumFile = "" + WorkspaceGoSumFiles = nil + + // Uses of lookupCache and downloadCache both can call checkModSum, + // which in turn sets the used bit on goSum.status for modules. + // Reset them so used can be computed properly. + lookupCache = par.Cache{} + downloadCache = par.Cache{} + + // Clear all fields on goSum. It will be initialized later + goSum.mu.Lock() + goSum.m = nil + goSum.w = nil + goSum.status = nil + goSum.overwrite = false + goSum.enabled = false + goSum.mu.Unlock() +} + // initGoSum initializes the go.sum data. // The boolean it returns reports whether the // use of go.sum is now enabled. @@ -417,23 +441,38 @@ func initGoSum() (bool, error) { goSum.m = make(map[module.Version][]string) goSum.status = make(map[modSum]modSumStatus) + goSum.w = make(map[string]map[module.Version][]string) + + for _, f := range WorkspaceGoSumFiles { + goSum.w[f] = make(map[module.Version][]string) + _, err := readGoSumFile(goSum.w[f], f) + if err != nil { + return false, err + } + } + + enabled, err := readGoSumFile(goSum.m, GoSumFile) + goSum.enabled = enabled + return enabled, err +} + +func readGoSumFile(dst map[module.Version][]string, file string) (bool, error) { var ( data []byte err error ) - if actualSumFile, ok := fsys.OverlayPath(GoSumFile); ok { + if actualSumFile, ok := fsys.OverlayPath(file); ok { // Don't lock go.sum if it's part of the overlay. // On Plan 9, locking requires chmod, and we don't want to modify any file // in the overlay. See #44700. data, err = os.ReadFile(actualSumFile) } else { - data, err = lockedfile.Read(GoSumFile) + data, err = lockedfile.Read(file) } if err != nil && !os.IsNotExist(err) { return false, err } - goSum.enabled = true - readGoSum(goSum.m, GoSumFile, data) + readGoSum(dst, file, data) return true, nil } @@ -485,6 +524,16 @@ func HaveSum(mod module.Version) bool { if err != nil || !inited { return false } + for _, goSums := range goSum.w { + for _, h := range goSums[mod] { + if !strings.HasPrefix(h, "h1:") { + continue + } + if !goSum.status[modSum{mod, h}].dirty { + return true + } + } + } for _, h := range goSum.m[mod] { if !strings.HasPrefix(h, "h1:") { continue @@ -602,15 +651,32 @@ func checkModSum(mod module.Version, h string) error { // If it finds a conflicting pair instead, it calls base.Fatalf. // goSum.mu must be locked. func haveModSumLocked(mod module.Version, h string) bool { + sumFileName := "go.sum" + if strings.HasSuffix(GoSumFile, "go.work.sum") { + sumFileName = "go.work.sum" + } for _, vh := range goSum.m[mod] { if h == vh { return true } if strings.HasPrefix(vh, "h1:") { - base.Fatalf("verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\tgo.sum: %v"+goSumMismatch, mod.Path, mod.Version, h, vh) + base.Fatalf("verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\t%s: %v"+goSumMismatch, mod.Path, mod.Version, h, sumFileName, vh) + } + } + // Also check workspace sums. + foundMatch := false + // Check sums from all files in case there are conflicts between + // the files. + for goSumFile, goSums := range goSum.w { + for _, vh := range goSums[mod] { + if h == vh { + foundMatch = true + } else if strings.HasPrefix(vh, "h1:") { + base.Fatalf("verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\t%s: %v"+goSumMismatch, mod.Path, mod.Version, h, goSumFile, vh) + } } } - return false + return foundMatch } // addModSumLocked adds the pair mod,h to go.sum. @@ -693,19 +759,21 @@ func isValidSum(data []byte) bool { return true } +var ErrGoSumDirty = errors.New("updates to go.sum needed, disabled by -mod=readonly") + // WriteGoSum writes the go.sum file if it needs to be updated. // // keep is used to check whether a newly added sum should be saved in go.sum. // It should have entries for both module content sums and go.mod sums // (version ends with "/go.mod"). Existing sums will be preserved unless they // have been marked for deletion with TrimGoSum. -func WriteGoSum(keep map[module.Version]bool) { +func WriteGoSum(keep map[module.Version]bool, readonly bool) error { goSum.mu.Lock() defer goSum.mu.Unlock() // If we haven't read the go.sum file yet, don't bother writing it. if !goSum.enabled { - return + return nil } // Check whether we need to add sums for which keep[m] is true or remove @@ -723,10 +791,10 @@ Outer: } } if !dirty { - return + return nil } - if cfg.BuildMod == "readonly" { - base.Fatalf("go: updates to go.sum needed, disabled by -mod=readonly") + if readonly { + return ErrGoSumDirty } if _, ok := fsys.OverlayPath(GoSumFile); ok { base.Fatalf("go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay") @@ -747,7 +815,7 @@ Outer: goSum.m = make(map[module.Version][]string, len(goSum.m)) readGoSum(goSum.m, GoSumFile, data) for ms, st := range goSum.status { - if st.used { + if st.used && !sumInWorkspaceModulesLocked(ms.mod) { addModSumLocked(ms.mod, ms.sum) } } @@ -765,7 +833,7 @@ Outer: sort.Strings(list) for _, h := range list { st := goSum.status[modSum{m, h}] - if !st.dirty || (st.used && keep[m]) { + if (!st.dirty || (st.used && keep[m])) && !sumInWorkspaceModulesLocked(m) { fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h) } } @@ -774,11 +842,21 @@ Outer: }) if err != nil { - base.Fatalf("go: updating go.sum: %v", err) + return fmt.Errorf("updating go.sum: %w", err) } goSum.status = make(map[modSum]modSumStatus) goSum.overwrite = false + return nil +} + +func sumInWorkspaceModulesLocked(m module.Version) bool { + for _, goSums := range goSum.w { + if _, ok := goSums[m]; ok { + return true + } + } + return false } // TrimGoSum trims go.sum to contain only the modules needed for reproducible diff --git a/libgo/go/cmd/go/internal/modfetch/repo.go b/libgo/go/cmd/go/internal/modfetch/repo.go index 0bffa55af6f..1b42ecb6edb 100644 --- a/libgo/go/cmd/go/internal/modfetch/repo.go +++ b/libgo/go/cmd/go/internal/modfetch/repo.go @@ -196,7 +196,7 @@ func Lookup(proxy, path string) Repo { type cached struct { r Repo } - c := lookupCache.Do(lookupCacheKey{proxy, path}, func() interface{} { + c := lookupCache.Do(lookupCacheKey{proxy, path}, func() any { r := newCachingRepo(path, func() (Repo, error) { r, err := lookup(proxy, path) if err == nil && traceRepo { @@ -308,7 +308,7 @@ func newLoggingRepo(r Repo) *loggingRepo { // defer logCall("hello %s", arg)() // // Note the final (). -func logCall(format string, args ...interface{}) func() { +func logCall(format string, args ...any) func() { start := time.Now() fmt.Fprintf(os.Stderr, "+++ %s\n", fmt.Sprintf(format, args...)) return func() { @@ -371,7 +371,7 @@ type notExistError struct { err error } -func notExistErrorf(format string, args ...interface{}) error { +func notExistErrorf(format string, args ...any) error { return notExistError{fmt.Errorf(format, args...)} } diff --git a/libgo/go/cmd/go/internal/modfetch/sumdb.go b/libgo/go/cmd/go/internal/modfetch/sumdb.go index f233cba6df1..492b03bd84a 100644 --- a/libgo/go/cmd/go/internal/modfetch/sumdb.go +++ b/libgo/go/cmd/go/internal/modfetch/sumdb.go @@ -5,7 +5,6 @@ // Go checksum database lookup //go:build !cmd_go_bootstrap -// +build !cmd_go_bootstrap package modfetch @@ -201,7 +200,8 @@ func (c *dbClient) ReadConfig(file string) (data []byte, err error) { } if cfg.SumdbDir == "" { - return nil, errors.New("could not locate sumdb file: missing $GOPATH") + return nil, fmt.Errorf("could not locate sumdb file: missing $GOPATH: %s", + cfg.GoPathError) } targ := filepath.Join(cfg.SumdbDir, file) data, err = lockedfile.Read(targ) @@ -220,7 +220,8 @@ func (*dbClient) WriteConfig(file string, old, new []byte) error { return fmt.Errorf("cannot write key") } if cfg.SumdbDir == "" { - return errors.New("could not locate sumdb file: missing $GOPATH") + return fmt.Errorf("could not locate sumdb file: missing $GOPATH: %s", + cfg.GoPathError) } targ := filepath.Join(cfg.SumdbDir, file) os.MkdirAll(filepath.Dir(targ), 0777) diff --git a/libgo/go/cmd/go/internal/modget/get.go b/libgo/go/cmd/go/internal/modget/get.go index 9672e5598e0..3d8463e892c 100644 --- a/libgo/go/cmd/go/internal/modget/get.go +++ b/libgo/go/cmd/go/internal/modget/get.go @@ -37,7 +37,6 @@ import ( "cmd/go/internal/base" "cmd/go/internal/imports" - "cmd/go/internal/load" "cmd/go/internal/modfetch" "cmd/go/internal/modload" "cmd/go/internal/par" @@ -50,14 +49,14 @@ import ( ) var CmdGet = &base.Command{ - // Note: -d -u are listed explicitly because they are the most common get flags. + // Note: flags below are listed explicitly because they're the most common. // Do not send CLs removing them because they're covered by [get flags]. - UsageLine: "go get [-d] [-t] [-u] [-v] [build flags] [packages]", + UsageLine: "go get [-t] [-u] [-v] [build flags] [packages]", Short: "add dependencies to current module and install them", Long: ` Get resolves its command-line arguments to packages at specific module versions, -updates go.mod to require those versions, downloads source code into the -module cache, then builds and installs the named packages. +updates go.mod to require those versions, and downloads source code into the +module cache. To add a dependency for a package or upgrade it to its latest version: @@ -73,17 +72,18 @@ To remove a dependency on a module and downgrade modules that require it: See https://golang.org/ref/mod#go-get for details. -The 'go install' command may be used to build and install packages. When a -version is specified, 'go install' runs in module-aware mode and ignores -the go.mod file in the current directory. For example: +In earlier versions of Go, 'go get' was used to build and install packages. +Now, 'go get' is dedicated to adjusting dependencies in go.mod. 'go install' +may be used to build and install commands instead. When a version is specified, +'go install' runs in module-aware mode and ignores the go.mod file in the +current directory. For example: go install example.com/pkg@v1.2.3 go install example.com/pkg@latest See 'go help install' or https://golang.org/ref/mod#go-install for details. -In addition to build flags (listed in 'go help build') 'go get' accepts the -following flags. +'go get' accepts the following flags. The -t flag instructs get to consider modules needed to build tests of packages specified on the command line. @@ -98,15 +98,9 @@ but changes the default to select patch releases. When the -t and -u flags are used together, get will update test dependencies as well. -The -d flag instructs get not to build or install packages. get will only -update go.mod and download source code needed to build packages. - -Building and installing packages with get is deprecated. In a future release, -the -d flag will be enabled by default, and 'go get' will be only be used to -adjust dependencies of the current module. To install a package using -dependencies from the current module, use 'go install'. To install a package -ignoring the current module, use 'go install' with an @version suffix like -"@latest" after each argument. +The -x flag prints commands as they are executed. This is useful for +debugging version control commands when a module is downloaded directly +from a repository. For more about modules, see https://golang.org/ref/mod. @@ -218,7 +212,7 @@ variable for future go command invocations. } var ( - getD = CmdGet.Flag.Bool("d", false, "") + getD = CmdGet.Flag.Bool("d", true, "") getF = CmdGet.Flag.Bool("f", false, "") getFix = CmdGet.Flag.Bool("fix", false, "") getM = CmdGet.Flag.Bool("m", false, "") @@ -263,30 +257,50 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { case "", "upgrade", "patch": // ok default: - base.Fatalf("go get: unknown upgrade flag -u=%s", getU.rawVersion) + base.Fatalf("go: unknown upgrade flag -u=%s", getU.rawVersion) + } + // TODO(#43684): in the future (Go 1.20), warn that -d is a no-op. + if !*getD { + base.Fatalf("go: -d flag may not be disabled") } if *getF { - fmt.Fprintf(os.Stderr, "go get: -f flag is a no-op when using modules\n") + fmt.Fprintf(os.Stderr, "go: -f flag is a no-op when using modules\n") } if *getFix { - fmt.Fprintf(os.Stderr, "go get: -fix flag is a no-op when using modules\n") + fmt.Fprintf(os.Stderr, "go: -fix flag is a no-op when using modules\n") } if *getM { - base.Fatalf("go get: -m flag is no longer supported; consider -d to skip building packages") + base.Fatalf("go: -m flag is no longer supported") } if *getInsecure { - base.Fatalf("go get: -insecure flag is no longer supported; use GOINSECURE instead") + base.Fatalf("go: -insecure flag is no longer supported; use GOINSECURE instead") } + modload.ForceUseModules = true + // Do not allow any updating of go.mod until we've applied // all the requested changes and checked that the result matches // what was requested. - modload.DisallowWriteGoMod() + modload.ExplicitWriteGoMod = true // Allow looking up modules for import paths when outside of a module. // 'go get' is expected to do this, unlike other commands. modload.AllowMissingModuleImports() + // 'go get' no longer builds or installs packages, so there's nothing to do + // if there's no go.mod file. + // TODO(#40775): make modload.Init return ErrNoModRoot instead of exiting. + // We could handle that here by printing a different message. + modload.Init() + if !modload.HasModRoot() { + base.Fatalf("go: go.mod file not found in current directory or any parent directory.\n" + + "\t'go get' is no longer supported outside a module.\n" + + "\tTo build and install a command, use 'go install' with a version,\n" + + "\tlike 'go install example.com/cmd@latest'\n" + + "\tFor more information, see https://golang.org/doc/go-get-install-deprecation\n" + + "\tor run 'go help get' or 'go help install'.") + } + queries := parseArgs(ctx, args) r := newResolver(ctx, queries) @@ -356,74 +370,12 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { } r.checkPackageProblems(ctx, pkgPatterns) - // We've already downloaded modules (and identified direct and indirect - // dependencies) by loading packages in findAndUpgradeImports. - // So if -d is set, we're done after the module work. - // - // Otherwise, we need to build and install the packages matched by - // command line arguments. - // Note that 'go get -u' without arguments is equivalent to - // 'go get -u .', so we'll typically build the package in the current - // directory. - if !*getD && len(pkgPatterns) > 0 { - work.BuildInit() - - pkgOpts := load.PackageOpts{ModResolveTests: *getT} - var pkgs []*load.Package - for _, pkg := range load.PackagesAndErrors(ctx, pkgOpts, pkgPatterns) { - if pkg.Error != nil { - var noGo *load.NoGoError - if errors.As(pkg.Error.Err, &noGo) { - if m := modload.PackageModule(pkg.ImportPath); m.Path == pkg.ImportPath { - // pkg is at the root of a module, and doesn't exist with the current - // build tags. Probably the user just wanted to change the version of - // that module — not also build the package — so suppress the error. - // (See https://golang.org/issue/33526.) - continue - } - } - } - pkgs = append(pkgs, pkg) - } - load.CheckPackageErrors(pkgs) - - haveExternalExe := false - for _, pkg := range pkgs { - if pkg.Name == "main" && pkg.Module != nil && pkg.Module.Path != modload.Target.Path { - haveExternalExe = true - break - } - } - if haveExternalExe { - fmt.Fprint(os.Stderr, "go get: installing executables with 'go get' in module mode is deprecated.") - var altMsg string - if modload.HasModRoot() { - altMsg = ` - To adjust and download dependencies of the current module, use 'go get -d'. - To install using requirements of the current module, use 'go install'. - To install ignoring the current module, use 'go install' with a version, - like 'go install example.com/cmd@latest'. -` - } else { - altMsg = "\n\tUse 'go install pkg@version' instead.\n" - } - fmt.Fprint(os.Stderr, altMsg) - fmt.Fprintf(os.Stderr, "\tFor more information, see https://golang.org/doc/go-get-install-deprecation\n\tor run 'go help get' or 'go help install'.\n") - } - - work.InstallPackages(ctx, pkgPatterns, pkgs) - } - - if !modload.HasModRoot() { - return - } - // Everything succeeded. Update go.mod. oldReqs := reqsFromGoMod(modload.ModFile()) - modload.AllowWriteGoMod() - modload.WriteGoMod(ctx) - modload.DisallowWriteGoMod() + if err := modload.WriteGoMod(ctx); err != nil { + base.Fatalf("go: %v", err) + } newReqs := reqsFromGoMod(modload.ModFile()) r.reportChanges(oldReqs, newReqs) @@ -440,7 +392,7 @@ func parseArgs(ctx context.Context, rawArgs []string) []*query { for _, arg := range search.CleanPatterns(rawArgs) { q, err := newQuery(arg) if err != nil { - base.Errorf("go get: %v", err) + base.Errorf("go: %v", err) continue } @@ -455,11 +407,11 @@ func parseArgs(ctx context.Context, rawArgs []string) []*query { // if the argument has no version and either has no slash or refers to an existing file. if strings.HasSuffix(q.raw, ".go") && q.rawVersion == "" { if !strings.Contains(q.raw, "/") { - base.Errorf("go get %s: arguments must be package or module paths", q.raw) + base.Errorf("go: %s: arguments must be package or module paths", q.raw) continue } if fi, err := os.Stat(q.raw); err == nil && !fi.IsDir() { - base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", q.raw) + base.Errorf("go: %s exists as a file, but 'go get' requires package arguments", q.raw) continue } } @@ -649,7 +601,7 @@ func (r *resolver) matchInModule(ctx context.Context, pattern string, m module.V err error } - e := r.matchInModuleCache.Do(key{pattern, m}, func() interface{} { + e := r.matchInModuleCache.Do(key{pattern, m}, func() any { match := modload.MatchInModule(ctx, pattern, m, imports.AnyTags()) if len(match.Errs) > 0 { return entry{match.Pkgs, match.Errs[0]} @@ -675,7 +627,9 @@ func (r *resolver) queryNone(ctx context.Context, q *query) { if !q.isWildcard() { q.pathOnce(q.pattern, func() pathSet { - if modload.HasModRoot() && q.pattern == modload.Target.Path { + hasModRoot := modload.HasModRoot() + if hasModRoot && modload.MainModules.Contains(q.pattern) { + v := module.Version{Path: q.pattern} // The user has explicitly requested to downgrade their own module to // version "none". This is not an entirely unreasonable request: it // could plausibly mean “downgrade away everything that depends on any @@ -686,7 +640,7 @@ func (r *resolver) queryNone(ctx context.Context, q *query) { // However, neither of those behaviors would be consistent with the // plain meaning of the query. To try to reduce confusion, reject the // query explicitly. - return errSet(&modload.QueryMatchesMainModuleError{Pattern: q.pattern, Query: q.version}) + return errSet(&modload.QueryMatchesMainModulesError{MainModules: []module.Version{v}, Pattern: q.pattern, Query: q.version}) } return pathSet{mod: module.Version{Path: q.pattern, Version: "none"}} @@ -698,8 +652,8 @@ func (r *resolver) queryNone(ctx context.Context, q *query) { continue } q.pathOnce(curM.Path, func() pathSet { - if modload.HasModRoot() && curM == modload.Target { - return errSet(&modload.QueryMatchesMainModuleError{Pattern: q.pattern, Query: q.version}) + if modload.HasModRoot() && curM.Version == "" && modload.MainModules.Contains(curM.Path) { + return errSet(&modload.QueryMatchesMainModulesError{MainModules: []module.Version{curM}, Pattern: q.pattern, Query: q.version}) } return pathSet{mod: module.Version{Path: curM.Path, Version: "none"}} }) @@ -718,28 +672,38 @@ func (r *resolver) performLocalQueries(ctx context.Context) { // Absolute paths like C:\foo and relative paths like ../foo... are // restricted to matching packages in the main module. - pkgPattern := modload.DirImportPath(ctx, q.pattern) + pkgPattern, mainModule := modload.MainModules.DirImportPath(ctx, q.pattern) if pkgPattern == "." { - return errSet(fmt.Errorf("%s%s is not within module rooted at %s", q.pattern, absDetail, modload.ModRoot())) + modload.MustHaveModRoot() + var modRoots []string + for _, m := range modload.MainModules.Versions() { + modRoots = append(modRoots, modload.MainModules.ModRoot(m)) + } + var plural string + if len(modRoots) != 1 { + plural = "s" + } + return errSet(fmt.Errorf("%s%s is not within module%s rooted at %s", q.pattern, absDetail, plural, strings.Join(modRoots, ", "))) } - match := modload.MatchInModule(ctx, pkgPattern, modload.Target, imports.AnyTags()) + match := modload.MatchInModule(ctx, pkgPattern, mainModule, imports.AnyTags()) if len(match.Errs) > 0 { return pathSet{err: match.Errs[0]} } if len(match.Pkgs) == 0 { if q.raw == "" || q.raw == "." { - return errSet(fmt.Errorf("no package in current directory")) + return errSet(fmt.Errorf("no package to get in current directory")) } if !q.isWildcard() { - return errSet(fmt.Errorf("%s%s is not a package in module rooted at %s", q.pattern, absDetail, modload.ModRoot())) + modload.MustHaveModRoot() + return errSet(fmt.Errorf("%s%s is not a package in module rooted at %s", q.pattern, absDetail, modload.MainModules.ModRoot(mainModule))) } search.WarnUnmatched([]*search.Match{match}) return pathSet{} } - return pathSet{pkgMods: []module.Version{modload.Target}} + return pathSet{pkgMods: []module.Version{mainModule}} }) } } @@ -789,11 +753,12 @@ func (r *resolver) queryWildcard(ctx context.Context, q *query) { return pathSet{} } - if curM.Path == modload.Target.Path && !versionOkForMainModule(q.version) { + if modload.MainModules.Contains(curM.Path) && !versionOkForMainModule(q.version) { if q.matchesPath(curM.Path) { - return errSet(&modload.QueryMatchesMainModuleError{ - Pattern: q.pattern, - Query: q.version, + return errSet(&modload.QueryMatchesMainModulesError{ + MainModules: []module.Version{curM}, + Pattern: q.pattern, + Query: q.version, }) } @@ -928,7 +893,7 @@ func (r *resolver) checkWildcardVersions(ctx context.Context) { // curM at its original version contains a path matching q.pattern, // but at rev.Version it does not, so (somewhat paradoxically) if // we changed the version of curM it would no longer match the query. - var version interface{} = m + var version any = m if rev.Version != q.version { version = fmt.Sprintf("%s@%s (%s)", m.Path, q.version, m.Version) } @@ -1159,8 +1124,8 @@ func (r *resolver) loadPackages(ctx context.Context, patterns []string, findPack } opts.AllowPackage = func(ctx context.Context, path string, m module.Version) error { - if m.Path == "" || m == modload.Target { - // Packages in the standard library and main module are already at their + if m.Path == "" || m.Version == "" { + // Packages in the standard library and main modules are already at their // latest (and only) available versions. return nil } @@ -1327,7 +1292,7 @@ func (r *resolver) applyUpgrades(ctx context.Context, upgrades []pathSet) (chang var tentative []module.Version for _, cs := range upgrades { if cs.err != nil { - base.Errorf("go get: %v", cs.err) + base.Errorf("go: %v", cs.err) continue } @@ -1370,11 +1335,11 @@ func (r *resolver) disambiguate(cs pathSet) (filtered pathSet, isPackage bool, m continue } - if m.Path == modload.Target.Path { - if m.Version == modload.Target.Version { + if modload.MainModules.Contains(m.Path) { + if m.Version == "" { return pathSet{}, true, m, true } - // The main module can only be set to its own version. + // A main module can only be set to its own version. continue } @@ -1720,13 +1685,13 @@ func (r *resolver) reportChanges(oldReqs, newReqs []module.Version) { }) for _, c := range sortedChanges { if c.old == "" { - fmt.Fprintf(os.Stderr, "go get: added %s %s\n", c.path, c.new) + fmt.Fprintf(os.Stderr, "go: added %s %s\n", c.path, c.new) } else if c.new == "none" || c.new == "" { - fmt.Fprintf(os.Stderr, "go get: removed %s %s\n", c.path, c.old) + fmt.Fprintf(os.Stderr, "go: removed %s %s\n", c.path, c.old) } else if semver.Compare(c.new, c.old) > 0 { - fmt.Fprintf(os.Stderr, "go get: upgraded %s %s => %s\n", c.path, c.old, c.new) + fmt.Fprintf(os.Stderr, "go: upgraded %s %s => %s\n", c.path, c.old, c.new) } else { - fmt.Fprintf(os.Stderr, "go get: downgraded %s %s => %s\n", c.path, c.old, c.new) + fmt.Fprintf(os.Stderr, "go: downgraded %s %s => %s\n", c.path, c.old, c.new) } } @@ -1744,10 +1709,11 @@ func (r *resolver) resolve(q *query, m module.Version) { panic("internal error: resolving a module.Version with an empty path") } - if m.Path == modload.Target.Path && m.Version != modload.Target.Version { - reportError(q, &modload.QueryMatchesMainModuleError{ - Pattern: q.pattern, - Query: q.version, + if modload.MainModules.Contains(m.Path) && m.Version != "" { + reportError(q, &modload.QueryMatchesMainModulesError{ + MainModules: []module.Version{{Path: m.Path}}, + Pattern: q.pattern, + Query: q.version, }) return } @@ -1775,7 +1741,7 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi resolved := make([]module.Version, 0, len(r.resolvedVersion)) for mPath, rv := range r.resolvedVersion { - if mPath != modload.Target.Path { + if !modload.MainModules.Contains(mPath) { resolved = append(resolved, module.Version{Path: mPath, Version: rv.version}) } } @@ -1784,7 +1750,7 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi if err != nil { var constraint *modload.ConstraintError if !errors.As(err, &constraint) { - base.Errorf("go get: %v", err) + base.Errorf("go: %v", err) return false } @@ -1796,7 +1762,7 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi return rv.reason.ResolvedString(module.Version{Path: m.Path, Version: rv.version}) } for _, c := range constraint.Conflicts { - base.Errorf("go get: %v requires %v, not %v", reason(c.Source), c.Dep, reason(c.Constraint)) + base.Errorf("go: %v requires %v, not %v", reason(c.Source), c.Dep, reason(c.Constraint)) } return false } diff --git a/libgo/go/cmd/go/internal/modget/query.go b/libgo/go/cmd/go/internal/modget/query.go index 1a5a60f7eb9..887cb51b317 100644 --- a/libgo/go/cmd/go/internal/modget/query.go +++ b/libgo/go/cmd/go/internal/modget/query.go @@ -192,9 +192,9 @@ func (q *query) validate() error { // TODO(bcmills): "all@none" seems like a totally reasonable way to // request that we remove all module requirements, leaving only the main // module and standard library. Perhaps we should implement that someday. - return &modload.QueryMatchesMainModuleError{ - Pattern: q.pattern, - Query: q.version, + return &modload.QueryUpgradesAllError{ + MainModules: modload.MainModules.Versions(), + Query: q.version, } } } @@ -284,21 +284,21 @@ func reportError(q *query, err error) { patternRE := regexp.MustCompile("(?m)(?:[ \t(\"`]|^)" + regexp.QuoteMeta(q.pattern) + "(?:[ @:;)\"`]|$)") if patternRE.MatchString(errStr) { if q.rawVersion == "" { - base.Errorf("go get: %s", errStr) + base.Errorf("go: %s", errStr) return } versionRE := regexp.MustCompile("(?m)(?:[ @(\"`]|^)" + regexp.QuoteMeta(q.version) + "(?:[ :;)\"`]|$)") if versionRE.MatchString(errStr) { - base.Errorf("go get: %s", errStr) + base.Errorf("go: %s", errStr) return } } if qs := q.String(); qs != "" { - base.Errorf("go get %s: %s", qs, errStr) + base.Errorf("go: %s: %s", qs, errStr) } else { - base.Errorf("go get: %s", errStr) + base.Errorf("go: %s", errStr) } } diff --git a/libgo/go/cmd/go/internal/modload/build.go b/libgo/go/cmd/go/internal/modload/build.go index becf6b87f80..bfc73cc2f9a 100644 --- a/libgo/go/cmd/go/internal/modload/build.go +++ b/libgo/go/cmd/go/internal/modload/build.go @@ -5,7 +5,6 @@ package modload import ( - "bytes" "context" "encoding/hex" "errors" @@ -80,7 +79,7 @@ func ModuleInfo(ctx context.Context, path string) *modinfo.ModulePublic { v string ok bool ) - if rs.depth == lazy { + if rs.pruning == pruned { v, ok = rs.rootSelected(path) } if !ok { @@ -212,20 +211,20 @@ func addDeprecation(ctx context.Context, m *modinfo.ModulePublic) { // in rs (which may be nil to indicate that m was not loaded from a requirement // graph). func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode ListMode) *modinfo.ModulePublic { - if m == Target { + if m.Version == "" && MainModules.Contains(m.Path) { info := &modinfo.ModulePublic{ Path: m.Path, Version: m.Version, Main: true, } - if v, ok := rawGoVersion.Load(Target); ok { + if v, ok := rawGoVersion.Load(m); ok { info.GoVersion = v.(string) } else { panic("internal error: GoVersion not set for main module") } - if HasModRoot() { - info.Dir = ModRoot() - info.GoMod = ModFilePath() + if modRoot := MainModules.ModRoot(m); modRoot != "" { + info.Dir = modRoot + info.GoMod = modFilePath(modRoot) } return info } @@ -322,7 +321,7 @@ func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode Li if filepath.IsAbs(r.Path) { info.Replace.Dir = r.Path } else { - info.Replace.Dir = filepath.Join(ModRoot(), r.Path) + info.Replace.Dir = filepath.Join(replaceRelativeTo(), r.Path) } info.Replace.GoMod = filepath.Join(info.Replace.Dir, "go.mod") } @@ -336,116 +335,33 @@ func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode Li return info } -// PackageBuildInfo returns a string containing module version information -// for modules providing packages named by path and deps. path and deps must -// name packages that were resolved successfully with LoadPackages. -func PackageBuildInfo(path string, deps []string) string { - if isStandardImportPath(path) || !Enabled() { - return "" - } - - target := mustFindModule(loaded, path, path) - mdeps := make(map[module.Version]bool) - for _, dep := range deps { - if !isStandardImportPath(dep) { - mdeps[mustFindModule(loaded, path, dep)] = true - } - } - var mods []module.Version - delete(mdeps, target) - for mod := range mdeps { - mods = append(mods, mod) - } - module.Sort(mods) - - var buf bytes.Buffer - fmt.Fprintf(&buf, "path\t%s\n", path) - - writeEntry := func(token string, m module.Version) { - mv := m.Version - if mv == "" { - mv = "(devel)" - } - fmt.Fprintf(&buf, "%s\t%s\t%s", token, m.Path, mv) - if r := Replacement(m); r.Path == "" { - fmt.Fprintf(&buf, "\t%s\n", modfetch.Sum(m)) - } else { - fmt.Fprintf(&buf, "\n=>\t%s\t%s\t%s\n", r.Path, r.Version, modfetch.Sum(r)) - } - } - - writeEntry("mod", target) - for _, mod := range mods { - writeEntry("dep", mod) - } - - return buf.String() -} - -// mustFindModule is like findModule, but it calls base.Fatalf if the -// module can't be found. -// -// TODO(jayconrod): remove this. Callers should use findModule and return -// errors instead of relying on base.Fatalf. -func mustFindModule(ld *loader, target, path string) module.Version { - pkg, ok := ld.pkgCache.Get(path).(*loadPkg) - if ok { - if pkg.err != nil { - base.Fatalf("build %v: cannot load %v: %v", target, path, pkg.err) - } - return pkg.mod - } - - if path == "command-line-arguments" { - return Target - } - - base.Fatalf("build %v: cannot find module for path %v", target, path) - panic("unreachable") -} - // findModule searches for the module that contains the package at path. // If the package was loaded, its containing module and true are returned. -// Otherwise, module.Version{} and false are returend. +// Otherwise, module.Version{} and false are returned. func findModule(ld *loader, path string) (module.Version, bool) { if pkg, ok := ld.pkgCache.Get(path).(*loadPkg); ok { return pkg.mod, pkg.mod != module.Version{} } - if path == "command-line-arguments" { - return Target, true - } return module.Version{}, false } func ModInfoProg(info string, isgccgo bool) []byte { - // Inject a variable with the debug information as runtime.modinfo, - // but compile it in package main so that it is specific to the binary. - // The variable must be a literal so that it will have the correct value - // before the initializer for package main runs. - // - // The runtime startup code refers to the variable, which keeps it live - // in all binaries. - // - // Note: we use an alternate recipe below for gccgo (based on an - // init function) due to the fact that gccgo does not support - // applying a "//go:linkname" directive to a variable. This has - // drawbacks in that other packages may want to look at the module - // info in their init functions (see issue 29628), which won't - // work for gccgo. See also issue 30344. - - if !isgccgo { - return []byte(fmt.Sprintf(`package main -import _ "unsafe" -//go:linkname __set_modinfo__ runtime.setmodinfo -func __set_modinfo__(string) -func init() { __set_modinfo__(%q) } - `, string(infoStart)+info+string(infoEnd))) - } else { + // Inject an init function to set runtime.modinfo. + // This is only used for gccgo - with gc we hand the info directly to the linker. + // The init function has the drawback that packages may want to + // look at the module info in their init functions (see issue 29628), + // which won't work. See also issue 30344. + if isgccgo { return []byte(fmt.Sprintf(`package main import _ "unsafe" //go:linkname __set_debug_modinfo__ runtime.setmodinfo func __set_debug_modinfo__(string) func init() { __set_debug_modinfo__(%q) } -`, string(infoStart)+info+string(infoEnd))) +`, ModInfoData(info))) } + return nil +} + +func ModInfoData(info string) []byte { + return []byte(string(infoStart) + info + string(infoEnd)) } diff --git a/libgo/go/cmd/go/internal/modload/buildlist.go b/libgo/go/cmd/go/internal/modload/buildlist.go index bf695673167..6f9072c8c48 100644 --- a/libgo/go/cmd/go/internal/modload/buildlist.go +++ b/libgo/go/cmd/go/internal/modload/buildlist.go @@ -30,18 +30,25 @@ func capVersionSlice(s []module.Version) []module.Version { // A Requirements represents a logically-immutable set of root module requirements. type Requirements struct { - // depth is the depth at which the requirement graph is computed. + // pruning is the pruning at which the requirement graph is computed. // - // If eager, the graph includes all transitive requirements regardless of depth. + // If unpruned, the graph includes all transitive requirements regardless + // of whether the requiring module supports pruning. // - // If lazy, the graph includes only the root modules, the explicit + // If pruned, the graph includes only the root modules, the explicit // requirements of those root modules, and the transitive requirements of only - // the *non-lazy* root modules. - depth modDepth - - // rootModules is the set of module versions explicitly required by the main - // module, sorted and capped to length. It may contain duplicates, and may - // contain multiple versions for a given module path. + // the root modules that do not support pruning. + // + // If workspace, the graph includes only the workspace modules, the explicit + // requirements of the workspace modules, and the transitive requirements of + // the workspace modules that do not support pruning. + pruning modPruning + + // rootModules is the set of root modules of the graph, sorted and capped to + // length. It may contain duplicates, and may contain multiple versions for a + // given module path. The root modules of the groph are the set of main + // modules in workspace mode, and the main module's direct requirements + // outside workspace mode. rootModules []module.Version maxRootVersion map[string]string @@ -97,10 +104,23 @@ var requirements *Requirements // // If vendoring is in effect, the caller must invoke initVendor on the returned // *Requirements before any other method. -func newRequirements(depth modDepth, rootModules []module.Version, direct map[string]bool) *Requirements { +func newRequirements(pruning modPruning, rootModules []module.Version, direct map[string]bool) *Requirements { + if pruning == workspace { + return &Requirements{ + pruning: pruning, + rootModules: capVersionSlice(rootModules), + maxRootVersion: nil, + direct: direct, + } + } + + if workFilePath != "" && pruning != workspace { + panic("in workspace mode, but pruning is not workspace in newRequirements") + } + for i, m := range rootModules { - if m == Target { - panic(fmt.Sprintf("newRequirements called with untrimmed build list: rootModules[%v] is Target", i)) + if m.Version == "" && MainModules.Contains(m.Path) { + panic(fmt.Sprintf("newRequirements called with untrimmed build list: rootModules[%v] is a main module", i)) } if m.Path == "" || m.Version == "" { panic(fmt.Sprintf("bad requirement: rootModules[%v] = %v", i, m)) @@ -114,7 +134,7 @@ func newRequirements(depth modDepth, rootModules []module.Version, direct map[st } rs := &Requirements{ - depth: depth, + pruning: pruning, rootModules: capVersionSlice(rootModules), maxRootVersion: make(map[string]string, len(rootModules)), direct: direct, @@ -135,13 +155,18 @@ func newRequirements(depth modDepth, rootModules []module.Version, direct map[st func (rs *Requirements) initVendor(vendorList []module.Version) { rs.graphOnce.Do(func() { mg := &ModuleGraph{ - g: mvs.NewGraph(cmpVersion, []module.Version{Target}), + g: mvs.NewGraph(cmpVersion, MainModules.Versions()), } - if rs.depth == lazy { - // The roots of a lazy module should already include every module in the - // vendor list, because the vendored modules are the same as those - // maintained as roots by the lazy loading “import invariant”. + if MainModules.Len() != 1 { + panic("There should be exactly one main module in Vendor mode.") + } + mainModule := MainModules.Versions()[0] + + if rs.pruning == pruned { + // The roots of a pruned module should already include every module in the + // vendor list, because the vendored modules are the same as those needed + // for graph pruning. // // Just to be sure, we'll double-check that here. inconsistent := false @@ -156,9 +181,9 @@ func (rs *Requirements) initVendor(vendorList []module.Version) { } // Now we can treat the rest of the module graph as effectively “pruned - // out”, like a more aggressive version of lazy loading: in vendor mode, - // the root requirements *are* the complete module graph. - mg.g.Require(Target, rs.rootModules) + // out”, as though we are viewing the main module from outside: in vendor + // mode, the root requirements *are* the complete module graph. + mg.g.Require(mainModule, rs.rootModules) } else { // The transitive requirements of the main module are not in general available // from the vendor directory, and we don't actually know how we got from @@ -170,7 +195,7 @@ func (rs *Requirements) initVendor(vendorList []module.Version) { // graph, but still distinguishes between direct and indirect // dependencies. vendorMod := module.Version{Path: "vendor/modules.txt", Version: ""} - mg.g.Require(Target, append(rs.rootModules, vendorMod)) + mg.g.Require(mainModule, append(rs.rootModules, vendorMod)) mg.g.Require(vendorMod, vendorList) } @@ -182,8 +207,8 @@ func (rs *Requirements) initVendor(vendorList []module.Version) { // path, or the zero module.Version and ok=false if the module is not a root // dependency. func (rs *Requirements) rootSelected(path string) (version string, ok bool) { - if path == Target.Path { - return Target.Version, true + if MainModules.Contains(path) { + return "", true } if v, ok := rs.maxRootVersion[path]; ok { return v, true @@ -197,7 +222,7 @@ func (rs *Requirements) rootSelected(path string) (version string, ok bool) { // selection. func (rs *Requirements) hasRedundantRoot() bool { for i, m := range rs.rootModules { - if m.Path == Target.Path || (i > 0 && m.Path == rs.rootModules[i-1].Path) { + if MainModules.Contains(m.Path) || (i > 0 && m.Path == rs.rootModules[i-1].Path) { return true } } @@ -214,7 +239,7 @@ func (rs *Requirements) hasRedundantRoot() bool { // returns a non-nil error of type *mvs.BuildListError. func (rs *Requirements) Graph(ctx context.Context) (*ModuleGraph, error) { rs.graphOnce.Do(func() { - mg, mgErr := readModGraph(ctx, rs.depth, rs.rootModules) + mg, mgErr := readModGraph(ctx, rs.pruning, rs.rootModules) rs.graph.Store(cachedGraph{mg, mgErr}) }) cached := rs.graph.Load().(cachedGraph) @@ -230,7 +255,7 @@ func (rs *Requirements) IsDirect(path string) bool { // A ModuleGraph represents the complete graph of module dependencies // of a main module. // -// If the main module is lazily loaded, the graph does not include +// If the main module supports module graph pruning, the graph does not include // transitive dependencies of non-root (implicit) dependencies. type ModuleGraph struct { g *mvs.Graph @@ -254,8 +279,16 @@ var readModGraphDebugOnce sync.Once // // Unlike LoadModGraph, readModGraph does not attempt to diagnose or update // inconsistent roots. -func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) (*ModuleGraph, error) { - if depth == lazy { +func readModGraph(ctx context.Context, pruning modPruning, roots []module.Version) (*ModuleGraph, error) { + if pruning == pruned { + // Enable diagnostics for lazy module loading + // (https://golang.org/ref/mod#lazy-loading) only if the module graph is + // pruned. + // + // In unpruned modules,we load the module graph much more aggressively (in + // order to detect inconsistencies that wouldn't be feasible to spot-check), + // so it wouldn't be useful to log when that occurs (because it happens in + // normal operation all the time). readModGraphDebugOnce.Do(func() { for _, f := range strings.Split(os.Getenv("GODEBUG"), ",") { switch f { @@ -274,21 +307,26 @@ func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) ( mu sync.Mutex // guards mg.g and hasError during loading hasError bool mg = &ModuleGraph{ - g: mvs.NewGraph(cmpVersion, []module.Version{Target}), + g: mvs.NewGraph(cmpVersion, MainModules.Versions()), } ) - mg.g.Require(Target, roots) + if pruning != workspace { + if inWorkspaceMode() { + panic("pruning is not workspace in workspace mode") + } + mg.g.Require(MainModules.mustGetSingleMainModule(), roots) + } var ( - loadQueue = par.NewQueue(runtime.GOMAXPROCS(0)) - loadingEager sync.Map // module.Version → nil; the set of modules that have been or are being loaded via eager roots + loadQueue = par.NewQueue(runtime.GOMAXPROCS(0)) + loadingUnpruned sync.Map // module.Version → nil; the set of modules that have been or are being loaded via roots that do not support pruning ) // loadOne synchronously loads the explicit requirements for module m. // It does not load the transitive requirements of m even if the go version in - // m's go.mod file indicates eager loading. + // m's go.mod file indicates that it supports graph pruning. loadOne := func(m module.Version) (*modFileSummary, error) { - cached := mg.loadCache.Do(m, func() interface{} { + cached := mg.loadCache.Do(m, func() any { summary, err := goModSummary(m) mu.Lock() @@ -305,16 +343,16 @@ func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) ( return cached.summary, cached.err } - var enqueue func(m module.Version, depth modDepth) - enqueue = func(m module.Version, depth modDepth) { + var enqueue func(m module.Version, pruning modPruning) + enqueue = func(m module.Version, pruning modPruning) { if m.Version == "none" { return } - if depth == eager { - if _, dup := loadingEager.LoadOrStore(m, nil); dup { - // m has already been enqueued for loading. Since eager loading may - // follow cycles in the the requirement graph, we need to return early + if pruning == unpruned { + if _, dup := loadingUnpruned.LoadOrStore(m, nil); dup { + // m has already been enqueued for loading. Since unpruned loading may + // follow cycles in the requirement graph, we need to return early // to avoid making the load queue infinitely long. return } @@ -326,24 +364,74 @@ func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) ( return // findError will report the error later. } - // If the version in m's go.mod file implies eager loading, then we cannot - // assume that the explicit requirements of m (added by loadOne) are - // sufficient to build the packages it contains. We must load its full + // If the version in m's go.mod file does not support pruning, then we + // cannot assume that the explicit requirements of m (added by loadOne) + // are sufficient to build the packages it contains. We must load its full // transitive dependency graph to be sure that we see all relevant // dependencies. - if depth == eager || summary.depth == eager { + if pruning != pruned || summary.pruning == unpruned { + nextPruning := summary.pruning + if pruning == unpruned { + nextPruning = unpruned + } for _, r := range summary.require { - enqueue(r, eager) + enqueue(r, nextPruning) } } }) } for _, m := range roots { - enqueue(m, depth) + enqueue(m, pruning) } <-loadQueue.Idle() + // Reload any dependencies of the main modules which are not + // at their selected versions at workspace mode, because the + // requirements don't accurately reflect the transitive imports. + if pruning == workspace { + // hasDepsInAll contains the set of modules that need to be loaded + // at workspace pruning because any of their dependencies may + // provide packages in all. + hasDepsInAll := make(map[string]bool) + seen := map[module.Version]bool{} + for _, m := range roots { + hasDepsInAll[m.Path] = true + seen[m] = true + } + // This loop will terminate because it will call enqueue on each version of + // each dependency of the modules in hasDepsInAll at most once (and only + // calls enqueue on successively increasing versions of each dependency). + for { + needsEnqueueing := map[module.Version]bool{} + for p := range hasDepsInAll { + m := module.Version{Path: p, Version: mg.g.Selected(p)} + reqs, ok := mg.g.RequiredBy(m) + if !ok { + needsEnqueueing[m] = true + continue + } + for _, r := range reqs { + s := module.Version{Path: r.Path, Version: mg.g.Selected(r.Path)} + if cmpVersion(s.Version, r.Version) > 0 && !seen[s] { + needsEnqueueing[s] = true + } + } + } + // add all needs enqueueing to paths we care about + if len(needsEnqueueing) == 0 { + break + } + + for p := range needsEnqueueing { + enqueue(p, workspace) + seen[p] = true + hasDepsInAll[p.Path] = true + } + <-loadQueue.Idle() + } + } + if hasError { return mg, mg.findError() } @@ -351,8 +439,7 @@ func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) ( } // RequiredBy returns the dependencies required by module m in the graph, -// or ok=false if module m's dependencies are not relevant (such as if they -// are pruned out by lazy loading). +// or ok=false if module m's dependencies are pruned out. // // The caller must not modify the returned slice, but may safely append to it // and may rely on it not to be modified. @@ -404,7 +491,12 @@ func (mg *ModuleGraph) findError() error { } func (mg *ModuleGraph) allRootsSelected() bool { - roots, _ := mg.g.RequiredBy(Target) + var roots []module.Version + if inWorkspaceMode() { + roots = MainModules.Versions() + } else { + roots, _ = mg.g.RequiredBy(MainModules.mustGetSingleMainModule()) + } for _, m := range roots { if mg.Selected(m.Path) != m.Version { return false @@ -427,12 +519,12 @@ func LoadModGraph(ctx context.Context, goVersion string) *ModuleGraph { rs := LoadModFile(ctx) if goVersion != "" { - depth := modDepthFromGoVersion(goVersion) - if depth == eager && rs.depth != eager { + pruning := pruningForGoVersion(goVersion) + if pruning == unpruned && rs.pruning != unpruned { // Use newRequirements instead of convertDepth because convertDepth // also updates roots; here, we want to report the unmodified roots // even though they may seem inconsistent. - rs = newRequirements(eager, rs.rootModules, rs.direct) + rs = newRequirements(unpruned, rs.rootModules, rs.direct) } mg, err := rs.Graph(ctx) @@ -447,7 +539,8 @@ func LoadModGraph(ctx context.Context, goVersion string) *ModuleGraph { base.Fatalf("go: %v", err) } - commitRequirements(ctx, modFileGoVersion(), rs) + requirements = rs + return mg } @@ -455,9 +548,8 @@ func LoadModGraph(ctx context.Context, goVersion string) *ModuleGraph { // // If the complete graph reveals that some root of rs is not actually the // selected version of its path, expandGraph computes a new set of roots that -// are consistent. (When lazy loading is implemented, this may result in -// upgrades to other modules due to requirements that were previously pruned -// out.) +// are consistent. (With a pruned module graph, this may result in upgrades to +// other modules due to requirements that were previously pruned out.) // // expandGraph returns the updated roots, along with the module graph loaded // from those roots and any error encountered while loading that graph. @@ -473,9 +565,9 @@ func expandGraph(ctx context.Context, rs *Requirements) (*Requirements, *ModuleG if !mg.allRootsSelected() { // The roots of rs are not consistent with the rest of the graph. Update - // them. In an eager module this is a no-op for the build list as a whole — + // them. In an unpruned module this is a no-op for the build list as a whole — // it just promotes what were previously transitive requirements to be - // roots — but in a lazy module it may pull in previously-irrelevant + // roots — but in a pruned module it may pull in previously-irrelevant // transitive dependencies. newRS, rsErr := updateRoots(ctx, rs.direct, rs, nil, nil, false) @@ -513,7 +605,7 @@ func EditBuildList(ctx context.Context, add, mustSelect []module.Version) (chang if err != nil { return false, err } - commitRequirements(ctx, modFileGoVersion(), rs) + requirements = rs return changed, err } @@ -544,23 +636,45 @@ type Conflict struct { // tidyRoots trims the root dependencies to the minimal requirements needed to // both retain the same versions of all packages in pkgs and satisfy the -// lazy loading invariants (if applicable). +// graph-pruning invariants (if applicable). func tidyRoots(ctx context.Context, rs *Requirements, pkgs []*loadPkg) (*Requirements, error) { - if rs.depth == eager { - return tidyEagerRoots(ctx, rs.direct, pkgs) + mainModule := MainModules.mustGetSingleMainModule() + if rs.pruning == unpruned { + return tidyUnprunedRoots(ctx, mainModule, rs.direct, pkgs) } - return tidyLazyRoots(ctx, rs.direct, pkgs) + return tidyPrunedRoots(ctx, mainModule, rs.direct, pkgs) } func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) { - if rs.depth == eager { - return updateEagerRoots(ctx, direct, rs, add) + switch rs.pruning { + case unpruned: + return updateUnprunedRoots(ctx, direct, rs, add) + case pruned: + return updatePrunedRoots(ctx, direct, rs, pkgs, add, rootsImported) + case workspace: + return updateWorkspaceRoots(ctx, rs, add) + default: + panic(fmt.Sprintf("unsupported pruning mode: %v", rs.pruning)) + } +} + +func updateWorkspaceRoots(ctx context.Context, rs *Requirements, add []module.Version) (*Requirements, error) { + if len(add) != 0 { + // add should be empty in workspace mode because workspace mode implies + // -mod=readonly, which in turn implies no new requirements. The code path + // that would result in add being non-empty returns an error before it + // reaches this point: The set of modules to add comes from + // resolveMissingImports, which in turn resolves each package by calling + // queryImport. But queryImport explicitly checks for -mod=readonly, and + // return an error. + panic("add is not empty") } - return updateLazyRoots(ctx, direct, rs, pkgs, add, rootsImported) + return rs, nil } -// tidyLazyRoots returns a minimal set of root requirements that maintains the -// "lazy loading" invariants of the go.mod file for the given packages: +// tidyPrunedRoots returns a minimal set of root requirements that maintains the +// invariants of the go.mod file needed to support graph pruning for the given +// packages: // // 1. For each package marked with pkgInAll, the module path that provided that // package is included as a root. @@ -568,16 +682,16 @@ func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements, // selected at the same version or is upgraded by the dependencies of a // root. // -// If any module that provided a package has been upgraded above its previous, +// If any module that provided a package has been upgraded above its previous // version, the caller may need to reload and recompute the package graph. // // To ensure that the loading process eventually converges, the caller should // add any needed roots from the tidy root set (without removing existing untidy // roots) until the set of roots has converged. -func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) { +func tidyPrunedRoots(ctx context.Context, mainModule module.Version, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) { var ( roots []module.Version - pathIncluded = map[string]bool{Target.Path: true} + pathIncluded = map[string]bool{mainModule.Path: true} ) // We start by adding roots for every package in "all". // @@ -605,7 +719,7 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) queued[pkg] = true } module.Sort(roots) - tidy := newRequirements(lazy, roots, direct) + tidy := newRequirements(pruned, roots, direct) for len(queue) > 0 { roots = tidy.rootModules @@ -641,7 +755,7 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) if len(roots) > len(tidy.rootModules) { module.Sort(roots) - tidy = newRequirements(lazy, roots, tidy.direct) + tidy = newRequirements(pruned, roots, tidy.direct) } } @@ -652,8 +766,8 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) return tidy, nil } -// updateLazyRoots returns a set of root requirements that maintains the “lazy -// loading” invariants of the go.mod file: +// updatePrunedRoots returns a set of root requirements that maintains the +// invariants of the go.mod file needed to support graph pruning: // // 1. The selected version of the module providing each package marked with // either pkgInAll or pkgIsRoot is included as a root. @@ -670,7 +784,7 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) // The packages in pkgs are assumed to have been loaded from either the roots of // rs or the modules selected in the graph of rs. // -// The above invariants together imply the “lazy loading” invariants for the +// The above invariants together imply the graph-pruning invariants for the // go.mod file: // // 1. (The import invariant.) Every module that provides a package transitively @@ -690,13 +804,13 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) // it requires explicitly. This invariant is left up to the caller, who must // not load packages from outside the module graph but may add roots to the // graph, but is facilited by (3). If the caller adds roots to the graph in -// order to resolve missing packages, then updateLazyRoots will retain them, +// order to resolve missing packages, then updatePrunedRoots will retain them, // the selected versions of those roots cannot regress, and they will // eventually be written back to the main module's go.mod file. // // (See https://golang.org/design/36460-lazy-module-loading#invariants for more // detail.) -func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) { +func updatePrunedRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) { roots := rs.rootModules rootsUpgraded := false @@ -717,11 +831,11 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen // pkg is transitively imported by a package or test in the main module. // We need to promote the module that maintains it to a root: if some // other module depends on the main module, and that other module also - // uses lazy loading, it will expect to find all of our transitive - // dependencies by reading just our go.mod file, not the go.mod files of - // everything we depend on. + // uses a pruned module graph, it will expect to find all of our + // transitive dependencies by reading just our go.mod file, not the go.mod + // files of everything we depend on. // - // (This is the “import invariant” that makes lazy loading possible.) + // (This is the “import invariant” that makes graph pruning possible.) case rootsImported && pkg.flags.has(pkgFromRoot): // pkg is a transitive dependency of some root, and we are treating the @@ -732,17 +846,18 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen // it matches a command-line argument.) We want future invocations of the // 'go' command — such as 'go test' on the same package — to continue to // use the same versions of its dependencies that we are using right now. - // So we need to bring this package's dependencies inside the lazy-loading - // horizon. + // So we need to bring this package's dependencies inside the pruned + // module graph. // // Making the module containing this package a root of the module graph - // does exactly that: if the module containing the package is lazy it - // should satisfy the import invariant itself, so all of its dependencies - // should be in its go.mod file, and if the module containing the package - // is eager then if we make it a root we will load all of its transitive - // dependencies into the module graph. + // does exactly that: if the module containing the package supports graph + // pruning then it should satisfy the import invariant itself, so all of + // its dependencies should be in its go.mod file, and if the module + // containing the package does not support pruning then if we make it a + // root we will load all of its (unpruned) transitive dependencies into + // the module graph. // - // (This is the “argument invariant” of lazy loading, and is important for + // (This is the “argument invariant”, and is important for // reproducibility.) default: @@ -807,16 +922,15 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen // We've added or upgraded one or more roots, so load the full module // graph so that we can update those roots to be consistent with other // requirements. - if cfg.BuildMod != "mod" { + if mustHaveCompleteRequirements() { // Our changes to the roots may have moved dependencies into or out of - // the lazy-loading horizon, which could in turn change the selected - // versions of other modules. (Unlike for eager modules, for lazy - // modules adding or removing an explicit root is a semantic change, not - // just a cosmetic one.) + // the graph-pruning horizon, which could in turn change the selected + // versions of other modules. (For pruned modules adding or removing an + // explicit root is a semantic change, not just a cosmetic one.) return rs, errGoModDirty } - rs = newRequirements(lazy, roots, direct) + rs = newRequirements(pruned, roots, direct) var err error mg, err = rs.Graph(ctx) if err != nil { @@ -831,7 +945,7 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen if rs.graph.Load() != nil { // We've already loaded the full module graph, which includes the // requirements of all of the root modules — even the transitive - // requirements, if they are eager! + // requirements, if they are unpruned! mg, _ = rs.Graph(ctx) } else if cfg.BuildMod == "vendor" { // We can't spot-check the requirements of other modules because we @@ -855,7 +969,9 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen roots = make([]module.Version, 0, len(rs.rootModules)) rootsUpgraded = false inRootPaths := make(map[string]bool, len(rs.rootModules)+1) - inRootPaths[Target.Path] = true + for _, mm := range MainModules.Versions() { + inRootPaths[mm.Path] = true + } for _, m := range rs.rootModules { if inRootPaths[m.Path] { // This root specifies a redundant path. We already retained the @@ -908,12 +1024,12 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen } } - if rs.depth == lazy && reflect.DeepEqual(roots, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) { - // The root set is unchanged and rs was already lazy, so keep rs to + if rs.pruning == pruned && reflect.DeepEqual(roots, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) { + // The root set is unchanged and rs was already pruned, so keep rs to // preserve its cached ModuleGraph (if any). return rs, nil } - return newRequirements(lazy, roots, direct), nil + return newRequirements(pruned, roots, direct), nil } // spotCheckRoots reports whether the versions of the roots in rs satisfy the @@ -955,17 +1071,37 @@ func spotCheckRoots(ctx context.Context, rs *Requirements, mods map[module.Versi return true } -// tidyEagerRoots returns a minimal set of root requirements that maintains the -// selected version of every module that provided a package in pkgs, and -// includes the selected version of every such module in direct as a root. -func tidyEagerRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) { +// tidyUnprunedRoots returns a minimal set of root requirements that maintains +// the selected version of every module that provided or lexically could have +// provided a package in pkgs, and includes the selected version of every such +// module in direct as a root. +func tidyUnprunedRoots(ctx context.Context, mainModule module.Version, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) { var ( + // keep is a set of of modules that provide packages or are needed to + // disambiguate imports. keep []module.Version keptPath = map[string]bool{} - ) - var ( - rootPaths []string // module paths that should be included as roots + + // rootPaths is a list of module paths that provide packages directly + // imported from the main module. They should be included as roots. + rootPaths []string inRootPaths = map[string]bool{} + + // altMods is a set of paths of modules that lexically could have provided + // imported packages. It may be okay to remove these from the list of + // explicit requirements if that removes them from the module graph. If they + // are present in the module graph reachable from rootPaths, they must not + // be at a lower version. That could cause a missing sum error or a new + // import ambiguity. + // + // For example, suppose a developer rewrites imports from example.com/m to + // example.com/m/v2, then runs 'go mod tidy'. Tidy may delete the + // requirement on example.com/m if there is no other transitive requirement + // on it. However, if example.com/m were downgraded to a version not in + // go.sum, when package example.com/m/v2/p is loaded, we'd get an error + // trying to disambiguate the import, since we can't check example.com/m + // without its sum. See #47738. + altMods = map[string]string{} ) for _, pkg := range pkgs { if !pkg.fromExternalModule() { @@ -979,16 +1115,48 @@ func tidyEagerRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg inRootPaths[m.Path] = true } } + for _, m := range pkg.altMods { + altMods[m.Path] = m.Version + } } - min, err := mvs.Req(Target, rootPaths, &mvsReqs{roots: keep}) + // Construct a build list with a minimal set of roots. + // This may remove or downgrade modules in altMods. + reqs := &mvsReqs{roots: keep} + min, err := mvs.Req(mainModule, rootPaths, reqs) if err != nil { return nil, err } - return newRequirements(eager, min, direct), nil + buildList, err := mvs.BuildList([]module.Version{mainModule}, reqs) + if err != nil { + return nil, err + } + + // Check if modules in altMods were downgraded but not removed. + // If so, add them to roots, which will retain an "// indirect" requirement + // in go.mod. See comment on altMods above. + keptAltMod := false + for _, m := range buildList { + if v, ok := altMods[m.Path]; ok && semver.Compare(m.Version, v) < 0 { + keep = append(keep, module.Version{Path: m.Path, Version: v}) + keptAltMod = true + } + } + if keptAltMod { + // We must run mvs.Req again instead of simply adding altMods to min. + // It's possible that a requirement in altMods makes some other + // explicit indirect requirement unnecessary. + reqs.roots = keep + min, err = mvs.Req(mainModule, rootPaths, reqs) + if err != nil { + return nil, err + } + } + + return newRequirements(unpruned, min, direct), nil } -// updateEagerRoots returns a set of root requirements that includes the selected +// updateUnprunedRoots returns a set of root requirements that includes the selected // version of every module path in direct as a root, and maintains the selected // version of every module selected in the graph of rs. // @@ -1002,7 +1170,7 @@ func tidyEagerRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg // by a dependency in add. // 4. Every version in add is selected at its given version unless upgraded by // (the dependencies of) an existing root or another module in add. -func updateEagerRoots(ctx context.Context, direct map[string]bool, rs *Requirements, add []module.Version) (*Requirements, error) { +func updateUnprunedRoots(ctx context.Context, direct map[string]bool, rs *Requirements, add []module.Version) (*Requirements, error) { mg, err := rs.Graph(ctx) if err != nil { // We can't ignore errors in the module graph even if the user passed the -e @@ -1011,7 +1179,7 @@ func updateEagerRoots(ctx context.Context, direct map[string]bool, rs *Requireme return rs, err } - if cfg.BuildMod != "mod" { + if mustHaveCompleteRequirements() { // Instead of actually updating the requirements, just check that no updates // are needed. if rs == nil { @@ -1067,10 +1235,10 @@ func updateEagerRoots(ctx context.Context, direct map[string]bool, rs *Requireme // “The selected version of every module path in direct is included as a root.” // - // This is only for convenience and clarity for end users: in an eager module, + // This is only for convenience and clarity for end users: in an unpruned module, // the choice of explicit vs. implicit dependency has no impact on MVS // selection (for itself or any other module). - keep := append(mg.BuildList()[1:], add...) + keep := append(mg.BuildList()[MainModules.Len():], add...) for _, m := range keep { if direct[m.Path] && !inRootPaths[m.Path] { rootPaths = append(rootPaths, m.Path) @@ -1078,44 +1246,53 @@ func updateEagerRoots(ctx context.Context, direct map[string]bool, rs *Requireme } } - min, err := mvs.Req(Target, rootPaths, &mvsReqs{roots: keep}) - if err != nil { - return rs, err + var roots []module.Version + for _, mainModule := range MainModules.Versions() { + min, err := mvs.Req(mainModule, rootPaths, &mvsReqs{roots: keep}) + if err != nil { + return rs, err + } + roots = append(roots, min...) } - if rs.depth == eager && reflect.DeepEqual(min, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) { - // The root set is unchanged and rs was already eager, so keep rs to + if MainModules.Len() > 1 { + module.Sort(roots) + } + if rs.pruning == unpruned && reflect.DeepEqual(roots, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) { + // The root set is unchanged and rs was already unpruned, so keep rs to // preserve its cached ModuleGraph (if any). return rs, nil } - return newRequirements(eager, min, direct), nil + + return newRequirements(unpruned, roots, direct), nil } -// convertDepth returns a version of rs with the given depth. -// If rs already has the given depth, convertDepth returns rs unmodified. -func convertDepth(ctx context.Context, rs *Requirements, depth modDepth) (*Requirements, error) { - if rs.depth == depth { +// convertPruning returns a version of rs with the given pruning behavior. +// If rs already has the given pruning, convertPruning returns rs unmodified. +func convertPruning(ctx context.Context, rs *Requirements, pruning modPruning) (*Requirements, error) { + if rs.pruning == pruning { return rs, nil + } else if rs.pruning == workspace || pruning == workspace { + panic("attempthing to convert to/from workspace pruning and another pruning type") } - if depth == eager { - // We are converting a lazy module to an eager one. The roots of an eager - // module graph are a superset of the roots of a lazy graph, so we don't - // need to add any new roots — we just need to prune away the ones that are - // redundant given eager loading, which is exactly what updateEagerRoots - // does. - return updateEagerRoots(ctx, rs.direct, rs, nil) + if pruning == unpruned { + // We are converting a pruned module to an unpruned one. The roots of a + // ppruned module graph are a superset of the roots of an unpruned one, so + // we don't need to add any new roots — we just need to drop the ones that + // are redundant, which is exactly what updateUnprunedRoots does. + return updateUnprunedRoots(ctx, rs.direct, rs, nil) } - // We are converting an eager module to a lazy one. The module graph of an - // eager module includes the transitive dependencies of every module in the - // build list. + // We are converting an unpruned module to a pruned one. // - // Hey, we can express that as a lazy root set! “Include the transitive - // dependencies of every module in the build list” is exactly what happens in - // a lazy module if we promote every module in the build list to a root! + // An unpruned module graph includes the transitive dependencies of every + // module in the build list. As it turns out, we can express that as a pruned + // root set! “Include the transitive dependencies of every module in the build + // list” is exactly what happens in a pruned module if we promote every module + // in the build list to a root. mg, err := rs.Graph(ctx) if err != nil { return rs, err } - return newRequirements(lazy, mg.BuildList()[1:], rs.direct), nil + return newRequirements(pruned, mg.BuildList()[MainModules.Len():], rs.direct), nil } diff --git a/libgo/go/cmd/go/internal/modload/edit.go b/libgo/go/cmd/go/internal/modload/edit.go index 47f236ce168..0f37e3b2e94 100644 --- a/libgo/go/cmd/go/internal/modload/edit.go +++ b/libgo/go/cmd/go/internal/modload/edit.go @@ -21,7 +21,7 @@ import ( // 2. Each module version in tryUpgrade is upgraded toward the indicated // version as far as can be done without violating (1). // -// 3. Each module version in rs.rootModules (or rs.graph, if rs.depth is eager) +// 3. Each module version in rs.rootModules (or rs.graph, if rs is unpruned) // is downgraded from its original version only to the extent needed to // satisfy (1), or upgraded only to the extent needed to satisfy (1) and // (2). @@ -69,13 +69,14 @@ func editRequirements(ctx context.Context, rs *Requirements, tryUpgrade, mustSel } var roots []module.Version - if rs.depth == eager { - // In an eager module, modules that provide packages imported by the main - // module may either be explicit roots or implicit transitive dependencies. - // We promote the modules in mustSelect to be explicit requirements. + if rs.pruning == unpruned { + // In a module without graph pruning, modules that provide packages imported + // by the main module may either be explicit roots or implicit transitive + // dependencies. We promote the modules in mustSelect to be explicit + // requirements. var rootPaths []string for _, m := range mustSelect { - if m.Version != "none" && m.Path != Target.Path { + if m.Version != "none" && !MainModules.Contains(m.Path) { rootPaths = append(rootPaths, m.Path) } } @@ -97,13 +98,13 @@ func editRequirements(ctx context.Context, rs *Requirements, tryUpgrade, mustSel } } - roots, err = mvs.Req(Target, rootPaths, &mvsReqs{roots: mods}) + roots, err = mvs.Req(MainModules.mustGetSingleMainModule(), rootPaths, &mvsReqs{roots: mods}) if err != nil { return nil, false, err } } else { - // In a lazy module, every module that provides a package imported by the - // main module must be retained as a root. + // In a module with a pruned graph, every module that provides a package + // imported by the main module must be retained as a root. roots = mods if !changed { // Because the roots we just computed are unchanged, the entire graph must @@ -126,7 +127,7 @@ func editRequirements(ctx context.Context, rs *Requirements, tryUpgrade, mustSel direct[m.Path] = true } } - return newRequirements(rs.depth, roots, direct), changed, nil + return newRequirements(rs.pruning, roots, direct), changed, nil } // limiterForEdit returns a versionLimiter with its max versions set such that @@ -149,11 +150,12 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec } } - if rs.depth == eager { - // Eager go.mod files don't indicate which transitive dependencies are - // actually relevant to the main module, so we have to assume that any module - // that could have provided any package — that is, any module whose selected - // version was not "none" — may be relevant. + if rs.pruning == unpruned { + // go.mod files that do not support graph pruning don't indicate which + // transitive dependencies are actually relevant to the main module, so we + // have to assume that any module that could have provided any package — + // that is, any module whose selected version was not "none" — may be + // relevant. for _, m := range mg.BuildList() { restrictTo(m) } @@ -175,7 +177,7 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec } } - if err := raiseLimitsForUpgrades(ctx, maxVersion, rs.depth, tryUpgrade, mustSelect); err != nil { + if err := raiseLimitsForUpgrades(ctx, maxVersion, rs.pruning, tryUpgrade, mustSelect); err != nil { return nil, err } @@ -185,7 +187,7 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec restrictTo(m) } - return newVersionLimiter(rs.depth, maxVersion), nil + return newVersionLimiter(rs.pruning, maxVersion), nil } // raiseLimitsForUpgrades increases the module versions in maxVersions to the @@ -195,12 +197,12 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec // // Versions not present in maxVersion are unrestricted, and it is assumed that // they will not be promoted to root requirements (and thus will not contribute -// their own dependencies if the main module is lazy). +// their own dependencies if the main module supports graph pruning). // // These limits provide an upper bound on how far a module may be upgraded as // part of an incidental downgrade, if downgrades are needed in order to select // the versions in mustSelect. -func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, depth modDepth, tryUpgrade []module.Version, mustSelect []module.Version) error { +func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, pruning modPruning, tryUpgrade []module.Version, mustSelect []module.Version) error { // allow raises the limit for m.Path to at least m.Version. // If m.Path was already unrestricted, it remains unrestricted. allow := func(m module.Version) { @@ -214,21 +216,21 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d } var ( - eagerUpgrades []module.Version - isLazyRootPath map[string]bool + unprunedUpgrades []module.Version + isPrunedRootPath map[string]bool ) - if depth == eager { - eagerUpgrades = tryUpgrade + if pruning == unpruned { + unprunedUpgrades = tryUpgrade } else { - isLazyRootPath = make(map[string]bool, len(maxVersion)) + isPrunedRootPath = make(map[string]bool, len(maxVersion)) for p := range maxVersion { - isLazyRootPath[p] = true + isPrunedRootPath[p] = true } for _, m := range tryUpgrade { - isLazyRootPath[m.Path] = true + isPrunedRootPath[m.Path] = true } for _, m := range mustSelect { - isLazyRootPath[m.Path] = true + isPrunedRootPath[m.Path] = true } allowedRoot := map[module.Version]bool{} @@ -240,10 +242,10 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d } allowedRoot[m] = true - if m.Path == Target.Path { - // Target is already considered to be higher than any possible m, so we - // won't be upgrading to it anyway and there is no point scanning its - // dependencies. + if MainModules.Contains(m.Path) { + // The main module versions are already considered to be higher than any + // possible m, so m cannot be selected as a root and there is no point + // scanning its dependencies. return nil } @@ -253,15 +255,15 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d if err != nil { return err } - if summary.depth == eager { - // For efficiency, we'll load all of the eager upgrades as one big + if summary.pruning == unpruned { + // For efficiency, we'll load all of the unpruned upgrades as one big // graph, rather than loading the (potentially-overlapping) subgraph for // each upgrade individually. - eagerUpgrades = append(eagerUpgrades, m) + unprunedUpgrades = append(unprunedUpgrades, m) return nil } for _, r := range summary.require { - if isLazyRootPath[r.Path] { + if isPrunedRootPath[r.Path] { // r could become a root as the result of an upgrade or downgrade, // in which case its dependencies will not be pruned out. // We need to allow those dependencies to be upgraded too. @@ -282,21 +284,19 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d } } - if len(eagerUpgrades) > 0 { - // Compute the max versions for eager upgrades all together. - // Since these modules are eager, we'll end up scanning all of their + if len(unprunedUpgrades) > 0 { + // Compute the max versions for unpruned upgrades all together. + // Since these modules are unpruned, we'll end up scanning all of their // transitive dependencies no matter which versions end up selected, // and since we have a large dependency graph to scan we might get // a significant benefit from not revisiting dependencies that are at // common versions among multiple upgrades. - upgradeGraph, err := readModGraph(ctx, eager, eagerUpgrades) + upgradeGraph, err := readModGraph(ctx, unpruned, unprunedUpgrades) if err != nil { - if go117LazyTODO { - // Compute the requirement path from a module path in tryUpgrade to the - // error, and the requirement path (if any) from rs.rootModules to the - // tryUpgrade module path. Return a *mvs.BuildListError showing the - // concatenation of the paths (with an upgrade in the middle). - } + // Compute the requirement path from a module path in tryUpgrade to the + // error, and the requirement path (if any) from rs.rootModules to the + // tryUpgrade module path. Return a *mvs.BuildListError showing the + // concatenation of the paths (with an upgrade in the middle). return err } @@ -311,7 +311,7 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d nextRoots := append([]module.Version(nil), mustSelect...) for nextRoots != nil { module.Sort(nextRoots) - rs := newRequirements(depth, nextRoots, nil) + rs := newRequirements(pruning, nextRoots, nil) nextRoots = nil rs, mustGraph, err := expandGraph(ctx, rs) @@ -325,7 +325,7 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d // which case we will error out either way). allow(r) - if isLazyRootPath[r.Path] { + if isPrunedRootPath[r.Path] { if v, ok := rs.rootSelected(r.Path); ok && r.Version == v { // r is already a root, so its requirements are already included in // the build list. @@ -365,12 +365,12 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit } var initial []module.Version - if rs.depth == eager { + if rs.pruning == unpruned { mg, err := rs.Graph(ctx) if err != nil { return nil, false, err } - initial = mg.BuildList()[1:] + initial = mg.BuildList()[MainModules.Len():] } else { initial = rs.rootModules } @@ -382,7 +382,7 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit mods = make([]module.Version, 0, len(limiter.selected)) for path, v := range limiter.selected { - if v != "none" && path != Target.Path { + if v != "none" && !MainModules.Contains(path) { mods = append(mods, module.Version{Path: path, Version: v}) } } @@ -392,13 +392,13 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit // downgraded module may require a higher (but still allowed) version of // another. The lower version may require extraneous dependencies that aren't // actually relevant, so we need to compute the actual selected versions. - mg, err := readModGraph(ctx, rs.depth, mods) + mg, err := readModGraph(ctx, rs.pruning, mods) if err != nil { return nil, false, err } mods = make([]module.Version, 0, len(limiter.selected)) for path, _ := range limiter.selected { - if path != Target.Path { + if !MainModules.Contains(path) { if v := mg.Selected(path); v != "none" { mods = append(mods, module.Version{Path: path, Version: v}) } @@ -414,16 +414,16 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit // A versionLimiter tracks the versions that may be selected for each module // subject to constraints on the maximum versions of transitive dependencies. type versionLimiter struct { - // depth is the depth at which the dependencies of the modules passed to + // pruning is the pruning at which the dependencies of the modules passed to // Select and UpgradeToward are loaded. - depth modDepth + pruning modPruning // max maps each module path to the maximum version that may be selected for // that path. // // Paths with no entry are unrestricted, and we assume that they will not be // promoted to root dependencies (so will not contribute dependencies if the - // main module is lazy). + // main module supports graph pruning). max map[string]string // selected maps each module path to a version of that path (if known) whose @@ -475,14 +475,18 @@ func (dq dqState) isDisqualified() bool { // in the map are unrestricted. The limiter assumes that unrestricted paths will // not be promoted to root dependencies. // -// If depth is lazy, then if a module passed to UpgradeToward or Select is -// itself lazy, its unrestricted dependencies are skipped when scanning -// requirements. -func newVersionLimiter(depth modDepth, max map[string]string) *versionLimiter { +// If module graph pruning is in effect, then if a module passed to +// UpgradeToward or Select supports pruning, its unrestricted dependencies are +// skipped when scanning requirements. +func newVersionLimiter(pruning modPruning, max map[string]string) *versionLimiter { + selected := make(map[string]string) + for _, m := range MainModules.Versions() { + selected[m.Path] = m.Version + } return &versionLimiter{ - depth: depth, + pruning: pruning, max: max, - selected: map[string]string{Target.Path: Target.Version}, + selected: selected, dqReason: map[module.Version]dqState{}, requiring: map[module.Version][]module.Version{}, } @@ -491,8 +495,8 @@ func newVersionLimiter(depth modDepth, max map[string]string) *versionLimiter { // UpgradeToward attempts to upgrade the selected version of m.Path as close as // possible to m.Version without violating l's maximum version limits. // -// If depth is lazy and m itself is lazy, the the dependencies of unrestricted -// dependencies of m will not be followed. +// If module graph pruning is in effect and m itself supports pruning, the +// dependencies of unrestricted dependencies of m will not be followed. func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) error { selected, ok := l.selected[m.Path] if ok { @@ -504,7 +508,7 @@ func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) er selected = "none" } - if l.check(m, l.depth).isDisqualified() { + if l.check(m, l.pruning).isDisqualified() { candidates, err := versions(ctx, m.Path, CheckAllowed) if err != nil { // This is likely a transient error reaching the repository, @@ -521,7 +525,7 @@ func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) er }) candidates = candidates[:i] - for l.check(m, l.depth).isDisqualified() { + for l.check(m, l.pruning).isDisqualified() { n := len(candidates) if n == 0 || cmpVersion(selected, candidates[n-1]) >= 0 { // We couldn't find a suitable candidate above the already-selected version. @@ -538,7 +542,7 @@ func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) er // Select attempts to set the selected version of m.Path to exactly m.Version. func (l *versionLimiter) Select(m module.Version) (conflict module.Version, err error) { - dq := l.check(m, l.depth) + dq := l.check(m, l.pruning) if !dq.isDisqualified() { l.selected[m.Path] = m.Version } @@ -548,15 +552,15 @@ func (l *versionLimiter) Select(m module.Version) (conflict module.Version, err // check determines whether m (or its transitive dependencies) would violate l's // maximum version limits if added to the module requirement graph. // -// If depth is lazy and m itself is lazy, then the dependencies of unrestricted -// dependencies of m will not be followed. If the lazy loading invariants hold -// for the main module up to this point, the packages in those modules are at -// best only imported by tests of dependencies that are themselves loaded from -// outside modules. Although we would like to keep 'go test all' as reproducible -// as is feasible, we don't want to retain test dependencies that are only -// marginally relevant at best. -func (l *versionLimiter) check(m module.Version, depth modDepth) dqState { - if m.Version == "none" || m == Target { +// If pruning is in effect and m itself supports graph pruning, the dependencies +// of unrestricted dependencies of m will not be followed. If the graph-pruning +// invariants hold for the main module up to this point, the packages in those +// modules are at best only imported by tests of dependencies that are +// themselves loaded from outside modules. Although we would like to keep +// 'go test all' as reproducible as is feasible, we don't want to retain test +// dependencies that are only marginally relevant at best. +func (l *versionLimiter) check(m module.Version, pruning modPruning) dqState { + if m.Version == "none" || m == MainModules.mustGetSingleMainModule() { // version "none" has no requirements, and the dependencies of Target are // tautological. return dqState{} @@ -586,20 +590,20 @@ func (l *versionLimiter) check(m module.Version, depth modDepth) dqState { return l.disqualify(m, dqState{err: err}) } - if summary.depth == eager { - depth = eager + if summary.pruning == unpruned { + pruning = unpruned } for _, r := range summary.require { - if depth == lazy { + if pruning == pruned { if _, restricted := l.max[r.Path]; !restricted { // r.Path is unrestricted, so we don't care at what version it is // selected. We assume that r.Path will not become a root dependency, so - // since m is lazy, r's dependencies won't be followed. + // since m supports pruning, r's dependencies won't be followed. continue } } - if dq := l.check(r, depth); dq.isDisqualified() { + if dq := l.check(r, pruning); dq.isDisqualified() { return l.disqualify(m, dq) } diff --git a/libgo/go/cmd/go/internal/modload/import.go b/libgo/go/cmd/go/internal/modload/import.go index d2bbe5cbe0b..812e48a1568 100644 --- a/libgo/go/cmd/go/internal/modload/import.go +++ b/libgo/go/cmd/go/internal/modload/import.go @@ -32,6 +32,8 @@ type ImportMissingError struct { Module module.Version QueryErr error + ImportingMainModule module.Version + // isStd indicates whether we would expect to find the package in the standard // library. This is normally true for all dotless import paths, but replace // directives can cause us to treat the replaced paths as also being in @@ -71,6 +73,9 @@ func (e *ImportMissingError) Error() string { if e.QueryErr != nil { return fmt.Sprintf("%s: %v", message, e.QueryErr) } + if e.ImportingMainModule.Path != "" && e.ImportingMainModule != MainModules.ModContainingCWD() { + return fmt.Sprintf("%s; to add it:\n\tcd %s\n\tgo get %s", message, MainModules.ModRoot(e.ImportingMainModule), e.Path) + } return fmt.Sprintf("%s; to add it:\n\tgo get %s", message, e.Path) } @@ -238,55 +243,63 @@ func (e *invalidImportError) Unwrap() error { // // If the package is not present in any module selected from the requirement // graph, importFromModules returns an *ImportMissingError. -func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, dir string, err error) { +// +// If the package is present in exactly one module, importFromModules will +// return the module, its root directory, and a list of other modules that +// lexically could have provided the package but did not. +func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, dir string, altMods []module.Version, err error) { if strings.Contains(path, "@") { - return module.Version{}, "", fmt.Errorf("import path should not have @version") + return module.Version{}, "", nil, fmt.Errorf("import path should not have @version") } if build.IsLocalImport(path) { - return module.Version{}, "", fmt.Errorf("relative import not supported") + return module.Version{}, "", nil, fmt.Errorf("relative import not supported") } if path == "C" { // There's no directory for import "C". - return module.Version{}, "", nil + return module.Version{}, "", nil, nil } // Before any further lookup, check that the path is valid. if err := module.CheckImportPath(path); err != nil { - return module.Version{}, "", &invalidImportError{importPath: path, err: err} + return module.Version{}, "", nil, &invalidImportError{importPath: path, err: err} } // Is the package in the standard library? pathIsStd := search.IsStandardImportPath(path) if pathIsStd && goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { - if targetInGorootSrc { - if dir, ok, err := dirInModule(path, targetPrefix, ModRoot(), true); err != nil { - return module.Version{}, dir, err - } else if ok { - return Target, dir, nil + for _, mainModule := range MainModules.Versions() { + if MainModules.InGorootSrc(mainModule) { + if dir, ok, err := dirInModule(path, MainModules.PathPrefix(mainModule), MainModules.ModRoot(mainModule), true); err != nil { + return module.Version{}, dir, nil, err + } else if ok { + return mainModule, dir, nil, nil + } } } dir := filepath.Join(cfg.GOROOT, "src", path) - return module.Version{}, dir, nil + return module.Version{}, dir, nil, nil } // -mod=vendor is special. // Everything must be in the main module or the main module's vendor directory. if cfg.BuildMod == "vendor" { - mainDir, mainOK, mainErr := dirInModule(path, targetPrefix, ModRoot(), true) - vendorDir, vendorOK, _ := dirInModule(path, "", filepath.Join(ModRoot(), "vendor"), false) + mainModule := MainModules.mustGetSingleMainModule() + modRoot := MainModules.ModRoot(mainModule) + mainDir, mainOK, mainErr := dirInModule(path, MainModules.PathPrefix(mainModule), modRoot, true) + vendorDir, vendorOK, _ := dirInModule(path, "", filepath.Join(modRoot, "vendor"), false) if mainOK && vendorOK { - return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: []string{mainDir, vendorDir}} + return module.Version{}, "", nil, &AmbiguousImportError{importPath: path, Dirs: []string{mainDir, vendorDir}} } // Prefer to return main directory if there is one, // Note that we're not checking that the package exists. // We'll leave that for load. if !vendorOK && mainDir != "" { - return Target, mainDir, nil + return mainModule, mainDir, nil, nil } if mainErr != nil { - return module.Version{}, "", mainErr + return module.Version{}, "", nil, mainErr } - readVendorList() - return vendorPkgModule[path], vendorDir, nil + readVendorList(mainModule) + return vendorPkgModule[path], vendorDir, nil, nil } // Check each module on the build list. @@ -307,7 +320,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M // already non-nil, then we attempt to load the package using the full // requirements in mg. for { - var sumErrMods []module.Version + var sumErrMods, altMods []module.Version for prefix := path; prefix != "."; prefix = pathpkg.Dir(prefix) { var ( v string @@ -341,13 +354,15 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M // continue the loop and find the package in some other module, // we need to look at this module to make sure the import is // not ambiguous. - return module.Version{}, "", err + return module.Version{}, "", nil, err } if dir, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil { - return module.Version{}, "", err + return module.Version{}, "", nil, err } else if ok { mods = append(mods, m) dirs = append(dirs, dir) + } else { + altMods = append(altMods, m) } } @@ -360,7 +375,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M mods[i], mods[j] = mods[j], mods[i] dirs[i], dirs[j] = dirs[j], dirs[i] } - return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods} + return module.Version{}, "", nil, &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods} } if len(sumErrMods) > 0 { @@ -368,7 +383,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M j := len(sumErrMods) - 1 - i sumErrMods[i], sumErrMods[j] = sumErrMods[j], sumErrMods[i] } - return module.Version{}, "", &ImportMissingSumError{ + return module.Version{}, "", nil, &ImportMissingSumError{ importPath: path, mods: sumErrMods, found: len(mods) > 0, @@ -376,7 +391,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M } if len(mods) == 1 { - return mods[0], dirs[0], nil + return mods[0], dirs[0], altMods, nil } if mg != nil { @@ -386,7 +401,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M if !HasModRoot() { queryErr = ErrNoModRoot } - return module.Version{}, "", &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd} + return module.Version{}, "", nil, &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd} } // So far we've checked the root dependencies. @@ -397,7 +412,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M // the module graph, so we can't return an ImportMissingError here — one // of the missing modules might actually contain the package in question, // in which case we shouldn't go looking for it in some new dependency. - return module.Version{}, "", err + return module.Version{}, "", nil, err } } } @@ -410,9 +425,9 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M func queryImport(ctx context.Context, path string, rs *Requirements) (module.Version, error) { // To avoid spurious remote fetches, try the latest replacement for each // module (golang.org/issue/26241). - if index != nil { - var mods []module.Version - for mp, mv := range index.highestReplaced { + var mods []module.Version + if MainModules != nil { // TODO(#48912): Ensure MainModules exists at this point, and remove the check. + for mp, mv := range MainModules.HighestReplaced() { if !maybeInModule(path, mp) { continue } @@ -439,40 +454,41 @@ func queryImport(ctx context.Context, path string, rs *Requirements) (module.Ver } mods = append(mods, module.Version{Path: mp, Version: mv}) } + } - // Every module path in mods is a prefix of the import path. - // As in QueryPattern, prefer the longest prefix that satisfies the import. - sort.Slice(mods, func(i, j int) bool { - return len(mods[i].Path) > len(mods[j].Path) - }) - for _, m := range mods { - needSum := true - root, isLocal, err := fetch(ctx, m, needSum) - if err != nil { - if sumErr := (*sumMissingError)(nil); errors.As(err, &sumErr) { - return module.Version{}, &ImportMissingSumError{importPath: path} - } - return module.Version{}, err - } - if _, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil { - return m, err - } else if ok { - if cfg.BuildMod == "readonly" { - return module.Version{}, &ImportMissingError{Path: path, replaced: m} - } - return m, nil + // Every module path in mods is a prefix of the import path. + // As in QueryPattern, prefer the longest prefix that satisfies the import. + sort.Slice(mods, func(i, j int) bool { + return len(mods[i].Path) > len(mods[j].Path) + }) + for _, m := range mods { + needSum := true + root, isLocal, err := fetch(ctx, m, needSum) + if err != nil { + if sumErr := (*sumMissingError)(nil); errors.As(err, &sumErr) { + return module.Version{}, &ImportMissingSumError{importPath: path} } + return module.Version{}, err } - if len(mods) > 0 && module.CheckPath(path) != nil { - // The package path is not valid to fetch remotely, - // so it can only exist in a replaced module, - // and we know from the above loop that it is not. - return module.Version{}, &PackageNotInModuleError{ - Mod: mods[0], - Query: "latest", - Pattern: path, - Replacement: Replacement(mods[0]), + if _, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil { + return m, err + } else if ok { + if cfg.BuildMod == "readonly" { + return module.Version{}, &ImportMissingError{Path: path, replaced: m} } + return m, nil + } + } + if len(mods) > 0 && module.CheckPath(path) != nil { + // The package path is not valid to fetch remotely, + // so it can only exist in a replaced module, + // and we know from the above loop that it is not. + replacement := Replacement(mods[0]) + return module.Version{}, &PackageNotInModuleError{ + Mod: mods[0], + Query: "latest", + Pattern: path, + Replacement: replacement, } } @@ -596,7 +612,7 @@ func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFile // (the main module, and any directory trees pointed at by replace directives). if isLocal { for d := dir; d != mdir && len(d) > len(mdir); { - haveGoMod := haveGoModCache.Do(d, func() interface{} { + haveGoMod := haveGoModCache.Do(d, func() any { fi, err := fsys.Stat(filepath.Join(d, "go.mod")) return err == nil && !fi.IsDir() }).(bool) @@ -619,7 +635,7 @@ func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFile // Are there Go source files in the directory? // We don't care about build tags, not even "+build ignore". // We're just looking for a plausible directory. - res := haveGoFilesCache.Do(dir, func() interface{} { + res := haveGoFilesCache.Do(dir, func() any { ok, err := fsys.IsDirWithGoFiles(dir) return goFilesEntry{haveGoFiles: ok, err: err} }).(goFilesEntry) @@ -638,14 +654,14 @@ func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFile // The isLocal return value reports whether the replacement, // if any, is local to the filesystem. func fetch(ctx context.Context, mod module.Version, needSum bool) (dir string, isLocal bool, err error) { - if mod == Target { - return ModRoot(), true, nil + if modRoot := MainModules.ModRoot(mod); modRoot != "" { + return modRoot, true, nil } if r := Replacement(mod); r.Path != "" { if r.Version == "" { dir = r.Path if !filepath.IsAbs(dir) { - dir = filepath.Join(ModRoot(), dir) + dir = filepath.Join(replaceRelativeTo(), dir) } // Ensure that the replacement directory actually exists: // dirInModule does not report errors for missing modules, @@ -667,7 +683,7 @@ func fetch(ctx context.Context, mod module.Version, needSum bool) (dir string, i mod = r } - if HasModRoot() && cfg.BuildMod == "readonly" && needSum && !modfetch.HaveSum(mod) { + if HasModRoot() && cfg.BuildMod == "readonly" && !inWorkspaceMode() && needSum && !modfetch.HaveSum(mod) { return "", false, module.VersionError(mod, &sumMissingError{}) } diff --git a/libgo/go/cmd/go/internal/modload/import_test.go b/libgo/go/cmd/go/internal/modload/import_test.go index 98145887e9d..65a889ec52f 100644 --- a/libgo/go/cmd/go/internal/modload/import_test.go +++ b/libgo/go/cmd/go/internal/modload/import_test.go @@ -69,7 +69,7 @@ func TestQueryImport(t *testing.T) { RootMode = NoRoot ctx := context.Background() - rs := newRequirements(eager, nil, nil) + rs := LoadModFile(ctx) for _, tt := range importTests { t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) { diff --git a/libgo/go/cmd/go/internal/modload/init.go b/libgo/go/cmd/go/internal/modload/init.go index 45f724d5e36..cdcfbeb8ded 100644 --- a/libgo/go/cmd/go/internal/modload/init.go +++ b/libgo/go/cmd/go/internal/modload/init.go @@ -12,11 +12,13 @@ import ( "fmt" "go/build" "internal/lazyregexp" + "io/ioutil" "os" "path" "path/filepath" "strconv" "strings" + "sync" "cmd/go/internal/base" "cmd/go/internal/cfg" @@ -43,28 +45,198 @@ var ( ForceUseModules bool allowMissingModuleImports bool + + // ExplicitWriteGoMod prevents LoadPackages, ListModules, and other functions + // from updating go.mod and go.sum or reporting errors when updates are + // needed. A package should set this if it would cause go.mod to be written + // multiple times (for example, 'go get' calls LoadPackages multiple times) or + // if it needs some other operation to be successful before go.mod and go.sum + // can be written (for example, 'go mod download' must download modules before + // adding sums to go.sum). Packages that set this are responsible for calling + // WriteGoMod explicitly. + ExplicitWriteGoMod bool ) // Variables set in Init. var ( initialized bool - modRoot string - gopath string + + // These are primarily used to initialize the MainModules, and should be + // eventually superceded by them but are still used in cases where the module + // roots are required but MainModules hasn't been initialized yet. Set to + // the modRoots of the main modules. + // modRoots != nil implies len(modRoots) > 0 + modRoots []string + gopath string ) -// Variables set in initTarget (during {Load,Create}ModFile). +// EnterModule resets MainModules and requirements to refer to just this one module. +func EnterModule(ctx context.Context, enterModroot string) { + MainModules = nil // reset MainModules + requirements = nil + workFilePath = "" // Force module mode + modfetch.Reset() + + modRoots = []string{enterModroot} + LoadModFile(ctx) +} + +// Variable set in InitWorkfile var ( - Target module.Version + // Set to the path to the go.work file, or "" if workspace mode is disabled. + workFilePath string +) + +type MainModuleSet struct { + // versions are the module.Version values of each of the main modules. + // For each of them, the Path fields are ordinary module paths and the Version + // fields are empty strings. + versions []module.Version + + // modRoot maps each module in versions to its absolute filesystem path. + modRoot map[module.Version]string - // targetPrefix is the path prefix for packages in Target, without a trailing - // slash. For most modules, targetPrefix is just Target.Path, but the + // pathPrefix is the path prefix for packages in the module, without a trailing + // slash. For most modules, pathPrefix is just version.Path, but the // standard-library module "std" has an empty prefix. - targetPrefix string + pathPrefix map[module.Version]string - // targetInGorootSrc caches whether modRoot is within GOROOT/src. + // inGorootSrc caches whether modRoot is within GOROOT/src. // The "std" module is special within GOROOT/src, but not otherwise. - targetInGorootSrc bool -) + inGorootSrc map[module.Version]bool + + modFiles map[module.Version]*modfile.File + + modContainingCWD module.Version + + workFileGoVersion string + + workFileReplaceMap map[module.Version]module.Version + // highest replaced version of each module path; empty string for wildcard-only replacements + highestReplaced map[string]string + + indexMu sync.Mutex + indices map[module.Version]*modFileIndex +} + +func (mms *MainModuleSet) PathPrefix(m module.Version) string { + return mms.pathPrefix[m] +} + +// Versions returns the module.Version values of each of the main modules. +// For each of them, the Path fields are ordinary module paths and the Version +// fields are empty strings. +// Callers should not modify the returned slice. +func (mms *MainModuleSet) Versions() []module.Version { + if mms == nil { + return nil + } + return mms.versions +} + +func (mms *MainModuleSet) Contains(path string) bool { + if mms == nil { + return false + } + for _, v := range mms.versions { + if v.Path == path { + return true + } + } + return false +} + +func (mms *MainModuleSet) ModRoot(m module.Version) string { + if mms == nil { + return "" + } + return mms.modRoot[m] +} + +func (mms *MainModuleSet) InGorootSrc(m module.Version) bool { + if mms == nil { + return false + } + return mms.inGorootSrc[m] +} + +func (mms *MainModuleSet) mustGetSingleMainModule() module.Version { + if mms == nil || len(mms.versions) == 0 { + panic("internal error: mustGetSingleMainModule called in context with no main modules") + } + if len(mms.versions) != 1 { + if inWorkspaceMode() { + panic("internal error: mustGetSingleMainModule called in workspace mode") + } else { + panic("internal error: multiple main modules present outside of workspace mode") + } + } + return mms.versions[0] +} + +func (mms *MainModuleSet) GetSingleIndexOrNil() *modFileIndex { + if mms == nil { + return nil + } + if len(mms.versions) == 0 { + return nil + } + return mms.indices[mms.mustGetSingleMainModule()] +} + +func (mms *MainModuleSet) Index(m module.Version) *modFileIndex { + mms.indexMu.Lock() + defer mms.indexMu.Unlock() + return mms.indices[m] +} + +func (mms *MainModuleSet) SetIndex(m module.Version, index *modFileIndex) { + mms.indexMu.Lock() + defer mms.indexMu.Unlock() + mms.indices[m] = index +} + +func (mms *MainModuleSet) ModFile(m module.Version) *modfile.File { + return mms.modFiles[m] +} + +func (mms *MainModuleSet) Len() int { + if mms == nil { + return 0 + } + return len(mms.versions) +} + +// ModContainingCWD returns the main module containing the working directory, +// or module.Version{} if none of the main modules contain the working +// directory. +func (mms *MainModuleSet) ModContainingCWD() module.Version { + return mms.modContainingCWD +} + +func (mms *MainModuleSet) HighestReplaced() map[string]string { + return mms.highestReplaced +} + +// GoVersion returns the go version set on the single module, in module mode, +// or the go.work file in workspace mode. +func (mms *MainModuleSet) GoVersion() string { + if !inWorkspaceMode() { + return modFileGoVersion(mms.ModFile(mms.mustGetSingleMainModule())) + } + v := mms.workFileGoVersion + if v == "" { + // Fall back to 1.18 for go.work files. + v = "1.18" + } + return v +} + +func (mms *MainModuleSet) WorkFileReplaceMap() map[module.Version]module.Version { + return mms.workFileReplaceMap +} + +var MainModules *MainModuleSet type Root int @@ -94,6 +266,7 @@ const ( // in go.mod, edit it before loading. func ModFile() *modfile.File { Init() + modFile := MainModules.ModFile(MainModules.mustGetSingleMainModule()) if modFile == nil { die() } @@ -102,9 +275,38 @@ func ModFile() *modfile.File { func BinDir() string { Init() + if cfg.GOBIN != "" { + return cfg.GOBIN + } + if gopath == "" { + return "" + } return filepath.Join(gopath, "bin") } +// InitWorkfile initializes the workFilePath variable for commands that +// operate in workspace mode. It should not be called by other commands, +// for example 'go mod tidy', that don't operate in workspace mode. +func InitWorkfile() { + switch cfg.WorkFile { + case "off": + workFilePath = "" + case "", "auto": + workFilePath = findWorkspaceFile(base.Cwd()) + default: + if !filepath.IsAbs(cfg.WorkFile) { + base.Fatalf("the path provided to -workfile must be an absolute path") + } + workFilePath = cfg.WorkFile + } +} + +// WorkFilePath returns the path of the go.work file, or "" if not in +// workspace mode. WorkFilePath must be called after InitWorkfile. +func WorkFilePath() string { + return workFilePath +} + // Init determines whether module mode is enabled, locates the root of the // current module (if any), sets environment variables for Git subprocesses, and // configures the cfg, codehost, load, modfetch, and search packages for use @@ -169,18 +371,18 @@ func Init() { if os.Getenv("GCM_INTERACTIVE") == "" { os.Setenv("GCM_INTERACTIVE", "never") } - - if modRoot != "" { + if modRoots != nil { // modRoot set before Init was called ("go mod init" does this). // No need to search for go.mod. } else if RootMode == NoRoot { if cfg.ModFile != "" && !base.InGOFLAGS("-modfile") { base.Fatalf("go: -modfile cannot be used with commands that ignore the current module") } - modRoot = "" + modRoots = nil + } else if inWorkspaceMode() { + // We're in workspace mode. } else { - modRoot = findModuleRoot(base.Cwd()) - if modRoot == "" { + if modRoot := findModuleRoot(base.Cwd()); modRoot == "" { if cfg.ModFile != "" { base.Fatalf("go: cannot find main module, but -modfile was set.\n\t-modfile cannot be used to set the module root directory.") } @@ -198,11 +400,12 @@ func Init() { // will find it and get modules when they're not expecting them. // It's a bit of a peculiar thing to disallow but quite mysterious // when it happens. See golang.org/issue/26708. - modRoot = "" fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir()) if !mustUseModules { return } + } else { + modRoots = []string{modRoot} } } if cfg.ModFile != "" && !strings.HasSuffix(cfg.ModFile, ".mod") { @@ -213,35 +416,11 @@ func Init() { cfg.ModulesEnabled = true setDefaultBuildMod() list := filepath.SplitList(cfg.BuildContext.GOPATH) - if len(list) == 0 || list[0] == "" { - base.Fatalf("missing $GOPATH") - } - gopath = list[0] - if _, err := fsys.Stat(filepath.Join(gopath, "go.mod")); err == nil { - base.Fatalf("$GOPATH/go.mod exists but should not") - } - - if modRoot == "" { - // We're in module mode, but not inside a module. - // - // Commands like 'go build', 'go run', 'go list' have no go.mod file to - // read or write. They would need to find and download the latest versions - // of a potentially large number of modules with no way to save version - // information. We can succeed slowly (but not reproducibly), but that's - // not usually a good experience. - // - // Instead, we forbid resolving import paths to modules other than std and - // cmd. Users may still build packages specified with .go files on the - // command line, but they'll see an error if those files import anything - // outside std. - // - // This can be overridden by calling AllowMissingModuleImports. - // For example, 'go get' does this, since it is expected to resolve paths. - // - // See golang.org/issue/32027. - } else { - modfetch.GoSumFile = strings.TrimSuffix(ModFilePath(), ".mod") + ".sum" - search.SetModRoot(modRoot) + if len(list) > 0 && list[0] != "" { + gopath = list[0] + if _, err := fsys.Stat(filepath.Join(gopath, "go.mod")); err == nil { + base.Fatalf("$GOPATH/go.mod exists but should not") + } } } @@ -255,7 +434,7 @@ func Init() { // be called until the command is installed and flags are parsed. Instead of // calling Init and Enabled, the main package can call this function. func WillBeEnabled() bool { - if modRoot != "" || cfg.ModulesEnabled { + if modRoots != nil || cfg.ModulesEnabled { // Already enabled. return true } @@ -297,16 +476,18 @@ func WillBeEnabled() bool { // (usually through MustModRoot). func Enabled() bool { Init() - return modRoot != "" || cfg.ModulesEnabled + return modRoots != nil || cfg.ModulesEnabled } -// ModRoot returns the root of the main module. -// It calls base.Fatalf if there is no main module. -func ModRoot() string { - if !HasModRoot() { - die() +func VendorDir() string { + return filepath.Join(MainModules.ModRoot(MainModules.mustGetSingleMainModule()), "vendor") +} + +func inWorkspaceMode() bool { + if !initialized { + panic("inWorkspaceMode called before modload.Init called") } - return modRoot + return workFilePath != "" } // HasModRoot reports whether a main module is present. @@ -314,17 +495,27 @@ func ModRoot() string { // does not require a main module. func HasModRoot() bool { Init() - return modRoot != "" + return modRoots != nil } -// ModFilePath returns the effective path of the go.mod file. Normally, this -// "go.mod" in the directory returned by ModRoot, but the -modfile flag may -// change its location. ModFilePath calls base.Fatalf if there is no main -// module, even if -modfile is set. -func ModFilePath() string { +// MustHaveModRoot checks that a main module or main modules are present, +// and calls base.Fatalf if there are no main modules. +func MustHaveModRoot() { + Init() if !HasModRoot() { die() } +} + +// ModFilePath returns the path that would be used for the go.mod +// file, if in module mode. ModFilePath calls base.Fatalf if there is no main +// module, even if -modfile is set. +func ModFilePath() string { + MustHaveModRoot() + return modFilePath(findModuleRoot(base.Cwd())) +} + +func modFilePath(modRoot string) string { if cfg.ModFile != "" { return cfg.ModFile } @@ -335,6 +526,9 @@ func die() { if cfg.Getenv("GO111MODULE") == "off" { base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'") } + if inWorkspaceMode() { + base.Fatalf("go: no modules were found in the current workspace; see 'go help work'") + } if dir, name := findAltConfig(base.Cwd()); dir != "" { rel, err := filepath.Rel(base.Cwd(), dir) if err != nil { @@ -365,12 +559,81 @@ func (goModDirtyError) Error() string { var errGoModDirty error = goModDirtyError{} +func loadWorkFile(path string) (goVersion string, modRoots []string, replaces []*modfile.Replace, err error) { + workDir := filepath.Dir(path) + wf, err := ReadWorkFile(path) + if err != nil { + return "", nil, nil, err + } + if wf.Go != nil { + goVersion = wf.Go.Version + } + seen := map[string]bool{} + for _, d := range wf.Use { + modRoot := d.Path + if !filepath.IsAbs(modRoot) { + modRoot = filepath.Join(workDir, modRoot) + } + + if seen[modRoot] { + return "", nil, nil, fmt.Errorf("path %s appears multiple times in workspace", modRoot) + } + seen[modRoot] = true + modRoots = append(modRoots, modRoot) + } + + return goVersion, modRoots, wf.Replace, nil +} + +// ReadWorkFile reads and parses the go.work file at the given path. +func ReadWorkFile(path string) (*modfile.WorkFile, error) { + workData, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + return modfile.ParseWork(path, workData, nil) +} + +// WriteWorkFile cleans and writes out the go.work file to the given path. +func WriteWorkFile(path string, wf *modfile.WorkFile) error { + wf.SortBlocks() + wf.Cleanup() + out := modfile.Format(wf.Syntax) + + return ioutil.WriteFile(path, out, 0666) +} + +// UpdateWorkFile updates comments on directory directives in the go.work +// file to include the associated module path. +func UpdateWorkFile(wf *modfile.WorkFile) { + missingModulePaths := map[string]string{} // module directory listed in file -> abspath modroot + + for _, d := range wf.Use { + modRoot := d.Path + if d.ModulePath == "" { + missingModulePaths[d.Path] = modRoot + } + } + + // Clean up and annotate directories. + // TODO(matloob): update x/mod to actually add module paths. + for moddir, absmodroot := range missingModulePaths { + _, f, err := ReadModFile(filepath.Join(absmodroot, "go.mod"), nil) + if err != nil { + continue // Error will be reported if modules are loaded. + } + wf.AddUse(moddir, f.Module.Mod.Path) + } +} + // LoadModFile sets Target and, if there is a main module, parses the initial // build list from its go.mod file. // // LoadModFile may make changes in memory, like adding a go directive and -// ensuring requirements are consistent, and will write those changes back to -// disk unless DisallowWriteGoMod is in effect. +// ensuring requirements are consistent. The caller is responsible for ensuring +// those changes are written to disk by calling LoadPackages or ListModules +// (unless ExplicitWriteGoMod is set) or by calling WriteGoMod directly. // // As a side-effect, LoadModFile may change cfg.BuildMod to "vendor" if // -mod wasn't set explicitly and automatic vendoring should be enabled. @@ -383,111 +646,142 @@ var errGoModDirty error = goModDirtyError{} // it for global consistency. Most callers outside of the modload package should // use LoadModGraph instead. func LoadModFile(ctx context.Context) *Requirements { - rs, needCommit := loadModFile(ctx) - if needCommit { - commitRequirements(ctx, modFileGoVersion(), rs) - } - return rs -} - -// loadModFile is like LoadModFile, but does not implicitly commit the -// requirements back to disk after fixing inconsistencies. -// -// If needCommit is true, after the caller makes any other needed changes to the -// returned requirements they should invoke commitRequirements to fix any -// inconsistencies that may be present in the on-disk go.mod file. -func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) { if requirements != nil { - return requirements, false + return requirements } Init() - if modRoot == "" { - Target = module.Version{Path: "command-line-arguments"} - targetPrefix = "command-line-arguments" - goVersion := LatestGoVersion() - rawGoVersion.Store(Target, goVersion) - requirements = newRequirements(modDepthFromGoVersion(goVersion), nil, nil) - return requirements, false - } - - gomod := ModFilePath() - var data []byte - var err error - if gomodActual, ok := fsys.OverlayPath(gomod); ok { - // Don't lock go.mod if it's part of the overlay. - // On Plan 9, locking requires chmod, and we don't want to modify any file - // in the overlay. See #44700. - data, err = os.ReadFile(gomodActual) + var ( + workFileGoVersion string + workFileReplaces []*modfile.Replace + ) + if inWorkspaceMode() { + var err error + workFileGoVersion, modRoots, workFileReplaces, err = loadWorkFile(workFilePath) + if err != nil { + base.Fatalf("reading go.work: %v", err) + } + for _, modRoot := range modRoots { + sumFile := strings.TrimSuffix(modFilePath(modRoot), ".mod") + ".sum" + modfetch.WorkspaceGoSumFiles = append(modfetch.WorkspaceGoSumFiles, sumFile) + } + modfetch.GoSumFile = workFilePath + ".sum" + } else if modRoots == nil { + // We're in module mode, but not inside a module. + // + // Commands like 'go build', 'go run', 'go list' have no go.mod file to + // read or write. They would need to find and download the latest versions + // of a potentially large number of modules with no way to save version + // information. We can succeed slowly (but not reproducibly), but that's + // not usually a good experience. + // + // Instead, we forbid resolving import paths to modules other than std and + // cmd. Users may still build packages specified with .go files on the + // command line, but they'll see an error if those files import anything + // outside std. + // + // This can be overridden by calling AllowMissingModuleImports. + // For example, 'go get' does this, since it is expected to resolve paths. + // + // See golang.org/issue/32027. } else { - data, err = lockedfile.Read(gomodActual) - } - if err != nil { - base.Fatalf("go: %v", err) + modfetch.GoSumFile = strings.TrimSuffix(modFilePath(modRoots[0]), ".mod") + ".sum" + } + if len(modRoots) == 0 { + // TODO(#49228): Instead of creating a fake module with an empty modroot, + // make MainModules.Len() == 0 mean that we're in module mode but not inside + // any module. + mainModule := module.Version{Path: "command-line-arguments"} + MainModules = makeMainModules([]module.Version{mainModule}, []string{""}, []*modfile.File{nil}, []*modFileIndex{nil}, "", nil) + goVersion := LatestGoVersion() + rawGoVersion.Store(mainModule, goVersion) + pruning := pruningForGoVersion(goVersion) + if inWorkspaceMode() { + pruning = workspace + } + requirements = newRequirements(pruning, nil, nil) + return requirements } - var fixed bool - f, err := modfile.Parse(gomod, data, fixVersion(ctx, &fixed)) - if err != nil { - // Errors returned by modfile.Parse begin with file:line. - base.Fatalf("go: errors parsing go.mod:\n%s\n", err) - } - if f.Module == nil { - // No module declaration. Must add module path. - base.Fatalf("go: no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod") - } + var modFiles []*modfile.File + var mainModules []module.Version + var indices []*modFileIndex + for _, modroot := range modRoots { + gomod := modFilePath(modroot) + var fixed bool + data, f, err := ReadModFile(gomod, fixVersion(ctx, &fixed)) + if err != nil { + base.Fatalf("go: %v", err) + } - modFile = f - initTarget(f.Module.Mod) - index = indexModFile(data, f, fixed) + modFiles = append(modFiles, f) + mainModule := f.Module.Mod + mainModules = append(mainModules, mainModule) + indices = append(indices, indexModFile(data, f, mainModule, fixed)) - if err := module.CheckImportPath(f.Module.Mod.Path); err != nil { - if pathErr, ok := err.(*module.InvalidPathError); ok { - pathErr.Kind = "module" + if err := module.CheckImportPath(f.Module.Mod.Path); err != nil { + if pathErr, ok := err.(*module.InvalidPathError); ok { + pathErr.Kind = "module" + } + base.Fatalf("go: %v", err) } - base.Fatalf("go: %v", err) } + MainModules = makeMainModules(mainModules, modRoots, modFiles, indices, workFileGoVersion, workFileReplaces) setDefaultBuildMod() // possibly enable automatic vendoring - rs = requirementsFromModFile() + rs := requirementsFromModFiles(ctx, modFiles) + + if inWorkspaceMode() { + // We don't need to do anything for vendor or update the mod file so + // return early. + requirements = rs + return rs + } + + mainModule := MainModules.mustGetSingleMainModule() + if cfg.BuildMod == "vendor" { - readVendorList() - checkVendorConsistency() + readVendorList(mainModule) + index := MainModules.Index(mainModule) + modFile := MainModules.ModFile(mainModule) + checkVendorConsistency(index, modFile) rs.initVendor(vendorList) } + if rs.hasRedundantRoot() { // If any module path appears more than once in the roots, we know that the // go.mod file needs to be updated even though we have not yet loaded any // transitive dependencies. + var err error rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false) if err != nil { base.Fatalf("go: %v", err) } } - if index.goVersionV == "" { + if MainModules.Index(mainModule).goVersionV == "" && rs.pruning != workspace { // TODO(#45551): Do something more principled instead of checking // cfg.CmdName directly here. if cfg.BuildMod == "mod" && cfg.CmdName != "mod graph" && cfg.CmdName != "mod why" { - addGoStmt(LatestGoVersion()) - if go117EnableLazyLoading { - // We need to add a 'go' version to the go.mod file, but we must assume - // that its existing contents match something between Go 1.11 and 1.16. - // Go 1.11 through 1.16 have eager requirements, but the latest Go - // version uses lazy requirements instead — so we need to cnvert the - // requirements to be lazy. - rs, err = convertDepth(ctx, rs, lazy) - if err != nil { - base.Fatalf("go: %v", err) - } + addGoStmt(MainModules.ModFile(mainModule), mainModule, LatestGoVersion()) + + // We need to add a 'go' version to the go.mod file, but we must assume + // that its existing contents match something between Go 1.11 and 1.16. + // Go 1.11 through 1.16 do not support graph pruning, but the latest Go + // version uses a pruned module graph — so we need to convert the + // requirements to support pruning. + var err error + rs, err = convertPruning(ctx, rs, pruned) + if err != nil { + base.Fatalf("go: %v", err) } } else { - rawGoVersion.Store(Target, modFileGoVersion()) + rawGoVersion.Store(mainModule, modFileGoVersion(MainModules.ModFile(mainModule))) } } requirements = rs - return requirements, true + return requirements } // CreateModFile initializes a new module by creating a go.mod file. @@ -500,9 +794,10 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) { // exactly the same as in the legacy configuration (for example, we can't get // packages at multiple versions from the same module). func CreateModFile(ctx context.Context, modPath string) { - modRoot = base.Cwd() + modRoot := base.Cwd() + modRoots = []string{modRoot} Init() - modFilePath := ModFilePath() + modFilePath := modFilePath(modRoot) if _, err := fsys.Stat(modFilePath); err == nil { base.Fatalf("go: %s already exists", modFilePath) } @@ -523,15 +818,22 @@ func CreateModFile(ctx context.Context, modPath string) { } } base.Fatalf("go: %v", err) + } else if _, _, ok := module.SplitPathVersion(modPath); !ok { + if strings.HasPrefix(modPath, "gopkg.in/") { + invalidMajorVersionMsg := fmt.Errorf("module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN:\n\tgo mod init %s", suggestGopkgIn(modPath)) + base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg) + } + invalidMajorVersionMsg := fmt.Errorf("major version suffixes must be in the form of /vN and are only allowed for v2 or later:\n\tgo mod init %s", suggestModulePath(modPath)) + base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg) } fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", modPath) - modFile = new(modfile.File) + modFile := new(modfile.File) modFile.AddModuleStmt(modPath) - initTarget(modFile.Module.Mod) - addGoStmt(LatestGoVersion()) // Add the go directive before converted module requirements. + MainModules = makeMainModules([]module.Version{modFile.Module.Mod}, []string{modRoot}, []*modfile.File{modFile}, []*modFileIndex{nil}, "", nil) + addGoStmt(modFile, modFile.Module.Mod, LatestGoVersion()) // Add the go directive before converted module requirements. - convertedFrom, err := convertLegacyConfig(modPath) + convertedFrom, err := convertLegacyConfig(modFile, modRoot) if convertedFrom != "" { fmt.Fprintf(os.Stderr, "go: copying requirements from %s\n", base.ShortPath(convertedFrom)) } @@ -539,12 +841,15 @@ func CreateModFile(ctx context.Context, modPath string) { base.Fatalf("go: %v", err) } - rs := requirementsFromModFile() + rs := requirementsFromModFiles(ctx, []*modfile.File{modFile}) rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false) if err != nil { base.Fatalf("go: %v", err) } - commitRequirements(ctx, modFileGoVersion(), rs) + requirements = rs + if err := commitRequirements(ctx); err != nil { + base.Fatalf("go: %v", err) + } // Suggest running 'go mod tidy' unless the project is empty. Even if we // imported all the correct requirements above, we're probably missing @@ -570,6 +875,32 @@ func CreateModFile(ctx context.Context, modPath string) { } } +// CreateWorkFile initializes a new workspace by creating a go.work file. +func CreateWorkFile(ctx context.Context, workFile string, modDirs []string) { + if _, err := fsys.Stat(workFile); err == nil { + base.Fatalf("go: %s already exists", workFile) + } + + goV := LatestGoVersion() // Use current Go version by default + workF := new(modfile.WorkFile) + workF.Syntax = new(modfile.FileSyntax) + workF.AddGoStmt(goV) + + for _, dir := range modDirs { + _, f, err := ReadModFile(filepath.Join(dir, "go.mod"), nil) + if err != nil { + if os.IsNotExist(err) { + base.Fatalf("go: creating workspace file: no go.mod file exists in directory %v", dir) + } + base.Fatalf("go: error parsing go.mod in directory %s: %v", dir, err) + } + workF.AddUse(ToDirectoryPath(dir), f.Module.Mod.Path) + } + + UpdateWorkFile(workF) + WriteWorkFile(workFile, workF) +} + // fixVersion returns a modfile.VersionFixer implemented using the Query function. // // It resolves commit hashes and branch names to versions, @@ -632,49 +963,125 @@ func AllowMissingModuleImports() { allowMissingModuleImports = true } -// initTarget sets Target and associated variables according to modFile, -func initTarget(m module.Version) { - Target = m - targetPrefix = m.Path - - if rel := search.InDir(base.Cwd(), cfg.GOROOTsrc); rel != "" { - targetInGorootSrc = true - if m.Path == "std" { - // The "std" module in GOROOT/src is the Go standard library. Unlike other - // modules, the packages in the "std" module have no import-path prefix. - // - // Modules named "std" outside of GOROOT/src do not receive this special - // treatment, so it is possible to run 'go test .' in other GOROOTs to - // test individual packages using a combination of the modified package - // and the ordinary standard library. - // (See https://golang.org/issue/30756.) - targetPrefix = "" +// makeMainModules creates a MainModuleSet and associated variables according to +// the given main modules. +func makeMainModules(ms []module.Version, rootDirs []string, modFiles []*modfile.File, indices []*modFileIndex, workFileGoVersion string, workFileReplaces []*modfile.Replace) *MainModuleSet { + for _, m := range ms { + if m.Version != "" { + panic("mainModulesCalled with module.Version with non empty Version field: " + fmt.Sprintf("%#v", m)) } } + modRootContainingCWD := findModuleRoot(base.Cwd()) + mainModules := &MainModuleSet{ + versions: ms[:len(ms):len(ms)], + inGorootSrc: map[module.Version]bool{}, + pathPrefix: map[module.Version]string{}, + modRoot: map[module.Version]string{}, + modFiles: map[module.Version]*modfile.File{}, + indices: map[module.Version]*modFileIndex{}, + workFileGoVersion: workFileGoVersion, + workFileReplaceMap: toReplaceMap(workFileReplaces), + highestReplaced: map[string]string{}, + } + mainModulePaths := make(map[string]bool) + for _, m := range ms { + mainModulePaths[m.Path] = true + } + replacedByWorkFile := make(map[string]bool) + replacements := make(map[module.Version]module.Version) + for _, r := range workFileReplaces { + if mainModulePaths[r.Old.Path] && r.Old.Version == "" { + base.Errorf("go: workspace module %v is replaced at all versions in the go.work file. To fix, remove the replacement from the go.work file or specify the version at which to replace the module.", r.Old.Path) + } + replacedByWorkFile[r.Old.Path] = true + v, ok := mainModules.highestReplaced[r.Old.Path] + if !ok || semver.Compare(r.Old.Version, v) > 0 { + mainModules.highestReplaced[r.Old.Path] = r.Old.Version + } + replacements[r.Old] = r.New + } + for i, m := range ms { + mainModules.pathPrefix[m] = m.Path + mainModules.modRoot[m] = rootDirs[i] + mainModules.modFiles[m] = modFiles[i] + mainModules.indices[m] = indices[i] + + if mainModules.modRoot[m] == modRootContainingCWD { + mainModules.modContainingCWD = m + } + + if rel := search.InDir(rootDirs[i], cfg.GOROOTsrc); rel != "" { + mainModules.inGorootSrc[m] = true + if m.Path == "std" { + // The "std" module in GOROOT/src is the Go standard library. Unlike other + // modules, the packages in the "std" module have no import-path prefix. + // + // Modules named "std" outside of GOROOT/src do not receive this special + // treatment, so it is possible to run 'go test .' in other GOROOTs to + // test individual packages using a combination of the modified package + // and the ordinary standard library. + // (See https://golang.org/issue/30756.) + mainModules.pathPrefix[m] = "" + } + } + + if modFiles[i] != nil { + curModuleReplaces := make(map[module.Version]bool) + for _, r := range modFiles[i].Replace { + if replacedByWorkFile[r.Old.Path] { + continue + } else if prev, ok := replacements[r.Old]; ok && !curModuleReplaces[r.Old] && prev != r.New { + base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v\nuse \"go work edit -replace %v=[override]\" to resolve", r.Old, prev, r.New, r.Old) + } + curModuleReplaces[r.Old] = true + replacements[r.Old] = r.New + + v, ok := mainModules.highestReplaced[r.Old.Path] + if !ok || semver.Compare(r.Old.Version, v) > 0 { + mainModules.highestReplaced[r.Old.Path] = r.Old.Version + } + } + } + } + return mainModules } -// requirementsFromModFile returns the set of non-excluded requirements from +// requirementsFromModFiles returns the set of non-excluded requirements from // the global modFile. -func requirementsFromModFile() *Requirements { - roots := make([]module.Version, 0, len(modFile.Require)) +func requirementsFromModFiles(ctx context.Context, modFiles []*modfile.File) *Requirements { + var roots []module.Version direct := map[string]bool{} - for _, r := range modFile.Require { - if index != nil && index.exclude[r.Mod] { - if cfg.BuildMod == "mod" { - fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version) - } else { - fmt.Fprintf(os.Stderr, "go: ignoring requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version) - } - continue + var pruning modPruning + if inWorkspaceMode() { + pruning = workspace + roots = make([]module.Version, len(MainModules.Versions())) + copy(roots, MainModules.Versions()) + } else { + pruning = pruningForGoVersion(MainModules.GoVersion()) + if len(modFiles) != 1 { + panic(fmt.Errorf("requirementsFromModFiles called with %v modfiles outside workspace mode", len(modFiles))) } + modFile := modFiles[0] + roots = make([]module.Version, 0, len(modFile.Require)) + mm := MainModules.mustGetSingleMainModule() + for _, r := range modFile.Require { + if index := MainModules.Index(mm); index != nil && index.exclude[r.Mod] { + if cfg.BuildMod == "mod" { + fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version) + } else { + fmt.Fprintf(os.Stderr, "go: ignoring requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version) + } + continue + } - roots = append(roots, r.Mod) - if !r.Indirect { - direct[r.Mod.Path] = true + roots = append(roots, r.Mod) + if !r.Indirect { + direct[r.Mod.Path] = true + } } } module.Sort(roots) - rs := newRequirements(modDepthFromGoVersion(modFileGoVersion()), roots, direct) + rs := newRequirements(pruning, roots, direct) return rs } @@ -682,18 +1089,35 @@ func requirementsFromModFile() *Requirements { // wasn't provided. setDefaultBuildMod may be called multiple times. func setDefaultBuildMod() { if cfg.BuildModExplicit { + if inWorkspaceMode() && cfg.BuildMod != "readonly" { + base.Fatalf("go: -mod may only be set to readonly when in workspace mode, but it is set to %q"+ + "\n\tRemove the -mod flag to use the default readonly value,"+ + "\n\tor set -workfile=off to disable workspace mode.", cfg.BuildMod) + } // Don't override an explicit '-mod=' argument. return } - if cfg.CmdName == "get" || strings.HasPrefix(cfg.CmdName, "mod ") { - // 'get' and 'go mod' commands may update go.mod automatically. - // TODO(jayconrod): should this narrower? Should 'go mod download' or - // 'go mod graph' update go.mod by default? + // TODO(#40775): commands should pass in the module mode as an option + // to modload functions instead of relying on an implicit setting + // based on command name. + switch cfg.CmdName { + case "get", "mod download", "mod init", "mod tidy", "work sync": + // These commands are intended to update go.mod and go.sum. + cfg.BuildMod = "mod" + return + case "mod graph", "mod verify", "mod why": + // These commands should not update go.mod or go.sum, but they should be + // able to fetch modules not in go.sum and should not report errors if + // go.mod is inconsistent. They're useful for debugging, and they need + // to work in buggy situations. cfg.BuildMod = "mod" return + case "mod vendor": + cfg.BuildMod = "readonly" + return } - if modRoot == "" { + if modRoots == nil { if allowMissingModuleImports { cfg.BuildMod = "mod" } else { @@ -702,31 +1126,38 @@ func setDefaultBuildMod() { return } - if fi, err := fsys.Stat(filepath.Join(modRoot, "vendor")); err == nil && fi.IsDir() { - modGo := "unspecified" - if index != nil && index.goVersionV != "" { - if semver.Compare(index.goVersionV, "v1.14") >= 0 { - // The Go version is at least 1.14, and a vendor directory exists. - // Set -mod=vendor by default. - cfg.BuildMod = "vendor" - cfg.BuildModReason = "Go version in go.mod is at least 1.14 and vendor directory exists." - return - } else { - modGo = index.goVersionV[1:] + if len(modRoots) == 1 { + index := MainModules.GetSingleIndexOrNil() + if fi, err := fsys.Stat(filepath.Join(modRoots[0], "vendor")); err == nil && fi.IsDir() { + modGo := "unspecified" + if index != nil && index.goVersionV != "" { + if semver.Compare(index.goVersionV, "v1.14") >= 0 { + // The Go version is at least 1.14, and a vendor directory exists. + // Set -mod=vendor by default. + cfg.BuildMod = "vendor" + cfg.BuildModReason = "Go version in go.mod is at least 1.14 and vendor directory exists." + return + } else { + modGo = index.goVersionV[1:] + } } - } - // Since a vendor directory exists, we should record why we didn't use it. - // This message won't normally be shown, but it may appear with import errors. - cfg.BuildModReason = fmt.Sprintf("Go version in go.mod is %s, so vendor directory was not used.", modGo) + // Since a vendor directory exists, we should record why we didn't use it. + // This message won't normally be shown, but it may appear with import errors. + cfg.BuildModReason = fmt.Sprintf("Go version in go.mod is %s, so vendor directory was not used.", modGo) + } } cfg.BuildMod = "readonly" } +func mustHaveCompleteRequirements() bool { + return cfg.BuildMod != "mod" && !inWorkspaceMode() +} + // convertLegacyConfig imports module requirements from a legacy vendoring // configuration file, if one is present. -func convertLegacyConfig(modPath string) (from string, err error) { +func convertLegacyConfig(modFile *modfile.File, modRoot string) (from string, err error) { noneSelected := func(path string) (version string) { return "none" } queryPackage := func(path, rev string) (module.Version, error) { pkgMods, modOnly, err := QueryPattern(context.Background(), path, rev, noneSelected, nil) @@ -757,14 +1188,14 @@ func convertLegacyConfig(modPath string) (from string, err error) { // addGoStmt adds a go directive to the go.mod file if it does not already // include one. The 'go' version added, if any, is the latest version supported // by this toolchain. -func addGoStmt(v string) { +func addGoStmt(modFile *modfile.File, mod module.Version, v string) { if modFile.Go != nil && modFile.Go.Version != "" { return } if err := modFile.AddGoStmt(v); err != nil { base.Fatalf("go: internal error: %v", err) } - rawGoVersion.Store(Target, v) + rawGoVersion.Store(mod, v) } // LatestGoVersion returns the latest version of the Go language supported by @@ -815,7 +1246,7 @@ var altConfigs = []string{ ".git/config", } -func findModuleRoot(dir string) (root string) { +func findModuleRoot(dir string) (roots string) { if dir == "" { panic("dir not set") } @@ -835,6 +1266,33 @@ func findModuleRoot(dir string) (root string) { return "" } +func findWorkspaceFile(dir string) (root string) { + if dir == "" { + panic("dir not set") + } + dir = filepath.Clean(dir) + + // Look for enclosing go.mod. + for { + f := filepath.Join(dir, "go.work") + if fi, err := fsys.Stat(f); err == nil && !fi.IsDir() { + return f + } + d := filepath.Dir(dir) + if d == dir { + break + } + if d == cfg.GOROOT { + // As a special case, don't cross GOROOT to find a go.work file. + // The standard library and commands built in go always use the vendored + // dependencies, so avoid using a most likely irrelevant go.work file. + return "" + } + dir = d + } + return "" +} + func findAltConfig(dir string) (root, name string) { if dir == "" { panic("dir not set") @@ -960,66 +1418,62 @@ func findImportComment(file string) string { return path } -var allowWriteGoMod = true - -// DisallowWriteGoMod causes future calls to WriteGoMod to do nothing at all. -func DisallowWriteGoMod() { - allowWriteGoMod = false -} - -// AllowWriteGoMod undoes the effect of DisallowWriteGoMod: -// future calls to WriteGoMod will update go.mod if needed. -// Note that any past calls have been discarded, so typically -// a call to AlowWriteGoMod should be followed by a call to WriteGoMod. -func AllowWriteGoMod() { - allowWriteGoMod = true -} - // WriteGoMod writes the current build list back to go.mod. -func WriteGoMod(ctx context.Context) { - if !allowWriteGoMod { - panic("WriteGoMod called while disallowed") - } - commitRequirements(ctx, modFileGoVersion(), LoadModFile(ctx)) +func WriteGoMod(ctx context.Context) error { + requirements = LoadModFile(ctx) + return commitRequirements(ctx) } -// commitRequirements writes sets the global requirements variable to rs and -// writes its contents back to the go.mod file on disk. -func commitRequirements(ctx context.Context, goVersion string, rs *Requirements) { - requirements = rs - - if !allowWriteGoMod { - // Some package outside of modload promised to update the go.mod file later. - return - } - - if modRoot == "" { +// commitRequirements ensures go.mod and go.sum are up to date with the current +// requirements. +// +// In "mod" mode, commitRequirements writes changes to go.mod and go.sum. +// +// In "readonly" and "vendor" modes, commitRequirements returns an error if +// go.mod or go.sum are out of date in a semantically significant way. +// +// In workspace mode, commitRequirements only writes changes to go.work.sum. +func commitRequirements(ctx context.Context) (err error) { + if inWorkspaceMode() { + // go.mod files aren't updated in workspace mode, but we still want to + // update the go.work.sum file. + return modfetch.WriteGoSum(keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements()) + } + if MainModules.Len() != 1 || MainModules.ModRoot(MainModules.Versions()[0]) == "" { // We aren't in a module, so we don't have anywhere to write a go.mod file. - return + return nil + } + mainModule := MainModules.mustGetSingleMainModule() + modFile := MainModules.ModFile(mainModule) + if modFile == nil { + // command-line-arguments has no .mod file to write. + return nil } + modFilePath := modFilePath(MainModules.ModRoot(mainModule)) var list []*modfile.Require - for _, m := range rs.rootModules { + for _, m := range requirements.rootModules { list = append(list, &modfile.Require{ Mod: m, - Indirect: !rs.direct[m.Path], + Indirect: !requirements.direct[m.Path], }) } - if goVersion != "" { - modFile.AddGoStmt(goVersion) + if modFile.Go == nil || modFile.Go.Version == "" { + modFile.AddGoStmt(modFileGoVersion(modFile)) } - if semver.Compare("v"+modFileGoVersion(), separateIndirectVersionV) < 0 { + if semver.Compare("v"+modFileGoVersion(modFile), separateIndirectVersionV) < 0 { modFile.SetRequire(list) } else { modFile.SetRequireSeparateIndirect(list) } modFile.Cleanup() + index := MainModules.GetSingleIndexOrNil() dirty := index.modFileIsDirty(modFile) if dirty && cfg.BuildMod != "mod" { // If we're about to fail due to -mod=readonly, // prefer to report a dirty go.mod over a dirty go.sum - base.Fatalf("go: %v", errGoModDirty) + return errGoModDirty } if !dirty && cfg.CmdName != "mod tidy" { @@ -1028,30 +1482,33 @@ func commitRequirements(ctx context.Context, goVersion string, rs *Requirements) // Don't write go.mod, but write go.sum in case we added or trimmed sums. // 'go mod init' shouldn't write go.sum, since it will be incomplete. if cfg.CmdName != "mod init" { - modfetch.WriteGoSum(keepSums(ctx, loaded, rs, addBuildListZipSums)) + if err := modfetch.WriteGoSum(keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements()); err != nil { + return err + } } - return + return nil } - gomod := ModFilePath() - if _, ok := fsys.OverlayPath(gomod); ok { + if _, ok := fsys.OverlayPath(modFilePath); ok { if dirty { - base.Fatalf("go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay") + return errors.New("updates to go.mod needed, but go.mod is part of the overlay specified with -overlay") } - return + return nil } new, err := modFile.Format() if err != nil { - base.Fatalf("go: %v", err) + return err } defer func() { // At this point we have determined to make the go.mod file on disk equal to new. - index = indexModFile(new, modFile, false) + MainModules.SetIndex(mainModule, indexModFile(new, modFile, mainModule, false)) // Update go.sum after releasing the side lock and refreshing the index. // 'go mod init' shouldn't write go.sum, since it will be incomplete. if cfg.CmdName != "mod init" { - modfetch.WriteGoSum(keepSums(ctx, loaded, rs, addBuildListZipSums)) + if err == nil { + err = modfetch.WriteGoSum(keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements()) + } } }() @@ -1063,7 +1520,7 @@ func commitRequirements(ctx context.Context, goVersion string, rs *Requirements) errNoChange := errors.New("no update needed") - err = lockedfile.Transform(ModFilePath(), func(old []byte) ([]byte, error) { + err = lockedfile.Transform(modFilePath, func(old []byte) ([]byte, error) { if bytes.Equal(old, new) { // The go.mod file is already equal to new, possibly as the result of some // other process. @@ -1084,8 +1541,9 @@ func commitRequirements(ctx context.Context, goVersion string, rs *Requirements) }) if err != nil && err != errNoChange { - base.Fatalf("go: updating go.mod: %v", err) + return fmt.Errorf("updating go.mod: %w", err) } + return nil } // keepSums returns the set of modules (and go.mod file entries) for which @@ -1113,16 +1571,18 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums continue } - if rs.depth == lazy && pkg.mod.Path != "" { + if rs.pruning == pruned && pkg.mod.Path != "" { if v, ok := rs.rootSelected(pkg.mod.Path); ok && v == pkg.mod.Version { - // pkg was loaded from a root module, and because the main module is - // lazy we do not check non-root modules for conflicts for packages - // that can be found in roots. So we only need the checksums for the - // root modules that may contain pkg, not all possible modules. + // pkg was loaded from a root module, and because the main module has + // a pruned module graph we do not check non-root modules for + // conflicts for packages that can be found in roots. So we only need + // the checksums for the root modules that may contain pkg, not all + // possible modules. for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) { if v, ok := rs.rootSelected(prefix); ok && v != "none" { m := module.Version{Path: prefix, Version: v} - keep[resolveReplacement(m)] = true + r := resolveReplacement(m) + keep[r] = true } } continue @@ -1133,15 +1593,15 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) { if v := mg.Selected(prefix); v != "none" { m := module.Version{Path: prefix, Version: v} - keep[resolveReplacement(m)] = true + r := resolveReplacement(m) + keep[r] = true } } } } if rs.graph.Load() == nil { - // The module graph was not loaded, possibly because the main module is lazy - // or possibly because we haven't needed to load the graph yet. + // We haven't needed to load the module graph so far. // Save sums for the root modules (or their replacements), but don't // incur the cost of loading the graph just to find and retain the sums. for _, m := range rs.rootModules { @@ -1158,13 +1618,15 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums // The requirements from m's go.mod file are present in the module graph, // so they are relevant to the MVS result regardless of whether m was // actually selected. - keep[modkey(resolveReplacement(m))] = true + r := resolveReplacement(m) + keep[modkey(r)] = true } }) if which == addBuildListZipSums { for _, m := range mg.BuildList() { - keep[resolveReplacement(m)] = true + r := resolveReplacement(m) + keep[r] = true } } } @@ -1184,3 +1646,56 @@ const ( func modkey(m module.Version) module.Version { return module.Version{Path: m.Path, Version: m.Version + "/go.mod"} } + +func suggestModulePath(path string) string { + var m string + + i := len(path) + for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') { + i-- + } + url := path[:i] + url = strings.TrimSuffix(url, "/v") + url = strings.TrimSuffix(url, "/") + + f := func(c rune) bool { + return c > '9' || c < '0' + } + s := strings.FieldsFunc(path[i:], f) + if len(s) > 0 { + m = s[0] + } + m = strings.TrimLeft(m, "0") + if m == "" || m == "1" { + return url + "/v2" + } + + return url + "/v" + m +} + +func suggestGopkgIn(path string) string { + var m string + i := len(path) + for i > 0 && (('0' <= path[i-1] && path[i-1] <= '9') || (path[i-1] == '.')) { + i-- + } + url := path[:i] + url = strings.TrimSuffix(url, ".v") + url = strings.TrimSuffix(url, "/v") + url = strings.TrimSuffix(url, "/") + + f := func(c rune) bool { + return c > '9' || c < '0' + } + s := strings.FieldsFunc(path, f) + if len(s) > 0 { + m = s[0] + } + + m = strings.TrimLeft(m, "0") + + if m == "" { + return url + ".v1" + } + return url + ".v" + m +} diff --git a/libgo/go/cmd/go/internal/modload/list.go b/libgo/go/cmd/go/internal/modload/list.go index ccdeb9b1d11..f782cd93db3 100644 --- a/libgo/go/cmd/go/internal/modload/list.go +++ b/libgo/go/cmd/go/internal/modload/list.go @@ -72,14 +72,21 @@ func ListModules(ctx context.Context, args []string, mode ListMode) ([]*modinfo. } if err == nil { - commitRequirements(ctx, modFileGoVersion(), rs) + requirements = rs + if !ExplicitWriteGoMod { + err = commitRequirements(ctx) + } } return mods, err } func listModules(ctx context.Context, rs *Requirements, args []string, mode ListMode) (_ *Requirements, mods []*modinfo.ModulePublic, mgErr error) { if len(args) == 0 { - return rs, []*modinfo.ModulePublic{moduleInfo(ctx, rs, Target, mode)}, nil + var ms []*modinfo.ModulePublic + for _, m := range MainModules.Versions() { + ms = append(ms, moduleInfo(ctx, rs, m, mode)) + } + return rs, ms, nil } needFullGraph := false @@ -101,7 +108,7 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List path := arg[:i] vers := arg[i+1:] if vers == "upgrade" || vers == "patch" { - if _, ok := rs.rootSelected(path); !ok || rs.depth == eager { + if _, ok := rs.rootSelected(path); !ok || rs.pruning == unpruned { needFullGraph = true if !HasModRoot() { base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot) @@ -110,7 +117,7 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List } continue } - if _, ok := rs.rootSelected(arg); !ok || rs.depth == eager { + if _, ok := rs.rootSelected(arg); !ok || rs.pruning == unpruned { needFullGraph = true if mode&ListVersions == 0 && !HasModRoot() { base.Fatalf("go: cannot match %q without -versions or an explicit version: %v", arg, ErrNoModRoot) diff --git a/libgo/go/cmd/go/internal/modload/load.go b/libgo/go/cmd/go/internal/modload/load.go index bce9ad85f42..617b634d263 100644 --- a/libgo/go/cmd/go/internal/modload/load.go +++ b/libgo/go/cmd/go/internal/modload/load.go @@ -40,9 +40,10 @@ package modload // - the main module specifies a go version ≤ 1.15, and the package is imported // by a *test of* another package in "all". // -// When we implement lazy loading, we will record the modules providing packages -// in "all" even when we are only loading individual packages, so we set the -// pkgInAll flag regardless of the whether the "all" pattern is a root. +// When graph pruning is in effect, we want to spot-check the graph-pruning +// invariants — which depend on which packages are known to be in "all" — even +// when we are only loading individual packages, so we set the pkgInAll flag +// regardless of the whether the "all" pattern is a root. // (This is necessary to maintain the “import invariant” described in // https://golang.org/design/36460-lazy-module-loading.) // @@ -230,6 +231,9 @@ type PackageOpts struct { // SilenceUnmatchedWarnings suppresses the warnings normally emitted for // patterns that did not match any packages. SilenceUnmatchedWarnings bool + + // Resolve the query against this module. + MainModule module.Version } // LoadPackages identifies the set of packages matching the given patterns and @@ -255,7 +259,11 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma case m.IsLocal(): // Evaluate list of file system directories on first iteration. if m.Dirs == nil { - matchLocalDirs(ctx, m, rs) + matchModRoots := modRoots + if opts.MainModule != (module.Version{}) { + matchModRoots = []string{MainModules.ModRoot(opts.MainModule)} + } + matchLocalDirs(ctx, matchModRoots, m, rs) } // Make a copy of the directory list and translate to import paths. @@ -274,7 +282,9 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma // If we're outside of a module, ensure that the failure mode // indicates that. - ModRoot() + if !HasModRoot() { + die() + } if ld != nil { m.AddError(err) @@ -306,7 +316,11 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma // The initial roots are the packages in the main module. // loadFromRoots will expand that to "all". m.Errs = m.Errs[:0] - matchPackages(ctx, m, opts.Tags, omitStd, []module.Version{Target}) + matchModules := MainModules.Versions() + if opts.MainModule != (module.Version{}) { + matchModules = []module.Version{opts.MainModule} + } + matchPackages(ctx, m, opts.Tags, omitStd, matchModules) } else { // Starting with the packages in the main module, // enumerate the full list of "all". @@ -324,7 +338,7 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma } } - initialRS, _ := loadModFile(ctx) // Ignore needCommit — we're going to commit at the end regardless. + initialRS := LoadModFile(ctx) ld := loadFromRoots(ctx, loaderParams{ PackageOpts: opts, @@ -365,7 +379,7 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma for _, m := range initialRS.rootModules { var unused bool - if ld.requirements.depth == eager { + if ld.requirements.pruning == unpruned { // m is unused if it was dropped from the module graph entirely. If it // was only demoted from direct to indirect, it may still be in use via // a transitive import. @@ -384,7 +398,7 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma } keep := keepSums(ctx, ld, ld.requirements, loadedZipSumsOnly) - if compatDepth := modDepthFromGoVersion(ld.TidyCompatibleVersion); compatDepth != ld.requirements.depth { + if compatDepth := pruningForGoVersion(ld.TidyCompatibleVersion); compatDepth != ld.requirements.pruning { compatRS := newRequirements(compatDepth, ld.requirements.rootModules, ld.requirements.direct) ld.checkTidyCompatibility(ctx, compatRS) @@ -393,7 +407,7 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma } } - if allowWriteGoMod { + if !ExplicitWriteGoMod { modfetch.TrimGoSum(keep) // commitRequirements below will also call WriteGoSum, but the "keep" map @@ -401,13 +415,24 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma // loaded.requirements, but here we may have also loaded (and want to // preserve checksums for) additional entities from compatRS, which are // only needed for compatibility with ld.TidyCompatibleVersion. - modfetch.WriteGoSum(keep) + if err := modfetch.WriteGoSum(keep, mustHaveCompleteRequirements()); err != nil { + base.Fatalf("go: %v", err) + } + } + + // Update the go.mod file's Go version if necessary. + modFile := MainModules.ModFile(MainModules.mustGetSingleMainModule()) + if ld.GoVersion != "" { + modFile.AddGoStmt(ld.GoVersion) } } // Success! Update go.mod and go.sum (if needed) and return the results. + // We'll skip updating if ExplicitWriteGoMod is true (the caller has opted + // to call WriteGoMod itself) or if ResolveMissingImports is false (the + // command wants to examine the package graph as-is). loaded = ld - commitRequirements(ctx, loaded.GoVersion, loaded.requirements) + requirements = loaded.requirements for _, pkg := range ld.pkgs { if !pkg.isTest() { @@ -415,12 +440,19 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma } } sort.Strings(loadedPackages) + + if !ExplicitWriteGoMod && opts.ResolveMissingImports { + if err := commitRequirements(ctx); err != nil { + base.Fatalf("go: %v", err) + } + } + return matches, loadedPackages } // matchLocalDirs is like m.MatchDirs, but tries to avoid scanning directories // outside of the standard library and active modules. -func matchLocalDirs(ctx context.Context, m *search.Match, rs *Requirements) { +func matchLocalDirs(ctx context.Context, modRoots []string, m *search.Match, rs *Requirements) { if !m.IsLocal() { panic(fmt.Sprintf("internal error: resolveLocalDirs on non-local pattern %s", m.Pattern())) } @@ -436,14 +468,23 @@ func matchLocalDirs(ctx context.Context, m *search.Match, rs *Requirements) { if !filepath.IsAbs(dir) { absDir = filepath.Join(base.Cwd(), dir) } - if search.InDir(absDir, cfg.GOROOTsrc) == "" && search.InDir(absDir, ModRoot()) == "" && pathInModuleCache(ctx, absDir, rs) == "" { + + modRoot := findModuleRoot(absDir) + found := false + for _, mainModuleRoot := range modRoots { + if mainModuleRoot == modRoot { + found = true + break + } + } + if !found && search.InDir(absDir, cfg.GOROOTsrc) == "" && pathInModuleCache(ctx, absDir, rs) == "" { m.Dirs = []string{} m.AddError(fmt.Errorf("directory prefix %s outside available modules", base.ShortPath(absDir))) return } } - m.MatchDirs() + m.MatchDirs(modRoots) } // resolveLocalPackage resolves a filesystem path to a package path. @@ -485,49 +526,69 @@ func resolveLocalPackage(ctx context.Context, dir string, rs *Requirements) (str } } - if modRoot != "" && absDir == modRoot { - if absDir == cfg.GOROOTsrc { - return "", errPkgIsGorootSrc + for _, mod := range MainModules.Versions() { + modRoot := MainModules.ModRoot(mod) + if modRoot != "" && absDir == modRoot { + if absDir == cfg.GOROOTsrc { + return "", errPkgIsGorootSrc + } + return MainModules.PathPrefix(mod), nil } - return targetPrefix, nil } // Note: The checks for @ here are just to avoid misinterpreting // the module cache directories (formerly GOPATH/src/mod/foo@v1.5.2/bar). // It's not strictly necessary but helpful to keep the checks. - if modRoot != "" && strings.HasPrefix(absDir, modRoot+string(filepath.Separator)) && !strings.Contains(absDir[len(modRoot):], "@") { - suffix := filepath.ToSlash(absDir[len(modRoot):]) - if strings.HasPrefix(suffix, "/vendor/") { - if cfg.BuildMod != "vendor" { - return "", fmt.Errorf("without -mod=vendor, directory %s has no package path", absDir) + var pkgNotFoundErr error + pkgNotFoundLongestPrefix := "" + for _, mainModule := range MainModules.Versions() { + modRoot := MainModules.ModRoot(mainModule) + if modRoot != "" && strings.HasPrefix(absDir, modRoot+string(filepath.Separator)) && !strings.Contains(absDir[len(modRoot):], "@") { + suffix := filepath.ToSlash(absDir[len(modRoot):]) + if strings.HasPrefix(suffix, "/vendor/") { + if cfg.BuildMod != "vendor" { + return "", fmt.Errorf("without -mod=vendor, directory %s has no package path", absDir) + } + + readVendorList(mainModule) + pkg := strings.TrimPrefix(suffix, "/vendor/") + if _, ok := vendorPkgModule[pkg]; !ok { + return "", fmt.Errorf("directory %s is not a package listed in vendor/modules.txt", absDir) + } + return pkg, nil } - readVendorList() - pkg := strings.TrimPrefix(suffix, "/vendor/") - if _, ok := vendorPkgModule[pkg]; !ok { - return "", fmt.Errorf("directory %s is not a package listed in vendor/modules.txt", absDir) + mainModulePrefix := MainModules.PathPrefix(mainModule) + if mainModulePrefix == "" { + pkg := strings.TrimPrefix(suffix, "/") + if pkg == "builtin" { + // "builtin" is a pseudo-package with a real source file. + // It's not included in "std", so it shouldn't resolve from "." + // within module "std" either. + return "", errPkgIsBuiltin + } + return pkg, nil } - return pkg, nil - } - if targetPrefix == "" { - pkg := strings.TrimPrefix(suffix, "/") - if pkg == "builtin" { - // "builtin" is a pseudo-package with a real source file. - // It's not included in "std", so it shouldn't resolve from "." - // within module "std" either. - return "", errPkgIsBuiltin + pkg := mainModulePrefix + suffix + if _, ok, err := dirInModule(pkg, mainModulePrefix, modRoot, true); err != nil { + return "", err + } else if !ok { + // This main module could contain the directory but doesn't. Other main + // modules might contain the directory, so wait till we finish the loop + // to see if another main module contains directory. But if not, + // return an error. + if len(mainModulePrefix) > len(pkgNotFoundLongestPrefix) { + pkgNotFoundLongestPrefix = mainModulePrefix + pkgNotFoundErr = &PackageNotInModuleError{MainModules: []module.Version{mainModule}, Pattern: pkg} + } + continue } return pkg, nil } - - pkg := targetPrefix + suffix - if _, ok, err := dirInModule(pkg, targetPrefix, modRoot, true); err != nil { - return "", err - } else if !ok { - return "", &PackageNotInModuleError{Mod: Target, Pattern: pkg} - } - return pkg, nil + } + if pkgNotFoundErr != nil { + return "", pkgNotFoundErr } if sub := search.InDir(absDir, cfg.GOROOTsrc); sub != "" && sub != "." && !strings.Contains(sub, "@") { @@ -560,7 +621,7 @@ func pathInModuleCache(ctx context.Context, dir string, rs *Requirements) string if repl := Replacement(m); repl.Path != "" && repl.Version == "" { root = repl.Path if !filepath.IsAbs(root) { - root = filepath.Join(ModRoot(), root) + root = filepath.Join(replaceRelativeTo(), root) } } else if repl.Path != "" { root, err = modfetch.DownloadDir(repl) @@ -583,7 +644,7 @@ func pathInModuleCache(ctx context.Context, dir string, rs *Requirements) string return path.Join(m.Path, filepath.ToSlash(sub)), true } - if rs.depth == lazy { + if rs.pruning == pruned { for _, m := range rs.rootModules { if v, _ := rs.rootSelected(m.Path); v != m.Version { continue // m is a root, but we have a higher root for the same path. @@ -596,9 +657,9 @@ func pathInModuleCache(ctx context.Context, dir string, rs *Requirements) string } } - // None of the roots contained dir, or we're in eager mode and want to load - // the full module graph more aggressively. Either way, check the full graph - // to see if the directory is a non-root dependency. + // None of the roots contained dir, or the graph is unpruned (so we don't want + // to distinguish between roots and transitive dependencies). Either way, + // check the full graph to see if the directory is a non-root dependency. // // If the roots are not consistent with the full module graph, the selected // versions of root modules may differ from what we already checked above. @@ -645,14 +706,14 @@ func ImportFromFiles(ctx context.Context, gofiles []string) { return roots }, }) - commitRequirements(ctx, loaded.GoVersion, loaded.requirements) + requirements = loaded.requirements } // DirImportPath returns the effective import path for dir, -// provided it is within the main module, or else returns ".". -func DirImportPath(ctx context.Context, dir string) string { +// provided it is within a main module, or else returns ".". +func (mms *MainModuleSet) DirImportPath(ctx context.Context, dir string) (path string, m module.Version) { if !HasModRoot() { - return "." + return ".", module.Version{} } LoadModFile(ctx) // Sets targetPrefix. @@ -662,17 +723,32 @@ func DirImportPath(ctx context.Context, dir string) string { dir = filepath.Clean(dir) } - if dir == modRoot { - return targetPrefix - } - if strings.HasPrefix(dir, modRoot+string(filepath.Separator)) { - suffix := filepath.ToSlash(dir[len(modRoot):]) - if strings.HasPrefix(suffix, "/vendor/") { - return strings.TrimPrefix(suffix, "/vendor/") + var longestPrefix string + var longestPrefixPath string + var longestPrefixVersion module.Version + for _, v := range mms.Versions() { + modRoot := mms.ModRoot(v) + if dir == modRoot { + return mms.PathPrefix(v), v + } + if strings.HasPrefix(dir, modRoot+string(filepath.Separator)) { + pathPrefix := MainModules.PathPrefix(v) + if pathPrefix > longestPrefix { + longestPrefix = pathPrefix + longestPrefixVersion = v + suffix := filepath.ToSlash(dir[len(modRoot):]) + if strings.HasPrefix(suffix, "/vendor/") { + longestPrefixPath = strings.TrimPrefix(suffix, "/vendor/") + } + longestPrefixPath = mms.PathPrefix(v) + suffix + } } - return targetPrefix + suffix } - return "." + if len(longestPrefix) > 0 { + return longestPrefixPath, longestPrefixVersion + } + + return ".", module.Version{} } // ImportMap returns the actual package import path @@ -783,7 +859,7 @@ func (ld *loader) reset() { // errorf reports an error via either os.Stderr or base.Errorf, // according to whether ld.AllowErrors is set. -func (ld *loader) errorf(format string, args ...interface{}) { +func (ld *loader) errorf(format string, args ...any) { if ld.AllowErrors { fmt.Fprintf(os.Stderr, format, args...) } else { @@ -807,6 +883,7 @@ type loadPkg struct { imports []*loadPkg // packages imported by this one testImports []string // test-only imports, saved for use by pkg.test. inStd bool + altMods []module.Version // modules that could have contained the package but did not // Populated by (*loader).pkgTest: testOnce sync.Once @@ -894,10 +971,7 @@ func (pkg *loadPkg) fromExternalModule() bool { if pkg.mod.Path == "" { return false // loaded from the standard library, not a module } - if pkg.mod.Path == Target.Path { - return false // loaded from the main module. - } - return true + return !MainModules.Contains(pkg.mod.Path) } var errMissing = errors.New("cannot find package") @@ -915,10 +989,10 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { } if ld.GoVersion == "" { - ld.GoVersion = modFileGoVersion() + ld.GoVersion = MainModules.GoVersion() if ld.Tidy && semver.Compare("v"+ld.GoVersion, "v"+LatestGoVersion()) > 0 { - ld.errorf("go mod tidy: go.mod file indicates go %s, but maximum supported version is %s\n", ld.GoVersion, LatestGoVersion()) + ld.errorf("go: go.mod file indicates go %s, but maximum version supported by tidy is %s\n", ld.GoVersion, LatestGoVersion()) base.ExitIfErrors() } } @@ -935,18 +1009,30 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { } if semver.Compare("v"+ld.GoVersion, narrowAllVersionV) < 0 && !ld.UseVendorAll { - // The module's go version explicitly predates the change in "all" for lazy - // loading, so continue to use the older interpretation. + // The module's go version explicitly predates the change in "all" for graph + // pruning, so continue to use the older interpretation. ld.allClosesOverTests = true } var err error - ld.requirements, err = convertDepth(ctx, ld.requirements, modDepthFromGoVersion(ld.GoVersion)) + desiredPruning := pruningForGoVersion(ld.GoVersion) + if ld.requirements.pruning == workspace { + desiredPruning = workspace + } + ld.requirements, err = convertPruning(ctx, ld.requirements, desiredPruning) if err != nil { ld.errorf("go: %v\n", err) } - if ld.requirements.depth == eager { + if ld.requirements.pruning == unpruned { + // If the module graph does not support pruning, we assume that we will need + // the full module graph in order to load package dependencies. + // + // This might not be strictly necessary, but it matches the historical + // behavior of the 'go' command and keeps the go.mod file more consistent in + // case of erroneous hand-edits — which are less likely to be detected by + // spot-checks in modules that do not maintain the expanded go.mod + // requirements needed for graph pruning. var err error ld.requirements, _, err = expandGraph(ctx, ld.requirements) if err != nil { @@ -963,7 +1049,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { // build list we're using. rootPkgs := ld.listRoots(ld.requirements) - if ld.requirements.depth == lazy && cfg.BuildMod == "mod" { + if ld.requirements.pruning == pruned && cfg.BuildMod == "mod" { // Before we start loading transitive imports of packages, locate all of // the root packages and promote their containing modules to root modules // dependencies. If their go.mod files are tidy (the common case) and the @@ -1005,7 +1091,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { break } if changed { - // Don't resolve missing imports until the module graph have stabilized. + // Don't resolve missing imports until the module graph has stabilized. // If the roots are still changing, they may turn out to specify a // requirement on the missing package(s), and we would rather use a // version specified by a new root than add a new dependency on an @@ -1074,15 +1160,15 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { ld.errorf("go: %v\n", err) } - if ld.requirements.depth == lazy { + if ld.requirements.pruning == pruned { // We continuously add tidy roots to ld.requirements during loading, so at // this point the tidy roots should be a subset of the roots of // ld.requirements, ensuring that no new dependencies are brought inside - // the lazy-loading horizon. + // the graph-pruning horizon. // If that is not the case, there is a bug in the loading loop above. for _, m := range rs.rootModules { if v, ok := ld.requirements.rootSelected(m.Path); !ok || v != m.Version { - ld.errorf("go mod tidy: internal error: a requirement on %v is needed but was not added during package loading\n", m) + ld.errorf("go: internal error: a requirement on %v is needed but was not added during package loading\n", m) base.ExitIfErrors() } } @@ -1124,8 +1210,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { } // updateRequirements ensures that ld.requirements is consistent with the -// information gained from ld.pkgs and includes the modules in add as roots at -// at least the given versions. +// information gained from ld.pkgs. // // In particular: // @@ -1168,7 +1253,7 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err } for _, pkg := range ld.pkgs { - if pkg.mod != Target { + if pkg.mod.Version != "" || !MainModules.Contains(pkg.mod.Path) { continue } for _, dep := range pkg.imports { @@ -1176,6 +1261,24 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err continue } + if inWorkspaceMode() { + // In workspace mode / workspace pruning mode, the roots are the main modules + // rather than the main module's direct dependencies. The check below on the selected + // roots does not apply. + if mg, err := rs.Graph(ctx); err != nil { + return false, err + } else if _, ok := mg.RequiredBy(dep.mod); !ok { + // dep.mod is not an explicit dependency, but needs to be. + // See comment on error returned below. + pkg.err = &DirectImportFromImplicitDependencyError{ + ImporterPath: pkg.path, + ImportedPath: dep.path, + Module: dep.mod, + } + } + continue + } + if pkg.err == nil && cfg.BuildMod != "mod" { if v, ok := rs.rootSelected(dep.mod.Path); !ok || v != dep.mod.Version { // dep.mod is not an explicit dependency, but needs to be. @@ -1206,14 +1309,14 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err var addRoots []module.Version if ld.Tidy { - // When we are tidying a lazy module, we may need to add roots to preserve - // the versions of indirect, test-only dependencies that are upgraded - // above or otherwise missing from the go.mod files of direct - // dependencies. (For example, the direct dependency might be a very + // When we are tidying a module with a pruned dependency graph, we may need + // to add roots to preserve the versions of indirect, test-only dependencies + // that are upgraded above or otherwise missing from the go.mod files of + // direct dependencies. (For example, the direct dependency might be a very // stable codebase that predates modules and thus lacks a go.mod file, or - // the author of the direct dependency may have forgotten to commit a - // change to the go.mod file, or may have made an erroneous hand-edit that - // causes it to be untidy.) + // the author of the direct dependency may have forgotten to commit a change + // to the go.mod file, or may have made an erroneous hand-edit that causes + // it to be untidy.) // // Promoting an indirect dependency to a root adds the next layer of its // dependencies to the module graph, which may increase the selected @@ -1283,7 +1386,7 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err // // In some sense, we can think of this as ‘upgraded the module providing // pkg.path from "none" to a version higher than "none"’. - if _, _, err = importFromModules(ctx, pkg.path, rs, nil); err == nil { + if _, _, _, err = importFromModules(ctx, pkg.path, rs, nil); err == nil { changed = true break } @@ -1327,6 +1430,15 @@ func (ld *loader) resolveMissingImports(ctx context.Context) (modAddedBy map[mod var err error mod, err = queryImport(ctx, pkg.path, ld.requirements) if err != nil { + var ime *ImportMissingError + if errors.As(err, &ime) { + for curstack := pkg.stack; curstack != nil; curstack = curstack.stack { + if MainModules.Contains(curstack.mod.Path) { + ime.ImportingMainModule = curstack.mod + break + } + } + } // pkg.err was already non-nil, so we can reasonably attribute the error // for pkg to either the original error or the one returned by // queryImport. The existing error indicates only that we couldn't find @@ -1380,7 +1492,7 @@ func (ld *loader) pkg(ctx context.Context, path string, flags loadPkgFlags) *loa panic("internal error: (*loader).pkg called with pkgImportsLoaded flag set") } - pkg := ld.pkgCache.Do(path, func() interface{} { + pkg := ld.pkgCache.Do(path, func() any { pkg := &loadPkg{ path: path, } @@ -1425,7 +1537,7 @@ func (ld *loader) applyPkgFlags(ctx context.Context, pkg *loadPkg, flags loadPkg // so it's ok if we call it more than is strictly necessary. wantTest := false switch { - case ld.allPatternIsRoot && pkg.mod == Target: + case ld.allPatternIsRoot && MainModules.Contains(pkg.mod.Path): // We are loading the "all" pattern, which includes packages imported by // tests in the main module. This package is in the main module, so we // need to identify the imports of its test even if LoadTests is not set. @@ -1446,7 +1558,7 @@ func (ld *loader) applyPkgFlags(ctx context.Context, pkg *loadPkg, flags loadPkg if wantTest { var testFlags loadPkgFlags - if pkg.mod == Target || (ld.allClosesOverTests && new.has(pkgInAll)) { + if MainModules.Contains(pkg.mod.Path) || (ld.allClosesOverTests && new.has(pkgInAll)) { // Tests of packages in the main module are in "all", in the sense that // they cause the packages they import to also be in "all". So are tests // of packages in "all" if "all" closes over test dependencies. @@ -1485,7 +1597,7 @@ func (ld *loader) preloadRootModules(ctx context.Context, rootPkgs []string) (ch // If the main module is tidy and the package is in "all" — or if we're // lucky — we can identify all of its imports without actually loading the // full module graph. - m, _, err := importFromModules(ctx, path, ld.requirements, nil) + m, _, _, err := importFromModules(ctx, path, ld.requirements, nil) if err != nil { var missing *ImportMissingError if errors.As(err, &missing) && ld.ResolveMissingImports { @@ -1511,7 +1623,8 @@ func (ld *loader) preloadRootModules(ctx context.Context, rootPkgs []string) (ch // module to a root to ensure that any other packages this package // imports are resolved from correct dependency versions. // - // (This is the “argument invariant” from the lazy loading design.) + // (This is the “argument invariant” from + // https://golang.org/design/36460-lazy-module-loading.) need := <-needc need[m] = true needc <- need @@ -1573,7 +1686,7 @@ func (ld *loader) load(ctx context.Context, pkg *loadPkg) { } var mg *ModuleGraph - if ld.requirements.depth == eager { + if ld.requirements.pruning == unpruned { var err error mg, err = ld.requirements.Graph(ctx) if err != nil { @@ -1589,11 +1702,11 @@ func (ld *loader) load(ctx context.Context, pkg *loadPkg) { } } - pkg.mod, pkg.dir, pkg.err = importFromModules(ctx, pkg.path, ld.requirements, mg) + pkg.mod, pkg.dir, pkg.altMods, pkg.err = importFromModules(ctx, pkg.path, ld.requirements, mg) if pkg.dir == "" { return } - if pkg.mod == Target { + if MainModules.Contains(pkg.mod.Path) { // Go ahead and mark pkg as in "all". This provides the invariant that a // package that is *only* imported by other packages in "all" is always // marked as such before loading its imports. @@ -1698,13 +1811,14 @@ func (ld *loader) stdVendor(parentPath, path string) string { } if str.HasPathPrefix(parentPath, "cmd") { - if !ld.VendorModulesInGOROOTSrc || Target.Path != "cmd" { + if !ld.VendorModulesInGOROOTSrc || !MainModules.Contains("cmd") { vendorPath := pathpkg.Join("cmd", "vendor", path) + if _, err := os.Stat(filepath.Join(cfg.GOROOTsrc, filepath.FromSlash(vendorPath))); err == nil { return vendorPath } } - } else if !ld.VendorModulesInGOROOTSrc || Target.Path != "std" || str.HasPathPrefix(parentPath, "vendor") { + } else if !ld.VendorModulesInGOROOTSrc || !MainModules.Contains("std") || str.HasPathPrefix(parentPath, "vendor") { // If we are outside of the 'std' module, resolve imports from within 'std' // to the vendor directory. // @@ -1781,7 +1895,7 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements) fmt.Fprintln(os.Stderr) goFlag := "" - if ld.GoVersion != modFileGoVersion() { + if ld.GoVersion != MainModules.GoVersion() { goFlag = " -go=" + ld.GoVersion } @@ -1813,7 +1927,7 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements) mg, err := rs.Graph(ctx) if err != nil { - ld.errorf("go mod tidy: error loading go %s module graph: %v\n", ld.TidyCompatibleVersion, err) + ld.errorf("go: error loading go %s module graph: %v\n", ld.TidyCompatibleVersion, err) suggestFixes() return } @@ -1847,7 +1961,7 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements) pkg := pkg ld.work.Add(func() { - mod, _, err := importFromModules(ctx, pkg.path, rs, mg) + mod, _, _, err := importFromModules(ctx, pkg.path, rs, mg) if mod != pkg.mod { mismatches := <-mismatchMu mismatches[pkg] = mismatch{mod: mod, err: err} @@ -1900,9 +2014,10 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements) case mismatch.err != nil: // pkg resolved successfully, but errors out using the requirements in rs. // - // This could occur because the import is provided by a single lazy root - // (and is thus unambiguous in lazy mode) and also one or more - // transitive dependencies (and is ambiguous in eager mode). + // This could occur because the import is provided by a single root (and + // is thus unambiguous in a main module with a pruned module graph) and + // also one or more transitive dependencies (and is ambiguous with an + // unpruned graph). // // It could also occur because some transitive dependency upgrades the // module that previously provided the package to a version that no @@ -1940,18 +2055,18 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements) } case pkg.err != nil: - // pkg had an error in lazy mode (presumably suppressed with the -e flag), - // but not in eager mode. + // pkg had an error in with a pruned module graph (presumably suppressed + // with the -e flag), but the error went away using an unpruned graph. // - // This is possible, if, say, the import is unresolved in lazy mode + // This is possible, if, say, the import is unresolved in the pruned graph // (because the "latest" version of each candidate module either is - // unavailable or does not contain the package), but is resolved in - // eager mode due to a newer-than-latest dependency that is normally - // runed out of the module graph. + // unavailable or does not contain the package), but is resolved in the + // unpruned graph due to a newer-than-latest dependency that is normally + // pruned out. // // This could also occur if the source code for the module providing the - // package in lazy mode has a checksum error, but eager mode upgrades - // that module to a version with a correct checksum. + // package in the pruned graph has a checksum error, but the unpruned + // graph upgrades that module to a version with a correct checksum. // // pkg.err should have already been logged elsewhere — along with a // stack trace — so log only the import path and non-error info here. diff --git a/libgo/go/cmd/go/internal/modload/modfile.go b/libgo/go/cmd/go/internal/modload/modfile.go index 03e02e73b63..627cf1dbc06 100644 --- a/libgo/go/cmd/go/internal/modload/modfile.go +++ b/libgo/go/cmd/go/internal/modload/modfile.go @@ -33,10 +33,13 @@ const ( // tests outside of the main module. narrowAllVersionV = "v1.16" - // lazyLoadingVersionV is the Go version (plus leading "v") at which a + // ExplicitIndirectVersionV is the Go version (plus leading "v") at which a // module's go.mod file is expected to list explicit requirements on every // module that provides any package transitively imported by that module. - lazyLoadingVersionV = "v1.17" + // + // Other indirect dependencies of such a module can be safely pruned out of + // the module graph; see https://golang.org/ref/mod#graph-pruning. + ExplicitIndirectVersionV = "v1.17" // separateIndirectVersionV is the Go version (plus leading "v") at which // "// indirect" dependencies are added in a block separate from the direct @@ -44,23 +47,37 @@ const ( separateIndirectVersionV = "v1.17" ) -const ( - // go117EnableLazyLoading toggles whether lazy-loading code paths should be - // active. It will be removed once the lazy loading implementation is stable - // and well-tested. - go117EnableLazyLoading = true - - // go1117LazyTODO is a constant that exists only until lazy loading is - // implemented. Its use indicates a condition that will need to change if the - // main module is lazy. - go117LazyTODO = false -) +// ReadModFile reads and parses the mod file at gomod. ReadModFile properly applies the +// overlay, locks the file while reading, and applies fix, if applicable. +func ReadModFile(gomod string, fix modfile.VersionFixer) (data []byte, f *modfile.File, err error) { + if gomodActual, ok := fsys.OverlayPath(gomod); ok { + // Don't lock go.mod if it's part of the overlay. + // On Plan 9, locking requires chmod, and we don't want to modify any file + // in the overlay. See #44700. + data, err = os.ReadFile(gomodActual) + } else { + data, err = lockedfile.Read(gomodActual) + } + if err != nil { + return nil, nil, err + } -var modFile *modfile.File + f, err = modfile.Parse(gomod, data, fix) + if err != nil { + // Errors returned by modfile.Parse begin with file:line. + return nil, nil, fmt.Errorf("errors parsing go.mod:\n%s\n", err) + } + if f.Module == nil { + // No module declaration. Must add module path. + return nil, nil, errors.New("no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod") + } + + return data, f, err +} // modFileGoVersion returns the (non-empty) Go version at which the requirements -// in modFile are intepreted, or the latest Go version if modFile is nil. -func modFileGoVersion() string { +// in modFile are interpreted, or the latest Go version if modFile is nil. +func modFileGoVersion(modFile *modfile.File) string { if modFile == nil { return LatestGoVersion() } @@ -71,9 +88,9 @@ func modFileGoVersion() string { // has been erroneously hand-edited. // // The semantics of the go.mod file are more-or-less the same from Go 1.11 - // through Go 1.16, changing at 1.17 for lazy loading. So even though a - // go.mod file without a 'go' directive is theoretically a Go 1.11 file, - // scripts may assume that it ends up as a Go 1.16 module. + // through Go 1.16, changing at 1.17 to support module graph pruning. + // So even though a go.mod file without a 'go' directive is theoretically a + // Go 1.11 file, scripts may assume that it ends up as a Go 1.16 module. return "1.16" } return modFile.Go.Version @@ -82,39 +99,37 @@ func modFileGoVersion() string { // A modFileIndex is an index of data corresponding to a modFile // at a specific point in time. type modFileIndex struct { - data []byte - dataNeedsFix bool // true if fixVersion applied a change while parsing data - module module.Version - goVersionV string // GoVersion with "v" prefix - require map[module.Version]requireMeta - replace map[module.Version]module.Version - highestReplaced map[string]string // highest replaced version of each module path; empty string for wildcard-only replacements - exclude map[module.Version]bool + data []byte + dataNeedsFix bool // true if fixVersion applied a change while parsing data + module module.Version + goVersionV string // GoVersion with "v" prefix + require map[module.Version]requireMeta + replace map[module.Version]module.Version + exclude map[module.Version]bool } -// index is the index of the go.mod file as of when it was last read or written. -var index *modFileIndex - type requireMeta struct { indirect bool } -// A modDepth indicates which dependencies should be loaded for a go.mod file. -type modDepth uint8 +// A modPruning indicates whether transitive dependencies of Go 1.17 dependencies +// are pruned out of the module subgraph rooted at a given module. +// (See https://golang.org/ref/mod#graph-pruning.) +type modPruning uint8 const ( - lazy modDepth = iota // load dependencies only as needed - eager // load all transitive dependencies eagerly + pruned modPruning = iota // transitive dependencies of modules at go 1.17 and higher are pruned out + unpruned // no transitive dependencies are pruned out + workspace // pruned to the union of modules in the workspace ) -func modDepthFromGoVersion(goVersion string) modDepth { - if !go117EnableLazyLoading { - return eager - } - if semver.Compare("v"+goVersion, lazyLoadingVersionV) < 0 { - return eager +func pruningForGoVersion(goVersion string) modPruning { + if semver.Compare("v"+goVersion, ExplicitIndirectVersionV) < 0 { + // The go.mod file does not duplicate relevant information about transitive + // dependencies, so they cannot be pruned out. + return unpruned } - return lazy + return pruned } // CheckAllowed returns an error equivalent to ErrDisallowed if m is excluded by @@ -137,8 +152,10 @@ var ErrDisallowed = errors.New("disallowed module version") // CheckExclusions returns an error equivalent to ErrDisallowed if module m is // excluded by the main module's go.mod file. func CheckExclusions(ctx context.Context, m module.Version) error { - if index != nil && index.exclude[m] { - return module.VersionError(m, errExcluded) + for _, mainModule := range MainModules.Versions() { + if index := MainModules.Index(mainModule); index != nil && index.exclude[m] { + return module.VersionError(m, errExcluded) + } } return nil } @@ -306,23 +323,77 @@ func CheckDeprecation(ctx context.Context, m module.Version) (deprecation string return summary.deprecated, nil } -// Replacement returns the replacement for mod, if any, from go.mod. -// If there is no replacement for mod, Replacement returns -// a module.Version with Path == "". +func replacement(mod module.Version, replace map[module.Version]module.Version) (fromVersion string, to module.Version, ok bool) { + if r, ok := replace[mod]; ok { + return mod.Version, r, true + } + if r, ok := replace[module.Version{Path: mod.Path}]; ok { + return "", r, true + } + return "", module.Version{}, false +} + +// Replacement returns the replacement for mod, if any. If the path in the +// module.Version is relative it's relative to the single main module outside +// workspace mode, or the workspace's directory in workspace mode. func Replacement(mod module.Version) module.Version { - if index != nil { - if r, ok := index.replace[mod]; ok { - return r - } - if r, ok := index.replace[module.Version{Path: mod.Path}]; ok { - return r + foundFrom, found, foundModRoot := "", module.Version{}, "" + if MainModules == nil { + return module.Version{} + } else if MainModules.Contains(mod.Path) && mod.Version == "" { + // Don't replace the workspace version of the main module. + return module.Version{} + } + if _, r, ok := replacement(mod, MainModules.WorkFileReplaceMap()); ok { + return r + } + for _, v := range MainModules.Versions() { + if index := MainModules.Index(v); index != nil { + if from, r, ok := replacement(mod, index.replace); ok { + modRoot := MainModules.ModRoot(v) + if foundModRoot != "" && foundFrom != from && found != r { + base.Errorf("conflicting replacements found for %v in workspace modules defined by %v and %v", + mod, modFilePath(foundModRoot), modFilePath(modRoot)) + return canonicalizeReplacePath(found, foundModRoot) + } + found, foundModRoot = r, modRoot + } } } - return module.Version{} + return canonicalizeReplacePath(found, foundModRoot) +} + +func replaceRelativeTo() string { + if workFilePath := WorkFilePath(); workFilePath != "" { + return filepath.Dir(workFilePath) + } + return MainModules.ModRoot(MainModules.mustGetSingleMainModule()) +} + +// canonicalizeReplacePath ensures that relative, on-disk, replaced module paths +// are relative to the workspace directory (in workspace mode) or to the module's +// directory (in module mode, as they already are). +func canonicalizeReplacePath(r module.Version, modRoot string) module.Version { + if filepath.IsAbs(r.Path) || r.Version != "" { + return r + } + workFilePath := WorkFilePath() + if workFilePath == "" { + return r + } + abs := filepath.Join(modRoot, r.Path) + if rel, err := filepath.Rel(filepath.Dir(workFilePath), abs); err == nil { + return module.Version{Path: rel, Version: r.Version} + } + // We couldn't make the version's path relative to the workspace's path, + // so just return the absolute path. It's the best we can do. + return module.Version{Path: abs, Version: r.Version} } // resolveReplacement returns the module actually used to load the source code // for m: either m itself, or the replacement for m (iff m is replaced). +// It also returns the modroot of the module providing the replacement if +// one was found. func resolveReplacement(m module.Version) module.Version { if r := Replacement(m); r.Path != "" { return r @@ -330,10 +401,21 @@ func resolveReplacement(m module.Version) module.Version { return m } +func toReplaceMap(replacements []*modfile.Replace) map[module.Version]module.Version { + replaceMap := make(map[module.Version]module.Version, len(replacements)) + for _, r := range replacements { + if prev, dup := replaceMap[r.Old]; dup && prev != r.New { + base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New) + } + replaceMap[r.Old] = r.New + } + return replaceMap +} + // indexModFile rebuilds the index of modFile. // If modFile has been changed since it was first read, // modFile.Cleanup must be called before indexModFile. -func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileIndex { +func indexModFile(data []byte, modFile *modfile.File, mod module.Version, needsFix bool) *modFileIndex { i := new(modFileIndex) i.data = data i.dataNeedsFix = needsFix @@ -345,12 +427,12 @@ func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileInd i.goVersionV = "" if modFile.Go == nil { - rawGoVersion.Store(Target, "") + rawGoVersion.Store(mod, "") } else { // We're going to use the semver package to compare Go versions, so go ahead // and add the "v" prefix it expects once instead of every time. i.goVersionV = "v" + modFile.Go.Version - rawGoVersion.Store(Target, modFile.Go.Version) + rawGoVersion.Store(mod, modFile.Go.Version) } i.require = make(map[module.Version]requireMeta, len(modFile.Require)) @@ -358,21 +440,7 @@ func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileInd i.require[r.Mod] = requireMeta{indirect: r.Indirect} } - i.replace = make(map[module.Version]module.Version, len(modFile.Replace)) - for _, r := range modFile.Replace { - if prev, dup := i.replace[r.Old]; dup && prev != r.New { - base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New) - } - i.replace[r.Old] = r.New - } - - i.highestReplaced = make(map[string]string) - for _, r := range modFile.Replace { - v, ok := i.highestReplaced[r.Old.Path] - if !ok || semver.Compare(r.Old.Version, v) > 0 { - i.highestReplaced[r.Old.Path] = r.Old.Version - } - } + i.replace = toReplaceMap(modFile.Replace) i.exclude = make(map[module.Version]bool, len(modFile.Exclude)) for _, x := range modFile.Exclude { @@ -465,7 +533,7 @@ var rawGoVersion sync.Map // map[module.Version]string type modFileSummary struct { module module.Version goVersion string - depth modDepth + pruning modPruning require []module.Version retract []retraction deprecated string @@ -490,8 +558,8 @@ type retraction struct { // // The caller must not modify the returned summary. func goModSummary(m module.Version) (*modFileSummary, error) { - if m == Target { - panic("internal error: goModSummary called on the Target module") + if m.Version == "" && !inWorkspaceMode() && MainModules.Contains(m.Path) { + panic("internal error: goModSummary called on a main module") } if cfg.BuildMod == "vendor" { @@ -506,7 +574,7 @@ func goModSummary(m module.Version) (*modFileSummary, error) { // For every module other than the target, // return the full list of modules from modules.txt. - readVendorList() + readVendorList(MainModules.mustGetSingleMainModule()) // We don't know what versions the vendored module actually relies on, // so assume that it requires everything. @@ -515,7 +583,7 @@ func goModSummary(m module.Version) (*modFileSummary, error) { } actual := resolveReplacement(m) - if HasModRoot() && cfg.BuildMod == "readonly" && actual.Version != "" { + if HasModRoot() && cfg.BuildMod == "readonly" && !inWorkspaceMode() && actual.Version != "" { key := module.Version{Path: actual.Path, Version: actual.Version + "/go.mod"} if !modfetch.HaveSum(key) { suggestion := fmt.Sprintf("; to add it:\n\tgo mod download %s", m.Path) @@ -553,27 +621,29 @@ func goModSummary(m module.Version) (*modFileSummary, error) { } } - if index != nil && len(index.exclude) > 0 { - // Drop any requirements on excluded versions. - // Don't modify the cached summary though, since we might need the raw - // summary separately. - haveExcludedReqs := false - for _, r := range summary.require { - if index.exclude[r] { - haveExcludedReqs = true - break - } - } - if haveExcludedReqs { - s := new(modFileSummary) - *s = *summary - s.require = make([]module.Version, 0, len(summary.require)) + for _, mainModule := range MainModules.Versions() { + if index := MainModules.Index(mainModule); index != nil && len(index.exclude) > 0 { + // Drop any requirements on excluded versions. + // Don't modify the cached summary though, since we might need the raw + // summary separately. + haveExcludedReqs := false for _, r := range summary.require { - if !index.exclude[r] { - s.require = append(s.require, r) + if index.exclude[r] { + haveExcludedReqs = true + break } } - summary = s + if haveExcludedReqs { + s := new(modFileSummary) + *s = *summary + s.require = make([]module.Version, 0, len(summary.require)) + for _, r := range summary.require { + if !index.exclude[r] { + s.require = append(s.require, r) + } + } + summary = s + } } } return summary, nil @@ -584,16 +654,20 @@ func goModSummary(m module.Version) (*modFileSummary, error) { // its dependencies. // // rawGoModSummary cannot be used on the Target module. + func rawGoModSummary(m module.Version) (*modFileSummary, error) { - if m == Target { + if m.Path == "" && MainModules.Contains(m.Path) { panic("internal error: rawGoModSummary called on the Target module") } + type key struct { + m module.Version + } type cached struct { summary *modFileSummary err error } - c := rawGoModSummaryCache.Do(m, func() interface{} { + c := rawGoModSummaryCache.Do(key{m}, func() any { summary := new(modFileSummary) name, data, err := rawGoModData(m) if err != nil { @@ -610,9 +684,9 @@ func rawGoModSummary(m module.Version) (*modFileSummary, error) { if f.Go != nil && f.Go.Version != "" { rawGoVersion.LoadOrStore(m, f.Go.Version) summary.goVersion = f.Go.Version - summary.depth = modDepthFromGoVersion(f.Go.Version) + summary.pruning = pruningForGoVersion(f.Go.Version) } else { - summary.depth = eager + summary.pruning = unpruned } if len(f.Require) > 0 { summary.require = make([]module.Version, 0, len(f.Require)) @@ -648,9 +722,14 @@ var rawGoModSummaryCache par.Cache // module.Version → rawGoModSummary result func rawGoModData(m module.Version) (name string, data []byte, err error) { if m.Version == "" { // m is a replacement module with only a file path. + dir := m.Path if !filepath.IsAbs(dir) { - dir = filepath.Join(ModRoot(), dir) + if inWorkspaceMode() && MainModules.Contains(m.Path) { + dir = MainModules.ModRoot(m) + } else { + dir = filepath.Join(replaceRelativeTo(), dir) + } } name = filepath.Join(dir, "go.mod") if gomodActual, ok := fsys.OverlayPath(name); ok { @@ -690,7 +769,7 @@ func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (la latest module.Version err error } - e := latestVersionIgnoringRetractionsCache.Do(path, func() interface{} { + e := latestVersionIgnoringRetractionsCache.Do(path, func() any { ctx, span := trace.StartSpan(ctx, "queryLatestVersionIgnoringRetractions "+path) defer span.Done() @@ -718,3 +797,15 @@ func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (la } var latestVersionIgnoringRetractionsCache par.Cache // path → queryLatestVersionIgnoringRetractions result + +// ToDirectoryPath adds a prefix if necessary so that path in unambiguously +// an absolute path or a relative path starting with a '.' or '..' +// path component. +func ToDirectoryPath(path string) string { + if modfile.IsDirectoryPath(path) { + return path + } + // The path is not a relative path or an absolute path, so make it relative + // to the current directory. + return "./" + filepath.ToSlash(filepath.Clean(path)) +} diff --git a/libgo/go/cmd/go/internal/modload/mvs.go b/libgo/go/cmd/go/internal/modload/mvs.go index 87619b4ace6..588bcf4bdc2 100644 --- a/libgo/go/cmd/go/internal/modload/mvs.go +++ b/libgo/go/cmd/go/internal/modload/mvs.go @@ -42,7 +42,7 @@ type mvsReqs struct { } func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) { - if mod == Target { + if mod.Version == "" && MainModules.Contains(mod.Path) { // Use the build list as it existed when r was constructed, not the current // global build list. return r.roots, nil @@ -108,12 +108,12 @@ func versions(ctx context.Context, path string, allowed AllowedFunc) ([]string, // previousVersion returns the tagged version of m.Path immediately prior to // m.Version, or version "none" if no prior version is tagged. // -// Since the version of Target is not found in the version list, +// Since the version of a main module is not found in the version list, // it has no previous version. func previousVersion(m module.Version) (module.Version, error) { // TODO(golang.org/issue/38714): thread tracing context through MVS. - if m == Target { + if m.Version == "" && MainModules.Contains(m.Path) { return module.Version{Path: m.Path, Version: "none"}, nil } diff --git a/libgo/go/cmd/go/internal/modload/query.go b/libgo/go/cmd/go/internal/modload/query.go index e737ca90fcd..33808ea1097 100644 --- a/libgo/go/cmd/go/internal/modload/query.go +++ b/libgo/go/cmd/go/internal/modload/query.go @@ -110,11 +110,12 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed allowed = func(context.Context, module.Version) error { return nil } } - if path == Target.Path && (query == "upgrade" || query == "patch") { - if err := allowed(ctx, Target); err != nil { + if MainModules.Contains(path) && (query == "upgrade" || query == "patch") { + m := module.Version{Path: path} + if err := allowed(ctx, m); err != nil { return nil, fmt.Errorf("internal error: main module version is not allowed: %w", err) } - return &modfetch.RevInfo{Version: Target.Version}, nil + return &modfetch.RevInfo{Version: m.Version}, nil } if path == "std" || path == "cmd" { @@ -512,9 +513,10 @@ func QueryPackages(ctx context.Context, pattern, query string, current func(stri pkgMods, modOnly, err := QueryPattern(ctx, pattern, query, current, allowed) if len(pkgMods) == 0 && err == nil { + replacement := Replacement(modOnly.Mod) return nil, &PackageNotInModuleError{ Mod: modOnly.Mod, - Replacement: Replacement(modOnly.Mod), + Replacement: replacement, Query: query, Pattern: pattern, } @@ -551,7 +553,7 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin return m.Errs[0] } - var match func(mod module.Version, root string, isLocal bool) *search.Match + var match func(mod module.Version, roots []string, isLocal bool) *search.Match matchPattern := search.MatchPattern(pattern) if i := strings.Index(pattern, "..."); i >= 0 { @@ -559,30 +561,32 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin if base == "." { return nil, nil, &WildcardInFirstElementError{Pattern: pattern, Query: query} } - match = func(mod module.Version, root string, isLocal bool) *search.Match { + match = func(mod module.Version, roots []string, isLocal bool) *search.Match { m := search.NewMatch(pattern) matchPackages(ctx, m, imports.AnyTags(), omitStd, []module.Version{mod}) return m } } else { - match = func(mod module.Version, root string, isLocal bool) *search.Match { + match = func(mod module.Version, roots []string, isLocal bool) *search.Match { m := search.NewMatch(pattern) prefix := mod.Path - if mod == Target { - prefix = targetPrefix + if MainModules.Contains(mod.Path) { + prefix = MainModules.PathPrefix(module.Version{Path: mod.Path}) } - if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil { - m.AddError(err) - } else if ok { - m.Pkgs = []string{pattern} + for _, root := range roots { + if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil { + m.AddError(err) + } else if ok { + m.Pkgs = []string{pattern} + } } return m } } - var queryMatchesMainModule bool - if HasModRoot() { - m := match(Target, modRoot, true) + var mainModuleMatches []module.Version + for _, mainModule := range MainModules.Versions() { + m := match(mainModule, modRoots, true) if len(m.Pkgs) > 0 { if query != "upgrade" && query != "patch" { return nil, nil, &QueryMatchesPackagesInMainModuleError{ @@ -591,12 +595,12 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin Packages: m.Pkgs, } } - if err := allowed(ctx, Target); err != nil { - return nil, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, Target.Path, err) + if err := allowed(ctx, mainModule); err != nil { + return nil, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, mainModule.Path, err) } return []QueryResult{{ - Mod: Target, - Rev: &modfetch.RevInfo{Version: Target.Version}, + Mod: mainModule, + Rev: &modfetch.RevInfo{Version: mainModule.Version}, Packages: m.Pkgs, }}, nil, nil } @@ -604,15 +608,17 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin return nil, nil, err } - if matchPattern(Target.Path) { - queryMatchesMainModule = true + var matchesMainModule bool + if matchPattern(mainModule.Path) { + mainModuleMatches = append(mainModuleMatches, mainModule) + matchesMainModule = true } - if (query == "upgrade" || query == "patch") && queryMatchesMainModule { - if err := allowed(ctx, Target); err == nil { + if (query == "upgrade" || query == "patch") && matchesMainModule { + if err := allowed(ctx, mainModule); err == nil { modOnly = &QueryResult{ - Mod: Target, - Rev: &modfetch.RevInfo{Version: Target.Version}, + Mod: mainModule, + Rev: &modfetch.RevInfo{Version: mainModule.Version}, } } } @@ -625,16 +631,17 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin if len(candidateModules) == 0 { if modOnly != nil { return nil, modOnly, nil - } else if queryMatchesMainModule { - return nil, nil, &QueryMatchesMainModuleError{ - Pattern: pattern, - Query: query, + } else if len(mainModuleMatches) != 0 { + return nil, nil, &QueryMatchesMainModulesError{ + MainModules: mainModuleMatches, + Pattern: pattern, + Query: query, } } else { return nil, nil, &PackageNotInModuleError{ - Mod: Target, - Query: query, - Pattern: pattern, + MainModules: mainModuleMatches, + Query: query, + Pattern: pattern, } } } @@ -656,15 +663,16 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin if err != nil { return r, err } - m := match(r.Mod, root, isLocal) + m := match(r.Mod, []string{root}, isLocal) r.Packages = m.Pkgs if len(r.Packages) == 0 && !matchPattern(path) { if err := firstError(m); err != nil { return r, err } + replacement := Replacement(r.Mod) return r, &PackageNotInModuleError{ Mod: r.Mod, - Replacement: Replacement(r.Mod), + Replacement: replacement, Query: query, Pattern: pattern, } @@ -684,8 +692,8 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin return err }) - if queryMatchesMainModule && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) { - return nil, nil, &QueryMatchesMainModuleError{ + if len(mainModuleMatches) > 0 && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) { + return nil, nil, &QueryMatchesMainModulesError{ Pattern: pattern, Query: query, } @@ -701,8 +709,13 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin func modulePrefixesExcludingTarget(path string) []string { prefixes := make([]string, 0, strings.Count(path, "/")+1) + mainModulePrefixes := make(map[string]bool) + for _, m := range MainModules.Versions() { + mainModulePrefixes[m.Path] = true + } + for { - if path != targetPrefix { + if !mainModulePrefixes[path] { if _, _, ok := module.SplitPathVersion(path); ok { prefixes = append(prefixes, path) } @@ -759,7 +772,7 @@ func queryPrefixModules(ctx context.Context, candidateModules []string, queryMod case *PackageNotInModuleError: // Given the option, prefer to attribute “package not in module” // to modules other than the main one. - if noPackage == nil || noPackage.Mod == Target { + if noPackage == nil || MainModules.Contains(noPackage.Mod.Path) { noPackage = rErr } case *NoMatchingVersionError: @@ -878,6 +891,7 @@ func (e *WildcardInFirstElementError) Error() string { // code for the versions it knows about, and thus did not have the opportunity // to return a non-400 status code to suppress fallback. type PackageNotInModuleError struct { + MainModules []module.Version Mod module.Version Replacement module.Version Query string @@ -885,11 +899,15 @@ type PackageNotInModuleError struct { } func (e *PackageNotInModuleError) Error() string { - if e.Mod == Target { + if len(e.MainModules) > 0 { + prefix := "workspace modules do" + if len(e.MainModules) == 1 { + prefix = fmt.Sprintf("main module (%s) does", e.MainModules[0]) + } if strings.Contains(e.Pattern, "...") { - return fmt.Sprintf("main module (%s) does not contain packages matching %s", Target.Path, e.Pattern) + return fmt.Sprintf("%s not contain packages matching %s", prefix, e.Pattern) } - return fmt.Sprintf("main module (%s) does not contain package %s", Target.Path, e.Pattern) + return fmt.Sprintf("%s not contain package %s", prefix, e.Pattern) } found := "" @@ -978,14 +996,13 @@ func lookupRepo(proxy, path string) (repo versionRepo, err error) { repo = emptyRepo{path: path, err: err} } - if index == nil { - return repo, err - } - if _, ok := index.highestReplaced[path]; !ok { + if MainModules == nil { return repo, err + } else if _, ok := MainModules.HighestReplaced()[path]; ok { + return &replacementRepo{repo: repo}, nil } - return &replacementRepo{repo: repo}, nil + return repo, err } // An emptyRepo is a versionRepo that contains no versions. @@ -1024,11 +1041,13 @@ func (rr *replacementRepo) Versions(prefix string) ([]string, error) { } versions := repoVersions - if index != nil && len(index.replace) > 0 { - path := rr.ModulePath() - for m, _ := range index.replace { - if m.Path == path && strings.HasPrefix(m.Version, prefix) && m.Version != "" && !module.IsPseudoVersion(m.Version) { - versions = append(versions, m.Version) + for _, mm := range MainModules.Versions() { + if index := MainModules.Index(mm); index != nil && len(index.replace) > 0 { + path := rr.ModulePath() + for m, _ := range index.replace { + if m.Path == path && strings.HasPrefix(m.Version, prefix) && m.Version != "" && !module.IsPseudoVersion(m.Version) { + versions = append(versions, m.Version) + } } } } @@ -1046,7 +1065,16 @@ func (rr *replacementRepo) Versions(prefix string) ([]string, error) { func (rr *replacementRepo) Stat(rev string) (*modfetch.RevInfo, error) { info, err := rr.repo.Stat(rev) - if err == nil || index == nil || len(index.replace) == 0 { + if err == nil { + return info, err + } + var hasReplacements bool + for _, v := range MainModules.Versions() { + if index := MainModules.Index(v); index != nil && len(index.replace) > 0 { + hasReplacements = true + } + } + if !hasReplacements { return info, err } @@ -1073,26 +1101,24 @@ func (rr *replacementRepo) Stat(rev string) (*modfetch.RevInfo, error) { func (rr *replacementRepo) Latest() (*modfetch.RevInfo, error) { info, err := rr.repo.Latest() + path := rr.ModulePath() - if index != nil { - path := rr.ModulePath() - if v, ok := index.highestReplaced[path]; ok { - if v == "" { - // The only replacement is a wildcard that doesn't specify a version, so - // synthesize a pseudo-version with an appropriate major version and a - // timestamp below any real timestamp. That way, if the main module is - // used from within some other module, the user will be able to upgrade - // the requirement to any real version they choose. - if _, pathMajor, ok := module.SplitPathVersion(path); ok && len(pathMajor) > 0 { - v = module.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000") - } else { - v = module.PseudoVersion("v0", "", time.Time{}, "000000000000") - } + if v, ok := MainModules.HighestReplaced()[path]; ok { + if v == "" { + // The only replacement is a wildcard that doesn't specify a version, so + // synthesize a pseudo-version with an appropriate major version and a + // timestamp below any real timestamp. That way, if the main module is + // used from within some other module, the user will be able to upgrade + // the requirement to any real version they choose. + if _, pathMajor, ok := module.SplitPathVersion(path); ok && len(pathMajor) > 0 { + v = module.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000") + } else { + v = module.PseudoVersion("v0", "", time.Time{}, "000000000000") } + } - if err != nil || semver.Compare(v, info.Version) > 0 { - return rr.replacementStat(v) - } + if err != nil || semver.Compare(v, info.Version) > 0 { + return rr.replacementStat(v) } } @@ -1108,20 +1134,46 @@ func (rr *replacementRepo) replacementStat(v string) (*modfetch.RevInfo, error) return rev, nil } -// A QueryMatchesMainModuleError indicates that a query requests +// A QueryMatchesMainModulesError indicates that a query requests // a version of the main module that cannot be satisfied. // (The main module's version cannot be changed.) -type QueryMatchesMainModuleError struct { - Pattern string - Query string +type QueryMatchesMainModulesError struct { + MainModules []module.Version + Pattern string + Query string } -func (e *QueryMatchesMainModuleError) Error() string { - if e.Pattern == Target.Path { +func (e *QueryMatchesMainModulesError) Error() string { + if MainModules.Contains(e.Pattern) { return fmt.Sprintf("can't request version %q of the main module (%s)", e.Query, e.Pattern) } - return fmt.Sprintf("can't request version %q of pattern %q that includes the main module (%s)", e.Query, e.Pattern, Target.Path) + plural := "" + mainModulePaths := make([]string, len(e.MainModules)) + for i := range e.MainModules { + mainModulePaths[i] = e.MainModules[i].Path + } + if len(e.MainModules) > 1 { + plural = "s" + } + return fmt.Sprintf("can't request version %q of pattern %q that includes the main module%s (%s)", e.Query, e.Pattern, plural, strings.Join(mainModulePaths, ", ")) +} + +// A QueryUpgradesAllError indicates that a query requests +// an upgrade on the all pattern. +// (The main module's version cannot be changed.) +type QueryUpgradesAllError struct { + MainModules []module.Version + Query string +} + +func (e *QueryUpgradesAllError) Error() string { + var plural string = "" + if len(e.MainModules) != 1 { + plural = "s" + } + + return fmt.Sprintf("can't request version %q of pattern \"all\" that includes the main module%s", e.Query, plural) } // A QueryMatchesPackagesInMainModuleError indicates that a query cannot be diff --git a/libgo/go/cmd/go/internal/modload/search.go b/libgo/go/cmd/go/internal/modload/search.go index 0c2e2660e9f..87c5c55471d 100644 --- a/libgo/go/cmd/go/internal/modload/search.go +++ b/libgo/go/cmd/go/internal/modload/search.go @@ -132,9 +132,10 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f } if cfg.BuildMod == "vendor" { - if HasModRoot() { - walkPkgs(ModRoot(), targetPrefix, pruneGoMod|pruneVendor) - walkPkgs(filepath.Join(ModRoot(), "vendor"), "", pruneVendor) + mod := MainModules.mustGetSingleMainModule() + if modRoot := MainModules.ModRoot(mod); modRoot != "" { + walkPkgs(modRoot, MainModules.PathPrefix(mod), pruneGoMod|pruneVendor) + walkPkgs(filepath.Join(modRoot, "vendor"), "", pruneVendor) } return } @@ -148,12 +149,12 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f root, modPrefix string isLocal bool ) - if mod == Target { - if !HasModRoot() { + if MainModules.Contains(mod.Path) { + if MainModules.ModRoot(mod) == "" { continue // If there is no main module, we can't search in it. } - root = ModRoot() - modPrefix = targetPrefix + root = MainModules.ModRoot(mod) + modPrefix = MainModules.PathPrefix(mod) isLocal = true } else { var err error diff --git a/libgo/go/cmd/go/internal/modload/stat_openfile.go b/libgo/go/cmd/go/internal/modload/stat_openfile.go index 368f8931984..ff7c124af58 100644 --- a/libgo/go/cmd/go/internal/modload/stat_openfile.go +++ b/libgo/go/cmd/go/internal/modload/stat_openfile.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (js && wasm) || plan9 -// +build js,wasm plan9 // On plan9, per http://9p.io/magic/man2html/2/access: “Since file permissions // are checked by the server and group information is not known to the client, diff --git a/libgo/go/cmd/go/internal/modload/stat_unix.go b/libgo/go/cmd/go/internal/modload/stat_unix.go index f5f7ce3e03d..38f5cc4ffb6 100644 --- a/libgo/go/cmd/go/internal/modload/stat_unix.go +++ b/libgo/go/cmd/go/internal/modload/stat_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris package modload diff --git a/libgo/go/cmd/go/internal/modload/stat_windows.go b/libgo/go/cmd/go/internal/modload/stat_windows.go index 825e60b27af..f29a99165e5 100644 --- a/libgo/go/cmd/go/internal/modload/stat_windows.go +++ b/libgo/go/cmd/go/internal/modload/stat_windows.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package modload diff --git a/libgo/go/cmd/go/internal/modload/vendor.go b/libgo/go/cmd/go/internal/modload/vendor.go index 80713b0812e..5ea82a86208 100644 --- a/libgo/go/cmd/go/internal/modload/vendor.go +++ b/libgo/go/cmd/go/internal/modload/vendor.go @@ -15,6 +15,7 @@ import ( "cmd/go/internal/base" + "golang.org/x/mod/modfile" "golang.org/x/mod/module" "golang.org/x/mod/semver" ) @@ -35,13 +36,13 @@ type vendorMetadata struct { } // readVendorList reads the list of vendored modules from vendor/modules.txt. -func readVendorList() { +func readVendorList(mainModule module.Version) { vendorOnce.Do(func() { vendorList = nil vendorPkgModule = make(map[string]module.Version) vendorVersion = make(map[string]string) vendorMeta = make(map[module.Version]vendorMetadata) - data, err := os.ReadFile(filepath.Join(ModRoot(), "vendor/modules.txt")) + data, err := os.ReadFile(filepath.Join(MainModules.ModRoot(mainModule), "vendor/modules.txt")) if err != nil { if !errors.Is(err, fs.ErrNotExist) { base.Fatalf("go: %s", err) @@ -134,8 +135,8 @@ func readVendorList() { // checkVendorConsistency verifies that the vendor/modules.txt file matches (if // go 1.14) or at least does not contradict (go 1.13 or earlier) the // requirements and replacements listed in the main module's go.mod file. -func checkVendorConsistency() { - readVendorList() +func checkVendorConsistency(index *modFileIndex, modFile *modfile.File) { + readVendorList(MainModules.mustGetSingleMainModule()) pre114 := false if semver.Compare(index.goVersionV, "v1.14") < 0 { @@ -146,7 +147,7 @@ func checkVendorConsistency() { } vendErrors := new(strings.Builder) - vendErrorf := func(mod module.Version, format string, args ...interface{}) { + vendErrorf := func(mod module.Version, format string, args ...any) { detail := fmt.Sprintf(format, args...) if mod.Version == "" { fmt.Fprintf(vendErrors, "\n\t%s: %s", mod.Path, detail) @@ -219,6 +220,7 @@ func checkVendorConsistency() { } if vendErrors.Len() > 0 { + modRoot := MainModules.ModRoot(MainModules.mustGetSingleMainModule()) base.Fatalf("go: inconsistent vendoring in %s:%s\n\n\tTo ignore the vendor directory, use -mod=readonly or -mod=mod.\n\tTo sync the vendor directory, run:\n\t\tgo mod vendor", modRoot, vendErrors) } } diff --git a/libgo/go/cmd/go/internal/mvs/mvs.go b/libgo/go/cmd/go/internal/mvs/mvs.go index 6969f90f2e6..d25d447b0ee 100644 --- a/libgo/go/cmd/go/internal/mvs/mvs.go +++ b/libgo/go/cmd/go/internal/mvs/mvs.go @@ -8,6 +8,7 @@ package mvs import ( "fmt" + "reflect" "sort" "sync" @@ -85,11 +86,11 @@ type DowngradeReqs interface { // of the list are sorted by path. // // See https://research.swtch.com/vgo-mvs for details. -func BuildList(target module.Version, reqs Reqs) ([]module.Version, error) { - return buildList(target, reqs, nil) +func BuildList(targets []module.Version, reqs Reqs) ([]module.Version, error) { + return buildList(targets, reqs, nil) } -func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (module.Version, error)) ([]module.Version, error) { +func buildList(targets []module.Version, reqs Reqs, upgrade func(module.Version) (module.Version, error)) ([]module.Version, error) { cmp := func(v1, v2 string) int { if reqs.Max(v1, v2) != v1 { return -1 @@ -102,7 +103,7 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m var ( mu sync.Mutex - g = NewGraph(cmp, []module.Version{target}) + g = NewGraph(cmp, targets) upgrades = map[module.Version]module.Version{} errs = map[module.Version]error{} // (non-nil errors only) ) @@ -110,8 +111,10 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m // Explore work graph in parallel in case reqs.Required // does high-latency network operations. var work par.Work - work.Add(target) - work.Do(10, func(item interface{}) { + for _, target := range targets { + work.Add(target) + } + work.Do(10, func(item any) { m := item.(module.Version) var required []module.Version @@ -168,12 +171,12 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m // The final list is the minimum version of each module found in the graph. list := g.BuildList() - if v := list[0]; v != target { + if vs := list[:len(targets)]; !reflect.DeepEqual(vs, targets) { // target.Version will be "" for modload, the main client of MVS. // "" denotes the main module, which has no version. However, MVS treats // version strings as opaque, so "" is not a special value here. // See golang.org/issue/31491, golang.org/issue/29773. - panic(fmt.Sprintf("mistake: chose version %q instead of target %+v", v, target)) + panic(fmt.Sprintf("mistake: chose versions %+v instead of targets %+v", vs, targets)) } return list, nil } @@ -181,8 +184,8 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m // Req returns the minimal requirement list for the target module, // with the constraint that all module paths listed in base must // appear in the returned list. -func Req(target module.Version, base []string, reqs Reqs) ([]module.Version, error) { - list, err := BuildList(target, reqs) +func Req(mainModule module.Version, base []string, reqs Reqs) ([]module.Version, error) { + list, err := BuildList([]module.Version{mainModule}, reqs) if err != nil { return nil, err } @@ -194,7 +197,8 @@ func Req(target module.Version, base []string, reqs Reqs) ([]module.Version, err // Compute postorder, cache requirements. var postorder []module.Version reqCache := map[module.Version][]module.Version{} - reqCache[target] = nil + reqCache[mainModule] = nil + var walk func(module.Version) error walk = func(m module.Version) error { _, ok := reqCache[m] @@ -273,7 +277,7 @@ func Req(target module.Version, base []string, reqs Reqs) ([]module.Version, err // UpgradeAll returns a build list for the target module // in which every module is upgraded to its latest version. func UpgradeAll(target module.Version, reqs UpgradeReqs) ([]module.Version, error) { - return buildList(target, reqs, func(m module.Version) (module.Version, error) { + return buildList([]module.Version{target}, reqs, func(m module.Version) (module.Version, error) { if m.Path == target.Path { return target, nil } @@ -308,7 +312,7 @@ func Upgrade(target module.Version, reqs UpgradeReqs, upgrade ...module.Version) } } - return buildList(target, &override{target, list, reqs}, func(m module.Version) (module.Version, error) { + return buildList([]module.Version{target}, &override{target, list, reqs}, func(m module.Version) (module.Version, error) { if v, ok := upgradeTo[m.Path]; ok { return module.Version{Path: m.Path, Version: v}, nil } @@ -331,7 +335,7 @@ func Downgrade(target module.Version, reqs DowngradeReqs, downgrade ...module.Ve // // In order to generate those new requirements, we need to identify versions // for every module in the build list — not just reqs.Required(target). - list, err := BuildList(target, reqs) + list, err := BuildList([]module.Version{target}, reqs) if err != nil { return nil, err } @@ -446,7 +450,7 @@ List: // list with the actual versions of the downgraded modules as selected by MVS, // instead of our initial downgrades. // (See the downhiddenartifact and downhiddencross test cases). - actual, err := BuildList(target, &override{ + actual, err := BuildList([]module.Version{target}, &override{ target: target, list: downgraded, Reqs: reqs, @@ -466,7 +470,7 @@ List: } } - return BuildList(target, &override{ + return BuildList([]module.Version{target}, &override{ target: target, list: downgraded, Reqs: reqs, diff --git a/libgo/go/cmd/go/internal/mvs/mvs_test.go b/libgo/go/cmd/go/internal/mvs/mvs_test.go index 598ed666889..26d004fee28 100644 --- a/libgo/go/cmd/go/internal/mvs/mvs_test.go +++ b/libgo/go/cmd/go/internal/mvs/mvs_test.go @@ -507,7 +507,7 @@ func Test(t *testing.T) { t.Fatalf("build takes one argument: %q", line) } fns = append(fns, func(t *testing.T) { - list, err := BuildList(m(kf[1]), reqs) + list, err := BuildList([]module.Version{m(kf[1])}, reqs) checkList(t, key, list, err, val) }) continue diff --git a/libgo/go/cmd/go/internal/par/work.go b/libgo/go/cmd/go/internal/par/work.go index 960cec6fb16..496c41b1509 100644 --- a/libgo/go/cmd/go/internal/par/work.go +++ b/libgo/go/cmd/go/internal/par/work.go @@ -14,24 +14,24 @@ import ( // Work manages a set of work items to be executed in parallel, at most once each. // The items in the set must all be valid map keys. type Work struct { - f func(interface{}) // function to run for each item - running int // total number of runners + f func(any) // function to run for each item + running int // total number of runners mu sync.Mutex - added map[interface{}]bool // items added to set - todo []interface{} // items yet to be run - wait sync.Cond // wait when todo is empty - waiting int // number of runners waiting for todo + added map[any]bool // items added to set + todo []any // items yet to be run + wait sync.Cond // wait when todo is empty + waiting int // number of runners waiting for todo } func (w *Work) init() { if w.added == nil { - w.added = make(map[interface{}]bool) + w.added = make(map[any]bool) } } // Add adds item to the work set, if it hasn't already been added. -func (w *Work) Add(item interface{}) { +func (w *Work) Add(item any) { w.mu.Lock() w.init() if !w.added[item] { @@ -51,7 +51,7 @@ func (w *Work) Add(item interface{}) { // before calling Do (or else Do returns immediately), // but it is allowed for f(item) to add new items to the set. // Do should only be used once on a given Work. -func (w *Work) Do(n int, f func(item interface{})) { +func (w *Work) Do(n int, f func(item any)) { if n < 1 { panic("par.Work.Do: n < 1") } @@ -110,13 +110,13 @@ type Cache struct { type cacheEntry struct { done uint32 mu sync.Mutex - result interface{} + result any } // Do calls the function f if and only if Do is being called for the first time with this key. // No call to Do with a given key returns until the one call to f returns. // Do returns the value returned by the one call to f. -func (c *Cache) Do(key interface{}, f func() interface{}) interface{} { +func (c *Cache) Do(key any, f func() any) any { entryIface, ok := c.m.Load(key) if !ok { entryIface, _ = c.m.LoadOrStore(key, new(cacheEntry)) @@ -136,7 +136,7 @@ func (c *Cache) Do(key interface{}, f func() interface{}) interface{} { // Get returns the cached result associated with key. // It returns nil if there is no such result. // If the result for key is being computed, Get does not wait for the computation to finish. -func (c *Cache) Get(key interface{}) interface{} { +func (c *Cache) Get(key any) any { entryIface, ok := c.m.Load(key) if !ok { return nil @@ -156,7 +156,7 @@ func (c *Cache) Get(key interface{}) interface{} { // TODO(jayconrod): Delete this after the package cache clearing functions // in internal/load have been removed. func (c *Cache) Clear() { - c.m.Range(func(key, value interface{}) bool { + c.m.Range(func(key, value any) bool { c.m.Delete(key) return true }) @@ -169,7 +169,7 @@ func (c *Cache) Clear() { // // TODO(jayconrod): Delete this after the package cache clearing functions // in internal/load have been removed. -func (c *Cache) Delete(key interface{}) { +func (c *Cache) Delete(key any) { c.m.Delete(key) } @@ -180,8 +180,8 @@ func (c *Cache) Delete(key interface{}) { // // TODO(jayconrod): Delete this after the package cache clearing functions // in internal/load have been removed. -func (c *Cache) DeleteIf(pred func(key interface{}) bool) { - c.m.Range(func(key, _ interface{}) bool { +func (c *Cache) DeleteIf(pred func(key any) bool) { + c.m.Range(func(key, _ any) bool { if pred(key) { c.Delete(key) } diff --git a/libgo/go/cmd/go/internal/par/work_test.go b/libgo/go/cmd/go/internal/par/work_test.go index f104bc4106f..add0e640d8c 100644 --- a/libgo/go/cmd/go/internal/par/work_test.go +++ b/libgo/go/cmd/go/internal/par/work_test.go @@ -16,7 +16,7 @@ func TestWork(t *testing.T) { const N = 10000 n := int32(0) w.Add(N) - w.Do(100, func(x interface{}) { + w.Do(100, func(x any) { atomic.AddInt32(&n, 1) i := x.(int) if i >= 2 { @@ -40,7 +40,7 @@ func TestWorkParallel(t *testing.T) { } start := time.Now() var n int32 - w.Do(N, func(x interface{}) { + w.Do(N, func(x any) { time.Sleep(1 * time.Millisecond) atomic.AddInt32(&n, +1) }) @@ -58,19 +58,19 @@ func TestCache(t *testing.T) { var cache Cache n := 1 - v := cache.Do(1, func() interface{} { n++; return n }) + v := cache.Do(1, func() any { n++; return n }) if v != 2 { t.Fatalf("cache.Do(1) did not run f") } - v = cache.Do(1, func() interface{} { n++; return n }) + v = cache.Do(1, func() any { n++; return n }) if v != 2 { t.Fatalf("cache.Do(1) ran f again!") } - v = cache.Do(2, func() interface{} { n++; return n }) + v = cache.Do(2, func() any { n++; return n }) if v != 3 { t.Fatalf("cache.Do(2) did not run f") } - v = cache.Do(1, func() interface{} { n++; return n }) + v = cache.Do(1, func() any { n++; return n }) if v != 2 { t.Fatalf("cache.Do(1) did not returned saved value from original cache.Do(1)") } diff --git a/libgo/go/cmd/go/internal/robustio/robustio_flaky.go b/libgo/go/cmd/go/internal/robustio/robustio_flaky.go index d5c241857b4..c56e36ca624 100644 --- a/libgo/go/cmd/go/internal/robustio/robustio_flaky.go +++ b/libgo/go/cmd/go/internal/robustio/robustio_flaky.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows || darwin -// +build windows darwin package robustio diff --git a/libgo/go/cmd/go/internal/robustio/robustio_other.go b/libgo/go/cmd/go/internal/robustio/robustio_other.go index 3a20cac6cf8..da9a46e4fac 100644 --- a/libgo/go/cmd/go/internal/robustio/robustio_other.go +++ b/libgo/go/cmd/go/internal/robustio/robustio_other.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows && !darwin -// +build !windows,!darwin package robustio diff --git a/libgo/go/cmd/go/internal/run/run.go b/libgo/go/cmd/go/internal/run/run.go index 784f7162dfd..c4b70b64fe6 100644 --- a/libgo/go/cmd/go/internal/run/run.go +++ b/libgo/go/cmd/go/internal/run/run.go @@ -65,14 +65,17 @@ func init() { CmdRun.Run = runRun // break init loop work.AddBuildFlags(CmdRun, work.DefaultBuildFlags) + base.AddWorkfileFlag(&CmdRun.Flag) CmdRun.Flag.Var((*base.StringsFlag)(&work.ExecCmd), "exec", "") } -func printStderr(args ...interface{}) (int, error) { +func printStderr(args ...any) (int, error) { return fmt.Fprint(os.Stderr, args...) } func runRun(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() + if shouldUseOutsideModuleMode(args) { // Set global module flags for 'go run cmd@version'. // This must be done before modload.Init, but we need to call work.BuildInit @@ -100,7 +103,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) { if strings.HasSuffix(file, "_test.go") { // GoFilesPackage is going to assign this to TestGoFiles. // Reject since it won't be part of the build. - base.Fatalf("go run: cannot run *_test.go files (%s)", file) + base.Fatalf("go: cannot run *_test.go files (%s)", file) } } p = load.GoFilesPackage(ctx, pkgOpts, files) @@ -111,26 +114,26 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) { var err error pkgs, err = load.PackagesAndErrorsOutsideModule(ctx, pkgOpts, args[:1]) if err != nil { - base.Fatalf("go run: %v", err) + base.Fatalf("go: %v", err) } } else { pkgs = load.PackagesAndErrors(ctx, pkgOpts, args[:1]) } if len(pkgs) == 0 { - base.Fatalf("go run: no packages loaded from %s", arg) + base.Fatalf("go: no packages loaded from %s", arg) } if len(pkgs) > 1 { var names []string for _, p := range pkgs { names = append(names, p.ImportPath) } - base.Fatalf("go run: pattern %s matches multiple packages:\n\t%s", arg, strings.Join(names, "\n\t")) + base.Fatalf("go: pattern %s matches multiple packages:\n\t%s", arg, strings.Join(names, "\n\t")) } p = pkgs[0] i++ } else { - base.Fatalf("go run: no go files listed") + base.Fatalf("go: no go files listed") } cmdArgs := args[i:] load.CheckPackageErrors([]*load.Package{p}) @@ -151,7 +154,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) { if !cfg.BuildContext.CgoEnabled { hint = " (cgo is disabled)" } - base.Fatalf("go run: no suitable source files%s", hint) + base.Fatalf("go: no suitable source files%s", hint) } p.Internal.ExeName = src[:len(src)-len(".go")] } else { diff --git a/libgo/go/cmd/go/internal/search/search.go b/libgo/go/cmd/go/internal/search/search.go index a0c806a2593..ebd4990a684 100644 --- a/libgo/go/cmd/go/internal/search/search.go +++ b/libgo/go/cmd/go/internal/search/search.go @@ -202,12 +202,6 @@ func (m *Match) MatchPackages() { } } -var modRoot string - -func SetModRoot(dir string) { - modRoot = dir -} - // MatchDirs sets m.Dirs to a non-nil slice containing all directories that // potentially match a local pattern. The pattern must begin with an absolute // path, or "./", or "../". On Windows, the pattern may use slash or backslash @@ -215,7 +209,7 @@ func SetModRoot(dir string) { // // If any errors may have caused the set of directories to be incomplete, // MatchDirs appends those errors to m.Errs. -func (m *Match) MatchDirs() { +func (m *Match) MatchDirs(modRoots []string) { m.Dirs = []string{} if !m.IsLocal() { m.AddError(fmt.Errorf("internal error: MatchDirs: %s is not a valid filesystem pattern", m.pattern)) @@ -253,15 +247,24 @@ func (m *Match) MatchDirs() { // We need to preserve the ./ for pattern matching // and in the returned import paths. - if modRoot != "" { + if len(modRoots) > 1 { abs, err := filepath.Abs(dir) if err != nil { m.AddError(err) return } - if !hasFilepathPrefix(abs, modRoot) { - m.AddError(fmt.Errorf("directory %s is outside module root (%s)", abs, modRoot)) - return + var found bool + for _, modRoot := range modRoots { + if modRoot != "" && hasFilepathPrefix(abs, modRoot) { + found = true + } + } + if !found { + plural := "" + if len(modRoots) > 1 { + plural = "s" + } + m.AddError(fmt.Errorf("directory %s is outside module root%s (%s)", abs, plural, strings.Join(modRoots, ", "))) } } @@ -424,19 +427,19 @@ func WarnUnmatched(matches []*Match) { // ImportPaths returns the matching paths to use for the given command line. // It calls ImportPathsQuiet and then WarnUnmatched. -func ImportPaths(patterns []string) []*Match { - matches := ImportPathsQuiet(patterns) +func ImportPaths(patterns, modRoots []string) []*Match { + matches := ImportPathsQuiet(patterns, modRoots) WarnUnmatched(matches) return matches } // ImportPathsQuiet is like ImportPaths but does not warn about patterns with no matches. -func ImportPathsQuiet(patterns []string) []*Match { +func ImportPathsQuiet(patterns, modRoots []string) []*Match { var out []*Match for _, a := range CleanPatterns(patterns) { m := NewMatch(a) if m.IsLocal() { - m.MatchDirs() + m.MatchDirs(modRoots) // Change the file import path to a regular import path if the package // is in GOPATH or GOROOT. We don't report errors here; LoadImport diff --git a/libgo/go/cmd/go/internal/str/path.go b/libgo/go/cmd/go/internal/str/path.go index 51ab2af82b5..0c8aaeaca1f 100644 --- a/libgo/go/cmd/go/internal/str/path.go +++ b/libgo/go/cmd/go/internal/str/path.go @@ -49,3 +49,17 @@ func HasFilePathPrefix(s, prefix string) bool { return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix } } + +// TrimFilePathPrefix returns s without the leading path elements in prefix. +// If s does not start with prefix (HasFilePathPrefix with the same arguments +// returns false), TrimFilePathPrefix returns s. If s equals prefix, +// TrimFilePathPrefix returns "". +func TrimFilePathPrefix(s, prefix string) string { + if !HasFilePathPrefix(s, prefix) { + return s + } + if len(s) == len(prefix) { + return "" + } + return s[len(prefix)+1:] +} diff --git a/libgo/go/cmd/go/internal/str/str.go b/libgo/go/cmd/go/internal/str/str.go index 9106ebf74d5..021bfbff779 100644 --- a/libgo/go/cmd/go/internal/str/str.go +++ b/libgo/go/cmd/go/internal/str/str.go @@ -14,7 +14,7 @@ import ( // StringList flattens its arguments into a single []string. // Each argument in args must have type string or []string. -func StringList(args ...interface{}) []string { +func StringList(args ...any) []string { var x []string for _, arg := range args { switch arg := arg.(type) { @@ -109,47 +109,3 @@ func Uniq(ss *[]string) { } *ss = uniq } - -func isSpaceByte(c byte) bool { - return c == ' ' || c == '\t' || c == '\n' || c == '\r' -} - -// SplitQuotedFields splits s into a list of fields, -// allowing single or double quotes around elements. -// There is no unescaping or other processing within -// quoted fields. -func SplitQuotedFields(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 -} diff --git a/libgo/go/cmd/go/internal/str/str_test.go b/libgo/go/cmd/go/internal/str/str_test.go index 147ce1a63ef..8ea758e0a8b 100644 --- a/libgo/go/cmd/go/internal/str/str_test.go +++ b/libgo/go/cmd/go/internal/str/str_test.go @@ -4,7 +4,9 @@ package str -import "testing" +import ( + "testing" +) var foldDupTests = []struct { list []string diff --git a/libgo/go/cmd/go/internal/test/flagdefs.go b/libgo/go/cmd/go/internal/test/flagdefs.go index 37ac81c2678..1b79314eff3 100644 --- a/libgo/go/cmd/go/internal/test/flagdefs.go +++ b/libgo/go/cmd/go/internal/test/flagdefs.go @@ -19,6 +19,9 @@ var passFlagToTest = map[string]bool{ "cpu": true, "cpuprofile": true, "failfast": true, + "fuzz": true, + "fuzzminimizetime": true, + "fuzztime": true, "list": true, "memprofile": true, "memprofilerate": true, @@ -33,3 +36,37 @@ var passFlagToTest = map[string]bool{ "trace": true, "v": true, } + +var passAnalyzersToVet = map[string]bool{ + "asmdecl": true, + "assign": true, + "atomic": true, + "bool": true, + "bools": true, + "buildtag": true, + "buildtags": true, + "cgocall": true, + "composites": true, + "copylocks": true, + "errorsas": true, + "framepointer": true, + "httpresponse": true, + "ifaceassert": true, + "loopclosure": true, + "lostcancel": true, + "methods": true, + "nilfunc": true, + "printf": true, + "rangeloops": true, + "shift": true, + "sigchanyzer": true, + "stdmethods": true, + "stringintconv": true, + "structtag": true, + "testinggoroutine": true, + "tests": true, + "unmarshal": true, + "unreachable": true, + "unsafeptr": true, + "unusedresult": true, +} diff --git a/libgo/go/cmd/go/internal/test/flagdefs_test.go b/libgo/go/cmd/go/internal/test/flagdefs_test.go index ab5440b3801..a2f09e5a960 100644 --- a/libgo/go/cmd/go/internal/test/flagdefs_test.go +++ b/libgo/go/cmd/go/internal/test/flagdefs_test.go @@ -5,7 +5,10 @@ package test import ( + "cmd/go/internal/test/internal/genflags" "flag" + "internal/testenv" + "reflect" "strings" "testing" ) @@ -17,7 +20,7 @@ func TestPassFlagToTestIncludesAllTestFlags(t *testing.T) { } name := strings.TrimPrefix(f.Name, "test.") switch name { - case "testlogfile", "paniconexit0": + case "testlogfile", "paniconexit0", "fuzzcachedir", "fuzzworker": // These are internal flags. default: if !passFlagToTest[name] { @@ -37,3 +40,22 @@ func TestPassFlagToTestIncludesAllTestFlags(t *testing.T) { } } } + +func TestVetAnalyzersSetIsCorrect(t *testing.T) { + testenv.MustHaveGoBuild(t) + + vetAns, err := genflags.VetAnalyzers() + if err != nil { + t.Fatal(err) + } + + want := make(map[string]bool) + for _, a := range vetAns { + want[a] = true + } + + if !reflect.DeepEqual(want, passAnalyzersToVet) { + t.Errorf("stale vet analyzers: want %v; got %v", want, passAnalyzersToVet) + t.Logf("(Run 'go generate cmd/go/internal/test' to refresh the set of analyzers.)") + } +} diff --git a/libgo/go/cmd/go/internal/test/genflags.go b/libgo/go/cmd/go/internal/test/genflags.go index 9277de7fee8..10f290090c7 100644 --- a/libgo/go/cmd/go/internal/test/genflags.go +++ b/libgo/go/cmd/go/internal/test/genflags.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore package main @@ -16,6 +15,8 @@ import ( "strings" "testing" "text/template" + + "cmd/go/internal/test/internal/genflags" ) func main() { @@ -25,9 +26,18 @@ func main() { } func regenerate() error { + vetAnalyzers, err := genflags.VetAnalyzers() + if err != nil { + return err + } + t := template.Must(template.New("fileTemplate").Parse(fileTemplate)) + tData := map[string][]string{ + "testFlags": testFlags(), + "vetAnalyzers": vetAnalyzers, + } buf := bytes.NewBuffer(nil) - if err := t.Execute(buf, testFlags()); err != nil { + if err := t.Execute(buf, tData); err != nil { return err } @@ -64,7 +74,7 @@ func testFlags() []string { name := strings.TrimPrefix(f.Name, "test.") switch name { - case "testlogfile", "paniconexit0": + case "testlogfile", "paniconexit0", "fuzzcachedir", "fuzzworker": // These flags are only for use by cmd/go. default: names = append(names, name) @@ -85,7 +95,13 @@ package test // passFlagToTest contains the flags that should be forwarded to // the test binary with the prefix "test.". var passFlagToTest = map[string]bool { -{{- range .}} +{{- range .testFlags}} + "{{.}}": true, +{{- end }} +} + +var passAnalyzersToVet = map[string]bool { +{{- range .vetAnalyzers}} "{{.}}": true, {{- end }} } diff --git a/libgo/go/cmd/go/internal/test/internal/genflags/vetflag.go b/libgo/go/cmd/go/internal/test/internal/genflags/vetflag.go new file mode 100644 index 00000000000..2195cc34479 --- /dev/null +++ b/libgo/go/cmd/go/internal/test/internal/genflags/vetflag.go @@ -0,0 +1,68 @@ +// Copyright 2019 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 genflags + +import ( + "bytes" + "cmd/go/internal/base" + "encoding/json" + "fmt" + exec "internal/execabs" + "regexp" + "sort" +) + +// VetAnalyzers computes analyzers and their aliases supported by vet. +func VetAnalyzers() ([]string, error) { + // get supported vet flag information + tool := base.Tool("vet") + vetcmd := exec.Command(tool, "-flags") + out := new(bytes.Buffer) + vetcmd.Stdout = out + if err := vetcmd.Run(); err != nil { + return nil, fmt.Errorf("go vet: can't execute %s -flags: %v\n", tool, err) + } + var analysisFlags []struct { + Name string + Bool bool + Usage string + } + if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil { + return nil, fmt.Errorf("go vet: can't unmarshal JSON from %s -flags: %v", tool, err) + } + + // parse the flags to figure out which ones stand for analyses + analyzerSet := make(map[string]bool) + rEnable := regexp.MustCompile("^enable .+ analysis$") + for _, flag := range analysisFlags { + if rEnable.MatchString(flag.Usage) { + analyzerSet[flag.Name] = true + } + } + + rDeprecated := regexp.MustCompile("^deprecated alias for -(?P<analyzer>(.+))$") + // Returns the original value matched by rDeprecated on input value. + // If there is no match, "" is returned. + originalValue := func(value string) string { + match := rDeprecated.FindStringSubmatch(value) + if len(match) < 2 { + return "" + } + return match[1] + } + // extract deprecated aliases for existing analyses + for _, flag := range analysisFlags { + if o := originalValue(flag.Usage); analyzerSet[o] { + analyzerSet[flag.Name] = true + } + } + + var analyzers []string + for a := range analyzerSet { + analyzers = append(analyzers, a) + } + sort.Strings(analyzers) + return analyzers, nil +} diff --git a/libgo/go/cmd/go/internal/test/test.go b/libgo/go/cmd/go/internal/test/test.go index 59ea1ef5445..50e6d5201b0 100644 --- a/libgo/go/cmd/go/internal/test/test.go +++ b/libgo/go/cmd/go/internal/test/test.go @@ -29,11 +29,15 @@ import ( "cmd/go/internal/cfg" "cmd/go/internal/load" "cmd/go/internal/lockedfile" + "cmd/go/internal/modload" "cmd/go/internal/search" "cmd/go/internal/str" "cmd/go/internal/trace" "cmd/go/internal/work" + "cmd/internal/sys" "cmd/internal/test2json" + + "golang.org/x/mod/module" ) // Break init loop. @@ -60,8 +64,8 @@ followed by detailed output for each failed package. 'Go test' recompiles each package along with any files with names matching the file pattern "*_test.go". -These additional files can contain test functions, benchmark functions, and -example functions. See 'go help testfunc' for more. +These additional files can contain test functions, benchmark functions, fuzz +tests and example functions. See 'go help testfunc' for more. Each listed package causes the execution of a separate test binary. Files whose names begin with "_" (including "_test.go") or "." are ignored. @@ -78,7 +82,8 @@ binary. Only a high-confidence subset of the default go vet checks are used. That subset is: 'atomic', 'bool', 'buildtags', 'errorsas', 'ifaceassert', 'nilfunc', 'printf', and 'stringintconv'. You can see the documentation for these and other vet tests via "go doc cmd/vet". -To disable the running of go vet, use the -vet=off flag. +To disable the running of go vet, use the -vet=off flag. To run all +checks, use the -vet=all flag. All test output and summary lines are printed to the go command's standard output, even if the test printed them to its own standard @@ -119,16 +124,16 @@ elapsed time in the summary line. The rule for a match in the cache is that the run involves the same test binary and the flags on the command line come entirely from a restricted set of 'cacheable' test flags, defined as -benchtime, -cpu, --list, -parallel, -run, -short, and -v. If a run of go test has any test -or non-test flags outside this set, the result is not cached. To -disable test caching, use any test flag or argument other than the -cacheable flags. The idiomatic way to disable test caching explicitly -is to use -count=1. Tests that open files within the package's source -root (usually $GOPATH) or that consult environment variables only -match future runs in which the files and environment variables are unchanged. -A cached test result is treated as executing in no time at all, -so a successful package test result will be cached and reused -regardless of -timeout setting. +-list, -parallel, -run, -short, -timeout, -failfast, and -v. +If a run of go test has any test or non-test flags outside this set, +the result is not cached. To disable test caching, use any test flag +or argument other than the cacheable flags. The idiomatic way to disable +test caching explicitly is to use -count=1. Tests that open files within +the package's source root (usually $GOPATH) or that consult environment +variables only match future runs in which the files and environment +variables are unchanged. A cached test result is treated as executing +in no time at all,so a successful package test result will be cached and +reused regardless of -timeout setting. In addition to the build flags, the flags handled by 'go test' itself are: @@ -206,9 +211,10 @@ control the execution of any test: (for example, -benchtime 100x). -count n - Run each test and benchmark n times (default 1). + Run each test, benchmark, and fuzz seed n times (default 1). If -cpu is set, run n times for each GOMAXPROCS value. - Examples are always run once. + Examples are always run once. -count does not apply to + fuzz tests matched by -fuzz. -cover Enable coverage analysis. @@ -235,32 +241,67 @@ control the execution of any test: Sets -cover. -cpu 1,2,4 - Specify a list of GOMAXPROCS values for which the tests or - benchmarks should be executed. The default is the current value - of GOMAXPROCS. + Specify a list of GOMAXPROCS values for which the tests, benchmarks or + fuzz tests should be executed. The default is the current value + of GOMAXPROCS. -cpu does not apply to fuzz tests matched by -fuzz. -failfast Do not start new tests after the first test failure. + -fuzz regexp + Run the fuzz test matching the regular expression. When specified, + the command line argument must match exactly one package within the + main module, and regexp must match exactly one fuzz test within + that package. Fuzzing will occur after tests, benchmarks, seed corpora + of other fuzz tests, and examples have completed. See the Fuzzing + section of the testing package documentation for details. + + -fuzztime t + Run enough iterations of the fuzz target during fuzzing to take t, + specified as a time.Duration (for example, -fuzztime 1h30s). + The default is to run forever. + The special syntax Nx means to run the fuzz target N times + (for example, -fuzztime 1000x). + + -fuzzminimizetime t + Run enough iterations of the fuzz target during each minimization + attempt to take t, as specified as a time.Duration (for example, + -fuzzminimizetime 30s). + The default is 60s. + The special syntax Nx means to run the fuzz target N times + (for example, -fuzzminimizetime 100x). + + -json + Log verbose output and test results in JSON. This presents the + same information as the -v flag in a machine-readable format. + -list regexp - List tests, benchmarks, or examples matching the regular expression. - No tests, benchmarks or examples will be run. This will only - list top-level tests. No subtest or subbenchmarks will be shown. + List tests, benchmarks, fuzz tests, or examples matching the regular + expression. No tests, benchmarks, fuzz tests, or examples will be run. + This will only list top-level tests. No subtest or subbenchmarks will be + shown. -parallel n - Allow parallel execution of test functions that call t.Parallel. + Allow parallel execution of test functions that call t.Parallel, and + fuzz targets that call t.Parallel when running the seed corpus. The value of this flag is the maximum number of tests to run - simultaneously; by default, it is set to the value of GOMAXPROCS. + simultaneously. + While fuzzing, the value of this flag is the maximum number of + subprocesses that may call the fuzz function simultaneously, regardless of + whether T.Parallel is called. + By default, -parallel is set to the value of GOMAXPROCS. + Setting -parallel to values higher than GOMAXPROCS may cause degraded + performance due to CPU contention, especially when fuzzing. Note that -parallel only applies within a single test binary. The 'go test' command may run tests for different packages in parallel as well, according to the setting of the -p flag (see 'go help build'). -run regexp - Run only those tests and examples matching the regular expression. - For tests, the regular expression is split by unbracketed slash (/) - characters into a sequence of regular expressions, and each part - of a test's identifier must match the corresponding element in + Run only those tests, examples, and fuzz tests matching the regular + expression. For tests, the regular expression is split by unbracketed + slash (/) characters into a sequence of regular expressions, and each + part of a test's identifier must match the corresponding element in the sequence, if any. Note that possible parents of matches are run too, so that -run=X/Y matches and runs and reports the result of all tests matching X, even those without sub-tests matching Y, @@ -273,11 +314,11 @@ control the execution of any test: exhaustive tests. -shuffle off,on,N - Randomize the execution order of tests and benchmarks. - It is off by default. If -shuffle is set to on, then it will seed - the randomizer using the system clock. If -shuffle is set to an - integer N, then N will be used as the seed value. In both cases, - the seed will be reported for reproducibility. + Randomize the execution order of tests and benchmarks. + It is off by default. If -shuffle is set to on, then it will seed + the randomizer using the system clock. If -shuffle is set to an + integer N, then N will be used as the seed value. In both cases, + the seed will be reported for reproducibility. -timeout d If a test binary runs longer than duration d, panic. @@ -373,7 +414,11 @@ leave the test binary in pkg.test for use when analyzing the profiles. When 'go test' runs a test binary, it does so from within the corresponding package's source code directory. Depending on the test, it may be necessary to do the same when invoking a generated test -binary directly. +binary directly. Because that directory may be located within the +module cache, which may be read-only and is verified by checksums, the +test must not write to it or any other directory within the module +unless explicitly requested by the user (such as with the -fuzz flag, +which writes failures to testdata/fuzz). The command-line package list, if present, must appear before any flag not known to the go test command. Continuing the example above, @@ -430,6 +475,10 @@ A benchmark function is one named BenchmarkXxx and should have the signature, func BenchmarkXxx(b *testing.B) { ... } +A fuzz test is one named FuzzXxx and should have the signature, + + func FuzzXxx(f *testing.F) { ... } + An example function is similar to a test function but, instead of using *testing.T to report success or failure, prints output to os.Stdout. If the last comment in the function starts with "Output:" then the output @@ -469,7 +518,7 @@ Here is another example where the ordering of the output is ignored: The entire test file is presented as the example when it contains a single example function, at least one other function, type, variable, or constant -declaration, and no test or benchmark functions. +declaration, and no tests, benchmarks, or fuzz tests. See the documentation of the testing package for more information. `, @@ -483,6 +532,7 @@ var ( testCoverPaths []string // -coverpkg flag testCoverPkgs []*load.Package // -coverpkg flag testCoverProfile string // -coverprofile flag + testFuzz string // -fuzz flag testJSON bool // -json flag testList string // -list flag testO string // -o flag @@ -578,6 +628,7 @@ var defaultVetFlags = []string{ func runTest(ctx context.Context, cmd *base.Command, args []string) { pkgArgs, testArgs = testFlags(args) + modload.InitWorkfile() // The test command does custom flag processing; initialize workspaces after that. if cfg.DebugTrace != "" { var close func() error @@ -615,6 +666,52 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { if testO != "" && len(pkgs) != 1 { base.Fatalf("cannot use -o flag with multiple packages") } + if testFuzz != "" { + if !sys.FuzzSupported(cfg.Goos, cfg.Goarch) { + base.Fatalf("-fuzz flag is not supported on %s/%s", cfg.Goos, cfg.Goarch) + } + if len(pkgs) != 1 { + base.Fatalf("cannot use -fuzz flag with multiple packages") + } + if testCoverProfile != "" { + base.Fatalf("cannot use -coverprofile flag with -fuzz flag") + } + if profileFlag := testProfile(); profileFlag != "" { + base.Fatalf("cannot use %s flag with -fuzz flag", profileFlag) + } + + // Reject the '-fuzz' flag if the package is outside the main module. + // Otherwise, if fuzzing identifies a failure it could corrupt checksums in + // the module cache (or permanently alter the behavior of std tests for all + // users) by writing the failing input to the package's testdata directory. + // (See https://golang.org/issue/48495 and test_fuzz_modcache.txt.) + mainMods := modload.MainModules + if m := pkgs[0].Module; m != nil && m.Path != "" { + if !mainMods.Contains(m.Path) { + base.Fatalf("cannot use -fuzz flag on package outside the main module") + } + } else if pkgs[0].Standard && modload.Enabled() { + // Because packages in 'std' and 'cmd' are part of the standard library, + // they are only treated as part of a module in 'go mod' subcommands and + // 'go get'. However, we still don't want to accidentally corrupt their + // testdata during fuzzing, nor do we want to fail with surprising errors + // if GOROOT isn't writable (as is often the case for Go toolchains + // installed through package managers). + // + // If the user is requesting to fuzz a standard-library package, ensure + // that they are in the same module as that package (just like when + // fuzzing any other package). + if strings.HasPrefix(pkgs[0].ImportPath, "cmd/") { + if !mainMods.Contains("cmd") || !mainMods.InGorootSrc(module.Version{Path: "cmd"}) { + base.Fatalf("cannot use -fuzz flag on package outside the main module") + } + } else { + if !mainMods.Contains("std") || !mainMods.InGorootSrc(module.Version{Path: "std"}) { + base.Fatalf("cannot use -fuzz flag on package outside the main module") + } + } + } + } if testProfile() != "" && len(pkgs) != 1 { base.Fatalf("cannot use %s flag with multiple packages", testProfile()) } @@ -625,7 +722,9 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { // to that timeout plus one minute. This is a backup alarm in case // the test wedges with a goroutine spinning and its background // timer does not get a chance to fire. - if testTimeout > 0 { + // Don't set this if fuzzing, since it should be able to run + // indefinitely. + if testTimeout > 0 && testFuzz == "" { testKillTimeout = testTimeout + 1*time.Minute } @@ -649,7 +748,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { b.Init() if cfg.BuildI { - fmt.Fprint(os.Stderr, "go test: -i flag is deprecated\n") + fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n") cfg.BuildV = testV deps := make(map[string]bool) @@ -775,6 +874,41 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { } } + // Inform the compiler that it should instrument the binary at + // build-time when fuzzing is enabled. + if testFuzz != "" { + // Don't instrument packages which may affect coverage guidance but are + // unlikely to be useful. Most of these are used by the testing or + // internal/fuzz packages concurrently with fuzzing. + var skipInstrumentation = map[string]bool{ + "context": true, + "internal/fuzz": true, + "reflect": true, + "runtime": true, + "sync": true, + "sync/atomic": true, + "syscall": true, + "testing": true, + "time": true, + } + for _, p := range load.TestPackageList(ctx, pkgOpts, pkgs) { + if !skipInstrumentation[p.ImportPath] { + p.Internal.FuzzInstrument = true + } + } + } + + // Collect all the packages imported by the packages being tested. + allImports := make(map[*load.Package]bool) + for _, p := range pkgs { + if p.Error != nil && p.Error.IsImportCycle { + continue + } + for _, p1 := range p.Internal.Imports { + allImports[p1] = true + } + } + // Prepare build + run + print actions for all packages being tested. for _, p := range pkgs { // sync/atomic import is inserted by the cover tool. See #18486 @@ -782,7 +916,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { ensureImport(p, "sync/atomic") } - buildTest, runTest, printTest, err := builderTest(&b, ctx, pkgOpts, p) + buildTest, runTest, printTest, err := builderTest(&b, ctx, pkgOpts, p, allImports[p]) if err != nil { str := err.Error() str = strings.TrimPrefix(str, "\n") @@ -849,7 +983,7 @@ var windowsBadWords = []string{ "update", } -func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts, p *load.Package) (buildAction, runAction, printAction *work.Action, err error) { +func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts, p *load.Package, imported bool) (buildAction, runAction, printAction *work.Action, err error) { if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 { build := b.CompileAction(work.ModeBuild, work.ModeBuild, p) run := &work.Action{Mode: "test run", Package: p, Deps: []*work.Action{build}} @@ -877,6 +1011,16 @@ func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts, return nil, nil, nil, err } + // If imported is true then this package is imported by some + // package being tested. Make building the test version of the + // package depend on building the non-test version, so that we + // only report build errors once. Issue #44624. + if imported && ptest != p { + buildTest := b.CompileAction(work.ModeBuild, work.ModeBuild, ptest) + buildP := b.CompileAction(work.ModeBuild, work.ModeBuild, p) + buildTest.Deps = append(buildTest.Deps, buildP) + } + // Use last element of import path, not package name. // They differ when package name is "main". // But if the import path is "command-line-arguments", @@ -1080,6 +1224,8 @@ func declareCoverVars(p *load.Package, files ...string) map[string]*load.CoverVa } var noTestsToRun = []byte("\ntesting: warning: no tests to run\n") +var noFuzzTestsToFuzz = []byte("\ntesting: warning: no fuzz tests to fuzz\n") +var tooManyFuzzTestsToFuzz = []byte("\ntesting: warning: -fuzz matches more than one fuzz test, won't fuzz\n") type runCache struct { disableCache bool // cache should be disabled for this run @@ -1127,10 +1273,10 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work. } var buf bytes.Buffer - if len(pkgArgs) == 0 || (testBench != "") { + if len(pkgArgs) == 0 || testBench != "" || testFuzz != "" { // Stream test output (no buffering) when no package has // been given on the command line (implicit current directory) - // or when benchmarking. + // or when benchmarking or fuzzing. // No change to stdout. } else { // If we're only running a single package under test or if parallelism is @@ -1183,7 +1329,12 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work. testlogArg = []string{"-test.testlogfile=" + a.Objdir + "testlog.txt"} } panicArg := "-test.paniconexit0" - args := str.StringList(execCmd, a.Deps[0].BuiltTarget(), testlogArg, panicArg, testArgs) + fuzzArg := []string{} + if testFuzz != "" { + fuzzCacheDir := filepath.Join(cache.Default().FuzzDir(), a.Package.ImportPath) + fuzzArg = []string{"-test.fuzzcachedir=" + fuzzCacheDir} + } + args := str.StringList(execCmd, a.Deps[0].BuiltTarget(), testlogArg, panicArg, fuzzArg, testArgs) if testCoverProfile != "" { // Write coverage to temporary profile, for merging later. @@ -1276,15 +1427,31 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work. if bytes.HasPrefix(out, noTestsToRun[1:]) || bytes.Contains(out, noTestsToRun) { norun = " [no tests to run]" } + if bytes.HasPrefix(out, noFuzzTestsToFuzz[1:]) || bytes.Contains(out, noFuzzTestsToFuzz) { + norun = " [no fuzz tests to fuzz]" + } + if bytes.HasPrefix(out, tooManyFuzzTestsToFuzz[1:]) || bytes.Contains(out, tooManyFuzzTestsToFuzz) { + norun = "[-fuzz matches more than one fuzz test, won't fuzz]" + } + if len(out) > 0 && !bytes.HasSuffix(out, []byte("\n")) { + // Ensure that the output ends with a newline before the "ok" + // line we're about to print (https://golang.org/issue/49317). + cmd.Stdout.Write([]byte("\n")) + } fmt.Fprintf(cmd.Stdout, "ok \t%s\t%s%s%s\n", a.Package.ImportPath, t, coveragePercentage(out), norun) c.saveOutput(a) } else { base.SetExitStatus(1) - // If there was test output, assume we don't need to print the exit status. - // Buf there's no test output, do print the exit status. if len(out) == 0 { + // If there was no test output, print the exit status so that the reason + // for failure is clear. fmt.Fprintf(cmd.Stdout, "%s\n", err) + } else if !bytes.HasSuffix(out, []byte("\n")) { + // Otherwise, ensure that the output ends with a newline before the FAIL + // line we're about to print (https://golang.org/issue/49317). + cmd.Stdout.Write([]byte("\n")) } + // NOTE(golang.org/issue/37555): test2json reports that a test passes // unless "FAIL" is printed at the beginning of a line. The test may not // actually print that if it panics, exits, or terminates abnormally, @@ -1347,6 +1514,7 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo "-test.run", "-test.short", "-test.timeout", + "-test.failfast", "-test.v": // These are cacheable. // Note that this list is documented above, @@ -1711,9 +1879,23 @@ func builderNoTest(b *work.Builder, ctx context.Context, a *work.Action) error { return nil } -// printExitStatus is the action for printing the exit status +// printExitStatus is the action for printing the final exit status. +// If we are running multiple test targets, print a final "FAIL" +// in case a failure in an early package has already scrolled +// off of the user's terminal. +// (See https://golang.org/issue/30507#issuecomment-470593235.) +// +// In JSON mode, we need to maintain valid JSON output and +// we assume that the test output is being parsed by a tool +// anyway, so the failure will not be missed and would be +// awkward to try to wedge into the JSON stream. +// +// In fuzz mode, we only allow a single package for now +// (see CL 350156 and https://golang.org/issue/46312), +// so there is no possibility of scrolling off and no need +// to print the final status. func printExitStatus(b *work.Builder, ctx context.Context, a *work.Action) error { - if !testJSON && len(pkgArgs) != 0 { + if !testJSON && testFuzz == "" && len(pkgArgs) != 0 { if base.GetExitStatus() != 0 { fmt.Println("FAIL") return nil diff --git a/libgo/go/cmd/go/internal/test/testflag.go b/libgo/go/cmd/go/internal/test/testflag.go index 08f1efa2c0d..b9d1ec91ff7 100644 --- a/libgo/go/cmd/go/internal/test/testflag.go +++ b/libgo/go/cmd/go/internal/test/testflag.go @@ -5,6 +5,10 @@ package test import ( + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/cmdflag" + "cmd/go/internal/work" "errors" "flag" "fmt" @@ -13,11 +17,6 @@ import ( "strconv" "strings" "time" - - "cmd/go/internal/base" - "cmd/go/internal/cfg" - "cmd/go/internal/cmdflag" - "cmd/go/internal/work" ) //go:generate go run ./genflags.go @@ -29,6 +28,7 @@ import ( func init() { work.AddBuildFlags(CmdTest, work.OmitVFlag) + base.AddWorkfileFlag(&CmdTest.Flag) cf := CmdTest.Flag cf.BoolVar(&testC, "c", false, "") @@ -57,6 +57,7 @@ func init() { cf.String("cpu", "", "") cf.StringVar(&testCPUProfile, "cpuprofile", "", "") cf.Bool("failfast", false, "") + cf.StringVar(&testFuzz, "fuzz", "", "") cf.StringVar(&testList, "list", "", "") cf.StringVar(&testMemProfile, "memprofile", "", "") cf.String("memprofilerate", "", "") @@ -67,6 +68,8 @@ func init() { cf.String("run", "", "") cf.Bool("short", false, "") cf.DurationVar(&testTimeout, "timeout", 10*time.Minute, "") + cf.String("fuzztime", "", "") + cf.String("fuzzminimizetime", "", "") cf.StringVar(&testTrace, "trace", "", "") cf.BoolVar(&testV, "v", false, "") cf.Var(&testShuffle, "shuffle", "") @@ -134,6 +137,7 @@ type outputdirFlag struct { func (f *outputdirFlag) String() string { return f.abs } + func (f *outputdirFlag) Set(value string) (err error) { if value == "" { f.abs = "" @@ -142,6 +146,7 @@ func (f *outputdirFlag) Set(value string) (err error) { } return err } + func (f *outputdirFlag) getAbs() string { if f.abs == "" { return base.Cwd() @@ -150,8 +155,12 @@ func (f *outputdirFlag) getAbs() string { } // vetFlag implements the special parsing logic for the -vet flag: -// a comma-separated list, with a distinguished value "off" and -// a boolean tracking whether it was set explicitly. +// a comma-separated list, with distinguished values "all" and +// "off", plus a boolean tracking whether it was set explicitly. +// +// "all" is encoded as vetFlag{true, false, nil}, since it will +// pass no flags to the vet binary, and by default, it runs all +// analyzers. type vetFlag struct { explicit bool off bool @@ -159,7 +168,10 @@ type vetFlag struct { } func (f *vetFlag) String() string { - if f.off { + switch { + case !f.off && !f.explicit && len(f.flags) == 0: + return "all" + case f.off: return "off" } @@ -174,31 +186,45 @@ func (f *vetFlag) String() string { } func (f *vetFlag) Set(value string) error { - if value == "" { + switch { + case value == "": *f = vetFlag{flags: defaultVetFlags} return nil - } - - if value == "off" { - *f = vetFlag{ - explicit: true, - off: true, - } - return nil - } - - if strings.Contains(value, "=") { + case strings.Contains(value, "="): return fmt.Errorf("-vet argument cannot contain equal signs") - } - if strings.Contains(value, " ") { + case strings.Contains(value, " "): return fmt.Errorf("-vet argument is comma-separated list, cannot contain spaces") } + *f = vetFlag{explicit: true} + var single string for _, arg := range strings.Split(value, ",") { - if arg == "" { + switch arg { + case "": return fmt.Errorf("-vet argument contains empty list element") + case "all": + single = arg + *f = vetFlag{explicit: true} + continue + case "off": + single = arg + *f = vetFlag{ + explicit: true, + off: true, + } + continue + default: + if _, ok := passAnalyzersToVet[arg]; !ok { + return fmt.Errorf("-vet argument must be a supported analyzer or a distinguished value; found %s", arg) + } + f.flags = append(f.flags, "-"+arg) } - f.flags = append(f.flags, "-"+arg) + } + if len(f.flags) > 1 && single != "" { + return fmt.Errorf("-vet does not accept %q in a list with other analyzers", single) + } + if len(f.flags) > 1 && single != "" { + return fmt.Errorf("-vet does not accept %q in a list with other analyzers", single) } return nil } @@ -369,7 +395,7 @@ func testFlags(args []string) (packageNames, passToTest []string) { if !testC { buildFlag = "-i" } - fmt.Fprintf(os.Stderr, "go test: unknown flag %s cannot be used with %s\n", firstUnknownFlag, buildFlag) + fmt.Fprintf(os.Stderr, "go: unknown flag %s cannot be used with %s\n", firstUnknownFlag, buildFlag) exitWithUsage() } diff --git a/libgo/go/cmd/go/internal/tool/tool.go b/libgo/go/cmd/go/internal/tool/tool.go index 95c90ea7c8d..4fe4c2baeda 100644 --- a/libgo/go/cmd/go/internal/tool/tool.go +++ b/libgo/go/cmd/go/internal/tool/tool.go @@ -61,7 +61,7 @@ func runTool(ctx context.Context, cmd *base.Command, args []string) { switch { case 'a' <= c && c <= 'z', '0' <= c && c <= '9', c == '_': default: - fmt.Fprintf(os.Stderr, "go tool: bad tool name %q\n", toolName) + fmt.Fprintf(os.Stderr, "go: bad tool name %q\n", toolName) base.SetExitStatus(2) return } @@ -117,14 +117,14 @@ func runTool(ctx context.Context, cmd *base.Command, args []string) { func listTools() { f, err := os.Open(base.ToolDir) if err != nil { - fmt.Fprintf(os.Stderr, "go tool: no tool directory: %s\n", err) + fmt.Fprintf(os.Stderr, "go: no tool directory: %s\n", err) base.SetExitStatus(2) return } defer f.Close() names, err := f.Readdirnames(-1) if err != nil { - fmt.Fprintf(os.Stderr, "go tool: can't read directory: %s\n", err) + fmt.Fprintf(os.Stderr, "go: can't read tool directory: %s\n", err) base.SetExitStatus(2) return } diff --git a/libgo/go/cmd/go/internal/txtar/archive.go b/libgo/go/cmd/go/internal/txtar/archive.go deleted file mode 100644 index 17966848771..00000000000 --- a/libgo/go/cmd/go/internal/txtar/archive.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2018 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 txtar implements a trivial text-based file archive format. -// -// The goals for the format are: -// -// - be trivial enough to create and edit by hand. -// - be able to store trees of text files describing go command test cases. -// - diff nicely in git history and code reviews. -// -// Non-goals include being a completely general archive format, -// storing binary data, storing file modes, storing special files like -// symbolic links, and so on. -// -// Txtar format -// -// A txtar archive is zero or more comment lines and then a sequence of file entries. -// Each file entry begins with a file marker line of the form "-- FILENAME --" -// and is followed by zero or more file content lines making up the file data. -// The comment or file content ends at the next file marker line. -// The file marker line must begin with the three-byte sequence "-- " -// and end with the three-byte sequence " --", but the enclosed -// file name can be surrounding by additional white space, -// all of which is stripped. -// -// If the txtar file is missing a trailing newline on the final line, -// parsers should consider a final newline to be present anyway. -// -// There are no possible syntax errors in a txtar archive. -package txtar - -import ( - "bytes" - "fmt" - "os" - "strings" -) - -// An Archive is a collection of files. -type Archive struct { - Comment []byte - Files []File -} - -// A File is a single file in an archive. -type File struct { - Name string // name of file ("foo/bar.txt") - Data []byte // text content of file -} - -// Format returns the serialized form of an Archive. -// It is assumed that the Archive data structure is well-formed: -// a.Comment and all a.File[i].Data contain no file marker lines, -// and all a.File[i].Name is non-empty. -func Format(a *Archive) []byte { - var buf bytes.Buffer - buf.Write(fixNL(a.Comment)) - for _, f := range a.Files { - fmt.Fprintf(&buf, "-- %s --\n", f.Name) - buf.Write(fixNL(f.Data)) - } - return buf.Bytes() -} - -// ParseFile parses the named file as an archive. -func ParseFile(file string) (*Archive, error) { - data, err := os.ReadFile(file) - if err != nil { - return nil, err - } - return Parse(data), nil -} - -// Parse parses the serialized form of an Archive. -// The returned Archive holds slices of data. -func Parse(data []byte) *Archive { - a := new(Archive) - var name string - a.Comment, name, data = findFileMarker(data) - for name != "" { - f := File{name, nil} - f.Data, name, data = findFileMarker(data) - a.Files = append(a.Files, f) - } - return a -} - -var ( - newlineMarker = []byte("\n-- ") - marker = []byte("-- ") - markerEnd = []byte(" --") -) - -// findFileMarker finds the next file marker in data, -// extracts the file name, and returns the data before the marker, -// the file name, and the data after the marker. -// If there is no next marker, findFileMarker returns before = fixNL(data), name = "", after = nil. -func findFileMarker(data []byte) (before []byte, name string, after []byte) { - var i int - for { - if name, after = isMarker(data[i:]); name != "" { - return data[:i], name, after - } - j := bytes.Index(data[i:], newlineMarker) - if j < 0 { - return fixNL(data), "", nil - } - i += j + 1 // positioned at start of new possible marker - } -} - -// isMarker checks whether data begins with a file marker line. -// If so, it returns the name from the line and the data after the line. -// Otherwise it returns name == "" with an unspecified after. -func isMarker(data []byte) (name string, after []byte) { - if !bytes.HasPrefix(data, marker) { - return "", nil - } - if i := bytes.IndexByte(data, '\n'); i >= 0 { - data, after = data[:i], data[i+1:] - } - if !bytes.HasSuffix(data, markerEnd) { - return "", nil - } - return strings.TrimSpace(string(data[len(marker) : len(data)-len(markerEnd)])), after -} - -// If data is empty or ends in \n, fixNL returns data. -// Otherwise fixNL returns a new slice consisting of data with a final \n added. -func fixNL(data []byte) []byte { - if len(data) == 0 || data[len(data)-1] == '\n' { - return data - } - d := make([]byte, len(data)+1) - copy(d, data) - d[len(data)] = '\n' - return d -} diff --git a/libgo/go/cmd/go/internal/txtar/archive_test.go b/libgo/go/cmd/go/internal/txtar/archive_test.go deleted file mode 100644 index 3f734f67625..00000000000 --- a/libgo/go/cmd/go/internal/txtar/archive_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2018 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 txtar - -import ( - "bytes" - "fmt" - "reflect" - "testing" -) - -var tests = []struct { - name string - text string - parsed *Archive -}{ - { - name: "basic", - text: `comment1 -comment2 --- file1 -- -File 1 text. --- foo --- -More file 1 text. --- file 2 -- -File 2 text. --- empty -- --- noNL -- -hello world`, - parsed: &Archive{ - Comment: []byte("comment1\ncomment2\n"), - Files: []File{ - {"file1", []byte("File 1 text.\n-- foo ---\nMore file 1 text.\n")}, - {"file 2", []byte("File 2 text.\n")}, - {"empty", []byte{}}, - {"noNL", []byte("hello world\n")}, - }, - }, - }, -} - -func Test(t *testing.T) { - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - a := Parse([]byte(tt.text)) - if !reflect.DeepEqual(a, tt.parsed) { - t.Fatalf("Parse: wrong output:\nhave:\n%s\nwant:\n%s", shortArchive(a), shortArchive(tt.parsed)) - } - text := Format(a) - a = Parse(text) - if !reflect.DeepEqual(a, tt.parsed) { - t.Fatalf("Parse after Format: wrong output:\nhave:\n%s\nwant:\n%s", shortArchive(a), shortArchive(tt.parsed)) - } - }) - } -} - -func shortArchive(a *Archive) string { - var buf bytes.Buffer - fmt.Fprintf(&buf, "comment: %q\n", a.Comment) - for _, f := range a.Files { - fmt.Fprintf(&buf, "file %q: %q\n", f.Name, f.Data) - } - return buf.String() -} diff --git a/libgo/go/cmd/go/internal/vcs/vcs.go b/libgo/go/cmd/go/internal/vcs/vcs.go index 91485f6f745..fd521b2eb15 100644 --- a/libgo/go/cmd/go/internal/vcs/vcs.go +++ b/libgo/go/cmd/go/internal/vcs/vcs.go @@ -5,7 +5,7 @@ package vcs import ( - "encoding/json" + "bytes" "errors" "fmt" exec "internal/execabs" @@ -17,8 +17,10 @@ import ( "os" "path/filepath" "regexp" + "strconv" "strings" "sync" + "time" "cmd/go/internal/base" "cmd/go/internal/cfg" @@ -29,11 +31,12 @@ import ( "golang.org/x/mod/module" ) -// A vcsCmd describes how to use a version control system +// A Cmd describes how to use a version control system // like Mercurial, Git, or Subversion. type Cmd struct { - Name string - Cmd string // name of binary to invoke command + Name string + Cmd string // name of binary to invoke command + RootNames []string // filename indicating the root of a checkout directory CreateCmd []string // commands to download a fresh copy of a repository DownloadCmd []string // commands to download updates into an existing repository @@ -48,6 +51,14 @@ type Cmd struct { RemoteRepo func(v *Cmd, rootDir string) (remoteRepo string, err error) ResolveRepo func(v *Cmd, rootDir, remoteRepo string) (realRepo string, err error) + Status func(v *Cmd, rootDir string) (Status, error) +} + +// Status is the current state of a local repository. +type Status struct { + Revision string // Optional. + CommitTime time.Time // Optional. + Uncommitted bool // Required. } var defaultSecureScheme = map[string]bool{ @@ -118,8 +129,9 @@ func vcsByCmd(cmd string) *Cmd { // vcsHg describes how to use Mercurial. var vcsHg = &Cmd{ - Name: "Mercurial", - Cmd: "hg", + Name: "Mercurial", + Cmd: "hg", + RootNames: []string{".hg"}, CreateCmd: []string{"clone -U -- {repo} {dir}"}, DownloadCmd: []string{"pull"}, @@ -139,6 +151,7 @@ var vcsHg = &Cmd{ Scheme: []string{"https", "http", "ssh"}, PingCmd: "identify -- {scheme}://{repo}", RemoteRepo: hgRemoteRepo, + Status: hgStatus, } func hgRemoteRepo(vcsHg *Cmd, rootDir string) (remoteRepo string, err error) { @@ -149,10 +162,64 @@ func hgRemoteRepo(vcsHg *Cmd, rootDir string) (remoteRepo string, err error) { return strings.TrimSpace(string(out)), nil } +func hgStatus(vcsHg *Cmd, rootDir string) (Status, error) { + // Output changeset ID and seconds since epoch. + out, err := vcsHg.runOutputVerboseOnly(rootDir, `log -l1 -T {node}:{date|hgdate}`) + if err != nil { + return Status{}, err + } + + // Successful execution without output indicates an empty repo (no commits). + var rev string + var commitTime time.Time + if len(out) > 0 { + // Strip trailing timezone offset. + if i := bytes.IndexByte(out, ' '); i > 0 { + out = out[:i] + } + rev, commitTime, err = parseRevTime(out) + if err != nil { + return Status{}, err + } + } + + // Also look for untracked files. + out, err = vcsHg.runOutputVerboseOnly(rootDir, "status") + if err != nil { + return Status{}, err + } + uncommitted := len(out) > 0 + + return Status{ + Revision: rev, + CommitTime: commitTime, + Uncommitted: uncommitted, + }, nil +} + +// parseRevTime parses commit details in "revision:seconds" format. +func parseRevTime(out []byte) (string, time.Time, error) { + buf := string(bytes.TrimSpace(out)) + + i := strings.IndexByte(buf, ':') + if i < 1 { + return "", time.Time{}, errors.New("unrecognized VCS tool output") + } + rev := buf[:i] + + secs, err := strconv.ParseInt(string(buf[i+1:]), 10, 64) + if err != nil { + return "", time.Time{}, fmt.Errorf("unrecognized VCS tool output: %v", err) + } + + return rev, time.Unix(secs, 0), nil +} + // vcsGit describes how to use Git. var vcsGit = &Cmd{ - Name: "Git", - Cmd: "git", + Name: "Git", + Cmd: "git", + RootNames: []string{".git"}, CreateCmd: []string{"clone -- {repo} {dir}", "-go-internal-cd {dir} submodule update --init --recursive"}, DownloadCmd: []string{"pull --ff-only", "submodule update --init --recursive"}, @@ -182,6 +249,7 @@ var vcsGit = &Cmd{ PingCmd: "ls-remote {scheme}://{repo}", RemoteRepo: gitRemoteRepo, + Status: gitStatus, } // scpSyntaxRe matches the SCP-like addresses used by Git to access @@ -232,10 +300,40 @@ func gitRemoteRepo(vcsGit *Cmd, rootDir string) (remoteRepo string, err error) { return "", errParse } +func gitStatus(vcsGit *Cmd, rootDir string) (Status, error) { + out, err := vcsGit.runOutputVerboseOnly(rootDir, "status --porcelain") + if err != nil { + return Status{}, err + } + uncommitted := len(out) > 0 + + // "git status" works for empty repositories, but "git show" does not. + // Assume there are no commits in the repo when "git show" fails with + // uncommitted files and skip tagging revision / committime. + var rev string + var commitTime time.Time + out, err = vcsGit.runOutputVerboseOnly(rootDir, "show -s --no-show-signature --format=%H:%ct") + if err != nil && !uncommitted { + return Status{}, err + } else if err == nil { + rev, commitTime, err = parseRevTime(out) + if err != nil { + return Status{}, err + } + } + + return Status{ + Revision: rev, + CommitTime: commitTime, + Uncommitted: uncommitted, + }, nil +} + // vcsBzr describes how to use Bazaar. var vcsBzr = &Cmd{ - Name: "Bazaar", - Cmd: "bzr", + Name: "Bazaar", + Cmd: "bzr", + RootNames: []string{".bzr"}, CreateCmd: []string{"branch -- {repo} {dir}"}, @@ -251,6 +349,7 @@ var vcsBzr = &Cmd{ PingCmd: "info -- {scheme}://{repo}", RemoteRepo: bzrRemoteRepo, ResolveRepo: bzrResolveRepo, + Status: bzrStatus, } func bzrRemoteRepo(vcsBzr *Cmd, rootDir string) (remoteRepo string, err error) { @@ -294,10 +393,68 @@ func bzrResolveRepo(vcsBzr *Cmd, rootDir, remoteRepo string) (realRepo string, e return strings.TrimSpace(out), nil } +func bzrStatus(vcsBzr *Cmd, rootDir string) (Status, error) { + outb, err := vcsBzr.runOutputVerboseOnly(rootDir, "version-info") + if err != nil { + return Status{}, err + } + out := string(outb) + + // Expect (non-empty repositories only): + // + // revision-id: gopher@gopher.net-20211021072330-qshok76wfypw9lpm + // date: 2021-09-21 12:00:00 +1000 + // ... + var rev string + var commitTime time.Time + + for _, line := range strings.Split(out, "\n") { + i := strings.IndexByte(line, ':') + if i < 0 { + continue + } + key := line[:i] + value := strings.TrimSpace(line[i+1:]) + + switch key { + case "revision-id": + rev = value + case "date": + var err error + commitTime, err = time.Parse("2006-01-02 15:04:05 -0700", value) + if err != nil { + return Status{}, errors.New("unable to parse output of bzr version-info") + } + } + } + + outb, err = vcsBzr.runOutputVerboseOnly(rootDir, "status") + if err != nil { + return Status{}, err + } + + // Skip warning when working directory is set to an older revision. + if bytes.HasPrefix(outb, []byte("working tree is out of date")) { + i := bytes.IndexByte(outb, '\n') + if i < 0 { + i = len(outb) + } + outb = outb[:i] + } + uncommitted := len(outb) > 0 + + return Status{ + Revision: rev, + CommitTime: commitTime, + Uncommitted: uncommitted, + }, nil +} + // vcsSvn describes how to use Subversion. var vcsSvn = &Cmd{ - Name: "Subversion", - Cmd: "svn", + Name: "Subversion", + Cmd: "svn", + RootNames: []string{".svn"}, CreateCmd: []string{"checkout -- {repo} {dir}"}, DownloadCmd: []string{"update"}, @@ -346,8 +503,9 @@ const fossilRepoName = ".fossil" // vcsFossil describes how to use Fossil (fossil-scm.org) var vcsFossil = &Cmd{ - Name: "Fossil", - Cmd: "fossil", + Name: "Fossil", + Cmd: "fossil", + RootNames: []string{".fslckout", "_FOSSIL_"}, CreateCmd: []string{"-go-internal-mkdir {dir} clone -- {repo} " + filepath.Join("{dir}", fossilRepoName), "-go-internal-cd {dir} open .fossil"}, DownloadCmd: []string{"up"}, @@ -358,6 +516,7 @@ var vcsFossil = &Cmd{ Scheme: []string{"https", "http"}, RemoteRepo: fossilRemoteRepo, + Status: fossilStatus, } func fossilRemoteRepo(vcsFossil *Cmd, rootDir string) (remoteRepo string, err error) { @@ -368,6 +527,60 @@ func fossilRemoteRepo(vcsFossil *Cmd, rootDir string) (remoteRepo string, err er return strings.TrimSpace(string(out)), nil } +var errFossilInfo = errors.New("unable to parse output of fossil info") + +func fossilStatus(vcsFossil *Cmd, rootDir string) (Status, error) { + outb, err := vcsFossil.runOutputVerboseOnly(rootDir, "info") + if err != nil { + return Status{}, err + } + out := string(outb) + + // Expect: + // ... + // checkout: 91ed71f22c77be0c3e250920f47bfd4e1f9024d2 2021-09-21 12:00:00 UTC + // ... + + // Extract revision and commit time. + // Ensure line ends with UTC (known timezone offset). + const prefix = "\ncheckout:" + const suffix = " UTC" + i := strings.Index(out, prefix) + if i < 0 { + return Status{}, errFossilInfo + } + checkout := out[i+len(prefix):] + i = strings.Index(checkout, suffix) + if i < 0 { + return Status{}, errFossilInfo + } + checkout = strings.TrimSpace(checkout[:i]) + + i = strings.IndexByte(checkout, ' ') + if i < 0 { + return Status{}, errFossilInfo + } + rev := checkout[:i] + + commitTime, err := time.ParseInLocation("2006-01-02 15:04:05", checkout[i+1:], time.UTC) + if err != nil { + return Status{}, fmt.Errorf("%v: %v", errFossilInfo, err) + } + + // Also look for untracked changes. + outb, err = vcsFossil.runOutputVerboseOnly(rootDir, "changes --differ") + if err != nil { + return Status{}, err + } + uncommitted := len(outb) > 0 + + return Status{ + Revision: rev, + CommitTime: commitTime, + Uncommitted: uncommitted, + }, nil +} + func (v *Cmd) String() string { return v.Name } @@ -395,6 +608,12 @@ func (v *Cmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error return v.run1(dir, cmd, keyval, true) } +// runOutputVerboseOnly is like runOutput but only generates error output to +// standard error in verbose mode. +func (v *Cmd) runOutputVerboseOnly(dir string, cmd string, keyval ...string) ([]byte, error) { + return v.run1(dir, cmd, keyval, false) +} + // run1 is the generalized implementation of run and runOutput. func (v *Cmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) { m := make(map[string]string) @@ -550,58 +769,86 @@ type vcsPath struct { // FromDir inspects dir and its parents to determine the // version control system and code repository to use. -// On return, root is the import path -// corresponding to the root of the repository. -func FromDir(dir, srcRoot string) (vcs *Cmd, root string, err error) { +// If no repository is found, FromDir returns an error +// equivalent to os.ErrNotExist. +func FromDir(dir, srcRoot string, allowNesting bool) (repoDir string, vcsCmd *Cmd, err error) { // Clean and double-check that dir is in (a subdirectory of) srcRoot. dir = filepath.Clean(dir) - srcRoot = filepath.Clean(srcRoot) - if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator { - return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot) + if srcRoot != "" { + srcRoot = filepath.Clean(srcRoot) + if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator { + return "", nil, fmt.Errorf("directory %q is outside source root %q", dir, srcRoot) + } } - var vcsRet *Cmd - var rootRet string - origDir := dir for len(dir) > len(srcRoot) { for _, vcs := range vcsList { - if _, err := os.Stat(filepath.Join(dir, "."+vcs.Cmd)); err == nil { - root := filepath.ToSlash(dir[len(srcRoot)+1:]) - // Record first VCS we find, but keep looking, - // to detect mistakes like one kind of VCS inside another. - if vcsRet == nil { - vcsRet = vcs - rootRet = root + if _, err := statAny(dir, vcs.RootNames); err == nil { + // Record first VCS we find. + // If allowNesting is false (as it is in GOPATH), keep looking for + // repositories in parent directories and report an error if one is + // found to mitigate VCS injection attacks. + if vcsCmd == nil { + vcsCmd = vcs + repoDir = dir + if allowNesting { + return repoDir, vcsCmd, nil + } continue } // Allow .git inside .git, which can arise due to submodules. - if vcsRet == vcs && vcs.Cmd == "git" { + if vcsCmd == vcs && vcs.Cmd == "git" { continue } // Otherwise, we have one VCS inside a different VCS. - return nil, "", fmt.Errorf("directory %q uses %s, but parent %q uses %s", - filepath.Join(srcRoot, rootRet), vcsRet.Cmd, filepath.Join(srcRoot, root), vcs.Cmd) + return "", nil, fmt.Errorf("directory %q uses %s, but parent %q uses %s", + repoDir, vcsCmd.Cmd, dir, vcs.Cmd) } } // Move to parent. ndir := filepath.Dir(dir) if len(ndir) >= len(dir) { - // Shouldn't happen, but just in case, stop. break } dir = ndir } + if vcsCmd == nil { + return "", nil, &vcsNotFoundError{dir: origDir} + } + return repoDir, vcsCmd, nil +} - if vcsRet != nil { - if err := checkGOVCS(vcsRet, rootRet); err != nil { - return nil, "", err +// statAny provides FileInfo for the first filename found in the directory. +// Otherwise, it returns the last error seen. +func statAny(dir string, filenames []string) (os.FileInfo, error) { + if len(filenames) == 0 { + return nil, errors.New("invalid argument: no filenames provided") + } + + var err error + var fi os.FileInfo + for _, name := range filenames { + fi, err = os.Stat(filepath.Join(dir, name)) + if err == nil { + return fi, nil } - return vcsRet, rootRet, nil } - return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir) + return nil, err +} + +type vcsNotFoundError struct { + dir string +} + +func (e *vcsNotFoundError) Error() string { + return fmt.Sprintf("directory %q is not using a known version control system", e.dir) +} + +func (e *vcsNotFoundError) Is(err error) bool { + return err == os.ErrNotExist } // A govcsRule is a single GOVCS rule like private:hg|svn. @@ -707,7 +954,11 @@ var defaultGOVCS = govcsConfig{ {"public", []string{"git", "hg"}}, } -func checkGOVCS(vcs *Cmd, root string) error { +// CheckGOVCS checks whether the policy defined by the environment variable +// GOVCS allows the given vcs command to be used with the given repository +// root path. Note that root may not be a real package or module path; it's +// the same as the root path in the go-import meta tag. +func CheckGOVCS(vcs *Cmd, root string) error { if vcs == vcsMod { // Direct module (proxy protocol) fetches don't // involve an external version control system @@ -745,7 +996,7 @@ func CheckNested(vcs *Cmd, dir, srcRoot string) error { otherDir := dir for len(otherDir) > len(srcRoot) { for _, otherVCS := range vcsList { - if _, err := os.Stat(filepath.Join(otherDir, "."+otherVCS.Cmd)); err == nil { + if _, err := statAny(otherDir, otherVCS.RootNames); err == nil { // Allow expected vcs in original dir. if otherDir == dir && otherVCS == vcs { continue @@ -885,7 +1136,7 @@ func repoRootFromVCSPaths(importPath string, security web.SecurityMode, vcsPaths if vcs == nil { return nil, fmt.Errorf("unknown version control system %q", match["vcs"]) } - if err := checkGOVCS(vcs, match["root"]); err != nil { + if err := CheckGOVCS(vcs, match["root"]); err != nil { return nil, err } var repoURL string @@ -1012,7 +1263,7 @@ func repoRootForImportDynamic(importPath string, mod ModuleMode, security web.Se } } - if err := checkGOVCS(vcs, mmi.Prefix); err != nil { + if err := CheckGOVCS(vcs, mmi.Prefix); err != nil { return nil, err } @@ -1063,7 +1314,7 @@ func metaImportsForPrefix(importPrefix string, mod ModuleMode, security web.Secu return res, nil } - resi, _, _ := fetchGroup.Do(importPrefix, func() (resi interface{}, err error) { + resi, _, _ := fetchGroup.Do(importPrefix, func() (resi any, err error) { fetchCacheMu.Lock() if res, ok := fetchCache[importPrefix]; ok { fetchCacheMu.Unlock() @@ -1189,8 +1440,9 @@ var vcsPaths = []*vcsPath{ { pathPrefix: "bitbucket.org", regexp: lazyregexp.New(`^(?P<root>bitbucket\.org/(?P<bitname>[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`), + vcs: "git", repo: "https://{root}", - check: bitbucketVCS, + check: noVCSSuffix, }, // IBM DevOps Services (JazzHub) @@ -1262,56 +1514,6 @@ func noVCSSuffix(match map[string]string) error { return nil } -// bitbucketVCS determines the version control system for a -// Bitbucket repository, by using the Bitbucket API. -func bitbucketVCS(match map[string]string) error { - if err := noVCSSuffix(match); err != nil { - return err - } - - var resp struct { - SCM string `json:"scm"` - } - url := &urlpkg.URL{ - Scheme: "https", - Host: "api.bitbucket.org", - Path: expand(match, "/2.0/repositories/{bitname}"), - RawQuery: "fields=scm", - } - data, err := web.GetBytes(url) - if err != nil { - if httpErr, ok := err.(*web.HTTPError); ok && httpErr.StatusCode == 403 { - // this may be a private repository. If so, attempt to determine which - // VCS it uses. See issue 5375. - root := match["root"] - for _, vcs := range []string{"git", "hg"} { - if vcsByCmd(vcs).Ping("https", root) == nil { - resp.SCM = vcs - break - } - } - } - - if resp.SCM == "" { - return err - } - } else { - if err := json.Unmarshal(data, &resp); err != nil { - return fmt.Errorf("decoding %s: %v", url, err) - } - } - - if vcsByCmd(resp.SCM) != nil { - match["vcs"] = resp.SCM - if resp.SCM == "git" { - match["repo"] += ".git" - } - return nil - } - - return fmt.Errorf("unable to detect version control system for bitbucket.org/ path") -} - // launchpadVCS solves the ambiguity for "lp.net/project/foo". In this case, // "foo" could be a series name registered in Launchpad with its own branch, // and it could also be the name of a directory within the main project @@ -1340,7 +1542,7 @@ type importError struct { err error } -func importErrorf(path, format string, args ...interface{}) error { +func importErrorf(path, format string, args ...any) error { err := &importError{importPath: path, err: fmt.Errorf(format, args...)} if errStr := err.Error(); !strings.Contains(errStr, path) { panic(fmt.Sprintf("path %q not in error %q", path, errStr)) diff --git a/libgo/go/cmd/go/internal/vcs/vcs_test.go b/libgo/go/cmd/go/internal/vcs/vcs_test.go index c5c7a3283bc..943d520d547 100644 --- a/libgo/go/cmd/go/internal/vcs/vcs_test.go +++ b/libgo/go/cmd/go/internal/vcs/vcs_test.go @@ -6,9 +6,9 @@ package vcs import ( "errors" + "fmt" "internal/testenv" "os" - "path" "path/filepath" "strings" "testing" @@ -183,6 +183,13 @@ func TestRepoRootForImportPath(t *testing.T) { "chiselapp.com/user/kyle/fossilgg", nil, }, + { + "bitbucket.org/workspace/pkgname", + &RepoRoot{ + VCS: vcsGit, + Repo: "https://bitbucket.org/workspace/pkgname", + }, + }, } for _, test := range tests { @@ -205,7 +212,8 @@ func TestRepoRootForImportPath(t *testing.T) { } } -// Test that vcsFromDir correctly inspects a given directory and returns the right VCS and root. +// Test that vcs.FromDir correctly inspects a given directory and returns the +// right VCS and repo directory. func TestFromDir(t *testing.T) { tempDir, err := os.MkdirTemp("", "vcstest") if err != nil { @@ -214,36 +222,35 @@ func TestFromDir(t *testing.T) { defer os.RemoveAll(tempDir) for j, vcs := range vcsList { - dir := filepath.Join(tempDir, "example.com", vcs.Name, "."+vcs.Cmd) - if j&1 == 0 { - err := os.MkdirAll(dir, 0755) - if err != nil { - t.Fatal(err) + for r, rootName := range vcs.RootNames { + vcsName := fmt.Sprint(vcs.Name, r) + dir := filepath.Join(tempDir, "example.com", vcsName, rootName) + if j&1 == 0 { + err := os.MkdirAll(dir, 0755) + if err != nil { + t.Fatal(err) + } + } else { + err := os.MkdirAll(filepath.Dir(dir), 0755) + if err != nil { + t.Fatal(err) + } + f, err := os.Create(dir) + if err != nil { + t.Fatal(err) + } + f.Close() } - } else { - err := os.MkdirAll(filepath.Dir(dir), 0755) + + wantRepoDir := filepath.Dir(dir) + gotRepoDir, gotVCS, err := FromDir(dir, tempDir, false) if err != nil { - t.Fatal(err) + t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err) + continue } - f, err := os.Create(dir) - if err != nil { - t.Fatal(err) + if gotRepoDir != wantRepoDir || gotVCS.Name != vcs.Name { + t.Errorf("FromDir(%q, %q) = RepoDir(%s), VCS(%s); want RepoDir(%s), VCS(%s)", dir, tempDir, gotRepoDir, gotVCS.Name, wantRepoDir, vcs.Name) } - f.Close() - } - - want := RepoRoot{ - VCS: vcs, - Root: path.Join("example.com", vcs.Name), - } - var got RepoRoot - got.VCS, got.Root, err = FromDir(dir, tempDir) - if err != nil { - t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err) - continue - } - if got.VCS.Name != want.VCS.Name || got.Root != want.Root { - t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.VCS, got.Root, want.VCS, want.Root) } } } diff --git a/libgo/go/cmd/go/internal/version/exe.go b/libgo/go/cmd/go/internal/version/exe.go deleted file mode 100644 index 0e7deef1491..00000000000 --- a/libgo/go/cmd/go/internal/version/exe.go +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 2019 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 version - -import ( - "bytes" - "debug/elf" - "debug/macho" - "debug/pe" - "fmt" - "internal/xcoff" - "io" - "os" -) - -// An exe is a generic interface to an OS executable (ELF, Mach-O, PE, XCOFF). -type exe interface { - // Close closes the underlying file. - Close() error - - // ReadData reads and returns up to size byte starting at virtual address addr. - ReadData(addr, size uint64) ([]byte, error) - - // DataStart returns the writable data segment start address. - DataStart() uint64 -} - -// openExe opens file and returns it as an exe. -func openExe(file string) (exe, error) { - f, err := os.Open(file) - if err != nil { - return nil, err - } - data := make([]byte, 16) - if _, err := io.ReadFull(f, data); err != nil { - return nil, err - } - f.Seek(0, 0) - if bytes.HasPrefix(data, []byte("\x7FELF")) { - e, err := elf.NewFile(f) - if err != nil { - f.Close() - return nil, err - } - return &elfExe{f, e}, nil - } - if bytes.HasPrefix(data, []byte("MZ")) { - e, err := pe.NewFile(f) - if err != nil { - f.Close() - return nil, err - } - return &peExe{f, e}, nil - } - if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) { - e, err := macho.NewFile(f) - if err != nil { - f.Close() - return nil, err - } - return &machoExe{f, e}, nil - } - if bytes.HasPrefix(data, []byte{0x01, 0xDF}) || bytes.HasPrefix(data, []byte{0x01, 0xF7}) { - e, err := xcoff.NewFile(f) - if err != nil { - f.Close() - return nil, err - } - return &xcoffExe{f, e}, nil - - } - return nil, fmt.Errorf("unrecognized executable format") -} - -// elfExe is the ELF implementation of the exe interface. -type elfExe struct { - os *os.File - f *elf.File -} - -func (x *elfExe) Close() error { - return x.os.Close() -} - -func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) { - for _, prog := range x.f.Progs { - if prog.Vaddr <= addr && addr <= prog.Vaddr+prog.Filesz-1 { - n := prog.Vaddr + prog.Filesz - addr - if n > size { - n = size - } - data := make([]byte, n) - _, err := prog.ReadAt(data, int64(addr-prog.Vaddr)) - if err != nil { - return nil, err - } - return data, nil - } - } - return nil, fmt.Errorf("address not mapped") -} - -func (x *elfExe) DataStart() uint64 { - for _, s := range x.f.Sections { - if s.Name == ".go.buildinfo" { - return s.Addr - } - } - for _, p := range x.f.Progs { - if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_X|elf.PF_W) == elf.PF_W { - return p.Vaddr - } - } - return 0 -} - -// peExe is the PE (Windows Portable Executable) implementation of the exe interface. -type peExe struct { - os *os.File - f *pe.File -} - -func (x *peExe) Close() error { - return x.os.Close() -} - -func (x *peExe) imageBase() uint64 { - switch oh := x.f.OptionalHeader.(type) { - case *pe.OptionalHeader32: - return uint64(oh.ImageBase) - case *pe.OptionalHeader64: - return oh.ImageBase - } - return 0 -} - -func (x *peExe) ReadData(addr, size uint64) ([]byte, error) { - addr -= x.imageBase() - for _, sect := range x.f.Sections { - if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) { - n := uint64(sect.VirtualAddress+sect.Size) - addr - if n > size { - n = size - } - data := make([]byte, n) - _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress))) - if err != nil { - return nil, err - } - return data, nil - } - } - return nil, fmt.Errorf("address not mapped") -} - -func (x *peExe) DataStart() uint64 { - // Assume data is first writable section. - const ( - IMAGE_SCN_CNT_CODE = 0x00000020 - IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 - IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 - IMAGE_SCN_MEM_EXECUTE = 0x20000000 - IMAGE_SCN_MEM_READ = 0x40000000 - IMAGE_SCN_MEM_WRITE = 0x80000000 - IMAGE_SCN_MEM_DISCARDABLE = 0x2000000 - IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000 - IMAGE_SCN_ALIGN_32BYTES = 0x600000 - ) - for _, sect := range x.f.Sections { - if sect.VirtualAddress != 0 && sect.Size != 0 && - sect.Characteristics&^IMAGE_SCN_ALIGN_32BYTES == IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE { - return uint64(sect.VirtualAddress) + x.imageBase() - } - } - return 0 -} - -// machoExe is the Mach-O (Apple macOS/iOS) implementation of the exe interface. -type machoExe struct { - os *os.File - f *macho.File -} - -func (x *machoExe) Close() error { - return x.os.Close() -} - -func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) { - for _, load := range x.f.Loads { - seg, ok := load.(*macho.Segment) - if !ok { - continue - } - if seg.Addr <= addr && addr <= seg.Addr+seg.Filesz-1 { - if seg.Name == "__PAGEZERO" { - continue - } - n := seg.Addr + seg.Filesz - addr - if n > size { - n = size - } - data := make([]byte, n) - _, err := seg.ReadAt(data, int64(addr-seg.Addr)) - if err != nil { - return nil, err - } - return data, nil - } - } - return nil, fmt.Errorf("address not mapped") -} - -func (x *machoExe) DataStart() uint64 { - // Look for section named "__go_buildinfo". - for _, sec := range x.f.Sections { - if sec.Name == "__go_buildinfo" { - return sec.Addr - } - } - // Try the first non-empty writable segment. - const RW = 3 - for _, load := range x.f.Loads { - seg, ok := load.(*macho.Segment) - if ok && seg.Addr != 0 && seg.Filesz != 0 && seg.Prot == RW && seg.Maxprot == RW { - return seg.Addr - } - } - return 0 -} - -// xcoffExe is the XCOFF (AIX eXtended COFF) implementation of the exe interface. -type xcoffExe struct { - os *os.File - f *xcoff.File -} - -func (x *xcoffExe) Close() error { - return x.os.Close() -} - -func (x *xcoffExe) ReadData(addr, size uint64) ([]byte, error) { - for _, sect := range x.f.Sections { - if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) { - n := uint64(sect.VirtualAddress+sect.Size) - addr - if n > size { - n = size - } - data := make([]byte, n) - _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress))) - if err != nil { - return nil, err - } - return data, nil - } - } - return nil, fmt.Errorf("address not mapped") -} - -func (x *xcoffExe) DataStart() uint64 { - return x.f.SectionByType(xcoff.STYP_DATA).VirtualAddress -} diff --git a/libgo/go/cmd/go/internal/version/version.go b/libgo/go/cmd/go/internal/version/version.go index 58cbd32e78d..52502e95c6d 100644 --- a/libgo/go/cmd/go/internal/version/version.go +++ b/libgo/go/cmd/go/internal/version/version.go @@ -8,7 +8,8 @@ package version import ( "bytes" "context" - "encoding/binary" + "debug/buildinfo" + "errors" "fmt" "io/fs" "os" @@ -62,8 +63,14 @@ func runVersion(ctx context.Context, cmd *base.Command, args []string) { // a reasonable use case. For example, imagine GOFLAGS=-v to // turn "verbose mode" on for all Go commands, which should not // break "go version". - if (!base.InGOFLAGS("-m") && *versionM) || (!base.InGOFLAGS("-v") && *versionV) { - fmt.Fprintf(os.Stderr, "go version: flags can only be used with arguments\n") + var argOnlyFlag string + if !base.InGOFLAGS("-m") && *versionM { + argOnlyFlag = "-m" + } else if !base.InGOFLAGS("-v") && *versionV { + argOnlyFlag = "-v" + } + if argOnlyFlag != "" { + fmt.Fprintf(os.Stderr, "go: 'go version' only accepts %s flag with arguments\n", argOnlyFlag) base.SetExitStatus(2) return } @@ -135,90 +142,26 @@ func scanFile(file string, info fs.FileInfo, mustPrint bool) { return } - x, err := openExe(file) + bi, err := buildinfo.ReadFile(file) if err != nil { if mustPrint { - fmt.Fprintf(os.Stderr, "%s: %v\n", file, err) - } - return - } - defer x.Close() - - vers, mod := findVers(x) - if vers == "" { - if mustPrint { - fmt.Fprintf(os.Stderr, "%s: go version not found\n", file) + if pathErr := (*os.PathError)(nil); errors.As(err, &pathErr) && filepath.Clean(pathErr.Path) == filepath.Clean(file) { + fmt.Fprintf(os.Stderr, "%v\n", file) + } else { + fmt.Fprintf(os.Stderr, "%s: %v\n", file, err) + } } return } - fmt.Printf("%s: %s\n", file, vers) - if *versionM && mod != "" { - fmt.Printf("\t%s\n", strings.ReplaceAll(mod[:len(mod)-1], "\n", "\n\t")) - } -} - -// The build info blob left by the linker is identified by -// a 16-byte header, consisting of buildInfoMagic (14 bytes), -// the binary's pointer size (1 byte), -// and whether the binary is big endian (1 byte). -var buildInfoMagic = []byte("\xff Go buildinf:") - -// findVers finds and returns the Go version and module version information -// in the executable x. -func findVers(x exe) (vers, mod string) { - // Read the first 64kB of text to find the build info blob. - text := x.DataStart() - data, err := x.ReadData(text, 64*1024) + fmt.Printf("%s: %s\n", file, bi.GoVersion) + bi.GoVersion = "" // suppress printing go version again + mod, err := bi.MarshalText() if err != nil { + fmt.Fprintf(os.Stderr, "%s: formatting build info: %v\n", file, err) return } - for ; !bytes.HasPrefix(data, buildInfoMagic); data = data[32:] { - if len(data) < 32 { - return - } - } - - // Decode the blob. - ptrSize := int(data[14]) - bigEndian := data[15] != 0 - var bo binary.ByteOrder - if bigEndian { - bo = binary.BigEndian - } else { - bo = binary.LittleEndian - } - var readPtr func([]byte) uint64 - if ptrSize == 4 { - readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) } - } else { - readPtr = bo.Uint64 - } - vers = readString(x, ptrSize, readPtr, readPtr(data[16:])) - if vers == "" { - return - } - mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:])) - if len(mod) >= 33 && mod[len(mod)-17] == '\n' { - // Strip module framing. - mod = mod[16 : len(mod)-16] - } else { - mod = "" - } - return -} - -// readString returns the string at address addr in the executable x. -func readString(x exe, ptrSize int, readPtr func([]byte) uint64, addr uint64) string { - hdr, err := x.ReadData(addr, uint64(2*ptrSize)) - if err != nil || len(hdr) < 2*ptrSize { - return "" - } - dataAddr := readPtr(hdr) - dataLen := readPtr(hdr[ptrSize:]) - data, err := x.ReadData(dataAddr, dataLen) - if err != nil || uint64(len(data)) < dataLen { - return "" + if *versionM && len(mod) > 0 { + fmt.Printf("\t%s\n", bytes.ReplaceAll(mod[:len(mod)-1], []byte("\n"), []byte("\n\t"))) } - return string(data) } diff --git a/libgo/go/cmd/go/internal/vet/vet.go b/libgo/go/cmd/go/internal/vet/vet.go index 1d419dddb98..88b3c570a03 100644 --- a/libgo/go/cmd/go/internal/vet/vet.go +++ b/libgo/go/cmd/go/internal/vet/vet.go @@ -103,7 +103,7 @@ func runVet(ctx context.Context, cmd *base.Command, args []string) { continue } if len(ptest.GoFiles) == 0 && len(ptest.CgoFiles) == 0 && pxtest == nil { - base.Errorf("go vet %s: no Go files in %s", p.ImportPath, p.Dir) + base.Errorf("go: can't vet %s: no Go files in %s", p.ImportPath, p.Dir) continue } if len(ptest.GoFiles) > 0 || len(ptest.CgoFiles) > 0 { diff --git a/libgo/go/cmd/go/internal/vet/vetflag.go b/libgo/go/cmd/go/internal/vet/vetflag.go index b5b3c462ff2..3551a5997c5 100644 --- a/libgo/go/cmd/go/internal/vet/vetflag.go +++ b/libgo/go/cmd/go/internal/vet/vetflag.go @@ -82,7 +82,7 @@ func vetFlags(args []string) (passToVet, packageNames []string) { vetcmd := exec.Command(tool, "-flags") vetcmd.Stdout = out if err := vetcmd.Run(); err != nil { - fmt.Fprintf(os.Stderr, "go vet: can't execute %s -flags: %v\n", tool, err) + fmt.Fprintf(os.Stderr, "go: can't execute %s -flags: %v\n", tool, err) base.SetExitStatus(2) base.Exit() } @@ -92,7 +92,7 @@ func vetFlags(args []string) (passToVet, packageNames []string) { Usage string } if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil { - fmt.Fprintf(os.Stderr, "go vet: can't unmarshal JSON from %s -flags: %v", tool, err) + fmt.Fprintf(os.Stderr, "go: can't unmarshal JSON from %s -flags: %v", tool, err) base.SetExitStatus(2) base.Exit() } diff --git a/libgo/go/cmd/go/internal/web/bootstrap.go b/libgo/go/cmd/go/internal/web/bootstrap.go index 08686cdfcf9..ab88e9e4781 100644 --- a/libgo/go/cmd/go/internal/web/bootstrap.go +++ b/libgo/go/cmd/go/internal/web/bootstrap.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build cmd_go_bootstrap -// +build cmd_go_bootstrap // This code is compiled only into the bootstrap 'go' binary. // These stubs avoid importing packages with large dependency diff --git a/libgo/go/cmd/go/internal/web/http.go b/libgo/go/cmd/go/internal/web/http.go index f177278eba1..a92326db01e 100644 --- a/libgo/go/cmd/go/internal/web/http.go +++ b/libgo/go/cmd/go/internal/web/http.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !cmd_go_bootstrap -// +build !cmd_go_bootstrap // This code is compiled into the real 'go' binary, but it is not // compiled into the binary that is built during all.bash, so as @@ -17,6 +16,7 @@ import ( "errors" "fmt" "mime" + "net" "net/http" urlpkg "net/url" "os" @@ -84,8 +84,15 @@ func get(security SecurityMode, url *urlpkg.URL) (*Response, error) { if url.Host == "localhost.localdev" { return nil, fmt.Errorf("no such host localhost.localdev") } - if os.Getenv("TESTGONETWORK") == "panic" && !strings.HasPrefix(url.Host, "127.0.0.1") && !strings.HasPrefix(url.Host, "0.0.0.0") { - panic("use of network: " + url.String()) + if os.Getenv("TESTGONETWORK") == "panic" { + host := url.Host + if h, _, err := net.SplitHostPort(url.Host); err == nil && h != "" { + host = h + } + addr := net.ParseIP(host) + if addr == nil || (!addr.IsLoopback() && !addr.IsUnspecified()) { + panic("use of network: " + url.String()) + } } fetch := func(url *urlpkg.URL) (*urlpkg.URL, *http.Response, error) { diff --git a/libgo/go/cmd/go/internal/web/url_other.go b/libgo/go/cmd/go/internal/web/url_other.go index 453af402b43..84bbd72820f 100644 --- a/libgo/go/cmd/go/internal/web/url_other.go +++ b/libgo/go/cmd/go/internal/web/url_other.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows -// +build !windows package web diff --git a/libgo/go/cmd/go/internal/web/url_other_test.go b/libgo/go/cmd/go/internal/web/url_other_test.go index 4d6ed2ec7f8..5c197de800d 100644 --- a/libgo/go/cmd/go/internal/web/url_other_test.go +++ b/libgo/go/cmd/go/internal/web/url_other_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows -// +build !windows package web diff --git a/libgo/go/cmd/go/internal/work/action.go b/libgo/go/cmd/go/internal/work/action.go index 2e00c0cb126..ba82e8ff225 100644 --- a/libgo/go/cmd/go/internal/work/action.go +++ b/libgo/go/cmd/go/internal/work/action.go @@ -38,7 +38,7 @@ type Builder struct { actionCache map[cacheKey]*Action // a cache of already-constructed actions mkdirCache map[string]bool // a cache of created directories flagCache map[[2]string]bool // a cache of supported compiler flags - Print func(args ...interface{}) (int, error) + Print func(args ...any) (int, error) IsCmdList bool // running as part of go list; set p.Stale and additional fields below NeedError bool // list needs p.Error @@ -121,8 +121,8 @@ type actionQueue []*Action func (q *actionQueue) Len() int { return len(*q) } func (q *actionQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] } func (q *actionQueue) Less(i, j int) bool { return (*q)[i].priority < (*q)[j].priority } -func (q *actionQueue) Push(x interface{}) { *q = append(*q, x.(*Action)) } -func (q *actionQueue) Pop() interface{} { +func (q *actionQueue) Push(x any) { *q = append(*q, x.(*Action)) } +func (q *actionQueue) Pop() any { n := len(*q) - 1 x := (*q)[n] *q = (*q)[:n] @@ -242,7 +242,7 @@ const ( ) func (b *Builder) Init() { - b.Print = func(a ...interface{}) (int, error) { + b.Print = func(a ...any) (int, error) { return fmt.Fprint(os.Stderr, a...) } b.actionCache = make(map[cacheKey]*Action) @@ -295,14 +295,14 @@ func (b *Builder) Init() { } if err := CheckGOOSARCHPair(cfg.Goos, cfg.Goarch); err != nil { - fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err) + fmt.Fprintf(os.Stderr, "go: %v\n", err) base.SetExitStatus(2) base.Exit() } for _, tag := range cfg.BuildContext.BuildTags { if strings.Contains(tag, ",") { - fmt.Fprintf(os.Stderr, "cmd/go: -tags space-separated list contains comma\n") + fmt.Fprintf(os.Stderr, "go: -tags space-separated list contains comma\n") base.SetExitStatus(2) base.Exit() } diff --git a/libgo/go/cmd/go/internal/work/build.go b/libgo/go/cmd/go/internal/work/build.go index 0ed2389cd5a..1c278d3d992 100644 --- a/libgo/go/cmd/go/internal/work/build.go +++ b/libgo/go/cmd/go/internal/work/build.go @@ -68,13 +68,16 @@ and test commands: The default is GOMAXPROCS, normally the number of CPUs available. -race enable data race detection. - Supported only on linux/amd64, freebsd/amd64, darwin/amd64, windows/amd64, + Supported only on linux/amd64, freebsd/amd64, darwin/amd64, darwin/arm64, windows/amd64, linux/ppc64le and linux/arm64 (only for 48-bit VMA). -msan enable interoperation with memory sanitizer. Supported only on linux/amd64, linux/arm64 and only with Clang/LLVM as the host C compiler. On linux/arm64, pie build mode will be used. + -asan + enable interoperation with address sanitizer. + Supported only on linux/arm64, linux/amd64. -v print the names of packages as they are compiled. -work @@ -87,6 +90,12 @@ and test commands: arguments to pass on each go tool asm invocation. -buildmode mode build mode to use. See 'go help buildmode' for more. + -buildvcs + Whether to stamp binaries with version control information. By default, + version control information is stamped into a binary if the main package + and the main module containing it are in the repository containing the + current directory (if there is a repository). Use -buildvcs=false to + omit version control information. -compiler name name of compiler to use, as in runtime.Compiler (gccgo or gc). -gccgoflags '[pattern=]arg list' @@ -98,8 +107,8 @@ and test commands: in order to keep output separate from default builds. If using the -race flag, the install suffix is automatically set to race or, if set explicitly, has _race appended to it. Likewise for the -msan - flag. Using a -buildmode option that requires non-default compile flags - has a similar effect. + and -asan flags. Using a -buildmode option that requires non-default compile + flags has a similar effect. -ldflags '[pattern=]arg list' arguments to pass on each go tool link invocation. -linkshared @@ -121,6 +130,14 @@ and test commands: directory, but it is not accessed. When -modfile is specified, an alternate go.sum file is also used: its path is derived from the -modfile flag by trimming the ".mod" extension and appending ".sum". + -workfile file + in module aware mode, use the given go.work file as a workspace file. + By default or when -workfile is "auto", the go command searches for a + file named go.work in the current directory and then containing directories + until one is found. If a valid go.work file is found, the modules + specified will collectively be used as the main modules. If -workfile + is "off", or a go.work file is not found in "auto" mode, workspace + mode is disabled. -overlay file read a JSON config file that provides an overlay for build operations. The file is a JSON struct with a single field, named 'Replace', that @@ -145,9 +162,8 @@ and test commands: -trimpath remove all file system paths from the resulting executable. Instead of absolute file system paths, the recorded file names - will begin with either "go" (for the standard library), - or a module path@version (when using modules), - or a plain import path (when using GOPATH). + will begin either a module path@version (when using modules), + or a plain import path (when using the standard library, or GOPATH). -toolexec 'cmd args' a program to use to invoke toolchain programs like vet and asm. For example, instead of running asm, the go command will run @@ -201,6 +217,7 @@ func init() { AddBuildFlags(CmdBuild, DefaultBuildFlags) AddBuildFlags(CmdInstall, DefaultBuildFlags) + base.AddWorkfileFlag(&CmdBuild.Flag) } // Note that flags consulted by other parts of the code @@ -289,10 +306,12 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) { cmd.Flag.StringVar(&cfg.BuildPkgdir, "pkgdir", "", "") cmd.Flag.BoolVar(&cfg.BuildRace, "race", false, "") cmd.Flag.BoolVar(&cfg.BuildMSan, "msan", false, "") + cmd.Flag.BoolVar(&cfg.BuildASan, "asan", false, "") cmd.Flag.Var((*tagsFlag)(&cfg.BuildContext.BuildTags), "tags", "") cmd.Flag.Var((*base.StringsFlag)(&cfg.BuildToolexec), "toolexec", "") cmd.Flag.BoolVar(&cfg.BuildTrimpath, "trimpath", false, "") cmd.Flag.BoolVar(&cfg.BuildWork, "work", false, "") + cmd.Flag.BoolVar(&cfg.BuildBuildvcs, "buildvcs", true, "") // Undocumented, unstable debugging flags. cmd.Flag.StringVar(&cfg.DebugActiongraph, "debug-actiongraph", "", "") @@ -364,6 +383,7 @@ var pkgsFilter = func(pkgs []*load.Package) []*load.Package { return pkgs } var runtimeVersion = runtime.Version() func runBuild(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() BuildInit() var b Builder b.Init() @@ -396,7 +416,7 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) { depMode := ModeBuild if cfg.BuildI { depMode = ModeInstall - fmt.Fprint(os.Stderr, "go build: -i flag is deprecated\n") + fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n") } pkgs = omitTestOnly(pkgsFilter(pkgs)) @@ -415,7 +435,7 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) { strings.HasSuffix(cfg.BuildO, "/") || strings.HasSuffix(cfg.BuildO, string(os.PathSeparator)) { if !explicitO { - base.Fatalf("go build: build output %q already exists and is a directory", cfg.BuildO) + base.Fatalf("go: build output %q already exists and is a directory", cfg.BuildO) } a := &Action{Mode: "go build"} for _, p := range pkgs { @@ -430,13 +450,13 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) { a.Deps = append(a.Deps, b.AutoAction(ModeInstall, depMode, p)) } if len(a.Deps) == 0 { - base.Fatalf("go build: no main packages to build") + base.Fatalf("go: no main packages to build") } b.Do(ctx, a) return } if len(pkgs) > 1 { - base.Fatalf("go build: cannot write multiple packages to non-directory %s", cfg.BuildO) + base.Fatalf("go: cannot write multiple packages to non-directory %s", cfg.BuildO) } else if len(pkgs) == 0 { base.Fatalf("no packages to build") } @@ -486,14 +506,17 @@ allowed, even if they refer to the same version. - All arguments must refer to packages in the same module at the same version. +- Package path arguments must refer to main packages. Pattern arguments +will only match main packages. + - No module is considered the "main" module. If the module containing packages named on the command line has a go.mod file, it must not contain directives (replace and exclude) that would cause it to be interpreted differently than if it were the main module. The module must not require a higher version of itself. -- Package path arguments must refer to main packages. Pattern arguments -will only match main packages. +- Vendor directories are not used in any module. (Vendor directories are not +included in the module zip files downloaded by 'go install'.) If the arguments don't have version suffixes, "go install" may run in module-aware mode or GOPATH mode, depending on the GO111MODULE environment @@ -580,13 +603,14 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) { for _, arg := range args { if strings.Contains(arg, "@") && !build.IsLocalImport(arg) && !filepath.IsAbs(arg) { if cfg.BuildI { - fmt.Fprint(os.Stderr, "go install: -i flag is deprecated\n") + fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n") } installOutsideModule(ctx, args) return } } + modload.InitWorkfile() BuildInit() pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{}, args) if cfg.ModulesEnabled && !modload.HasModRoot() { @@ -608,7 +632,7 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) { latestArgs[i] = args[i] + "@latest" } hint := strings.Join(latestArgs, " ") - base.Fatalf("go install: version is required when current directory is not in a module\n\tTry 'go install %s' to install the latest version", hint) + base.Fatalf("go: 'go install' requires a version when current directory is not in a module\n\tTry 'go install %s' to install the latest version", hint) } } load.CheckPackageErrors(pkgs) @@ -621,7 +645,7 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) { } } if !allGoroot { - fmt.Fprint(os.Stderr, "go install: -i flag is deprecated\n") + fmt.Fprintf(os.Stderr, "go: -i flag is deprecated\n") } } @@ -667,14 +691,14 @@ func InstallPackages(ctx context.Context, patterns []string, pkgs []*load.Packag case p.Name != "main" && p.Module != nil: // Non-executables have no target (except the cache) when building with modules. case p.Internal.GobinSubdir: - base.Errorf("go %s: cannot install cross-compiled binaries when GOBIN is set", cfg.CmdName) + base.Errorf("go: cannot install cross-compiled binaries when GOBIN is set") case p.Internal.CmdlineFiles: - base.Errorf("go %s: no install location for .go files listed on command line (GOBIN not set)", cfg.CmdName) + base.Errorf("go: no install location for .go files listed on command line (GOBIN not set)") case p.ConflictDir != "": - base.Errorf("go %s: no install location for %s: hidden by %s", cfg.CmdName, p.Dir, p.ConflictDir) + base.Errorf("go: no install location for %s: hidden by %s", p.Dir, p.ConflictDir) default: - base.Errorf("go %s: no install location for directory %s outside GOPATH\n"+ - "\tFor more details see: 'go help gopath'", cfg.CmdName, p.Dir) + base.Errorf("go: no install location for directory %s outside GOPATH\n"+ + "\tFor more details see: 'go help gopath'", p.Dir) } } } @@ -769,7 +793,7 @@ func installOutsideModule(ctx context.Context, args []string) { pkgOpts := load.PackageOpts{MainOnly: true} pkgs, err := load.PackagesAndErrorsOutsideModule(ctx, pkgOpts, args) if err != nil { - base.Fatalf("go install: %v", err) + base.Fatalf("go: %v", err) } load.CheckPackageErrors(pkgs) patterns := make([]string, len(args)) diff --git a/libgo/go/cmd/go/internal/work/build_test.go b/libgo/go/cmd/go/internal/work/build_test.go index 600fc3083f0..0b6b83a706c 100644 --- a/libgo/go/cmd/go/internal/work/build_test.go +++ b/libgo/go/cmd/go/internal/work/build_test.go @@ -234,7 +234,7 @@ func TestRespectSetgidDir(t *testing.T) { // of `(*Builder).ShowCmd` afterwards as a sanity check. cfg.BuildX = true var cmdBuf bytes.Buffer - b.Print = func(a ...interface{}) (int, error) { + b.Print = func(a ...any) (int, error) { return cmdBuf.WriteString(fmt.Sprint(a...)) } diff --git a/libgo/go/cmd/go/internal/work/buildid.go b/libgo/go/cmd/go/internal/work/buildid.go index 4e9189a3632..76335e9bb17 100644 --- a/libgo/go/cmd/go/internal/work/buildid.go +++ b/libgo/go/cmd/go/internal/work/buildid.go @@ -570,6 +570,8 @@ func showStdout(b *Builder, c *cache.Cache, actionID cache.ActionID, key string) b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList("cat", c.OutputFile(stdoutEntry.OutputID)))) } if !cfg.BuildN { + b.output.Lock() + defer b.output.Unlock() b.Print(string(stdout)) } } @@ -578,6 +580,8 @@ func showStdout(b *Builder, c *cache.Cache, actionID cache.ActionID, key string) // flushOutput flushes the output being queued in a. func (b *Builder) flushOutput(a *Action) { + b.output.Lock() + defer b.output.Unlock() b.Print(string(a.output)) a.output = nil } diff --git a/libgo/go/cmd/go/internal/work/exec.go b/libgo/go/cmd/go/internal/work/exec.go index b0281041c98..d3f0ecac049 100644 --- a/libgo/go/cmd/go/internal/work/exec.go +++ b/libgo/go/cmd/go/internal/work/exec.go @@ -36,6 +36,8 @@ import ( "cmd/go/internal/modload" "cmd/go/internal/str" "cmd/go/internal/trace" + "cmd/internal/quoted" + "cmd/internal/sys" ) // actionList returns the list of actions in the dag rooted at root @@ -222,18 +224,34 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { // same compiler settings and can reuse each other's results. // If not, the reason is already recorded in buildGcflags. fmt.Fprintf(h, "compile\n") - // Only include the package directory if it may affect the output. - // We trim workspace paths for all packages when -trimpath is set. - // The compiler hides the exact value of $GOROOT - // when building things in GOROOT. - // Assume b.WorkDir is being trimmed properly. - // When -trimpath is used with a package built from the module cache, - // use the module path and version instead of the directory. - if !p.Goroot && !cfg.BuildTrimpath && !strings.HasPrefix(p.Dir, b.WorkDir) { + + // Include information about the origin of the package that + // may be embedded in the debug info for the object file. + if cfg.BuildTrimpath { + // When -trimpath is used with a package built from the module cache, + // its debug information refers to the module path and version + // instead of the directory. + if p.Module != nil { + fmt.Fprintf(h, "module %s@%s\n", p.Module.Path, p.Module.Version) + } + } else if p.Goroot { + // The Go compiler always hides the exact value of $GOROOT + // when building things in GOROOT. + // + // The C compiler does not, but for packages in GOROOT we rewrite the path + // as though -trimpath were set, so that we don't invalidate the build cache + // (and especially any precompiled C archive files) when changing + // GOROOT_FINAL. (See https://go.dev/issue/50183.) + // + // b.WorkDir is always either trimmed or rewritten to + // the literal string "/tmp/go-build". + } else if !strings.HasPrefix(p.Dir, b.WorkDir) { + // -trimpath is not set and no other rewrite rules apply, + // so the object file may refer to the absolute directory + // containing the package. fmt.Fprintf(h, "dir %s\n", p.Dir) - } else if cfg.BuildTrimpath && p.Module != nil { - fmt.Fprintf(h, "module %s@%s\n", p.Module.Path, p.Module.Version) } + if p.Module != nil { fmt.Fprintf(h, "go %s\n", p.Module.GoVersion) } @@ -281,6 +299,11 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { if p.Internal.CoverMode != "" { fmt.Fprintf(h, "cover %q %q\n", p.Internal.CoverMode, b.toolID("cover")) } + if p.Internal.FuzzInstrument { + if fuzzFlags := fuzzInstrumentFlags(); fuzzFlags != nil { + fmt.Fprintf(h, "fuzz %q\n", fuzzFlags) + } + } fmt.Fprintf(h, "modinfo %q\n", p.Internal.BuildInfo) // Configuration specific to compiler toolchain. @@ -775,10 +798,13 @@ OverlayLoop: } if p.Internal.BuildInfo != "" && cfg.ModulesEnabled { - if err := b.writeFile(objdir+"_gomod_.go", modload.ModInfoProg(p.Internal.BuildInfo, cfg.BuildToolchainName == "gccgo")); err != nil { - return err + prog := modload.ModInfoProg(p.Internal.BuildInfo, cfg.BuildToolchainName == "gccgo") + if len(prog) > 0 { + if err := b.writeFile(objdir+"_gomod_.go", prog); err != nil { + return err + } + gofiles = append(gofiles, objdir+"_gomod_.go") } - gofiles = append(gofiles, objdir+"_gomod_.go") } // Compile Go. @@ -1375,6 +1401,7 @@ func (b *Builder) writeLinkImportcfg(a *Action, file string) error { fmt.Fprintf(&icfg, "packageshlib %s=%s\n", p1.ImportPath, p1.Shlib) } } + fmt.Fprintf(&icfg, "modinfo %q\n", modload.ModInfoData(a.Package.Internal.BuildInfo)) return b.writeFile(file, icfg.Bytes()) } @@ -1489,6 +1516,8 @@ func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string, return nil, nil, errPrintedOutput } if len(out) > 0 { + // NOTE: we don't attempt to parse quotes and unescapes here. pkg-config + // is typically used within shell backticks, which treats quotes literally. ldflags = strings.Fields(string(out)) if err := checkLinkerFlags("LDFLAGS", "pkg-config --libs", ldflags); err != nil { return nil, nil, err @@ -1923,7 +1952,7 @@ func mayberemovefile(s string) { // fmtcmd replaces the name of the current directory with dot (.) // but only when it is at the beginning of a space-separated token. // -func (b *Builder) fmtcmd(dir string, format string, args ...interface{}) string { +func (b *Builder) fmtcmd(dir string, format string, args ...any) string { cmd := fmt.Sprintf(format, args...) if dir != "" && dir != "/" { dot := " ." @@ -1949,7 +1978,7 @@ func (b *Builder) fmtcmd(dir string, format string, args ...interface{}) string // showcmd prints the given command to standard output // for the implementation of -n or -x. -func (b *Builder) Showcmd(dir string, format string, args ...interface{}) { +func (b *Builder) Showcmd(dir string, format string, args ...any) { b.output.Lock() defer b.output.Unlock() b.Print(b.fmtcmd(dir, format, args...) + "\n") @@ -2013,7 +2042,7 @@ var cgoTypeSigRe = lazyregexp.New(`\b_C2?(type|func|var|macro)_\B`) // run runs the command given by cmdline in the directory dir. // If the command fails, run prints information about the failure // and returns a non-nil error. -func (b *Builder) run(a *Action, dir string, desc string, env []string, cmdargs ...interface{}) error { +func (b *Builder) run(a *Action, dir string, desc string, env []string, cmdargs ...any) error { out, err := b.runOut(a, dir, env, cmdargs...) if len(out) > 0 { if desc == "" { @@ -2047,7 +2076,7 @@ func (b *Builder) processOutput(out []byte) string { // runOut runs the command given by cmdline in the directory dir. // It returns the command output and any errors that occurred. // It accumulates execution time in a. -func (b *Builder) runOut(a *Action, dir string, env []string, cmdargs ...interface{}) ([]byte, error) { +func (b *Builder) runOut(a *Action, dir string, env []string, cmdargs ...any) ([]byte, error) { cmdline := str.StringList(cmdargs...) for _, arg := range cmdline { @@ -2312,7 +2341,7 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s // directives pointing to the source directory. It should not generate those // when -trimpath is enabled. if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") { - if cfg.BuildTrimpath { + if cfg.BuildTrimpath || p.Goroot { // Keep in sync with Action.trimpath. // The trimmed paths are a little different, but we need to trim in the // same situations. @@ -2334,8 +2363,6 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s to = filepath.Join("/_", toPath) } flags = append(flags[:len(flags):len(flags)], "-fdebug-prefix-map="+from+"="+to) - } else if p.Goroot && cfg.GOROOT_FINAL != cfg.GOROOT { - flags = append(flags[:len(flags):len(flags)], "-fdebug-prefix-map="+cfg.GOROOT+"="+cfg.GOROOT_FINAL) } } @@ -2384,7 +2411,7 @@ func (b *Builder) gccld(a *Action, p *load.Package, objdir, outfile string, flag cmd = b.GccCmd(p.Dir, objdir) } - cmdargs := []interface{}{cmd, "-o", outfile, objs, flags} + cmdargs := []any{cmd, "-o", outfile, objs, flags} dir := p.Dir out, err := b.runOut(a, base.Cwd(), b.cCompilerEnv(), cmdargs...) @@ -2431,12 +2458,6 @@ func (b *Builder) gccld(a *Action, p *load.Package, objdir, outfile string, flag return err } -// Grab these before main helpfully overwrites them. -var ( - origCC = cfg.Getenv("CC") - origCXX = cfg.Getenv("CXX") -) - // gccCmd returns a gcc command line prefix // defaultCC is defined in zdefaultcc.go, written by cmd/dist. func (b *Builder) GccCmd(incdir, workdir string) []string { @@ -2456,40 +2477,23 @@ func (b *Builder) gfortranCmd(incdir, workdir string) []string { // ccExe returns the CC compiler setting without all the extra flags we add implicitly. func (b *Builder) ccExe() []string { - return b.compilerExe(origCC, cfg.DefaultCC(cfg.Goos, cfg.Goarch)) + return envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch)) } // cxxExe returns the CXX compiler setting without all the extra flags we add implicitly. func (b *Builder) cxxExe() []string { - return b.compilerExe(origCXX, cfg.DefaultCXX(cfg.Goos, cfg.Goarch)) + return envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch)) } // fcExe returns the FC compiler setting without all the extra flags we add implicitly. func (b *Builder) fcExe() []string { - return b.compilerExe(cfg.Getenv("FC"), "gfortran") -} - -// compilerExe returns the compiler to use given an -// environment variable setting (the value not the name) -// and a default. The resulting slice is usually just the name -// of the compiler but can have additional arguments if they -// were present in the environment value. -// For example if CC="gcc -DGOPHER" then the result is ["gcc", "-DGOPHER"]. -func (b *Builder) compilerExe(envValue string, def string) []string { - compiler := strings.Fields(envValue) - if len(compiler) == 0 { - compiler = strings.Fields(def) - } - return compiler + return envList("FC", "gfortran") } // compilerCmd returns a command line prefix for the given environment // variable and using the default command when the variable is empty. func (b *Builder) compilerCmd(compiler []string, incdir, workdir string) []string { - // NOTE: env.go's mkEnv knows that the first three - // strings returned are "gcc", "-I", incdir (and cuts them off). - a := []string{compiler[0], "-I", incdir} - a = append(a, compiler[1:]...) + a := append(compiler, "-I", incdir) // Definitely want -fPIC but on Windows gcc complains // "-fPIC ignored for target (all code is position independent)" @@ -2670,12 +2674,20 @@ func (b *Builder) gccArchArgs() []string { // envList returns the value of the given environment variable broken // into fields, using the default value when the variable is empty. +// +// The environment variable must be quoted correctly for +// str.SplitQuotedFields. This should be done before building +// anything, for example, in BuildInit. func envList(key, def string) []string { v := cfg.Getenv(key) if v == "" { v = def } - return strings.Fields(v) + args, err := quoted.Split(v) + if err != nil { + panic(fmt.Sprintf("could not parse environment variable %s with value %q: %v", key, v, err)) + } + return args } // CFlags returns the flags to use when invoking the C, C++ or Fortran compilers, or cgo. @@ -2741,6 +2753,10 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo cgoCFLAGS = append([]string{"-fsanitize=memory"}, cgoCFLAGS...) cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...) } + if cfg.BuildASan { + cgoCFLAGS = append([]string{"-fsanitize=address"}, cgoCFLAGS...) + cgoLDFLAGS = append([]string{"-fsanitize=address"}, cgoLDFLAGS...) + } // Allows including _cgo_export.h, as well as the user's .h files, // from .[ch] files in the package. @@ -2762,7 +2778,7 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo if p.Standard && p.ImportPath == "runtime/cgo" { cgoflags = append(cgoflags, "-import_runtime_cgo=false") } - if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/msan" || p.ImportPath == "runtime/cgo") { + if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/msan" || p.ImportPath == "runtime/cgo" || p.ImportPath == "runtime/asan") { cgoflags = append(cgoflags, "-import_syscall=false") } @@ -2988,18 +3004,24 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe linkobj := str.StringList(ofile, outObj, mkAbsFiles(p.Dir, p.SysoFiles)) dynobj := objdir + "_cgo_.o" - // we need to use -pie for Linux/ARM to get accurate imported sym ldflags := cgoLDFLAGS if (cfg.Goarch == "arm" && cfg.Goos == "linux") || cfg.Goos == "android" { - // -static -pie doesn't make sense, and causes link errors. - // Issue 26197. - n := make([]string, 0, len(ldflags)) - for _, flag := range ldflags { - if flag != "-static" { - n = append(n, flag) + if !str.Contains(ldflags, "-no-pie") { + // we need to use -pie for Linux/ARM to get accurate imported sym (added in https://golang.org/cl/5989058) + // this seems to be outdated, but we don't want to break existing builds depending on this (Issue 45940) + ldflags = append(ldflags, "-pie") + } + if str.Contains(ldflags, "-pie") && str.Contains(ldflags, "-static") { + // -static -pie doesn't make sense, and causes link errors. + // Issue 26197. + n := make([]string, 0, len(ldflags)-1) + for _, flag := range ldflags { + if flag != "-static" { + n = append(n, flag) + } } + ldflags = n } - ldflags = append(n, "-pie") } if err := b.gccld(a, p, objdir, dynobj, ldflags, linkobj); err != nil { return err @@ -3321,12 +3343,6 @@ func passLongArgsInResponseFiles(cmd *exec.Cmd) (cleanup func()) { return cleanup } -// Windows has a limit of 32 KB arguments. To be conservative and not worry -// about whether that includes spaces or not, just use 30 KB. Darwin's limit is -// less clear. The OS claims 256KB, but we've seen failures with arglen as -// small as 50KB. -const ArgLengthForResponseFile = (30 << 10) - func useResponseFile(path string, argLen int) bool { // Unless the program uses objabi.Flagparse, which understands // response files, don't use response files. @@ -3338,7 +3354,7 @@ func useResponseFile(path string, argLen int) bool { return false } - if argLen > ArgLengthForResponseFile { + if argLen > sys.ExecArgLengthLimit { return true } diff --git a/libgo/go/cmd/go/internal/work/exec_test.go b/libgo/go/cmd/go/internal/work/exec_test.go index 4eb762cb289..8bbf25bb337 100644 --- a/libgo/go/cmd/go/internal/work/exec_test.go +++ b/libgo/go/cmd/go/internal/work/exec_test.go @@ -7,6 +7,7 @@ package work import ( "bytes" "cmd/internal/objabi" + "cmd/internal/sys" "fmt" "math/rand" "testing" @@ -56,7 +57,7 @@ func TestEncodeDecodeFuzz(t *testing.T) { } t.Parallel() - nRunes := ArgLengthForResponseFile + 100 + nRunes := sys.ExecArgLengthLimit + 100 rBuffer := make([]rune, nRunes) buf := bytes.NewBuffer([]byte(string(rBuffer))) @@ -67,7 +68,7 @@ func TestEncodeDecodeFuzz(t *testing.T) { for i := 0; i < 50; i++ { // Generate a random string of runes. buf.Reset() - for buf.Len() < ArgLengthForResponseFile+1 { + for buf.Len() < sys.ExecArgLengthLimit+1 { var r rune for { r = rune(rng.Intn(utf8.MaxRune + 1)) diff --git a/libgo/go/cmd/go/internal/work/gc.go b/libgo/go/cmd/go/internal/work/gc.go index 85da4f89f99..40175324d26 100644 --- a/libgo/go/cmd/go/internal/work/gc.go +++ b/libgo/go/cmd/go/internal/work/gc.go @@ -22,6 +22,7 @@ import ( "cmd/go/internal/load" "cmd/go/internal/str" "cmd/internal/objabi" + "cmd/internal/quoted" "cmd/internal/sys" "crypto/sha1" ) @@ -29,6 +30,18 @@ import ( // The 'path' used for GOROOT_FINAL when -trimpath is specified const trimPathGoRootFinal = "go" +var runtimePackages = map[string]struct{}{ + "internal/abi": struct{}{}, + "internal/bytealg": struct{}{}, + "internal/cpu": struct{}{}, + "internal/goarch": struct{}{}, + "internal/goos": struct{}{}, + "runtime": struct{}{}, + "runtime/internal/atomic": struct{}{}, + "runtime/internal/math": struct{}{}, + "runtime/internal/sys": struct{}{}, +} + // The Go toolchain. type gcToolchain struct{} @@ -63,7 +76,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg } pkgpath := pkgPath(a) - gcargs := []string{"-p", pkgpath} + defaultGcFlags := []string{"-p", pkgpath} if p.Module != nil { v := p.Module.GoVersion if v == "" { @@ -82,22 +95,19 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg v = "1.16" } if allowedVersion(v) { - gcargs = append(gcargs, "-lang=go"+v) + defaultGcFlags = append(defaultGcFlags, "-lang=go"+v) } } if p.Standard { - gcargs = append(gcargs, "-std") - } - compilingRuntime := p.Standard && (p.ImportPath == "runtime" || strings.HasPrefix(p.ImportPath, "runtime/internal")) - // The runtime package imports a couple of general internal packages. - if p.Standard && (p.ImportPath == "internal/cpu" || p.ImportPath == "internal/bytealg" || p.ImportPath == "internal/abi") { - compilingRuntime = true + defaultGcFlags = append(defaultGcFlags, "-std") } + _, compilingRuntime := runtimePackages[p.ImportPath] + compilingRuntime = compilingRuntime && p.Standard if compilingRuntime { // runtime compiles with a special gc flag to check for // memory allocations that are invalid in the runtime package, // and to implement some special compiler pragmas. - gcargs = append(gcargs, "-+") + defaultGcFlags = append(defaultGcFlags, "-+") } // If we're giving the compiler the entire package (no C etc files), tell it that, @@ -116,25 +126,28 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg } } if extFiles == 0 { - gcargs = append(gcargs, "-complete") + defaultGcFlags = append(defaultGcFlags, "-complete") } if cfg.BuildContext.InstallSuffix != "" { - gcargs = append(gcargs, "-installsuffix", cfg.BuildContext.InstallSuffix) + defaultGcFlags = append(defaultGcFlags, "-installsuffix", cfg.BuildContext.InstallSuffix) } if a.buildID != "" { - gcargs = append(gcargs, "-buildid", a.buildID) + defaultGcFlags = append(defaultGcFlags, "-buildid", a.buildID) } if p.Internal.OmitDebug || cfg.Goos == "plan9" || cfg.Goarch == "wasm" { - gcargs = append(gcargs, "-dwarf=false") + defaultGcFlags = append(defaultGcFlags, "-dwarf=false") } if strings.HasPrefix(runtimeVersion, "go1") && !strings.Contains(os.Args[0], "go_bootstrap") { - gcargs = append(gcargs, "-goversion", runtimeVersion) + defaultGcFlags = append(defaultGcFlags, "-goversion", runtimeVersion) } if symabis != "" { - gcargs = append(gcargs, "-symabis", symabis) + defaultGcFlags = append(defaultGcFlags, "-symabis", symabis) } gcflags := str.StringList(forcedGcflags, p.Internal.Gcflags) + if p.Internal.FuzzInstrument { + gcflags = append(gcflags, fuzzInstrumentFlags()...) + } if compilingRuntime { // Remove -N, if present. // It is not possible to build the runtime with no optimizations, @@ -147,10 +160,15 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg } } } + // Add -c=N to use concurrent backend compilation, if possible. + if c := gcBackendConcurrency(gcflags); c > 1 { + gcflags = append(gcflags, fmt.Sprintf("-c=%d", c)) + } - args := []interface{}{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), gcflags, gcargs} - if p.Internal.LocalPrefix != "" { - // Workaround #43883. + args := []any{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), defaultGcFlags, gcflags} + if p.Internal.LocalPrefix == "" { + args = append(args, "-nolocalimports") + } else { args = append(args, "-D", p.Internal.LocalPrefix) } if importcfg != nil { @@ -172,11 +190,6 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg args = append(args, "-asmhdr", objdir+"go_asm.h") } - // Add -c=N to use concurrent backend compilation, if possible. - if c := gcBackendConcurrency(gcflags); c > 1 { - args = append(args, fmt.Sprintf("-c=%d", c)) - } - for _, f := range gofiles { f := mkAbs(p.Dir, f) @@ -223,7 +236,7 @@ CheckFlags: // except for known commonly used flags. // If the user knows better, they can manually add their own -c to the gcflags. switch flag { - case "-N", "-l", "-S", "-B", "-C", "-I": + case "-N", "-l", "-S", "-B", "-C", "-I", "-shared": // OK default: canDashC = false @@ -349,11 +362,11 @@ func (a *Action) trimpath() string { return rewrite } -func asmArgs(a *Action, p *load.Package) []interface{} { +func asmArgs(a *Action, p *load.Package) []any { // Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files. inc := filepath.Join(cfg.GOROOT, "pkg", "include") pkgpath := pkgPath(a) - args := []interface{}{cfg.BuildToolexec, base.Tool("asm"), "-p", pkgpath, "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags} + args := []any{cfg.BuildToolexec, base.Tool("asm"), "-p", pkgpath, "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags} if p.ImportPath == "runtime" && cfg.Goarch == "386" { for _, arg := range forcedAsmflags { if arg == "-dynlink" { @@ -365,6 +378,16 @@ func asmArgs(a *Action, p *load.Package) []interface{} { args = append(args, "-compiling-runtime") } + if cfg.Goarch == "386" { + // Define GO386_value from cfg.GO386. + args = append(args, "-D", "GO386_"+cfg.GO386) + } + + if cfg.Goarch == "amd64" { + // Define GOAMD64_value from cfg.GOAMD64. + args = append(args, "-D", "GOAMD64_"+cfg.GOAMD64) + } + if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" { // Define GOMIPS_value from cfg.GOMIPS. args = append(args, "-D", "GOMIPS_"+cfg.GOMIPS) @@ -432,8 +455,8 @@ func (gcToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, erro // toolVerify checks that the command line args writes the same output file // if run using newTool instead. // Unused now but kept around for future use. -func toolVerify(a *Action, b *Builder, p *load.Package, newTool string, ofile string, args []interface{}) error { - newArgs := make([]interface{}, len(args)) +func toolVerify(a *Action, b *Builder, p *load.Package, newTool string, ofile string, args []any) error { + newArgs := make([]any, len(args)) copy(newArgs, args) newArgs[1] = base.Tool(newTool) newArgs[3] = ofile + ".new" // x.6 becomes x.6.new @@ -536,33 +559,18 @@ func packInternal(afile string, ofiles []string) error { } // setextld sets the appropriate linker flags for the specified compiler. -func setextld(ldflags []string, compiler []string) []string { +func setextld(ldflags []string, compiler []string) ([]string, error) { for _, f := range ldflags { if f == "-extld" || strings.HasPrefix(f, "-extld=") { // don't override -extld if supplied - return ldflags + return ldflags, nil } } - ldflags = append(ldflags, "-extld="+compiler[0]) - if len(compiler) > 1 { - extldflags := false - add := strings.Join(compiler[1:], " ") - for i, f := range ldflags { - if f == "-extldflags" && i+1 < len(ldflags) { - ldflags[i+1] = add + " " + ldflags[i+1] - extldflags = true - break - } else if strings.HasPrefix(f, "-extldflags=") { - ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):] - extldflags = true - break - } - } - if !extldflags { - ldflags = append(ldflags, "-extldflags="+add) - } + joined, err := quoted.Join(compiler) + if err != nil { + return nil, err } - return ldflags + return append(ldflags, "-extld="+joined), nil } // pluginPath computes the package path for a plugin main package. @@ -649,7 +657,10 @@ func (gcToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) } ldflags = append(ldflags, forcedLdflags...) ldflags = append(ldflags, root.Package.Internal.Ldflags...) - ldflags = setextld(ldflags, compiler) + ldflags, err := setextld(ldflags, compiler) + if err != nil { + return err + } // On OS X when using external linking to build a shared library, // the argument passed here to -o ends up recorded in the final @@ -693,7 +704,10 @@ func (gcToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, } else { compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch)) } - ldflags = setextld(ldflags, compiler) + ldflags, err := setextld(ldflags, compiler) + if err != nil { + return err + } for _, d := range toplevelactions { if !strings.HasSuffix(d.Target, ".a") { // omit unsafe etc and actions for other shared libraries continue diff --git a/libgo/go/cmd/go/internal/work/init.go b/libgo/go/cmd/go/internal/work/init.go index 37a3e2d0ffd..26192ecaed1 100644 --- a/libgo/go/cmd/go/internal/work/init.go +++ b/libgo/go/cmd/go/internal/work/init.go @@ -11,8 +11,8 @@ import ( "cmd/go/internal/cfg" "cmd/go/internal/fsys" "cmd/go/internal/modload" + "cmd/internal/quoted" "cmd/internal/sys" - "flag" "fmt" "os" "path/filepath" @@ -32,27 +32,63 @@ func BuildInit() { if cfg.BuildPkgdir != "" && !filepath.IsAbs(cfg.BuildPkgdir) { p, err := filepath.Abs(cfg.BuildPkgdir) if err != nil { - fmt.Fprintf(os.Stderr, "go %s: evaluating -pkgdir: %v\n", flag.Args()[0], err) + fmt.Fprintf(os.Stderr, "go: evaluating -pkgdir: %v\n", err) base.SetExitStatus(2) base.Exit() } cfg.BuildPkgdir = p } - // Make sure CC and CXX are absolute paths - for _, key := range []string{"CC", "CXX"} { - if path := cfg.Getenv(key); !filepath.IsAbs(path) && path != "" && path != filepath.Base(path) { - base.Fatalf("go %s: %s environment variable is relative; must be absolute path: %s\n", flag.Args()[0], key, path) + if cfg.BuildP <= 0 { + base.Fatalf("go: -p must be a positive integer: %v\n", cfg.BuildP) + } + + // Make sure CC, CXX, and FC are absolute paths. + for _, key := range []string{"CC", "CXX", "FC"} { + value := cfg.Getenv(key) + args, err := quoted.Split(value) + if err != nil { + base.Fatalf("go: %s environment variable could not be parsed: %v", key, err) } + if len(args) == 0 { + continue + } + path := args[0] + if !filepath.IsAbs(path) && path != filepath.Base(path) { + base.Fatalf("go: %s environment variable is relative; must be absolute path: %s\n", key, path) + } + } +} + +// fuzzInstrumentFlags returns compiler flags that enable fuzzing instrumation +// on supported platforms. +// +// On unsupported platforms, fuzzInstrumentFlags returns nil, meaning no +// instrumentation is added. 'go test -fuzz' still works without coverage, +// but it generates random inputs without guidance, so it's much less effective. +func fuzzInstrumentFlags() []string { + if !sys.FuzzInstrumented(cfg.Goos, cfg.Goarch) { + return nil } + return []string{"-d=libfuzzer"} } func instrumentInit() { - if !cfg.BuildRace && !cfg.BuildMSan { + if !cfg.BuildRace && !cfg.BuildMSan && !cfg.BuildASan { return } if cfg.BuildRace && cfg.BuildMSan { - fmt.Fprintf(os.Stderr, "go %s: may not use -race and -msan simultaneously\n", flag.Args()[0]) + fmt.Fprintf(os.Stderr, "go: may not use -race and -msan simultaneously\n") + base.SetExitStatus(2) + base.Exit() + } + if cfg.BuildRace && cfg.BuildASan { + fmt.Fprintf(os.Stderr, "go: may not use -race and -asan simultaneously\n") + base.SetExitStatus(2) + base.Exit() + } + if cfg.BuildMSan && cfg.BuildASan { + fmt.Fprintf(os.Stderr, "go: may not use -msan and -asan simultaneously\n") base.SetExitStatus(2) base.Exit() } @@ -61,12 +97,15 @@ func instrumentInit() { base.SetExitStatus(2) base.Exit() } - if cfg.BuildRace { - if !sys.RaceDetectorSupported(cfg.Goos, cfg.Goarch) { - fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, linux/ppc64le, linux/arm64, freebsd/amd64, netbsd/amd64, darwin/amd64, darwin/arm64, and windows/amd64\n", flag.Args()[0]) - base.SetExitStatus(2) - base.Exit() - } + if cfg.BuildRace && !sys.RaceDetectorSupported(cfg.Goos, cfg.Goarch) { + fmt.Fprintf(os.Stderr, "-race is not supported on %s/%s\n", cfg.Goos, cfg.Goarch) + base.SetExitStatus(2) + base.Exit() + } + if cfg.BuildASan && !sys.ASanSupported(cfg.Goos, cfg.Goarch) { + fmt.Fprintf(os.Stderr, "-asan is not supported on %s/%s\n", cfg.Goos, cfg.Goarch) + base.SetExitStatus(2) + base.Exit() } mode := "race" if cfg.BuildMSan { @@ -77,13 +116,16 @@ func instrumentInit() { cfg.BuildBuildmode = "pie" } } + if cfg.BuildASan { + mode = "asan" + } modeFlag := "-" + mode if !cfg.BuildContext.CgoEnabled { if runtime.GOOS != cfg.Goos || runtime.GOARCH != cfg.Goarch { - fmt.Fprintf(os.Stderr, "go %s: %s requires cgo\n", flag.Args()[0], modeFlag) + fmt.Fprintf(os.Stderr, "go: %s requires cgo\n", modeFlag) } else { - fmt.Fprintf(os.Stderr, "go %s: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", flag.Args()[0], modeFlag) + fmt.Fprintf(os.Stderr, "go: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", modeFlag) } base.SetExitStatus(2) @@ -96,7 +138,7 @@ func instrumentInit() { cfg.BuildContext.InstallSuffix += "_" } cfg.BuildContext.InstallSuffix += mode - cfg.BuildContext.BuildTags = append(cfg.BuildContext.BuildTags, mode) + cfg.BuildContext.ToolTags = append(cfg.BuildContext.ToolTags, mode) } func buildModeInit() { diff --git a/libgo/go/cmd/go/internal/work/testgo.go b/libgo/go/cmd/go/internal/work/testgo.go index 8b77871b23f..a09b65a23c3 100644 --- a/libgo/go/cmd/go/internal/work/testgo.go +++ b/libgo/go/cmd/go/internal/work/testgo.go @@ -5,7 +5,6 @@ // This file contains extra hooks for testing the go command. //go:build testgo -// +build testgo package work diff --git a/libgo/go/cmd/go/internal/workcmd/edit.go b/libgo/go/cmd/go/internal/workcmd/edit.go new file mode 100644 index 00000000000..c42000710e0 --- /dev/null +++ b/libgo/go/cmd/go/internal/workcmd/edit.go @@ -0,0 +1,315 @@ +// 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. + +// go work edit + +package workcmd + +import ( + "cmd/go/internal/base" + "cmd/go/internal/modload" + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + "golang.org/x/mod/module" + + "golang.org/x/mod/modfile" +) + +var cmdEdit = &base.Command{ + UsageLine: "go work edit [editing flags] [go.work]", + Short: "edit go.work from tools or scripts", + Long: `Edit provides a command-line interface for editing go.work, +for use primarily by tools or scripts. It only reads go.work; +it does not look up information about the modules involved. +If no file is specified, Edit looks for a go.work file in the current +directory and its parent directories + +The editing flags specify a sequence of editing operations. + +The -fmt flag reformats the go.work file without making other changes. +This reformatting is also implied by any other modifications that use or +rewrite the go.mod file. The only time this flag is needed is if no other +flags are specified, as in 'go work edit -fmt'. + +The -use=path and -dropuse=path flags +add and drop a use directive from the go.work file's set of module directories. + +The -replace=old[@v]=new[@v] flag adds a replacement of the given +module path and version pair. If the @v in old@v is omitted, a +replacement without a version on the left side is added, which applies +to all versions of the old module path. If the @v in new@v is omitted, +the new path should be a local module root directory, not a module +path. Note that -replace overrides any redundant replacements for old[@v], +so omitting @v will drop existing replacements for specific versions. + +The -dropreplace=old[@v] flag drops a replacement of the given +module path and version pair. If the @v is omitted, a replacement without +a version on the left side is dropped. + +The -use, -dropuse, -replace, and -dropreplace, +editing flags may be repeated, and the changes are applied in the order given. + +The -go=version flag sets the expected Go language version. + +The -print flag prints the final go.work in its text format instead of +writing it back to go.mod. + +The -json flag prints the final go.work file in JSON format instead of +writing it back to go.mod. The JSON output corresponds to these Go types: + + type Module struct { + Path string + Version string + } + + type GoWork struct { + Go string + Directory []Directory + Replace []Replace + } + + type Use struct { + Path string + ModulePath string + } + + type Replace struct { + Old Module + New Module + } + +See the workspaces design proposal at +https://go.googlesource.com/proposal/+/master/design/45713-workspace.md for +more information. +`, +} + +var ( + editFmt = cmdEdit.Flag.Bool("fmt", false, "") + editGo = cmdEdit.Flag.String("go", "", "") + editJSON = cmdEdit.Flag.Bool("json", false, "") + editPrint = cmdEdit.Flag.Bool("print", false, "") + workedits []func(file *modfile.WorkFile) // edits specified in flags +) + +type flagFunc func(string) + +func (f flagFunc) String() string { return "" } +func (f flagFunc) Set(s string) error { f(s); return nil } + +func init() { + cmdEdit.Run = runEditwork // break init cycle + + cmdEdit.Flag.Var(flagFunc(flagEditworkUse), "use", "") + cmdEdit.Flag.Var(flagFunc(flagEditworkDropUse), "dropuse", "") + cmdEdit.Flag.Var(flagFunc(flagEditworkReplace), "replace", "") + cmdEdit.Flag.Var(flagFunc(flagEditworkDropReplace), "dropreplace", "") + + base.AddWorkfileFlag(&cmdEdit.Flag) +} + +func runEditwork(ctx context.Context, cmd *base.Command, args []string) { + anyFlags := + *editGo != "" || + *editJSON || + *editPrint || + *editFmt || + len(workedits) > 0 + + if !anyFlags { + base.Fatalf("go: no flags specified (see 'go help work edit').") + } + + if *editJSON && *editPrint { + base.Fatalf("go: cannot use both -json and -print") + } + + if len(args) > 1 { + base.Fatalf("go: 'go help work edit' accepts at most one argument") + } + var gowork string + if len(args) == 1 { + gowork = args[0] + } else { + modload.InitWorkfile() + gowork = modload.WorkFilePath() + } + + if *editGo != "" { + if !modfile.GoVersionRE.MatchString(*editGo) { + base.Fatalf(`go mod: invalid -go option; expecting something like "-go %s"`, modload.LatestGoVersion()) + } + } + + workFile, err := modload.ReadWorkFile(gowork) + if err != nil { + base.Fatalf("go: errors parsing %s:\n%s", base.ShortPath(gowork), err) + } + + if *editGo != "" { + if err := workFile.AddGoStmt(*editGo); err != nil { + base.Fatalf("go: internal error: %v", err) + } + } + + if len(workedits) > 0 { + for _, edit := range workedits { + edit(workFile) + } + } + + modload.UpdateWorkFile(workFile) + + workFile.SortBlocks() + workFile.Cleanup() // clean file after edits + + if *editJSON { + editPrintJSON(workFile) + return + } + + if *editPrint { + os.Stdout.Write(modfile.Format(workFile.Syntax)) + return + } + + modload.WriteWorkFile(gowork, workFile) +} + +// flagEditworkUse implements the -use flag. +func flagEditworkUse(arg string) { + workedits = append(workedits, func(f *modfile.WorkFile) { + _, mf, err := modload.ReadModFile(filepath.Join(arg, "go.mod"), nil) + modulePath := "" + if err == nil { + modulePath = mf.Module.Mod.Path + } + f.AddUse(modload.ToDirectoryPath(arg), modulePath) + if err := f.AddUse(modload.ToDirectoryPath(arg), ""); err != nil { + base.Fatalf("go: -use=%s: %v", arg, err) + } + }) +} + +// flagEditworkDropUse implements the -dropuse flag. +func flagEditworkDropUse(arg string) { + workedits = append(workedits, func(f *modfile.WorkFile) { + if err := f.DropUse(modload.ToDirectoryPath(arg)); err != nil { + base.Fatalf("go: -dropdirectory=%s: %v", arg, err) + } + }) +} + +// allowedVersionArg returns whether a token may be used as a version in go.mod. +// We don't call modfile.CheckPathVersion, because that insists on versions +// being in semver form, but here we want to allow versions like "master" or +// "1234abcdef", which the go command will resolve the next time it runs (or +// during -fix). Even so, we need to make sure the version is a valid token. +func allowedVersionArg(arg string) bool { + return !modfile.MustQuote(arg) +} + +// parsePathVersionOptional parses path[@version], using adj to +// describe any errors. +func parsePathVersionOptional(adj, arg string, allowDirPath bool) (path, version string, err error) { + if i := strings.Index(arg, "@"); i < 0 { + path = arg + } else { + path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:]) + } + if err := module.CheckImportPath(path); err != nil { + if !allowDirPath || !modfile.IsDirectoryPath(path) { + return path, version, fmt.Errorf("invalid %s path: %v", adj, err) + } + } + if path != arg && !allowedVersionArg(version) { + return path, version, fmt.Errorf("invalid %s version: %q", adj, version) + } + return path, version, nil +} + +// flagReplace implements the -replace flag. +func flagEditworkReplace(arg string) { + var i int + if i = strings.Index(arg, "="); i < 0 { + base.Fatalf("go: -replace=%s: need old[@v]=new[@w] (missing =)", arg) + } + old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:]) + if strings.HasPrefix(new, ">") { + base.Fatalf("go: -replace=%s: separator between old and new is =, not =>", arg) + } + oldPath, oldVersion, err := parsePathVersionOptional("old", old, false) + if err != nil { + base.Fatalf("go: -replace=%s: %v", arg, err) + } + newPath, newVersion, err := parsePathVersionOptional("new", new, true) + if err != nil { + base.Fatalf("go: -replace=%s: %v", arg, err) + } + if newPath == new && !modfile.IsDirectoryPath(new) { + base.Fatalf("go: -replace=%s: unversioned new path must be local directory", arg) + } + + workedits = append(workedits, func(f *modfile.WorkFile) { + if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil { + base.Fatalf("go: -replace=%s: %v", arg, err) + } + }) +} + +// flagDropReplace implements the -dropreplace flag. +func flagEditworkDropReplace(arg string) { + path, version, err := parsePathVersionOptional("old", arg, true) + if err != nil { + base.Fatalf("go: -dropreplace=%s: %v", arg, err) + } + workedits = append(workedits, func(f *modfile.WorkFile) { + if err := f.DropReplace(path, version); err != nil { + base.Fatalf("go: -dropreplace=%s: %v", arg, err) + } + }) +} + +type replaceJSON struct { + Old module.Version + New module.Version +} + +// editPrintJSON prints the -json output. +func editPrintJSON(workFile *modfile.WorkFile) { + var f workfileJSON + if workFile.Go != nil { + f.Go = workFile.Go.Version + } + for _, d := range workFile.Use { + f.Use = append(f.Use, useJSON{DiskPath: d.Path, ModPath: d.ModulePath}) + } + + for _, r := range workFile.Replace { + f.Replace = append(f.Replace, replaceJSON{r.Old, r.New}) + } + data, err := json.MarshalIndent(&f, "", "\t") + if err != nil { + base.Fatalf("go: internal error: %v", err) + } + data = append(data, '\n') + os.Stdout.Write(data) +} + +// workfileJSON is the -json output data structure. +type workfileJSON struct { + Go string `json:",omitempty"` + Use []useJSON + Replace []replaceJSON +} + +type useJSON struct { + DiskPath string + ModPath string `json:",omitempty"` +} diff --git a/libgo/go/cmd/go/internal/workcmd/init.go b/libgo/go/cmd/go/internal/workcmd/init.go new file mode 100644 index 00000000000..cefecee8329 --- /dev/null +++ b/libgo/go/cmd/go/internal/workcmd/init.go @@ -0,0 +1,52 @@ +// 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. + +// go work init + +package workcmd + +import ( + "cmd/go/internal/base" + "cmd/go/internal/modload" + "context" + "path/filepath" +) + +var cmdInit = &base.Command{ + UsageLine: "go work init [moddirs]", + Short: "initialize workspace file", + Long: `Init initializes and writes a new go.work file in the +current directory, in effect creating a new workspace at the current +directory. + +go work init optionally accepts paths to the workspace modules as +arguments. If the argument is omitted, an empty workspace with no +modules will be created. + +Each argument path is added to a use directive in the go.work file. The +current go version will also be listed in the go.work file. + +`, + Run: runInit, +} + +func init() { + base.AddModCommonFlags(&cmdInit.Flag) + base.AddWorkfileFlag(&cmdInit.Flag) +} + +func runInit(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() + + modload.ForceUseModules = true + + // TODO(matloob): support using the -workfile path + // To do that properly, we'll have to make the module directories + // make dirs relative to workFile path before adding the paths to + // the directory entries + + workFile := filepath.Join(base.Cwd(), "go.work") + + modload.CreateWorkFile(ctx, workFile, args) +} diff --git a/libgo/go/cmd/go/internal/workcmd/sync.go b/libgo/go/cmd/go/internal/workcmd/sync.go new file mode 100644 index 00000000000..1cca817517c --- /dev/null +++ b/libgo/go/cmd/go/internal/workcmd/sync.go @@ -0,0 +1,130 @@ +// 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. + +// go work sync + +package workcmd + +import ( + "cmd/go/internal/base" + "cmd/go/internal/imports" + "cmd/go/internal/modload" + "context" + + "golang.org/x/mod/module" +) + +var cmdSync = &base.Command{ + UsageLine: "go work sync", + Short: "sync workspace build list to modules", + Long: `Sync syncs the workspace's build list back to the +workspace's modules + +The workspace's build list is the set of versions of all the +(transitive) dependency modules used to do builds in the workspace. go +work sync generates that build list using the Minimal Version Selection +algorithm, and then syncs those versions back to each of modules +specified in the workspace (with use directives). + +The syncing is done by sequentially upgrading each of the dependency +modules specified in a workspace module to the version in the build list +if the dependency module's version is not already the same as the build +list's version. Note that Minimal Version Selection guarantees that the +build list's version of each module is always the same or higher than +that in each workspace module. +`, + Run: runSync, +} + +func init() { + base.AddModCommonFlags(&cmdSync.Flag) + base.AddWorkfileFlag(&cmdSync.Flag) +} + +func runSync(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() + + modload.ForceUseModules = true + + workGraph := modload.LoadModGraph(ctx, "") + _ = workGraph + mustSelectFor := map[module.Version][]module.Version{} + + mms := modload.MainModules + + opts := modload.PackageOpts{ + Tags: imports.AnyTags(), + VendorModulesInGOROOTSrc: true, + ResolveMissingImports: false, + LoadTests: true, + AllowErrors: true, + SilencePackageErrors: true, + SilenceUnmatchedWarnings: true, + } + for _, m := range mms.Versions() { + opts.MainModule = m + _, pkgs := modload.LoadPackages(ctx, opts, "all") + opts.MainModule = module.Version{} // reset + + var ( + mustSelect []module.Version + inMustSelect = map[module.Version]bool{} + ) + for _, pkg := range pkgs { + if r := modload.PackageModule(pkg); r.Version != "" && !inMustSelect[r] { + // r has a known version, so force that version. + mustSelect = append(mustSelect, r) + inMustSelect[r] = true + } + } + module.Sort(mustSelect) // ensure determinism + mustSelectFor[m] = mustSelect + } + + workFilePath := modload.WorkFilePath() // save go.work path because EnterModule clobbers it. + + for _, m := range mms.Versions() { + if mms.ModRoot(m) == "" && m.Path == "command-line-arguments" { + // This is not a real module. + // TODO(#49228): Remove this special case once the special + // command-line-arguments module is gone. + continue + } + + // Use EnterModule to reset the global state in modload to be in + // single-module mode using the modroot of m. + modload.EnterModule(ctx, mms.ModRoot(m)) + + // Edit the build list in the same way that 'go get' would if we + // requested the relevant module versions explicitly. + changed, err := modload.EditBuildList(ctx, nil, mustSelectFor[m]) + if err != nil { + base.Errorf("go: %v", err) + } + if !changed { + continue + } + + modload.LoadPackages(ctx, modload.PackageOpts{ + Tags: imports.AnyTags(), + Tidy: true, + VendorModulesInGOROOTSrc: true, + ResolveMissingImports: false, + LoadTests: true, + AllowErrors: true, + SilenceMissingStdImports: true, + SilencePackageErrors: true, + }, "all") + modload.WriteGoMod(ctx) + } + + wf, err := modload.ReadWorkFile(workFilePath) + if err != nil { + base.Fatalf("go: %v", err) + } + modload.UpdateWorkFile(wf) + if err := modload.WriteWorkFile(workFilePath, wf); err != nil { + base.Fatalf("go: %v", err) + } +} diff --git a/libgo/go/cmd/go/internal/workcmd/use.go b/libgo/go/cmd/go/internal/workcmd/use.go new file mode 100644 index 00000000000..852e5b910c2 --- /dev/null +++ b/libgo/go/cmd/go/internal/workcmd/use.go @@ -0,0 +1,119 @@ +// 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. + +// go work use + +package workcmd + +import ( + "cmd/go/internal/base" + "cmd/go/internal/fsys" + "cmd/go/internal/modload" + "context" + "io/fs" + "os" + "path/filepath" +) + +var cmdUse = &base.Command{ + UsageLine: "go work use [-r] [moddirs]", + Short: "add modules to workspace file", + Long: `Use provides a command-line interface for adding +directories, optionally recursively, to a go.work file. + +A use directive will be added to the go.work file for each argument +directory listed on the command line go.work file, if it exists on disk, +or removed from the go.work file if it does not exist on disk. + +The -r flag searches recursively for modules in the argument +directories, and the use command operates as if each of the directories +were specified as arguments: namely, use directives will be added for +directories that exist, and removed for directories that do not exist. +`, +} + +var useR = cmdUse.Flag.Bool("r", false, "") + +func init() { + cmdUse.Run = runUse // break init cycle + + base.AddModCommonFlags(&cmdUse.Flag) + base.AddWorkfileFlag(&cmdUse.Flag) +} + +func runUse(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() + + modload.ForceUseModules = true + + var gowork string + modload.InitWorkfile() + gowork = modload.WorkFilePath() + + workFile, err := modload.ReadWorkFile(gowork) + if err != nil { + base.Fatalf("go: %v", err) + } + + haveDirs := make(map[string]bool) + for _, dir := range workFile.Use { + haveDirs[filepath.Join(filepath.Dir(gowork), filepath.FromSlash(dir.Path))] = true + } + + addDirs := make(map[string]bool) + removeDirs := make(map[string]bool) + lookDir := func(dir string) { + absDir := filepath.Join(base.Cwd(), dir) + // If the path is absolute, keep it absolute. If it's relative, + // make it relative to the go.work file rather than the working directory. + if !filepath.IsAbs(dir) { + rel, err := filepath.Rel(filepath.Dir(gowork), absDir) + if err == nil { + dir = rel + } + } + fi, err := os.Stat(filepath.Join(dir, "go.mod")) + if err != nil { + if os.IsNotExist(err) { + + if haveDirs[absDir] { + removeDirs[dir] = true + } + return + } + base.Errorf("go: %v", err) + } + + if !fi.Mode().IsRegular() { + base.Errorf("go: %v is not regular", filepath.Join(dir, "go.mod")) + } + + if !haveDirs[absDir] { + addDirs[dir] = true + } + } + + for _, useDir := range args { + if *useR { + fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error { + if !info.IsDir() { + return nil + } + lookDir(path) + return nil + }) + continue + } + lookDir(useDir) + } + + for dir := range removeDirs { + workFile.DropUse(filepath.ToSlash(dir)) + } + for dir := range addDirs { + workFile.AddUse(filepath.ToSlash(dir), "") + } + modload.UpdateWorkFile(workFile) + modload.WriteWorkFile(gowork, workFile) +} diff --git a/libgo/go/cmd/go/internal/workcmd/work.go b/libgo/go/cmd/go/internal/workcmd/work.go new file mode 100644 index 00000000000..5bb0a2e8bad --- /dev/null +++ b/libgo/go/cmd/go/internal/workcmd/work.go @@ -0,0 +1,72 @@ +// 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 workcmd implements the ``go work'' command. +package workcmd + +import ( + "cmd/go/internal/base" +) + +var CmdWork = &base.Command{ + UsageLine: "go work", + Short: "workspace maintenance", + Long: `Go workspace provides access to operations on workspaces. + +Note that support for workspaces is built into many other commands, not +just 'go work'. + +See 'go help modules' for information about Go's module system of which +workspaces are a part. + +A workspace is specified by a go.work file that specifies a set of +module directories with the "use" directive. These modules are used as +root modules by the go command for builds and related operations. A +workspace that does not specify modules to be used cannot be used to do +builds from local modules. + +go.work files are line-oriented. Each line holds a single directive, +made up of a keyword followed by aruments. For example: + + go 1.18 + + use ../foo/bar + use ./baz + + replace example.com/foo v1.2.3 => example.com/bar v1.4.5 + +The leading keyword can be factored out of adjacent lines to create a block, +like in Go imports. + + use ( + ../foo/bar + ./baz + ) + +The use directive specifies a module to be included in the workspace's +set of main modules. The argument to the use directive is the directory +containing the module's go.mod file. + +The go directive specifies the version of Go the file was written at. It +is possible there may be future changes in the semantics of workspaces +that could be controlled by this version, but for now the version +specified has no effect. + +The replace directive has the same syntax as the replace directive in a +go.mod file and takes precedence over replaces in go.mod files. It is +primarily intended to override conflicting replaces in different workspace +modules. + +To determine whether the go command is operating in workspace mode, use +the "go env GOWORK" command. This will specify the workspace file being +used. +`, + + Commands: []*base.Command{ + cmdEdit, + cmdInit, + cmdSync, + cmdUse, + }, +} diff --git a/libgo/go/cmd/go/main.go b/libgo/go/cmd/go/main.go index a3251723c55..d7034084dc8 100644 --- a/libgo/go/cmd/go/main.go +++ b/libgo/go/cmd/go/main.go @@ -7,6 +7,7 @@ package main import ( + "cmd/go/internal/workcmd" "context" "flag" "fmt" @@ -56,6 +57,7 @@ func init() { work.CmdInstall, list.CmdList, modcmd.CmdMod, + workcmd.CmdWork, run.CmdRun, test.CmdTest, tool.CmdTool, diff --git a/libgo/go/cmd/go/proxy_test.go b/libgo/go/cmd/go/proxy_test.go index 74bfecc08db..517a8855428 100644 --- a/libgo/go/cmd/go/proxy_test.go +++ b/libgo/go/cmd/go/proxy_test.go @@ -25,12 +25,12 @@ import ( "cmd/go/internal/modfetch/codehost" "cmd/go/internal/par" - "cmd/go/internal/txtar" "golang.org/x/mod/module" "golang.org/x/mod/semver" "golang.org/x/mod/sumdb" "golang.org/x/mod/sumdb/dirhash" + "golang.org/x/tools/txtar" ) var ( @@ -357,7 +357,7 @@ func proxyHandler(w http.ResponseWriter, r *http.Request) { zip []byte err error } - c := zipCache.Do(a, func() interface{} { + c := zipCache.Do(a, func() any { var buf bytes.Buffer z := zip.NewWriter(&buf) for _, f := range a.Files { @@ -431,7 +431,7 @@ func readArchive(path, vers string) (*txtar.Archive, error) { prefix := strings.ReplaceAll(enc, "/", "_") name := filepath.Join(cmdGoDir, "testdata/mod", prefix+"_"+encVers+".txt") - a := archiveCache.Do(name, func() interface{} { + a := archiveCache.Do(name, func() any { a, err := txtar.ParseFile(name) if err != nil { if testing.Verbose() || !os.IsNotExist(err) { diff --git a/libgo/go/cmd/go/script_test.go b/libgo/go/cmd/go/script_test.go index 639e907db07..0fc4b0f7abd 100644 --- a/libgo/go/cmd/go/script_test.go +++ b/libgo/go/cmd/go/script_test.go @@ -11,6 +11,7 @@ import ( "bytes" "context" "errors" + "flag" "fmt" "go/build" "internal/testenv" @@ -30,11 +31,14 @@ import ( "cmd/go/internal/imports" "cmd/go/internal/par" "cmd/go/internal/robustio" - "cmd/go/internal/txtar" "cmd/go/internal/work" "cmd/internal/sys" + + "golang.org/x/tools/txtar" ) +var testSum = flag.String("testsum", "", `may be tidy, listm, or listall. If set, TestScript generates a go.sum file at the beginning of each test and updates test files if they pass.`) + // TestScript runs the tests in testdata/script/*.txt. func TestScript(t *testing.T) { testenv.MustHaveGoBuild(t) @@ -181,6 +185,7 @@ func (ts *testScript) setup() { "devnull=" + os.DevNull, "goversion=" + goVersion(ts), ":=" + string(os.PathListSeparator), + "/=" + string(os.PathSeparator), } if !testenv.HasExternalNetwork() { ts.env = append(ts.env, "TESTGONETWORK=panic", "TESTGOVCS=panic") @@ -269,6 +274,22 @@ func (ts *testScript) run() { ts.mark = ts.log.Len() } + // With -testsum, if a go.mod file is present in the test's initial + // working directory, run 'go mod tidy'. + if *testSum != "" { + if ts.updateSum(a) { + defer func() { + if ts.t.Failed() { + return + } + data := txtar.Format(a) + if err := os.WriteFile(ts.file, data, 0666); err != nil { + ts.t.Errorf("rewriting test file: %v", err) + } + }() + } + } + // Run script. // See testdata/script/README for documentation of script form. script := string(a.Comment) @@ -332,8 +353,14 @@ Script: ok = canCgo case "msan": ok = canMSan + case "asan": + ok = canASan case "race": ok = canRace + case "fuzz": + ok = canFuzz + case "fuzz-instrumented": + ok = fuzzInstrumented case "net": ok = testenv.HasExternalNetwork() case "link": @@ -347,7 +374,7 @@ Script: default: if strings.HasPrefix(cond.tag, "exec:") { prog := cond.tag[len("exec:"):] - ok = execCache.Do(prog, func() interface{} { + ok = execCache.Do(prog, func() any { if runtime.GOOS == "plan9" && prog == "git" { // The Git command is usually not the real Git on Plan 9. // See https://golang.org/issues/29640. @@ -464,6 +491,7 @@ var scriptCmds = map[string]func(*testScript, simpleStatus, []string){ "go": (*testScript).cmdGo, "grep": (*testScript).cmdGrep, "mkdir": (*testScript).cmdMkdir, + "mv": (*testScript).cmdMv, "rm": (*testScript).cmdRm, "skip": (*testScript).cmdSkip, "stale": (*testScript).cmdStale, @@ -558,10 +586,6 @@ func (ts *testScript) cmdChmod(want simpleStatus, args []string) { // cmp compares two files. func (ts *testScript) cmdCmp(want simpleStatus, args []string) { - if want != success { - // It would be strange to say "this file can have any content except this precise byte sequence". - ts.fatalf("unsupported: %v cmp", want) - } quiet := false if len(args) > 0 && args[0] == "-q" { quiet = true @@ -570,14 +594,11 @@ func (ts *testScript) cmdCmp(want simpleStatus, args []string) { if len(args) != 2 { ts.fatalf("usage: cmp file1 file2") } - ts.doCmdCmp(args, false, quiet) + ts.doCmdCmp(want, args, false, quiet) } // cmpenv compares two files with environment variable substitution. func (ts *testScript) cmdCmpenv(want simpleStatus, args []string) { - if want != success { - ts.fatalf("unsupported: %v cmpenv", want) - } quiet := false if len(args) > 0 && args[0] == "-q" { quiet = true @@ -586,17 +607,18 @@ func (ts *testScript) cmdCmpenv(want simpleStatus, args []string) { if len(args) != 2 { ts.fatalf("usage: cmpenv file1 file2") } - ts.doCmdCmp(args, true, quiet) + ts.doCmdCmp(want, args, true, quiet) } -func (ts *testScript) doCmdCmp(args []string, env, quiet bool) { +func (ts *testScript) doCmdCmp(want simpleStatus, args []string, env, quiet bool) { name1, name2 := args[0], args[1] var text1, text2 string - if name1 == "stdout" { + switch name1 { + case "stdout": text1 = ts.stdout - } else if name1 == "stderr" { + case "stderr": text1 = ts.stderr - } else { + default: data, err := os.ReadFile(ts.mkabs(name1)) ts.check(err) text1 = string(data) @@ -611,14 +633,28 @@ func (ts *testScript) doCmdCmp(args []string, env, quiet bool) { text2 = ts.expand(text2, false) } - if text1 == text2 { - return - } - - if !quiet { + eq := text1 == text2 + if !eq && !quiet && want != failure { fmt.Fprintf(&ts.log, "[diff -%s +%s]\n%s\n", name1, name2, diff(text1, text2)) } - ts.fatalf("%s and %s differ", name1, name2) + switch want { + case failure: + if eq { + ts.fatalf("%s and %s do not differ", name1, name2) + } + case success: + if !eq { + ts.fatalf("%s and %s differ", name1, name2) + } + case successOrFailure: + if eq { + fmt.Fprintf(&ts.log, "%s and %s do not differ", name1, name2) + } else { + fmt.Fprintf(&ts.log, "%s and %s differ", name1, name2) + } + default: + ts.fatalf("unsupported: %v cmp", want) + } } // cp copies files, maybe eventually directories. @@ -813,6 +849,16 @@ func (ts *testScript) cmdMkdir(want simpleStatus, args []string) { } } +func (ts *testScript) cmdMv(want simpleStatus, args []string) { + if want != success { + ts.fatalf("unsupported: %v mv", want) + } + if len(args) != 2 { + ts.fatalf("usage: mv old new") + } + ts.check(os.Rename(ts.mkabs(args[0]), ts.mkabs(args[1]))) +} + // rm removes files or directories. func (ts *testScript) cmdRm(want simpleStatus, args []string) { if want != success { @@ -1109,6 +1155,17 @@ func (ts *testScript) startBackground(want simpleStatus, command string, args .. done: done, } + // Use the script's PATH to look up the command if it contains a separator + // instead of the test process's PATH (see lookPath). + // Don't use filepath.Clean, since that changes "./foo" to "foo". + command = filepath.FromSlash(command) + if !strings.Contains(command, string(filepath.Separator)) { + var err error + command, err = ts.lookPath(command) + if err != nil { + return nil, err + } + } cmd := exec.Command(command, args...) cmd.Dir = ts.cd cmd.Env = append(ts.env, "PWD="+ts.cd) @@ -1125,6 +1182,73 @@ func (ts *testScript) startBackground(want simpleStatus, command string, args .. return bg, nil } +// lookPath is (roughly) like exec.LookPath, but it uses the test script's PATH +// instead of the test process's PATH to find the executable. We don't change +// the test process's PATH since it may run scripts in parallel. +func (ts *testScript) lookPath(command string) (string, error) { + var strEqual func(string, string) bool + if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { + // Using GOOS as a proxy for case-insensitive file system. + strEqual = strings.EqualFold + } else { + strEqual = func(a, b string) bool { return a == b } + } + + var pathExt []string + var searchExt bool + var isExecutable func(os.FileInfo) bool + if runtime.GOOS == "windows" { + // Use the test process's PathExt instead of the script's. + // If PathExt is set in the command's environment, cmd.Start fails with + // "parameter is invalid". Not sure why. + // If the command already has an extension in PathExt (like "cmd.exe") + // don't search for other extensions (not "cmd.bat.exe"). + pathExt = strings.Split(os.Getenv("PathExt"), string(filepath.ListSeparator)) + searchExt = true + cmdExt := filepath.Ext(command) + for _, ext := range pathExt { + if strEqual(cmdExt, ext) { + searchExt = false + break + } + } + isExecutable = func(fi os.FileInfo) bool { + return fi.Mode().IsRegular() + } + } else { + isExecutable = func(fi os.FileInfo) bool { + return fi.Mode().IsRegular() && fi.Mode().Perm()&0111 != 0 + } + } + + pathName := "PATH" + if runtime.GOOS == "plan9" { + pathName = "path" + } + + for _, dir := range strings.Split(ts.envMap[pathName], string(filepath.ListSeparator)) { + if searchExt { + ents, err := os.ReadDir(dir) + if err != nil { + continue + } + for _, ent := range ents { + for _, ext := range pathExt { + if !ent.IsDir() && strEqual(ent.Name(), command+ext) { + return dir + string(filepath.Separator) + ent.Name(), nil + } + } + } + } else { + path := dir + string(filepath.Separator) + command + if fi, err := os.Stat(path); err == nil && isExecutable(fi) { + return path, nil + } + } + } + return "", &exec.Error{Name: command, Err: exec.ErrNotFound} +} + // waitOrStop waits for the already-started command cmd by calling its Wait method. // // If cmd does not return before ctx is done, waitOrStop sends it the given interrupt signal. @@ -1151,7 +1275,7 @@ func waitOrStop(ctx context.Context, cmd *exec.Cmd, interrupt os.Signal, killDel err := cmd.Process.Signal(interrupt) if err == nil { err = ctx.Err() // Report ctx.Err() as the reason we interrupted. - } else if err.Error() == "os: process already finished" { + } else if err == os.ErrProcessDone { errc <- nil return } @@ -1204,7 +1328,7 @@ func (ts *testScript) expand(s string, inRegexp bool) string { } // fatalf aborts the test with the given failure message. -func (ts *testScript) fatalf(format string, args ...interface{}) { +func (ts *testScript) fatalf(format string, args ...any) { fmt.Fprintf(&ts.log, "FAIL: %s:%d: %s\n", ts.file, ts.lineno, fmt.Sprintf(format, args...)) ts.t.FailNow() } @@ -1341,6 +1465,68 @@ func (ts *testScript) parse(line string) command { return cmd } +// updateSum runs 'go mod tidy', 'go list -mod=mod -m all', or +// 'go list -mod=mod all' in the test's current directory if a file named +// "go.mod" is present after the archive has been extracted. updateSum modifies +// archive and returns true if go.mod or go.sum were changed. +func (ts *testScript) updateSum(archive *txtar.Archive) (rewrite bool) { + gomodIdx, gosumIdx := -1, -1 + for i := range archive.Files { + switch archive.Files[i].Name { + case "go.mod": + gomodIdx = i + case "go.sum": + gosumIdx = i + } + } + if gomodIdx < 0 { + return false + } + + switch *testSum { + case "tidy": + ts.cmdGo(success, []string{"mod", "tidy"}) + case "listm": + ts.cmdGo(success, []string{"list", "-m", "-mod=mod", "all"}) + case "listall": + ts.cmdGo(success, []string{"list", "-mod=mod", "all"}) + default: + ts.t.Fatalf(`unknown value for -testsum %q; may be "tidy", "listm", or "listall"`, *testSum) + } + + newGomodData, err := os.ReadFile(filepath.Join(ts.cd, "go.mod")) + if err != nil { + ts.t.Fatalf("reading go.mod after -testsum: %v", err) + } + if !bytes.Equal(newGomodData, archive.Files[gomodIdx].Data) { + archive.Files[gomodIdx].Data = newGomodData + rewrite = true + } + + newGosumData, err := os.ReadFile(filepath.Join(ts.cd, "go.sum")) + if err != nil && !os.IsNotExist(err) { + ts.t.Fatalf("reading go.sum after -testsum: %v", err) + } + switch { + case os.IsNotExist(err) && gosumIdx >= 0: + // go.sum was deleted. + rewrite = true + archive.Files = append(archive.Files[:gosumIdx], archive.Files[gosumIdx+1:]...) + case err == nil && gosumIdx < 0: + // go.sum was created. + rewrite = true + gosumIdx = gomodIdx + 1 + archive.Files = append(archive.Files, txtar.File{}) + copy(archive.Files[gosumIdx+1:], archive.Files[gosumIdx:]) + archive.Files[gosumIdx] = txtar.File{Name: "go.sum", Data: newGosumData} + case err == nil && gosumIdx >= 0 && !bytes.Equal(newGosumData, archive.Files[gosumIdx].Data): + // go.sum was changed. + rewrite = true + archive.Files[gosumIdx].Data = newGosumData + } + return rewrite +} + // diff returns a formatted diff of the two texts, // showing the entire text and the minimum line-level // additions and removals to turn text1 into text2. diff --git a/libgo/go/cmd/go/stop_other_test.go b/libgo/go/cmd/go/stop_other_test.go index e1cc6cf8ba7..35c12858c1d 100644 --- a/libgo/go/cmd/go/stop_other_test.go +++ b/libgo/go/cmd/go/stop_other_test.go @@ -3,15 +3,6 @@ // license that can be found in the LICENSE file. //go:build !(aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris) -// +build !aix -// +build !darwin -// +build !dragonfly -// +build !freebsd -// +build !js !wasm -// +build !linux -// +build !netbsd -// +build !openbsd -// +build !solaris package main_test diff --git a/libgo/go/cmd/go/stop_unix_test.go b/libgo/go/cmd/go/stop_unix_test.go index ac35b240f0a..5939f0d40d9 100644 --- a/libgo/go/cmd/go/stop_unix_test.go +++ b/libgo/go/cmd/go/stop_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris package main_test diff --git a/libgo/go/cmd/go/testdata/addmod.go b/libgo/go/cmd/go/testdata/addmod.go index 03869e68def..eac2a7ad449 100644 --- a/libgo/go/cmd/go/testdata/addmod.go +++ b/libgo/go/cmd/go/testdata/addmod.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore // Addmod adds a module as a txtar archive to the testdata/mod directory. @@ -29,7 +30,7 @@ import ( "path/filepath" "strings" - "cmd/go/internal/txtar" + "golang.org/x/tools/txtar" ) func usage() { @@ -39,7 +40,7 @@ func usage() { var tmpdir string -func fatalf(format string, args ...interface{}) { +func fatalf(format string, args ...any) { os.RemoveAll(tmpdir) log.Fatalf(format, args...) } diff --git a/libgo/go/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt b/libgo/go/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt index 35c3f277103..00076d74fc2 100644 --- a/libgo/go/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt +++ b/libgo/go/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt @@ -1,6 +1,6 @@ Written by hand. Test case for getting a package that has been moved to a nested module, -with a +incompatible verison (and thus no go.mod file) at the root module. +with a +incompatible version (and thus no go.mod file) at the root module. -- .mod -- module example.com/split-incompatible diff --git a/libgo/go/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt b/libgo/go/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt index 917fc0f5599..bb1c1fecc9d 100644 --- a/libgo/go/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt +++ b/libgo/go/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt @@ -1,6 +1,6 @@ Written by hand. Test case for getting a package that has been moved to a nested module, -with a +incompatible verison (and thus no go.mod file) at the root module. +with a +incompatible version (and thus no go.mod file) at the root module. -- .mod -- module example.com/split-incompatible diff --git a/libgo/go/cmd/go/testdata/savedir.go b/libgo/go/cmd/go/testdata/savedir.go index d469c31a919..53c78cfb00b 100644 --- a/libgo/go/cmd/go/testdata/savedir.go +++ b/libgo/go/cmd/go/testdata/savedir.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore // Savedir archives a directory tree as a txtar archive printed to standard output. @@ -24,7 +25,7 @@ import ( "strings" "unicode/utf8" - "../internal/txtar" + "golang.org/x/tools/txtar" ) func usage() { diff --git a/libgo/go/cmd/go/testdata/script/README b/libgo/go/cmd/go/testdata/script/README index 48e4055b0bd..b2a7fd19156 100644 --- a/libgo/go/cmd/go/testdata/script/README +++ b/libgo/go/cmd/go/testdata/script/README @@ -79,7 +79,9 @@ should only run when the condition is satisfied. The available conditions are: - Compiler names, like [gccgo], [gc]. - Test environment details: - [short] for testing.Short() - - [cgo], [msan], [race] for whether cgo, msan, and the race detector can be used + - [cgo], [msan], [asan], [race] for whether cgo, msan, asan, and the race detector can be used + - [fuzz] for whether 'go test -fuzz' can be used at all + - [fuzz-instrumented] for whether 'go test -fuzz' uses coverage-instrumented binaries - [net] for whether the external network can be used - [link] for testenv.HasLink() - [root] for os.Geteuid() == 0 @@ -108,14 +110,15 @@ The commands are: Change the permissions of the files or directories named by the path arguments to be equal to perm. Only numerical permissions are supported. -- cmp file1 file2 - Check that the named files have the same content. +- [! | ?] cmp file1 file2 + Check that the named files have (or do not have) the same content. By convention, file1 is the actual data and file2 the expected data. File1 can be "stdout" or "stderr" to use the standard output or standard error from the most recent exec or go command. - (If the files have differing content, the failure prints a diff.) + (If the file contents differ and the command is not negated, + the failure prints a diff.) -- cmpenv file1 file2 +- [! | ?] cmpenv file1 file2 Like cmp, but environment variables are substituted in the file contents before the comparison. For example, $GOOS is replaced by the target GOOS. @@ -161,6 +164,10 @@ The commands are: - mkdir path... Create the listed directories, if they do not already exists. +- mv path1 path2 + Rename path1 to path2. OS-specific restrictions may apply when path1 and path2 + are in different directories. + - rm file... Remove the listed files or directories. diff --git a/libgo/go/cmd/go/testdata/script/build_cache_disabled.txt b/libgo/go/cmd/go/testdata/script/build_cache_disabled.txt index 68e2773a724..a4cc33c357b 100644 --- a/libgo/go/cmd/go/testdata/script/build_cache_disabled.txt +++ b/libgo/go/cmd/go/testdata/script/build_cache_disabled.txt @@ -9,7 +9,7 @@ # * go fix # * go fmt # * go generate -# * go get -d +# * go get # * go list (without -export or -compiled) env GOCACHE=off diff --git a/libgo/go/cmd/go/testdata/script/build_cache_trimpath.txt b/libgo/go/cmd/go/testdata/script/build_cache_trimpath.txt index 9a4b9d7b40c..7ee3c3b41d4 100644 --- a/libgo/go/cmd/go/testdata/script/build_cache_trimpath.txt +++ b/libgo/go/cmd/go/testdata/script/build_cache_trimpath.txt @@ -16,10 +16,10 @@ stderr 'link( |\.exe)' # Two distinct versions of the same module with identical content should # still be cached separately. # Verifies golang.org/issue/35412. -go get -d example.com/stack@v1.0.0 +go get example.com/stack@v1.0.0 go run -trimpath printstack.go stdout '^example.com/stack@v1.0.0/stack.go$' -go get -d example.com/stack@v1.0.1 +go get example.com/stack@v1.0.1 go run -trimpath printstack.go stdout '^example.com/stack@v1.0.1/stack.go$' diff --git a/libgo/go/cmd/go/testdata/script/build_concurrent_backend.txt b/libgo/go/cmd/go/testdata/script/build_concurrent_backend.txt new file mode 100644 index 00000000000..3587e22038e --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/build_concurrent_backend.txt @@ -0,0 +1,12 @@ +# Tests golang.org/issue/48490 +# cmd/go should enable concurrent compilation by default + +[gccgo] skip + +# Reset all experiments, since one of them can disable +# concurrent compilation, e.g: fieldtrack. +env GOEXPERIMENT=none + +env GOMAXPROCS=4 +go build -n -x -a fmt +stderr ' -c=4 ' diff --git a/libgo/go/cmd/go/testdata/script/build_gcflags_order.txt b/libgo/go/cmd/go/testdata/script/build_gcflags_order.txt new file mode 100644 index 00000000000..bab02786ec9 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/build_gcflags_order.txt @@ -0,0 +1,20 @@ +# Tests golang.org/issue/47682 +# Flags specified with -gcflags should appear after other flags generated by cmd/go. + +cd m +go build -n -gcflags=-lang=go1.17 +[!gccgo] stderr ' -lang=go1.16.* -lang=go1.17' + +-- m/go.mod -- +module example.com + +go 1.16 + +-- m/main.go -- +package main + +func main() { + var s = []int{1, 2, 3} + var pa = (*[2]int)(s[1:]) + println(pa[1]) +} diff --git a/libgo/go/cmd/go/testdata/script/build_i_deprecate.txt b/libgo/go/cmd/go/testdata/script/build_i_deprecate.txt index 71356e5321e..5c179956699 100644 --- a/libgo/go/cmd/go/testdata/script/build_i_deprecate.txt +++ b/libgo/go/cmd/go/testdata/script/build_i_deprecate.txt @@ -2,13 +2,13 @@ # TODO(golang.org/issue/41696): remove the -i flag after Go 1.16, and this test. go build -n -i -stderr '^go build: -i flag is deprecated$' +stderr '^go: -i flag is deprecated$' go install -n -i -stderr '^go install: -i flag is deprecated$' +stderr '^go: -i flag is deprecated$' go test -n -i -stderr '^go test: -i flag is deprecated$' +stderr '^go: -i flag is deprecated$' # 'go clean -i' should not print a deprecation warning. diff --git a/libgo/go/cmd/go/testdata/script/build_issue48319.txt b/libgo/go/cmd/go/testdata/script/build_issue48319.txt new file mode 100644 index 00000000000..3979247f2fd --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/build_issue48319.txt @@ -0,0 +1,47 @@ +# Regression test for https://go.dev/issue/48319: +# cgo builds should not include debug information from a stale GOROOT_FINAL. + +[short] skip +[!cgo] skip +[windows] skip # The Go Windows builders have an extremely out-of-date gcc that does not support reproducible builds; see https://go.dev/issue/50824. + +# This test is sensitive to cache invalidation, +# so use a separate build cache that we can control. +env GOCACHE=$WORK/gocache +mkdir $GOCACHE + +# Build a binary using a specific value of GOROOT_FINAL. +env GOROOT_FINAL=$WORK${/}goroot1 +go build -o main.exe +mv main.exe main1.exe + +# Now clean the cache and build using a different GOROOT_FINAL. +# The resulting binaries should differ in their debug metadata. +go clean -cache +env GOROOT_FINAL=$WORK${/}goroot2 +go build -o main.exe +mv main.exe main2.exe +! cmp main2.exe main1.exe + +# Set GOROOT_FINAL back to the first value. +# If the build is properly reproducible, the two binaries should match. +env GOROOT_FINAL=$WORK${/}goroot1 +go build -o main.exe +cmp -q main.exe main1.exe + +-- go.mod -- +module main + +go 1.18 +-- main.go -- +package main + +import "C" + +import "runtime" + +var _ C.int + +func main() { + println(runtime.GOROOT()) +} diff --git a/libgo/go/cmd/go/testdata/script/build_negative_p.txt b/libgo/go/cmd/go/testdata/script/build_negative_p.txt new file mode 100644 index 00000000000..9123907dc87 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/build_negative_p.txt @@ -0,0 +1,5 @@ +! go build -p=-1 example.go +stderr 'go: -p must be a positive integer: -1' + +-- example.go -- +package example
\ No newline at end of file diff --git a/libgo/go/cmd/go/testdata/script/build_overlay.txt b/libgo/go/cmd/go/testdata/script/build_overlay.txt index 2932b94e6c4..56e812f44b2 100644 --- a/libgo/go/cmd/go/testdata/script/build_overlay.txt +++ b/libgo/go/cmd/go/testdata/script/build_overlay.txt @@ -31,17 +31,17 @@ exec ./print_trimpath_two_files$GOEXE stdout $WORK[/\\]gopath[/\\]src[/\\]m[/\\]printpath[/\\]main.go stdout $WORK[/\\]gopath[/\\]src[/\\]m[/\\]printpath[/\\]other.go -go build -overlay overlay.json -o main_cgo_replace$GOEXE ./cgo_hello_replace -exec ./main_cgo_replace$GOEXE -stdout '^hello cgo\r?\n' +[cgo] go build -overlay overlay.json -o main_cgo_replace$GOEXE ./cgo_hello_replace +[cgo] exec ./main_cgo_replace$GOEXE +[cgo] stdout '^hello cgo\r?\n' -go build -overlay overlay.json -o main_cgo_quote$GOEXE ./cgo_hello_quote -exec ./main_cgo_quote$GOEXE -stdout '^hello cgo\r?\n' +[cgo] go build -overlay overlay.json -o main_cgo_quote$GOEXE ./cgo_hello_quote +[cgo] exec ./main_cgo_quote$GOEXE +[cgo] stdout '^hello cgo\r?\n' -go build -overlay overlay.json -o main_cgo_angle$GOEXE ./cgo_hello_angle -exec ./main_cgo_angle$GOEXE -stdout '^hello cgo\r?\n' +[cgo] go build -overlay overlay.json -o main_cgo_angle$GOEXE ./cgo_hello_angle +[cgo] exec ./main_cgo_angle$GOEXE +[cgo] stdout '^hello cgo\r?\n' go build -overlay overlay.json -o main_call_asm$GOEXE ./call_asm exec ./main_call_asm$GOEXE @@ -55,11 +55,11 @@ cp overlay/test_cache_different.go overlay/test_cache.go go list -overlay overlay.json -f '{{.Stale}}' ./test_cache stdout '^true$' -go list -compiled -overlay overlay.json -f '{{range .CompiledGoFiles}}{{. | printf "%s\n"}}{{end}}' ./cgo_hello_replace -cp stdout compiled_cgo_sources.txt -go run ../print_line_comments.go compiled_cgo_sources.txt -stdout $GOPATH[/\\]src[/\\]m[/\\]cgo_hello_replace[/\\]cgo_hello_replace.go -! stdout $GOPATH[/\\]src[/\\]m[/\\]overlay[/\\]hello.c +[cgo] go list -compiled -overlay overlay.json -f '{{range .CompiledGoFiles}}{{. | printf "%s\n"}}{{end}}' ./cgo_hello_replace +[cgo] cp stdout compiled_cgo_sources.txt +[cgo] go run ../print_line_comments.go compiled_cgo_sources.txt +[cgo] stdout $GOPATH[/\\]src[/\\]m[/\\]cgo_hello_replace[/\\]cgo_hello_replace.go +[cgo] ! stdout $GOPATH[/\\]src[/\\]m[/\\]overlay[/\\]hello.c # Run same tests but with gccgo. env GO111MODULE=off diff --git a/libgo/go/cmd/go/testdata/script/build_runtime_gcflags.txt b/libgo/go/cmd/go/testdata/script/build_runtime_gcflags.txt index da1b65f06c8..c87e4809119 100644 --- a/libgo/go/cmd/go/testdata/script/build_runtime_gcflags.txt +++ b/libgo/go/cmd/go/testdata/script/build_runtime_gcflags.txt @@ -8,4 +8,4 @@ mkdir $GOCACHE # Verify the standard library (specifically runtime/internal/atomic) can be # built with -gcflags when -n is given. See golang.org/issue/29346. go build -n -gcflags=all='-l' std -stderr 'compile.* -l .* runtime/internal/atomic' +stderr 'compile.* runtime/internal/atomic .* -l' diff --git a/libgo/go/cmd/go/testdata/script/build_single_error.txt b/libgo/go/cmd/go/testdata/script/build_single_error.txt new file mode 100644 index 00000000000..241cdb954ba --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/build_single_error.txt @@ -0,0 +1,18 @@ +# go test ./... with a bad package should report the error once (#44624). +! go test ./... +stderr -count=1 undefined + +-- go.mod -- +module example.com + +go 1.18 +-- a/a.go -- +package a + +import "example.com/b" +-- b/b.go -- +package b + +var X = Y +-- b/b_test.go -- +package b diff --git a/libgo/go/cmd/go/testdata/script/build_trimpath.txt b/libgo/go/cmd/go/testdata/script/build_trimpath.txt index 2c3bee8fdc7..f36b1237dc0 100644 --- a/libgo/go/cmd/go/testdata/script/build_trimpath.txt +++ b/libgo/go/cmd/go/testdata/script/build_trimpath.txt @@ -32,7 +32,8 @@ stdout 'binary contains GOROOT: false' # A binary from an external module built with -trimpath should not contain # the current workspace or GOROOT. -go get -trimpath rsc.io/fortune +go get rsc.io/fortune +go install -trimpath rsc.io/fortune exec $WORK/paths-a.exe $GOPATH/bin/fortune$GOEXE stdout 'binary contains module root: false' stdout 'binary contains GOROOT: false' diff --git a/libgo/go/cmd/go/testdata/script/cgo_path_space_quote.txt b/libgo/go/cmd/go/testdata/script/cgo_path_space_quote.txt new file mode 100644 index 00000000000..95561013008 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/cgo_path_space_quote.txt @@ -0,0 +1,58 @@ +# This test checks that the CC environment variable may contain quotes and +# spaces. Arguments are normally split on spaces, tabs, newlines. If an +# argument contains these characters, the entire argument may be quoted +# with single or double quotes. This is the same as -gcflags and similar +# options. + +[short] skip +[!exec:clang] [!exec:gcc] skip +[!cgo] skip + +env GOENV=$WORK/go.env +mkdir 'program files' +go build -o 'program files' './which cc/which cc.go' +[exec:clang] env CC='"'$PWD${/}program' 'files${/}which' 'cc"' 'clang +[!exec:clang] env CC='"'$PWD${/}program' 'files${/}which' 'cc"' 'gcc +go env CC +stdout 'program files[/\\]which cc" (clang|gcc)$' +go env -w CC=$CC +env CC= +go env CC +stdout 'program files[/\\]which cc" (clang|gcc)$' + +go run . +stdout 1 + +-- go.mod -- +module test + +go 1.17 +-- which cc/which cc.go -- +package main + +import ( + "fmt" + "os" + "os/exec" +) + +func main() { + args := append([]string{"-DWRAPPER_WAS_USED=1"}, os.Args[2:]...) + cmd := exec.Command(os.Args[1], args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} +-- hello.go -- +package main + +// int x = WRAPPER_WAS_USED; +import "C" +import "fmt" + +func main() { + fmt.Println(C.x) +} diff --git a/libgo/go/cmd/go/testdata/script/cgo_stale_precompiled.txt b/libgo/go/cmd/go/testdata/script/cgo_stale_precompiled.txt new file mode 100644 index 00000000000..80ed751afcd --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/cgo_stale_precompiled.txt @@ -0,0 +1,28 @@ +# Regression test for https://go.dev/issue/47215 and https://go.dev/issue/50183: +# A mismatched $GOROOT_FINAL or missing $CC caused the C dependencies of the net +# package to appear stale, and it could not be rebuilt due to a missing $CC. + +[!cgo] skip + +# This test may start with the runtime/cgo package already stale. +# Explicitly rebuild it to ensure that it is cached. +# (See https://go.dev/issue/50892.) +# +# If running in non-short mode, explicitly vary CGO_CFLAGS +# as a control case (to ensure that our regexps do catch rebuilds). + +[!short] env GOCACHE=$WORK/cache +[!short] env CGO_CFLAGS=-DTestScript_cgo_stale_precompiled=true +go build -x runtime/cgo +[!short] stderr '[/\\]cgo'$GOEXE'["]? .* -importpath runtime/cgo' + +# https://go.dev/issue/47215: a missing $(go env CC) caused the precompiled net to be stale. +[!plan9] env PATH='' # Guaranteed not to include $(go env CC)! +[plan9] env path='' +go build -x runtime/cgo +! stderr '[/\\]cgo'$GOEXE'["]? .* -importpath runtime/cgo' + +# https://go.dev/issue/50183: a mismatched GOROOT_FINAL caused net to be stale. +env GOROOT_FINAL=$WORK${/}goroot +go build -x runtime/cgo +! stderr '[/\\]cgo'$GOEXE'["]? .* -importpath runtime/cgo' diff --git a/libgo/go/cmd/go/testdata/script/embed.txt b/libgo/go/cmd/go/testdata/script/embed.txt index 04b17cd62b3..5f7f6edd77e 100644 --- a/libgo/go/cmd/go/testdata/script/embed.txt +++ b/libgo/go/cmd/go/testdata/script/embed.txt @@ -60,6 +60,18 @@ rm t/x.txt ! go build m/use stderr '^x.go:5:12: pattern [*]t: cannot embed directory t: contains no embeddable files$' +# all still ignores .git and symlinks +cp x.go3 x.go +! go build -x +stderr '^x.go:5:12: pattern all:t: cannot embed directory t: contains no embeddable files$' + +# all finds dot files and underscore files +cp x.txt t/.x.txt +go build -x +rm t/.x.txt +cp x.txt t/_x.txt +go build -x + -- x.go -- package p @@ -92,6 +104,14 @@ import "embed" //go:embed *t var X embed.FS +-- x.go3 -- +package p + +import "embed" + +//go:embed all:t +var X embed.FS + -- x.txt -- hello diff --git a/libgo/go/cmd/go/testdata/script/env_unset.txt b/libgo/go/cmd/go/testdata/script/env_unset.txt index 647f26525dd..107b605a313 100644 --- a/libgo/go/cmd/go/testdata/script/env_unset.txt +++ b/libgo/go/cmd/go/testdata/script/env_unset.txt @@ -14,13 +14,13 @@ stderr '^go(\.exe)?: unknown GOEXPERIMENT badexp$' go env -u GOEXPERIMENT ! go env -stderr '^cmd/go: unsupported GOOS/GOARCH pair bados/badarch$' +stderr '^go: unsupported GOOS/GOARCH pair bados/badarch$' ! go env -u GOOS -stderr '^go env -u: unsupported GOOS/GOARCH pair \w+/badarch$' +stderr '^go: unsupported GOOS/GOARCH pair \w+/badarch$' ! go env -u GOARCH -stderr '^go env -u: unsupported GOOS/GOARCH pair bados/\w+$' +stderr '^go: unsupported GOOS/GOARCH pair bados/\w+$' go env -u GOOS GOARCH diff --git a/libgo/go/cmd/go/testdata/script/env_write.txt b/libgo/go/cmd/go/testdata/script/env_write.txt index 7920fbaceff..6f8a366d618 100644 --- a/libgo/go/cmd/go/testdata/script/env_write.txt +++ b/libgo/go/cmd/go/testdata/script/env_write.txt @@ -30,9 +30,9 @@ go env # checking errors ! go env -w -stderr 'go env -w: no KEY=VALUE arguments given' +stderr 'go: no KEY=VALUE arguments given' ! go env -u -stderr 'go env -u: no arguments given' +stderr 'go: ''go env -u'' requires an argument' # go env -w changes default setting env root= @@ -111,7 +111,7 @@ stderr 'GOPATH entry is relative; must be absolute path' # go env -w rejects invalid GOTMPDIR values ! go env -w GOTMPDIR=x -stderr 'go env -w: GOTMPDIR must be an absolute path' +stderr 'go: GOTMPDIR must be an absolute path' # go env -w should accept absolute GOTMPDIR value # and should not create it @@ -134,24 +134,24 @@ stdout ^$ go env -w CC=clang [!windows] ! go env -w CC=./clang [!windows] ! go env -w CC=bin/clang -[!windows] stderr 'go env -w: CC entry is relative; must be absolute path' +[!windows] stderr 'go: CC entry is relative; must be absolute path' [windows] go env -w CC=$WORK\bin\clang [windows] ! go env -w CC=.\clang [windows] ! go env -w CC=bin\clang -[windows] stderr 'go env -w: CC entry is relative; must be absolute path' +[windows] stderr 'go: CC entry is relative; must be absolute path' # go env -w rejects relative CXX values [!windows] go env -w CC=/usr/bin/cpp go env -w CXX=cpp [!windows] ! go env -w CXX=./cpp [!windows] ! go env -w CXX=bin/cpp -[!windows] stderr 'go env -w: CXX entry is relative; must be absolute path' +[!windows] stderr 'go: CXX entry is relative; must be absolute path' [windows] go env -w CXX=$WORK\bin\cpp [windows] ! go env -w CXX=.\cpp [windows] ! go env -w CXX=bin\cpp -[windows] stderr 'go env -w: CXX entry is relative; must be absolute path' +[windows] stderr 'go: CXX entry is relative; must be absolute path' # go env -w/-u checks validity of GOOS/ARCH combinations [gccgo] skip @@ -177,9 +177,9 @@ stderr 'unsupported GOOS/GOARCH.*windows/mips$' # go env -w should reject relative paths in GOMODCACHE environment. ! go env -w GOMODCACHE=~/test -stderr 'go env -w: GOMODCACHE entry is relative; must be absolute path: "~/test"' +stderr 'go: GOMODCACHE entry is relative; must be absolute path: "~/test"' ! go env -w GOMODCACHE=./test -stderr 'go env -w: GOMODCACHE entry is relative; must be absolute path: "./test"' +stderr 'go: GOMODCACHE entry is relative; must be absolute path: "./test"' # go env -w checks validity of GOEXPERIMENT env GOEXPERIMENT= diff --git a/libgo/go/cmd/go/testdata/script/gcflags_patterns.txt b/libgo/go/cmd/go/testdata/script/gcflags_patterns.txt index f23cecefd3a..24ec5aa11b0 100644 --- a/libgo/go/cmd/go/testdata/script/gcflags_patterns.txt +++ b/libgo/go/cmd/go/testdata/script/gcflags_patterns.txt @@ -7,28 +7,28 @@ env GOCACHE=$WORK/gocache # Looking for compile commands, so need a clean cache # -gcflags=-e applies to named packages, not dependencies go build -n -v -gcflags=-e z1 z2 -stderr 'compile.* -e.* -p z1' -stderr 'compile.* -e.* -p z2' +stderr 'compile.* -p z1.* -e ' +stderr 'compile.* -p z2.* -e ' stderr 'compile.* -p y' -! stderr 'compile.* -e.* -p [^z]' +! stderr 'compile.* -p [^z].* -e ' # -gcflags can specify package=flags, and can be repeated; last match wins go build -n -v -gcflags=-e -gcflags=z1=-N z1 z2 -stderr 'compile.* -N.* -p z1' -! stderr 'compile.* -e.* -p z1' -! stderr 'compile.* -N.* -p z2' -stderr 'compile.* -e.* -p z2' +stderr 'compile.* -p z1.* -N ' +! stderr 'compile.* -p z1.* -e ' +! stderr 'compile.* -p z2.* -N ' +stderr 'compile.* -p z2.* -e ' stderr 'compile.* -p y' -! stderr 'compile.* -e.* -p [^z]' -! stderr 'compile.* -N.* -p [^z]' +! stderr 'compile.* -p [^z].* -e ' +! stderr 'compile.* -p [^z].* -N ' # -gcflags can have arbitrary spaces around the flags go build -n -v -gcflags=' z1 = -e ' z1 -stderr 'compile.* -e.* -p z1' +stderr 'compile.* -p z1.* -e ' # -gcflags='all=-e' should apply to all packages, even with go test go test -c -n -gcflags='all=-e' z1 -stderr 'compile.* -e.* -p z3 ' +stderr 'compile.* -p z3.* -e ' # this particular -gcflags argument made the compiler crash ! go build -gcflags=-d=ssa/ z1 @@ -58,8 +58,7 @@ go build -n -ldflags=-X=math.pi=3 stderr 'link.* -X=math.pi=3' # -ldflags applies to current directory even if GOPATH is funny -[windows] cd $WORK/GoPath/src/my/cmd/prog -[darwin] cd $WORK/GoPath/src/my/cmd/prog +[!case-sensitive] cd $WORK/GoPath/src/my/cmd/prog go build -n -ldflags=-X=math.pi=3 stderr 'link.* -X=math.pi=3' diff --git a/libgo/go/cmd/go/testdata/script/get_404_meta.txt b/libgo/go/cmd/go/testdata/script/get_404_meta.txt index ec4f8d32432..29fc5421e12 100644 --- a/libgo/go/cmd/go/testdata/script/get_404_meta.txt +++ b/libgo/go/cmd/go/testdata/script/get_404_meta.txt @@ -9,4 +9,10 @@ go get -d bazil.org/fuse/fs/fstestutil env GO111MODULE=on env GOPROXY=direct -go get -d bazil.org/fuse/fs/fstestutil +go get bazil.org/fuse/fs/fstestutil + + +-- go.mod -- +module m + +go 1.18 diff --git a/libgo/go/cmd/go/testdata/script/get_go_file.txt b/libgo/go/cmd/go/testdata/script/get_go_file.txt index bed87209876..f6d0de4d06f 100644 --- a/libgo/go/cmd/go/testdata/script/get_go_file.txt +++ b/libgo/go/cmd/go/testdata/script/get_go_file.txt @@ -9,15 +9,15 @@ go get -d test # argument has .go suffix, is a file and exists ! go get -d test.go -stderr 'go get test.go: arguments must be package or module paths' +stderr 'go: test.go: arguments must be package or module paths' # argument has .go suffix, doesn't exist and has no slashes ! go get -d test_missing.go -stderr 'go get test_missing.go: arguments must be package or module paths' +stderr 'go: test_missing.go: arguments must be package or module paths' # argument has .go suffix, is a file and exists in sub-directory ! go get -d test/test.go -stderr 'go get: test/test.go exists as a file, but ''go get'' requires package arguments' +stderr 'go: test/test.go exists as a file, but ''go get'' requires package arguments' # argument has .go suffix, doesn't exist and has slashes ! go get -d test/test_missing.go @@ -27,19 +27,19 @@ stderr 'go get: test/test.go exists as a file, but ''go get'' requires package a # argument has .go suffix, is a symlink and exists [symlink] symlink test_sym.go -> test.go [symlink] ! go get -d test_sym.go -[symlink] stderr 'go get test_sym.go: arguments must be package or module paths' +[symlink] stderr 'go: test_sym.go: arguments must be package or module paths' [symlink] rm test_sym.go # argument has .go suffix, is a symlink and exists in sub-directory [symlink] symlink test/test_sym.go -> test.go [symlink] ! go get -d test/test_sym.go -[symlink] stderr 'go get: test/test_sym.go exists as a file, but ''go get'' requires package arguments' +[symlink] stderr 'go: test/test_sym.go exists as a file, but ''go get'' requires package arguments' [symlink] rm test_sym.go # argument has .go suffix, is a directory and exists mkdir test_dir.go ! go get -d test_dir.go -stderr 'go get test_dir.go: arguments must be package or module paths' +stderr 'go: test_dir.go: arguments must be package or module paths' rm test_dir.go # argument has .go suffix, is a directory and exists in sub-directory diff --git a/libgo/go/cmd/go/testdata/script/get_insecure_no_longer_supported.txt b/libgo/go/cmd/go/testdata/script/get_insecure_no_longer_supported.txt index 2517664dd02..00bf32fc78e 100644 --- a/libgo/go/cmd/go/testdata/script/get_insecure_no_longer_supported.txt +++ b/libgo/go/cmd/go/testdata/script/get_insecure_no_longer_supported.txt @@ -3,11 +3,11 @@ env GO111MODULE=off # GOPATH: Fetch with insecure, should error ! go get -insecure test -stderr 'go get: -insecure flag is no longer supported; use GOINSECURE instead' +stderr 'go: -insecure flag is no longer supported; use GOINSECURE instead' # Modules: Set up env GO111MODULE=on # Modules: Fetch with insecure, should error ! go get -insecure test -stderr 'go get: -insecure flag is no longer supported; use GOINSECURE instead' +stderr 'go: -insecure flag is no longer supported; use GOINSECURE instead' diff --git a/libgo/go/cmd/go/testdata/script/go_version.txt b/libgo/go/cmd/go/testdata/script/go_version.txt new file mode 100644 index 00000000000..1a787e1b18c --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/go_version.txt @@ -0,0 +1,9 @@ +# test that go version doesn't panic on non-go binaries
+# See Issue #49181
+
+[exec:/bin/true] cp /bin/true true
+[exec:C:\windows\system32\help.exe] cp C:\windows\system32\help.exe help.exe
+
+go version -m .
+! stdout .
+! stderr .
diff --git a/libgo/go/cmd/go/testdata/script/goflags.txt b/libgo/go/cmd/go/testdata/script/goflags.txt index 686d1138b83..f4872ffd356 100644 --- a/libgo/go/cmd/go/testdata/script/goflags.txt +++ b/libgo/go/cmd/go/testdata/script/goflags.txt @@ -9,7 +9,7 @@ stdout '[\\/]runtime$' env GOFLAGS=-race OLDGOARCH=$GOARCH OLDGOOS=$GOOS GOARCH=386 GOOS=linux ! go list runtime -stderr 'race is only supported on' +stderr 'race is not supported on linux/386' env GOARCH=$OLDGOARCH GOOS=$OLDGOOS diff --git a/libgo/go/cmd/go/testdata/script/gopath_install.txt b/libgo/go/cmd/go/testdata/script/gopath_install.txt index 4b42fc593f9..6c572eae619 100644 --- a/libgo/go/cmd/go/testdata/script/gopath_install.txt +++ b/libgo/go/cmd/go/testdata/script/gopath_install.txt @@ -26,7 +26,7 @@ cd .. env GOPATH= # reset to default ($HOME/go, which does not exist) env GOBIN= ! go install go-cmd-test/helloworld.go -stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$' +stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$' # With $GOBIN set, should install there. env GOBIN=$WORK/bin1 diff --git a/libgo/go/cmd/go/testdata/script/gopath_local.txt b/libgo/go/cmd/go/testdata/script/gopath_local.txt index 7ee1f83471c..54beaca5e8a 100644 --- a/libgo/go/cmd/go/testdata/script/gopath_local.txt +++ b/libgo/go/cmd/go/testdata/script/gopath_local.txt @@ -22,7 +22,7 @@ stdout '^sub\.Hello' # Explicit source files listed on the command line should not install without # GOBIN set, since individual source files aren't part of the containing GOPATH. ! go install testdata/local/easy.go -stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$' +stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$' [windows] stop # Windows does not allow the ridiculous directory name we're about to use. @@ -58,7 +58,7 @@ stdout '^sub\.Hello' # Explicit source files listed on the command line should not install without # GOBIN set, since individual source files aren't part of the containing GOPATH. ! go install testdata/$BAD_DIR_NAME/easy.go -stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$' +stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$' -- testdata/local/easy.go -- package main diff --git a/libgo/go/cmd/go/testdata/script/govcs.txt b/libgo/go/cmd/go/testdata/script/govcs.txt index 4180d7da6ab..46f1bd0da2a 100644 --- a/libgo/go/cmd/go/testdata/script/govcs.txt +++ b/libgo/go/cmd/go/testdata/script/govcs.txt @@ -5,64 +5,64 @@ env GOPROXY=direct # GOVCS stops go get env GOVCS='*:none' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' env GOPRIVATE='github.com/google' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$' # public pattern works env GOPRIVATE='github.com/google' env GOVCS='public:all,private:none' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$' # private pattern works env GOPRIVATE='hubgit.com/google' env GOVCS='private:all,public:none' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' # other patterns work (for more patterns, see TestGOVCS) env GOPRIVATE= env GOVCS='github.com:svn|hg' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' env GOVCS='github.com/google/go-cmp/inner:git,github.com:svn|hg' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' # bad patterns are reported (for more bad patterns, see TestGOVCSErrors) env GOVCS='git' ! go get github.com/google/go-cmp -stderr '^go get github.com/google/go-cmp: malformed entry in GOVCS \(missing colon\): "git"$' +stderr '^go: github.com/google/go-cmp: malformed entry in GOVCS \(missing colon\): "git"$' env GOVCS=github.com:hg,github.com:git ! go get github.com/google/go-cmp -stderr '^go get github.com/google/go-cmp: unreachable pattern in GOVCS: "github.com:git" after "github.com:hg"$' +stderr '^go: github.com/google/go-cmp: unreachable pattern in GOVCS: "github.com:git" after "github.com:hg"$' # bad GOVCS patterns do not stop commands that do not need to check VCS go list env GOPROXY=$proxy -go get -d rsc.io/quote # ok because used proxy +go get rsc.io/quote # ok because used proxy env GOPROXY=direct # svn is disallowed by default env GOPRIVATE= env GOVCS= ! go get rsc.io/nonexist.svn/hello -stderr '^go get rsc.io/nonexist.svn/hello: GOVCS disallows using svn for public rsc.io/nonexist.svn; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.svn/hello: GOVCS disallows using svn for public rsc.io/nonexist.svn; see ''go help vcs''$' # fossil is disallowed by default env GOPRIVATE= env GOVCS= ! go get rsc.io/nonexist.fossil/hello -stderr '^go get rsc.io/nonexist.fossil/hello: GOVCS disallows using fossil for public rsc.io/nonexist.fossil; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.fossil/hello: GOVCS disallows using fossil for public rsc.io/nonexist.fossil; see ''go help vcs''$' # bzr is disallowed by default env GOPRIVATE= env GOVCS= ! go get rsc.io/nonexist.bzr/hello -stderr '^go get rsc.io/nonexist.bzr/hello: GOVCS disallows using bzr for public rsc.io/nonexist.bzr; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.bzr/hello: GOVCS disallows using bzr for public rsc.io/nonexist.bzr; see ''go help vcs''$' # git is OK by default env GOVCS= @@ -77,12 +77,12 @@ env GONOSUMDB='*' # git can be disallowed env GOVCS=public:hg ! go get rsc.io/nonexist.git/hello -stderr '^go get rsc.io/nonexist.git/hello: GOVCS disallows using git for public rsc.io/nonexist.git; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.git/hello: GOVCS disallows using git for public rsc.io/nonexist.git; see ''go help vcs''$' # hg can be disallowed env GOVCS=public:git ! go get rsc.io/nonexist.hg/hello -stderr '^go get rsc.io/nonexist.hg/hello: GOVCS disallows using hg for public rsc.io/nonexist.hg; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.hg/hello: GOVCS disallows using hg for public rsc.io/nonexist.hg; see ''go help vcs''$' # Repeat in GOPATH mode. Error texts slightly different. diff --git a/libgo/go/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt b/libgo/go/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt index 7985cd2ab27..5e88f7b8dbd 100644 --- a/libgo/go/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt +++ b/libgo/go/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt @@ -1,7 +1,5 @@ # Tests Issue #21895 -[!msan] [!race] skip 'skipping because both msan and the race detector are not supported' - env CGO_ENABLED=0 [race] ! go install -race triv.go @@ -12,6 +10,10 @@ env CGO_ENABLED=0 [msan] stderr '-msan requires cgo' [msan] ! stderr '-race' +[asan] ! go install -asan triv.go +[asan] stderr '-asan requires cgo' +[asan] ! stderr '-msan' + -- triv.go -- package main diff --git a/libgo/go/cmd/go/testdata/script/link_external_undef.txt b/libgo/go/cmd/go/testdata/script/link_external_undef.txt new file mode 100644 index 00000000000..5d2d87644d9 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/link_external_undef.txt @@ -0,0 +1,49 @@ + +# Test case for issue 47993, in which the linker crashes +# on a bad input instead of issuing an error and exiting. + +# This test requires external linking, so use cgo as a proxy +[!cgo] skip + +! go build -ldflags='-linkmode=external' . +! stderr 'panic' +[!gccgo] stderr '^.*unreachable sym in relocation.*' +[gccgo] stderr '^.*undefined reference to.*' + +-- go.mod -- + +module issue47993 + +go 1.16 + +-- main.go -- + +package main + +type M struct { + b bool +} + +// Note the body-less func def here. This is what causes the problems. +func (m *M) run(fp func()) + +func doit(m *M) { + InAsm() + m.run(func() { + }) +} + +func main() { + m := &M{true} + doit(m) +} + +func InAsm() + +-- main.s -- + +// Add an assembly function so as to leave open the possibility +// that body-less functions in Go might be defined in assembly. + +// Currently we just need an empty file here. + diff --git a/libgo/go/cmd/go/testdata/script/list_all_gobuild.txt b/libgo/go/cmd/go/testdata/script/list_all_gobuild.txt new file mode 100644 index 00000000000..e0a47398bbd --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/list_all_gobuild.txt @@ -0,0 +1,41 @@ +# go list all should work with GOOS=linux because all packages build on Linux +env GOOS=linux +go list all + +# go list all should work with GOOS=darwin, but it used to fail because +# in the absence of //go:build support, p looked like it needed q +# (p_test.go was not properly excluded), and q was Linux-only. +# +# Also testing with r and s that +build lines keep working. +env GOOS=darwin +go list all + +-- go.mod -- +go 1.17 +module m + +-- p/p.go -- +package p + +-- p/p_test.go -- +//go:build linux + +package p + +import "m/q" + +-- q/q_linux.go -- +package q + +-- r/r.go -- +package r + +-- r/r_test.go -- +// +build linux + +package r + +import "m/s" + +-- s/s_linux.go -- +package s diff --git a/libgo/go/cmd/go/testdata/script/list_cgo_compiled_importmap.txt b/libgo/go/cmd/go/testdata/script/list_cgo_compiled_importmap.txt index 3d68ef30559..30effb104b4 100644 --- a/libgo/go/cmd/go/testdata/script/list_cgo_compiled_importmap.txt +++ b/libgo/go/cmd/go/testdata/script/list_cgo_compiled_importmap.txt @@ -12,7 +12,7 @@ env CGO_ENABLED=1 env GOFLAGS=-tags=netcgo # Force net to use cgo even on Windows. -# "runtime/cgo [runtime.test]" appears in the the test dependencies of "runtime", +# "runtime/cgo [runtime.test]" appears in the test dependencies of "runtime", # because "runtime/cgo" itself depends on "runtime" go list -deps -test -compiled -f '{{if eq .ImportPath "net [runtime.test]"}}{{printf "%q" .Imports}}{{end}}' runtime diff --git a/libgo/go/cmd/go/testdata/script/list_reserved.txt b/libgo/go/cmd/go/testdata/script/list_reserved.txt new file mode 100644 index 00000000000..b9c5361492b --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/list_reserved.txt @@ -0,0 +1,7 @@ +# https://golang.org/issue/37641: the paths "example" and "test" are reserved +# for end users, and must never exist in the standard library. + +go list example/... test/... +stderr 'go: warning: "example/..." matched no packages$' +stderr 'go: warning: "test/..." matched no packages$' +! stdout . diff --git a/libgo/go/cmd/go/testdata/script/list_shadow.txt b/libgo/go/cmd/go/testdata/script/list_shadow.txt index 00e3f6f99a2..c66dae8b8f6 100644 --- a/libgo/go/cmd/go/testdata/script/list_shadow.txt +++ b/libgo/go/cmd/go/testdata/script/list_shadow.txt @@ -17,7 +17,7 @@ stdout '^\(.*gopath(\\|/)src(\\|/)shadow(\\|/)root2(\\|/)src(\\|/)foo\) \('$WORK # The error for go install should mention the conflicting directory. ! go install -n ./shadow/root2/src/foo -stderr 'go install: no install location for '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root2(\\|/)src(\\|/)foo: hidden by '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root1(\\|/)src(\\|/)foo' +stderr 'go: no install location for '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root2(\\|/)src(\\|/)foo: hidden by '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root1(\\|/)src(\\|/)foo' -- shadow/root1/src/foo/foo.go -- package foo diff --git a/libgo/go/cmd/go/testdata/script/list_swigcxx.txt b/libgo/go/cmd/go/testdata/script/list_swigcxx.txt index c6acd9ecdba..d4227a80e8e 100644 --- a/libgo/go/cmd/go/testdata/script/list_swigcxx.txt +++ b/libgo/go/cmd/go/testdata/script/list_swigcxx.txt @@ -2,17 +2,19 @@ [!exec:swig] skip [!exec:g++] skip +[!cgo] skip # CompiledGoFiles should contain 4 files: # a.go # a.swigcxx.go # _cgo_gotypes.go # a.cgo1.go +# +# These names we see here, other than a.go, will be from the build cache, +# so we just count them. go list -f '{{.CompiledGoFiles}}' -compiled=true example/swig -# These names we see here, other than a.go, will be from the build cache, -# so we just count them. stdout a\.go stdout -count=3 $GOCACHE diff --git a/libgo/go/cmd/go/testdata/script/list_test_err.txt b/libgo/go/cmd/go/testdata/script/list_test_err.txt index c6f1ecf4003..25dbb969b01 100644 --- a/libgo/go/cmd/go/testdata/script/list_test_err.txt +++ b/libgo/go/cmd/go/testdata/script/list_test_err.txt @@ -44,6 +44,10 @@ stdout 'testdep_b <nil>' stdout 'nameerr\.test "[^"]*wrong signature for TestBad' ! stderr 'wrong signature for TestBad' +# go list prints a useful error for generic test functions +! go list -test -deps genericerr +stderr 'wrong signature for TestGeneric, test functions cannot have type parameters' + # go list prints partial information with error if test has cyclic import ! go list -test -deps cycleerr stdout cycleerr @@ -106,6 +110,16 @@ import ( func TestBad(t *testing.B) {} +-- genericerr/genericerr.go -- +package genericerr + +-- genericerr/genericerr_test.go -- +package genericerr + +import "testing" + +func TestGeneric[T any](t *testing.T) {} + -- cycleerr/cycleerr_test.go -- package cycleerr diff --git a/libgo/go/cmd/go/testdata/script/mod_all.txt b/libgo/go/cmd/go/testdata/script/mod_all.txt index 090eeee22df..b71a920870a 100644 --- a/libgo/go/cmd/go/testdata/script/mod_all.txt +++ b/libgo/go/cmd/go/testdata/script/mod_all.txt @@ -202,9 +202,9 @@ go mod edit -go=1.17 u/go.mod go mod edit -go=1.17 w/go.mod go mod edit -go=1.17 x/go.mod go mod edit -go=1.17 -cp go.mod go.mod.orig +cmp go.mod go.mod.beforetidy go mod tidy -cmp go.mod go.mod.orig +cmp go.mod go.mod.aftertidy # With lazy loading, 'go list all' with neither -mod=vendor nor -test should # match -mod=vendor without -test in 1.15. @@ -315,7 +315,7 @@ go 1.15 require ( example.com/a v0.1.0 - example.com/b v0.1.0 + example.com/b v0.1.0 // indirect example.com/q v0.1.0 example.com/r v0.1.0 // indirect example.com/t v0.1.0 @@ -466,3 +466,66 @@ module example.com/x go 1.15 -- x/x.go -- package x +-- go.mod.beforetidy -- +module example.com/main + +// Note: this go.mod file initially specifies go 1.15, +// but includes some redundant roots so that it +// also already obeys the 1.17 lazy loading invariants. +go 1.17 + +require ( + example.com/a v0.1.0 + example.com/b v0.1.0 // indirect + example.com/q v0.1.0 + example.com/r v0.1.0 // indirect + example.com/t v0.1.0 + example.com/u v0.1.0 // indirect +) + +replace ( + example.com/a v0.1.0 => ./a + example.com/b v0.1.0 => ./b + example.com/c v0.1.0 => ./c + example.com/d v0.1.0 => ./d + example.com/q v0.1.0 => ./q + example.com/r v0.1.0 => ./r + example.com/s v0.1.0 => ./s + example.com/t v0.1.0 => ./t + example.com/u v0.1.0 => ./u + example.com/w v0.1.0 => ./w + example.com/x v0.1.0 => ./x +) +-- go.mod.aftertidy -- +module example.com/main + +// Note: this go.mod file initially specifies go 1.15, +// but includes some redundant roots so that it +// also already obeys the 1.17 lazy loading invariants. +go 1.17 + +require ( + example.com/a v0.1.0 + example.com/q v0.1.0 + example.com/t v0.1.0 +) + +require ( + example.com/b v0.1.0 // indirect + example.com/r v0.1.0 // indirect + example.com/u v0.1.0 // indirect +) + +replace ( + example.com/a v0.1.0 => ./a + example.com/b v0.1.0 => ./b + example.com/c v0.1.0 => ./c + example.com/d v0.1.0 => ./d + example.com/q v0.1.0 => ./q + example.com/r v0.1.0 => ./r + example.com/s v0.1.0 => ./s + example.com/t v0.1.0 => ./t + example.com/u v0.1.0 => ./u + example.com/w v0.1.0 => ./w + example.com/x v0.1.0 => ./x +) diff --git a/libgo/go/cmd/go/testdata/script/mod_bad_domain.txt b/libgo/go/cmd/go/testdata/script/mod_bad_domain.txt index 7a270d0f07c..afd6e5186c1 100644 --- a/libgo/go/cmd/go/testdata/script/mod_bad_domain.txt +++ b/libgo/go/cmd/go/testdata/script/mod_bad_domain.txt @@ -2,7 +2,7 @@ env GO111MODULE=on # explicit get should report errors about bad names ! go get appengine -stderr '^go get: malformed module path "appengine": missing dot in first path element$' +stderr '^go: malformed module path "appengine": missing dot in first path element$' ! go get x/y.z stderr 'malformed module path "x/y.z": missing dot in first path element' @@ -24,10 +24,10 @@ stderr '^usenonexistent[/\\]x.go:2:8: no required module provides package nonexi # 'get -d' should be similarly definitive -go get -d ./useappengine # TODO(#41315): This should fail. +go get ./useappengine # TODO(#41315): This should fail. # stderr '^useappengine[/\\]x.go:2:8: cannot find package$' -! go get -d ./usenonexistent +! go get ./usenonexistent stderr '^x/usenonexistent imports\n\tnonexistent.rsc.io: cannot find module providing package nonexistent.rsc.io$' diff --git a/libgo/go/cmd/go/testdata/script/mod_build_info_err.txt b/libgo/go/cmd/go/testdata/script/mod_build_info_err.txt index cee055eabe9..5c3c309b0dc 100644 --- a/libgo/go/cmd/go/testdata/script/mod_build_info_err.txt +++ b/libgo/go/cmd/go/testdata/script/mod_build_info_err.txt @@ -11,7 +11,7 @@ stderr '^bad[/\\]bad.go:3:8: malformed import path "🐧.example.com/string": in # TODO(#41688): This should include a file and line, and report the reason for the error.. # (Today it includes only an import stack.) -! go get -d ./main +! go get ./main stderr '^m/main imports\n\tm/bad imports\n\t🐧.example.com/string: malformed import path "🐧.example.com/string": invalid char ''🐧''$' diff --git a/libgo/go/cmd/go/testdata/script/mod_build_trimpath_issue48557.txt b/libgo/go/cmd/go/testdata/script/mod_build_trimpath_issue48557.txt new file mode 100644 index 00000000000..859eafcf84d --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_build_trimpath_issue48557.txt @@ -0,0 +1,52 @@ +# Regression test for issue #48557. +# Since builds in module mode do not support relative imports at all, the build +# ID for (and other contents of) a binary built with -trimpath in module mode +# should not depend on its working directory, even if the binary is specified as +# a list of relative source files. + +[short] skip # links and runs binaries + +env GOFLAGS=-trimpath +env GOCACHE=$WORK/gocache + + +# When we build a binary in module mode with -trimpath, the -D flag (for the +# "local import prefix") should not be passed to it. + +cd $WORK/tmp/foo +go build -x -o a.exe main.go +stderr ${/}compile$GOEXE.*' -nolocalimports' +! stderr ${/}compile$GOEXE.*' -D[ =]' + +go tool buildid a.exe +cp stdout ../foo-buildid.txt +go version a.exe +cp stdout ../foo-version.txt +cd .. + + +# On the second build — in a different directory but with -trimpath — the +# compiler should not be invoked, since the cache key should be identical. +# Only the linker and buildid tool should be needed. + +mkdir bar +cp foo/main.go bar/main.go +cd bar +go build -x -o a.exe main.go +! stderr ${/}compile$GOEXE + +go tool buildid a.exe +cp stdout ../bar-buildid.txt +go version a.exe +cp stdout ../bar-version.txt +cd .. + +cmp bar-buildid.txt foo-buildid.txt +cmp bar-version.txt foo-version.txt +cmp bar/a.exe foo/a.exe + + +-- $WORK/tmp/foo/main.go -- +package main + +func main() {} diff --git a/libgo/go/cmd/go/testdata/script/mod_build_versioned.txt b/libgo/go/cmd/go/testdata/script/mod_build_versioned.txt index d1d74de10c2..f55041834ae 100644 --- a/libgo/go/cmd/go/testdata/script/mod_build_versioned.txt +++ b/libgo/go/cmd/go/testdata/script/mod_build_versioned.txt @@ -1,7 +1,7 @@ env GO111MODULE=on [short] skip -go get -d rsc.io/fortune/v2 +go get rsc.io/fortune/v2 # The default executable name shouldn't be v2$GOEXE go build rsc.io/fortune/v2 diff --git a/libgo/go/cmd/go/testdata/script/mod_cache_dir.txt b/libgo/go/cmd/go/testdata/script/mod_cache_dir.txt index 7284ccf8bab..4045928a978 100644 --- a/libgo/go/cmd/go/testdata/script/mod_cache_dir.txt +++ b/libgo/go/cmd/go/testdata/script/mod_cache_dir.txt @@ -3,9 +3,9 @@ env GO111MODULE=on # Go should reject relative paths in GOMODCACHE environment. env GOMODCACHE="~/test" -! go get example.com/tools/cmd/hello +! go install example.com/tools/cmd/hello@latest stderr 'must be absolute path' env GOMODCACHE="./test" -! go get example.com/tools/cmd/hello +! go install example.com/tools/cmd/hello@latest stderr 'must be absolute path' diff --git a/libgo/go/cmd/go/testdata/script/mod_cache_rw.txt b/libgo/go/cmd/go/testdata/script/mod_cache_rw.txt index a5410764bc0..07755415d60 100644 --- a/libgo/go/cmd/go/testdata/script/mod_cache_rw.txt +++ b/libgo/go/cmd/go/testdata/script/mod_cache_rw.txt @@ -5,7 +5,7 @@ env GO111MODULE=on # golang.org/issue/31481: an explicit flag should make directories in the module # cache writable in order to work around the historical inability of 'rm -rf' to # forcibly remove files in unwritable directories. -go get -modcacherw -d rsc.io/quote@v1.5.2 +go get -modcacherw rsc.io/quote@v1.5.2 cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go # After adding an extraneous file, 'go mod verify' should fail. @@ -28,7 +28,7 @@ cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go # Windows does not respect FILE_ATTRIBUTE_READONLY on directories, according # to MSDN, so there we disable testing whether the directory itself is # unwritable. -go get -d rsc.io/quote@latest +go get rsc.io/quote@latest [!root] ! cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod [!windows] [!root] ! cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go ! exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go diff --git a/libgo/go/cmd/go/testdata/script/mod_case.txt b/libgo/go/cmd/go/testdata/script/mod_case.txt index 4a4698600f7..d3fb11dfa33 100644 --- a/libgo/go/cmd/go/testdata/script/mod_case.txt +++ b/libgo/go/cmd/go/testdata/script/mod_case.txt @@ -1,6 +1,6 @@ env GO111MODULE=on -go get -d +go get go list -m all stdout '^rsc.io/quote v1.5.2' stdout '^rsc.io/QUOTE v1.5.2' @@ -9,7 +9,7 @@ go list -f 'DIR {{.Dir}} DEPS {{.Deps}}' rsc.io/QUOTE/QUOTE stdout 'DEPS.*rsc.io/quote' stdout 'DIR.*!q!u!o!t!e' -go get -d rsc.io/QUOTE@v1.5.3-PRE +go get rsc.io/QUOTE@v1.5.3-PRE go list -m all stdout '^rsc.io/QUOTE v1.5.3-PRE' diff --git a/libgo/go/cmd/go/testdata/script/mod_case_cgo.txt b/libgo/go/cmd/go/testdata/script/mod_case_cgo.txt index f3d6aaa5abc..7c768a09639 100644 --- a/libgo/go/cmd/go/testdata/script/mod_case_cgo.txt +++ b/libgo/go/cmd/go/testdata/script/mod_case_cgo.txt @@ -2,7 +2,7 @@ env GO111MODULE=on -go get -d rsc.io/CGO +go get rsc.io/CGO [short] stop go build rsc.io/CGO diff --git a/libgo/go/cmd/go/testdata/script/mod_concurrent.txt b/libgo/go/cmd/go/testdata/script/mod_concurrent.txt index 8c215251587..e2224d659b2 100644 --- a/libgo/go/cmd/go/testdata/script/mod_concurrent.txt +++ b/libgo/go/cmd/go/testdata/script/mod_concurrent.txt @@ -1,7 +1,7 @@ env GO111MODULE=on # Concurrent builds should succeed, even if they need to download modules. -go get -d ./x ./y +go get ./x ./y go build ./x & go build ./y wait diff --git a/libgo/go/cmd/go/testdata/script/mod_deprecate_message.txt b/libgo/go/cmd/go/testdata/script/mod_deprecate_message.txt index 567027935dc..8e2771dc1f2 100644 --- a/libgo/go/cmd/go/testdata/script/mod_deprecate_message.txt +++ b/libgo/go/cmd/go/testdata/script/mod_deprecate_message.txt @@ -1,25 +1,25 @@ # When there is a short single-line message, 'go get' should print it all. -go get -d short +go get short stderr '^go: module short is deprecated: short$' go list -m -u -f '{{.Deprecated}}' short stdout '^short$' # When there is a multi-line message, 'go get' should print the first line. -go get -d multiline +go get multiline stderr '^go: module multiline is deprecated: first line$' ! stderr 'second line' go list -m -u -f '{{.Deprecated}}' multiline stdout '^first line\nsecond line.$' # When there is a long message, 'go get' should print a placeholder. -go get -d long +go get long stderr '^go: module long is deprecated: \(message omitted: too long\)$' go list -m -u -f '{{.Deprecated}}' long stdout '^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$' # When a message contains unprintable chracters, 'go get' should say that # without printing the message. -go get -d unprintable +go get unprintable stderr '^go: module unprintable is deprecated: \(message omitted: contains non-printable characters\)$' go list -m -u -f '{{.Deprecated}}' unprintable stdout '^message contains ASCII BEL\x07$' diff --git a/libgo/go/cmd/go/testdata/script/mod_domain_root.txt b/libgo/go/cmd/go/testdata/script/mod_domain_root.txt index 14745b5812c..c13029d5341 100644 --- a/libgo/go/cmd/go/testdata/script/mod_domain_root.txt +++ b/libgo/go/cmd/go/testdata/script/mod_domain_root.txt @@ -2,7 +2,7 @@ # (example.com not example.com/something) env GO111MODULE=on -go get -d +go get -- go.mod -- module x diff --git a/libgo/go/cmd/go/testdata/script/mod_dot.txt b/libgo/go/cmd/go/testdata/script/mod_dot.txt index e5813206474..925a724c528 100644 --- a/libgo/go/cmd/go/testdata/script/mod_dot.txt +++ b/libgo/go/cmd/go/testdata/script/mod_dot.txt @@ -5,11 +5,11 @@ env GO111MODULE=on # to resolve an external module. cd dir ! go get -stderr '^go get: no package in current directory$' +stderr '^go: no package to get in current directory$' ! go get . -stderr '^go get \.: no package in current directory$' +stderr '^go: .: no package to get in current directory$' ! go get ./subdir -stderr '^go get: \.[/\\]subdir \('$WORK'[/\\]gopath[/\\]src[/\\]dir[/\\]subdir\) is not a package in module rooted at '$WORK'[/\\]gopath[/\\]src[/\\]dir$' +stderr '^go: \.[/\\]subdir \('$WORK'[/\\]gopath[/\\]src[/\\]dir[/\\]subdir\) is not a package in module rooted at '$WORK'[/\\]gopath[/\\]src[/\\]dir$' ! go list ! stderr 'cannot find module providing package' stderr '^no Go files in '$WORK'[/\\]gopath[/\\]src[/\\]dir$' diff --git a/libgo/go/cmd/go/testdata/script/mod_download.txt b/libgo/go/cmd/go/testdata/script/mod_download.txt index c2b72b2a02c..154e68338b6 100644 --- a/libgo/go/cmd/go/testdata/script/mod_download.txt +++ b/libgo/go/cmd/go/testdata/script/mod_download.txt @@ -93,19 +93,19 @@ exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.1.zip # download reports errors encountered when locating modules ! go mod download bad/path -stderr '^go mod download: module bad/path: not a known dependency$' +stderr '^go: module bad/path: not a known dependency$' ! go mod download bad/path@latest -stderr '^go mod download: bad/path@latest: malformed module path "bad/path": missing dot in first path element$' +stderr '^go: bad/path@latest: malformed module path "bad/path": missing dot in first path element$' ! go mod download rsc.io/quote@v1.999.999 -stderr '^go mod download: rsc.io/quote@v1.999.999: reading .*/v1.999.999.info: 404 Not Found$' +stderr '^go: rsc.io/quote@v1.999.999: reading .*/v1.999.999.info: 404 Not Found$' ! go mod download -json bad/path stdout '^\t"Error": "module bad/path: not a known dependency"' # download main module produces a warning or error go mod download m -stderr '^go mod download: skipping argument m that resolves to the main module\n' +stderr '^go: skipping download of m that resolves to the main module\n' ! go mod download m@latest -stderr '^go mod download: m@latest: malformed module path "m": missing dot in first path element$' +stderr '^go: m@latest: malformed module path "m": missing dot in first path element$' # download without arguments updates go.mod and go.sum after loading the # build list, but does not save sums for downloaded zips. @@ -128,6 +128,50 @@ rm go.sum go mod download all cmp go.mod.update go.mod grep '^rsc.io/sampler v1.3.0 ' go.sum + +# https://golang.org/issue/44435: At go 1.17 or higher, 'go mod download' +# (without arguments) should only download the modules explicitly required in +# the go.mod file, not (presumed-irrelevant) transitive dependencies. +# +# (If the go.mod file is inconsistent, the version downloaded should be the +# selected version from the broader graph, but the go.mod file will also be +# updated to list the correct versions. If at some point we change 'go mod +# download' to stop updating for consistency, then it should fail if the +# requirements are inconsistent.) + +rm go.sum +cp go.mod.orig go.mod +go mod edit -go=1.17 +cp go.mod.update go.mod.go117 +go mod edit -go=1.17 go.mod.go117 + +go clean -modcache +go mod download +cmp go.mod go.mod.go117 + +go list -e -m all +stdout '^rsc.io/quote v1.5.2$' +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip +stdout '^rsc.io/sampler v1.3.0$' +! exists $GOPATH/pkg/mod/cache/download/rsc.io/sampler/@v/v1.2.1.zip +exists $GOPATH/pkg/mod/cache/download/rsc.io/sampler/@v/v1.3.0.zip +stdout '^golang\.org/x/text v0.0.0-20170915032832-14c0d48ead0c$' +! exists $GOPATH/pkg/mod/cache/download/golang.org/x/text/@v/v0.0.0-20170915032832-14c0d48ead0c.zip +cmp go.mod go.mod.go117 + +# However, 'go mod download all' continues to download the selected version +# of every module reported by 'go list -m all'. + +cp go.mod.orig go.mod +go mod edit -go=1.17 +go clean -modcache +go mod download all +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip +! exists $GOPATH/pkg/mod/cache/download/rsc.io/sampler/@v/v1.2.1.zip +exists $GOPATH/pkg/mod/cache/download/rsc.io/sampler/@v/v1.3.0.zip +exists $GOPATH/pkg/mod/cache/download/golang.org/x/text/@v/v0.0.0-20170915032832-14c0d48ead0c.zip +cmp go.mod go.mod.go117 + cd .. # allow go mod download without go.mod diff --git a/libgo/go/cmd/go/testdata/script/mod_get_insecure_redirect.txt b/libgo/go/cmd/go/testdata/script/mod_download_insecure_redirect.txt index 2e128344955..46eb6666865 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_insecure_redirect.txt +++ b/libgo/go/cmd/go/testdata/script/mod_download_insecure_redirect.txt @@ -7,26 +7,26 @@ env GO111MODULE=on env GOPROXY=direct env GOSUMDB=off -! go get -d vcs-test.golang.org/insecure/go/insecure +! go mod download vcs-test.golang.org/insecure/go/insecure@latest stderr 'redirected .* to insecure URL' # insecure host env GOINSECURE=vcs-test.golang.org go clean -modcache -go get -d vcs-test.golang.org/insecure/go/insecure +go mod download vcs-test.golang.org/insecure/go/insecure@latest # insecure glob host env GOINSECURE=*.golang.org go clean -modcache -go get -d vcs-test.golang.org/insecure/go/insecure +go mod download vcs-test.golang.org/insecure/go/insecure@latest # insecure multiple host env GOINSECURE=somewhere-else.com,*.golang.org go clean -modcache -go get -d vcs-test.golang.org/insecure/go/insecure +go mod download vcs-test.golang.org/insecure/go/insecure@latest # different insecure host does not fetch env GOINSECURE=somewhere-else.com go clean -modcache -! go get -d vcs-test.golang.org/insecure/go/insecure +! go mod download vcs-test.golang.org/insecure/go/insecure@latest stderr 'redirected .* to insecure URL' diff --git a/libgo/go/cmd/go/testdata/script/mod_download_partial.txt b/libgo/go/cmd/go/testdata/script/mod_download_partial.txt index 0aab60ddaf0..3a02fcd7474 100644 --- a/libgo/go/cmd/go/testdata/script/mod_download_partial.txt +++ b/libgo/go/cmd/go/testdata/script/mod_download_partial.txt @@ -1,5 +1,5 @@ # Download modules and populate go.sum. -go get -d -modcacherw +go get -modcacherw exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod # 'go mod verify' should fail if we delete a file. diff --git a/libgo/go/cmd/go/testdata/script/mod_get_private_vcs.txt b/libgo/go/cmd/go/testdata/script/mod_download_private_vcs.txt index 75c776a7fa2..e1267939072 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_private_vcs.txt +++ b/libgo/go/cmd/go/testdata/script/mod_download_private_vcs.txt @@ -5,18 +5,18 @@ env GO111MODULE=on [!exec:git] skip env GOPROXY=direct -! go get github.com/golang/nonexist +! go mod download github.com/golang/nonexist@latest stderr 'Confirm the import path was entered correctly.' stderr 'If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.' ! stdout . # Fetching a nonexistent commit should return an "unknown revision" # error message. -! go get github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b -stderr '^go get: github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b: invalid version: unknown revision 86186f3aba07ed0212cfb944f3398997d2d07c6b$' +! go mod download github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b +stderr '^go: github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b: invalid version: unknown revision 86186f3aba07ed0212cfb944f3398997d2d07c6b$' ! stdout . -! go get github.com/golang/nonexist@master +! go mod download github.com/golang/nonexist@master stderr '^Confirm the import path was entered correctly.$' stderr '^If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.$' ! stderr 'unknown revision' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_svn.txt b/libgo/go/cmd/go/testdata/script/mod_download_svn.txt index 3817fce9b61..79e00dc9706 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_svn.txt +++ b/libgo/go/cmd/go/testdata/script/mod_download_svn.txt @@ -1,7 +1,7 @@ [!net] skip [!exec:svn] skip -# 'go get' will fall back to svn+ssh once svn fails over protocols like https. +# 'go mod download' will fall back to svn+ssh once svn fails over protocols like https. # If vcs-test.golang.org isn't in the user's known_hosts file, this will result # in an ssh prompt, which will stop 'go test' entirely # @@ -19,18 +19,11 @@ env GOPROXY=direct env GOSUMDB=off # Attempting to get a module zip using svn should succeed. -go get vcs-test.golang.org/svn/hello.svn@000000000001 +go mod download vcs-test.golang.org/svn/hello.svn@000000000001 exists $GOPATH/pkg/mod/cache/download/vcs-test.golang.org/svn/hello.svn/@v/v0.0.0-20170922011245-000000000001.zip -exists $GOPATH/bin/hello.svn$GOEXE # Attempting to get a nonexistent module using svn should fail with a # reasonable message instead of a panic. -! go get -d vcs-test.golang.org/svn/nonexistent.svn +! go mod download vcs-test.golang.org/svn/nonexistent.svn@latest ! stderr panic -stderr 'go get vcs-test.golang.org/svn/nonexistent.svn: no matching versions for query "upgrade"' - --- go.mod -- -module golang/go/issues/28943/main --- go.sum -- -vcs-test.golang.org/svn/hello.svn v0.0.0-20170922011245-000000000001 h1:rZjvboXMfQICKXdhx/QHqJ2Y/AQsJVrXnwGqwcTxQiw= -vcs-test.golang.org/svn/hello.svn v0.0.0-20170922011245-000000000001/go.mod h1:0memnh/BRLuxiK2zF4rvUgz6ts/fhhB28l3ULFWPusc= +stderr 'go: module vcs-test.golang.org/svn/nonexistent.svn: no matching versions for query "latest"$' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_too_many_redirects.txt b/libgo/go/cmd/go/testdata/script/mod_download_too_many_redirects.txt index 9cbe0d279de..a6b5a590548 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_too_many_redirects.txt +++ b/libgo/go/cmd/go/testdata/script/mod_download_too_many_redirects.txt @@ -3,8 +3,8 @@ env GOPROXYBASE=$GOPROXY env GOPROXY=$GOPROXYBASE/redirect/11 env GOSUMDB=off -! go get -d rsc.io/quote@v1.2.0 +! go mod download rsc.io/quote@v1.2.0 stderr 'stopped after 10 redirects' env GOPROXY=$GOPROXYBASE/redirect/9 -go get -d rsc.io/quote@v1.2.0 +go mod download rsc.io/quote@v1.2.0 diff --git a/libgo/go/cmd/go/testdata/script/mod_e.txt b/libgo/go/cmd/go/testdata/script/mod_e.txt index 3a0d18dabc2..3cffaf6ef1c 100644 --- a/libgo/go/cmd/go/testdata/script/mod_e.txt +++ b/libgo/go/cmd/go/testdata/script/mod_e.txt @@ -24,11 +24,11 @@ cmp go.mod.orig go.mod ! go mod vendor -stderr '^example.com/untidy imports\n\texample.net/directnotfound: cannot find module providing package example.net/directnotfound: module example.net/directnotfound: reading http://.*: 404 Not Found$' +stderr '^example.com/untidy imports\n\texample.net/directnotfound: no required module provides package example.net/directnotfound; to add it:\n\tgo get example.net/directnotfound$' -stderr '^example.com/untidy imports\n\texample.net/m imports\n\texample.net/indirectnotfound: cannot find module providing package example.net/indirectnotfound: module example.net/indirectnotfound: reading http://.*: 404 Not Found$' +stderr '^example.com/untidy imports\n\texample.net/m: module example.net/m provides package example.net/m and is replaced but not required; to add it:\n\tgo get example.net/m@v0.1.0$' -stderr '^example.com/untidy tested by\n\texample.com/untidy.test imports\n\texample.net/directtestnotfound: cannot find module providing package example.net/directtestnotfound: module example.net/directtestnotfound: reading http://.*: 404 Not Found$' +stderr '^example.com/untidy tested by\n\texample.com/untidy.test imports\n\texample.net/directtestnotfound: no required module provides package example.net/directtestnotfound; to add it:\n\tgo get example.net/directtestnotfound$' ! stderr 'indirecttestnotfound' # Vendor prunes test dependencies. @@ -43,15 +43,22 @@ stderr -count=4 'cannot find module providing package' cmp go.mod.final go.mod -# 'go mod vendor -e' still logs the errors, but succeeds and updates go.mod. - +# 'go mod vendor -e' still logs the errors, but creates a vendor directory +# and exits with status 0. +# 'go mod vendor -e' does not update go.mod and will not vendor packages that +# would require changing go.mod, for example, by adding a requirement. cp go.mod.orig go.mod go mod vendor -e -stderr -count=3 'cannot find module providing package' -cmp go.mod.final go.mod +stderr -count=2 'no required module provides package' +stderr '^example.com/untidy imports\n\texample.net/m: module example.net/m provides package example.net/m and is replaced but not required; to add it:\n\tgo get example.net/m@v0.1.0$' exists vendor/modules.txt -exists vendor/example.net/m/m.go +! exists vendor/example.net +go mod edit -require example.net/m@v0.1.0 +go mod vendor -e +stderr -count=3 'no required module provides package' +exists vendor/modules.txt +exists vendor/example.net/m/m.go -- go.mod -- module example.com/untidy diff --git a/libgo/go/cmd/go/testdata/script/mod_edit.txt b/libgo/go/cmd/go/testdata/script/mod_edit.txt index 5aa5ca1ffc0..ebc032a73cf 100644 --- a/libgo/go/cmd/go/testdata/script/mod_edit.txt +++ b/libgo/go/cmd/go/testdata/script/mod_edit.txt @@ -23,18 +23,18 @@ cmpenv go.mod $WORK/go.mod.edit2 # -exclude and -retract reject invalid versions. ! go mod edit -exclude=example.com/m@bad -stderr '^go mod: -exclude=example.com/m@bad: version "bad" invalid: must be of the form v1.2.3$' +stderr '^go: -exclude=example.com/m@bad: version "bad" invalid: must be of the form v1.2.3$' ! go mod edit -retract=bad -stderr '^go mod: -retract=bad: version "bad" invalid: must be of the form v1.2.3$' +stderr '^go: -retract=bad: version "bad" invalid: must be of the form v1.2.3$' ! go mod edit -exclude=example.com/m@v2.0.0 -stderr '^go mod: -exclude=example.com/m@v2\.0\.0: version "v2\.0\.0" invalid: should be v2\.0\.0\+incompatible \(or module example\.com/m/v2\)$' +stderr '^go: -exclude=example.com/m@v2\.0\.0: version "v2\.0\.0" invalid: should be v2\.0\.0\+incompatible \(or module example\.com/m/v2\)$' ! go mod edit -exclude=example.com/m/v2@v1.0.0 -stderr '^go mod: -exclude=example.com/m/v2@v1\.0\.0: version "v1\.0\.0" invalid: should be v2, not v1$' +stderr '^go: -exclude=example.com/m/v2@v1\.0\.0: version "v1\.0\.0" invalid: should be v2, not v1$' ! go mod edit -exclude=gopkg.in/example.v1@v2.0.0 -stderr '^go mod: -exclude=gopkg\.in/example\.v1@v2\.0\.0: version "v2\.0\.0" invalid: should be v1, not v2$' +stderr '^go: -exclude=gopkg\.in/example\.v1@v2\.0\.0: version "v2\.0\.0" invalid: should be v1, not v2$' cmpenv go.mod $WORK/go.mod.edit2 diff --git a/libgo/go/cmd/go/testdata/script/mod_edit_go.txt b/libgo/go/cmd/go/testdata/script/mod_edit_go.txt index 78b48f291c0..58520fab353 100644 --- a/libgo/go/cmd/go/testdata/script/mod_edit_go.txt +++ b/libgo/go/cmd/go/testdata/script/mod_edit_go.txt @@ -4,7 +4,7 @@ env GO111MODULE=on ! go build -stderr 'type aliases only supported as of' +stderr ' type aliases requires' go mod edit -go=1.9 grep 'go 1.9' go.mod go build @@ -13,7 +13,7 @@ go build # the cached 1.9 build. (https://golang.org/issue/37804) go mod edit -go=1.8 ! go build -stderr 'type aliases only supported as of' +stderr 'type aliases requires' -- go.mod -- diff --git a/libgo/go/cmd/go/testdata/script/mod_get_ambiguous_arg.txt b/libgo/go/cmd/go/testdata/script/mod_get_ambiguous_arg.txt index daed03b02c6..92149933686 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_ambiguous_arg.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_ambiguous_arg.txt @@ -4,14 +4,14 @@ cp go.mod go.mod.orig # If there is no sensible *package* meaning for 'm/p', it should refer # to *module* m/p. -go get -d m/p # @latest +go get m/p # @latest go list -m all stdout '^m/p v0.3.0 ' ! stdout '^m ' cp go.mod.orig go.mod -go get -d m/p@v0.1.0 +go get m/p@v0.1.0 go list -m all stdout '^m/p v0.1.0 ' ! stdout '^m ' @@ -22,7 +22,7 @@ stdout '^m/p v0.1.0 ' # (It only refers to *module* m/p if there is no such package at the # requested version.) -go get -d m/p@v0.2.0 +go get m/p@v0.2.0 go list -m all stdout '^m v0.2.0 ' stdout '^m/p v0.1.0 ' # unchanged from the previous case @@ -30,7 +30,7 @@ stdout '^m/p v0.1.0 ' # unchanged from the previous case # Repeating the above with module m/p already in the module graph does not # change its meaning. -go get -d m/p@v0.2.0 +go get m/p@v0.2.0 go list -m all stdout '^m v0.2.0 ' stdout '^m/p v0.1.0 ' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_ambiguous_import.txt b/libgo/go/cmd/go/testdata/script/mod_get_ambiguous_import.txt index 33605f51a5b..0af78bd4f25 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_ambiguous_import.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_ambiguous_import.txt @@ -8,14 +8,14 @@ cp go.mod go.mod.orig # # TODO(#27899): Should we automatically upgrade example.net/m to v0.2.0 # to resolve the conflict? -! go get -d example.net/m/p@v1.0.0 +! go get example.net/m/p@v1.0.0 stderr '^example.net/m/p: ambiguous import: found package example.net/m/p in multiple modules:\n\texample.net/m v0.1.0 \(.*[/\\]m1[/\\]p\)\n\texample.net/m/p v1.0.0 \(.*[/\\]p0\)\n\z' cmp go.mod go.mod.orig # Upgrading both modules simultaneously resolves the ambiguous upgrade. # Note that this command line mixes a module path (example.net/m) # and a package path (example.net/m/p) in the same command. -go get -d example.net/m@v0.2.0 example.net/m/p@v1.0.0 +go get example.net/m@v0.2.0 example.net/m/p@v1.0.0 go list -m all stdout '^example.net/m v0.2.0 ' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_ambiguous_pkg.txt b/libgo/go/cmd/go/testdata/script/mod_get_ambiguous_pkg.txt index 0e7f93bccb5..1641196007b 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_ambiguous_pkg.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_ambiguous_pkg.txt @@ -8,7 +8,7 @@ cp go.mod go.mod.orig # From a clean slate, 'go get' currently does the same thing as 'go mod tidy': # it resolves the package from the module with the longest matching prefix. -go get -d example.net/ambiguous/nested/pkg@v0.1.0 +go get example.net/ambiguous/nested/pkg@v0.1.0 go list -m all stdout '^example.net/ambiguous/nested v0.1.0$' ! stdout '^example.net/ambiguous ' @@ -21,7 +21,7 @@ stdout '^example.net/ambiguous/nested v0.1.0$' cp go.mod.orig go.mod go mod edit -require=example.net/ambiguous@v0.1.0 -go get -d example.net/ambiguous/nested/pkg@v0.1.0 +go get example.net/ambiguous/nested/pkg@v0.1.0 go list -m all stdout '^example.net/ambiguous v0.1.0$' ! stdout '^example.net/ambiguous/nested ' @@ -30,7 +30,7 @@ stdout '^example.net/ambiguous v0.1.0$' # The user should be able to make the command unambiguous by explicitly # upgrading the conflicting module... -go get -d example.net/ambiguous@v0.2.0 example.net/ambiguous/nested/pkg@v0.1.0 +go get example.net/ambiguous@v0.2.0 example.net/ambiguous/nested/pkg@v0.1.0 go list -m all stdout '^example.net/ambiguous/nested v0.1.0$' stdout '^example.net/ambiguous v0.2.0$' @@ -41,7 +41,7 @@ stdout '^example.net/ambiguous v0.2.0$' cp go.mod.orig go.mod go mod edit -require=example.net/ambiguous@v0.1.0 -go get -d example.net/ambiguous/nested/pkg@v0.1.0 example.net/ambiguous/nested@none +go get example.net/ambiguous/nested/pkg@v0.1.0 example.net/ambiguous/nested@none go list -m all ! stdout '^example.net/ambiguous/nested ' stdout '^example.net/ambiguous v0.1.0$' @@ -53,7 +53,7 @@ stdout '^example.net/ambiguous v0.1.0$' cp go.mod.orig go.mod go mod edit -require=example.net/ambiguous@v0.1.0 -go get -d example.net/ambiguous@none example.net/ambiguous/nested/pkg@v0.1.0 +go get example.net/ambiguous@none example.net/ambiguous/nested/pkg@v0.1.0 go list -m all stdout '^example.net/ambiguous/nested v0.1.0$' ! stdout '^example.net/ambiguous ' @@ -66,7 +66,7 @@ stdout '^example.net/ambiguous/nested v0.1.0$' cp go.mod.orig go.mod -go get -d example.net/ambiguous/nested/pkg/...@v0.1.0 +go get example.net/ambiguous/nested/pkg/...@v0.1.0 go list -m all stdout '^example.net/ambiguous/nested v0.1.0$' ! stdout '^example.net/ambiguous ' @@ -75,7 +75,7 @@ stdout '^example.net/ambiguous/nested v0.1.0$' cp go.mod.orig go.mod go mod edit -require=example.net/ambiguous@v0.1.0 -go get -d example.net/ambiguous/nested/pkg/...@v0.1.0 +go get example.net/ambiguous/nested/pkg/...@v0.1.0 go list -m all ! stdout '^example.net/ambiguous/nested ' stdout '^example.net/ambiguous v0.1.0$' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_changes.txt b/libgo/go/cmd/go/testdata/script/mod_get_changes.txt index 3287b2a6095..12a112b4d87 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_changes.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_changes.txt @@ -3,9 +3,9 @@ # for changed indirect dependencies. go list -m all ! stdout golang.org/x/text -go get -d rsc.io/quote@v1.5.2 -stderr '^go get: added rsc.io/quote v1.5.2$' -stderr '^go get: upgraded rsc.io/sampler v1.0.0 => v1.3.0$' +go get rsc.io/quote@v1.5.2 +stderr '^go: added rsc.io/quote v1.5.2$' +stderr '^go: upgraded rsc.io/sampler v1.0.0 => v1.3.0$' ! stderr '^go get.*golang.org/x/text' go list -m all stdout golang.org/x/text @@ -14,18 +14,18 @@ cmp go.mod go.mod.upgrade # When removing a requirement, 'go get' prints a message for the requiremnent # and for changed explicit dependencies. 'go get' does not print messages # for changed indirect dependencies. -go get -d rsc.io/sampler@none -stderr '^go get: downgraded rsc.io/quote v1.5.2 => v1.3.0$' -stderr '^go get: removed rsc.io/sampler v1.3.0$' +go get rsc.io/sampler@none +stderr '^go: downgraded rsc.io/quote v1.5.2 => v1.3.0$' +stderr '^go: removed rsc.io/sampler v1.3.0$' ! stderr '^go get.*golang.org/x/text' cmp go.mod go.mod.downgrade # When removing or downgrading a requirement, 'go get' also prints a message # for explicit dependencies removed as a consequence. cp go.mod.usequote go.mod -go get -d rsc.io/quote@v1.5.1 -stderr '^go get: downgraded rsc.io/quote v1.5.2 => v1.5.1$' -stderr '^go get: removed usequote v0.0.0$' +go get rsc.io/quote@v1.5.1 +stderr '^go: downgraded rsc.io/quote v1.5.2 => v1.5.1$' +stderr '^go: removed usequote v0.0.0$' -- go.mod -- module m diff --git a/libgo/go/cmd/go/testdata/script/mod_get_cmd.txt b/libgo/go/cmd/go/testdata/script/mod_get_cmd.txt deleted file mode 100644 index d31cee1444a..00000000000 --- a/libgo/go/cmd/go/testdata/script/mod_get_cmd.txt +++ /dev/null @@ -1,20 +0,0 @@ -env GO111MODULE=on -[short] skip - -# Test that when 'go get' is run from $GOBIN, it does not delete binaries -# after it installs them. Verifies golang.org/issue/32766. - -go get example.com/tools/cmd/hello - -# 'go get' should not delete the command when run from $GOPATH/bin -cd $GOPATH/bin -exists hello$GOEXE -go get example.com/tools/cmd/hello -exists hello$GOEXE - -# 'go get' should not delete the command when run from a different $GOBIN -mkdir $WORK/bin -cd $WORK/bin -env GOBIN=$WORK/bin -go get example.com/tools/cmd/hello -exists hello$GOEXE diff --git a/libgo/go/cmd/go/testdata/script/mod_get_commit.txt b/libgo/go/cmd/go/testdata/script/mod_get_commit.txt index 4649491a532..f60eaab3a77 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_commit.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_commit.txt @@ -5,18 +5,18 @@ env GO111MODULE=on # golang.org/x/text/language@commit should resolve. # Because of -d, the compiler should not run. -go get -d -x golang.org/x/text/language@14c0d48 +go get -x golang.org/x/text/language@14c0d48 ! stderr 'compile|cp|gccgo .*language\.a$' # go get should skip build with no Go files in root -go get -d golang.org/x/text@14c0d48 +go get golang.org/x/text@14c0d48 # dropping -d, we should see a build. [short] skip env GOCACHE=$WORK/gocache # Looking for compile commands, so need a clean cache. -go get -x golang.org/x/text/language@14c0d48 +go build -x golang.org/x/text/language stderr 'compile|cp|gccgo .*language\.a$' # BUG: after the build, the package should not be stale, as 'go install' would @@ -24,19 +24,20 @@ stderr 'compile|cp|gccgo .*language\.a$' go list -f '{{.Stale}}' golang.org/x/text/language stdout ^true -# install after get should not run the compiler again. +# install after build should not run the compiler again. go install -x golang.org/x/text/language ! stderr 'compile|cp|gccgo .*language\.a$' -# even with -d, we should see an error for unknown packages. -! go get -d -x golang.org/x/text/foo@14c0d48 +# we should see an error for unknown packages. +! go get -x golang.org/x/text/foo@14c0d48 +stderr '^go: module golang.org/x/text@14c0d48 found \(v0.3.0\), but does not contain package golang.org/x/text/foo$' # get pseudo-version should record that version -go get -d rsc.io/quote@v0.0.0-20180214005840-23179ee8a569 +go get rsc.io/quote@v0.0.0-20180214005840-23179ee8a569 grep 'rsc.io/quote v0.0.0-20180214005840-23179ee8a569' go.mod # but as commit should record as v1.5.1 -go get -d rsc.io/quote@23179ee8 +go get rsc.io/quote@23179ee8 grep 'rsc.io/quote v1.5.1' go.mod # go mod edit -require does not interpret commits @@ -44,7 +45,7 @@ go mod edit -require rsc.io/quote@23179ee grep 'rsc.io/quote 23179ee' go.mod # but other commands fix them -go mod graph +go list -m -mod=mod all grep 'rsc.io/quote v1.5.1' go.mod -- go.mod -- diff --git a/libgo/go/cmd/go/testdata/script/mod_get_deprecate_install.txt b/libgo/go/cmd/go/testdata/script/mod_get_deprecate_install.txt index 63cd27a42d2..03258f52966 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_deprecate_install.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_deprecate_install.txt @@ -2,10 +2,10 @@ env GO111MODULE=on -# 'go get' outside a module with an executable prints a deprecation message. -go get example.com/cmd/a -stderr '^go get: installing executables with ''go get'' in module mode is deprecated.$' -stderr 'Use ''go install pkg@version'' instead.' +# 'go get' outside a module prints an error. +! go get example.com/cmd/a +stderr '^go: go.mod file not found in current directory or any parent directory.$' +stderr '^\t''go get'' is no longer supported outside a module.$' cp go.mod.orig go.mod @@ -13,13 +13,16 @@ cp go.mod.orig go.mod # This will stop building in the future, but it's the command we want to use. go get rsc.io/quote ! stderr deprecated +! stderr 'no longer installs' cp go.mod.orig go.mod -# 'go get' inside a module with an executable prints a different -# deprecation message. +# 'go get' inside a module with an executable does not print a message. +# In 1.16 and 1.17, 'go get' did print a message in this case suggesting the +# use of -d. In 1.18, -d is a no-op, and we'd like to begin discouraging +# its use. go get example.com/cmd/a -stderr '^go get: installing executables with ''go get'' in module mode is deprecated.$' -stderr 'To adjust and download dependencies of the current module, use ''go get -d''' +! stderr deprecated +! stderr 'no longer installs' cp go.mod.orig go.mod # 'go get' should not print a warning for a main package inside the main module. diff --git a/libgo/go/cmd/go/testdata/script/mod_get_deprecated.txt b/libgo/go/cmd/go/testdata/script/mod_get_deprecated.txt index 7bdd7a58a89..ec7bcfdb997 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_deprecated.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_deprecated.txt @@ -1,31 +1,31 @@ # 'go get pkg' should not show a deprecation message for an unrelated module. -go get -d ./use/nothing +go get ./use/nothing ! stderr 'module.*is deprecated' # 'go get pkg' should show a deprecation message for the module providing pkg. -go get -d example.com/deprecated/a +go get example.com/deprecated/a stderr '^go: module example.com/deprecated/a is deprecated: in example.com/deprecated/a@v1.9.0$' -go get -d example.com/deprecated/a@v1.0.0 +go get example.com/deprecated/a@v1.0.0 stderr '^go: module example.com/deprecated/a is deprecated: in example.com/deprecated/a@v1.9.0$' # 'go get pkg' should show a deprecation message for a module providing # packages directly imported by pkg. -go get -d ./use/a +go get ./use/a stderr '^go: module example.com/deprecated/a is deprecated: in example.com/deprecated/a@v1.9.0$' # 'go get pkg' may show a deprecation message for an indirectly required module # if it provides a package named on the command line. -go get -d ./use/b +go get ./use/b ! stderr 'module.*is deprecated' -go get -d local/use +go get local/use ! stderr 'module.*is deprecated' -go get -d example.com/deprecated/b +go get example.com/deprecated/b stderr '^go: module example.com/deprecated/b is deprecated: in example.com/deprecated/b@v1.9.0$' # 'go get pkg' does not show a deprecation message for a module providing a # directly imported package if the module is no longer deprecated in its # latest version, even if the module is deprecated in its current version. -go get -d ./use/undeprecated +go get ./use/undeprecated ! stderr 'module.*is deprecated' -- go.mod -- diff --git a/libgo/go/cmd/go/testdata/script/mod_get_direct.txt b/libgo/go/cmd/go/testdata/script/mod_get_direct.txt index 42ccbcd38a1..856e05bc329 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_direct.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_direct.txt @@ -10,7 +10,7 @@ env GO111MODULE=on env GOPROXY=direct env GOSUMDB=off -go list -m cloud.google.com/go@master +go list -m cloud.google.com/go@main ! stdout 'v0.0.0-' -- go.mod -- diff --git a/libgo/go/cmd/go/testdata/script/mod_get_downadd_indirect.txt b/libgo/go/cmd/go/testdata/script/mod_get_downadd_indirect.txt index efc38f77c85..0f5ba992da2 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_downadd_indirect.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_downadd_indirect.txt @@ -18,7 +18,7 @@ cp go.mod go.mod.orig go mod tidy cmp go.mod.orig go.mod -go get -d example.com/d@v0.1.0 +go get example.com/d@v0.1.0 go list -m all stdout '^example.com/b v0.1.0 ' stdout '^example.com/c v0.1.0 ' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_downgrade.txt b/libgo/go/cmd/go/testdata/script/mod_get_downgrade.txt index c26c5e1c210..2eed56d9b71 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_downgrade.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_downgrade.txt @@ -3,25 +3,25 @@ env GO111MODULE=on # downgrade sampler should downgrade quote cp go.mod.orig go.mod -go get -d rsc.io/sampler@v1.0.0 +go get rsc.io/sampler@v1.0.0 go list -m all stdout 'rsc.io/quote v1.4.0' stdout 'rsc.io/sampler v1.0.0' # downgrade sampler away should downgrade quote further -go get -d rsc.io/sampler@none +go get rsc.io/sampler@none go list -m all stdout 'rsc.io/quote v1.3.0' # downgrade should report inconsistencies and not change go.mod -go get -d rsc.io/quote@v1.5.1 +go get rsc.io/quote@v1.5.1 go list -m all stdout 'rsc.io/quote v1.5.1' stdout 'rsc.io/sampler v1.3.0' -! go get -d rsc.io/sampler@v1.0.0 rsc.io/quote@v1.5.2 golang.org/x/text@none -stderr -count=1 '^go get:' -stderr '^go get: rsc.io/quote@v1.5.2 requires rsc.io/sampler@v1.3.0, not rsc.io/sampler@v1.0.0$' +! go get rsc.io/sampler@v1.0.0 rsc.io/quote@v1.5.2 golang.org/x/text@none +! stderr add|remove|upgrad|downgrad +stderr '^go: rsc.io/quote@v1.5.2 requires rsc.io/sampler@v1.3.0, not rsc.io/sampler@v1.0.0$' go list -m all stdout 'rsc.io/quote v1.5.1' @@ -29,7 +29,7 @@ stdout 'rsc.io/sampler v1.3.0' # go get -u args should limit upgrades cp go.mod.empty go.mod -go get -d -u rsc.io/quote@v1.4.0 rsc.io/sampler@v1.0.0 +go get -u rsc.io/quote@v1.4.0 rsc.io/sampler@v1.0.0 go list -m all stdout 'rsc.io/quote v1.4.0' stdout 'rsc.io/sampler v1.0.0' @@ -40,7 +40,7 @@ stdout 'rsc.io/sampler v1.0.0' cp go.mod.orig go.mod go list -m -versions example.com/latemigrate/v2 stdout v2.0.0 # proxy may serve incompatible versions -go get -d rsc.io/quote@none +go get rsc.io/quote@none go list -m all ! stdout 'example.com/latemigrate/v2' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_downgrade_missing.txt b/libgo/go/cmd/go/testdata/script/mod_get_downgrade_missing.txt index 5b768faeb18..582593b96cd 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_downgrade_missing.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_downgrade_missing.txt @@ -4,15 +4,15 @@ cp go.mod go.mod.orig # not yet present in that module should report the version mismatch # rather than a "matched no packages" warning. -! go get -d example.net/pkgadded@v1.1.0 example.net/pkgadded/subpkg/... -stderr '^go get: example.net/pkgadded@v1.1.0 conflicts with example.net/pkgadded/subpkg/...@upgrade \(v1.2.0\)$' +! go get example.net/pkgadded@v1.1.0 example.net/pkgadded/subpkg/... +stderr '^go: example.net/pkgadded@v1.1.0 conflicts with example.net/pkgadded/subpkg/...@upgrade \(v1.2.0\)$' ! stderr 'matched no packages' cmp go.mod.orig go.mod # A wildcard pattern should match the pattern with that path. -go get -d example.net/pkgadded/...@v1.0.0 +go get example.net/pkgadded/...@v1.0.0 go list -m all stdout '^example.net/pkgadded v1.0.0' cp go.mod.orig go.mod @@ -22,12 +22,12 @@ cp go.mod.orig go.mod # and another argument constrains away the version that provides that # package, then 'go get' should fail with a useful error message. -! go get -d example.net/pkgadded@v1.0.0 . +! go get example.net/pkgadded@v1.0.0 . stderr '^example.com/m imports\n\texample.net/pkgadded/subpkg: cannot find module providing package example.net/pkgadded/subpkg$' ! stderr 'example.net/pkgadded v1\.2\.0' cmp go.mod.orig go.mod -go get -d example.net/pkgadded@v1.0.0 +go get example.net/pkgadded@v1.0.0 ! go list -deps -mod=readonly . stderr '^m.go:3:8: cannot find module providing package example\.net/pkgadded/subpkg: ' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_downup_artifact.txt b/libgo/go/cmd/go/testdata/script/mod_get_downup_artifact.txt index c20583b22a1..111a54f8f73 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_downup_artifact.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_downup_artifact.txt @@ -55,7 +55,7 @@ stdout '^example.com/d v0.1.0 ' # upgrades of module d and addition of module e, which are not relevant to # b@v0.1.0 and should not be added to the main module's dependencies. -go get -u -d example.com/a@latest example.com/c@v0.1.0 +go get -u example.com/a@latest example.com/c@v0.1.0 go list -m all stdout '^example.com/a v0.1.0 ' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_downup_indirect.txt b/libgo/go/cmd/go/testdata/script/mod_get_downup_indirect.txt index ced1dcd6b14..3a46a774ce7 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_downup_indirect.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_downup_indirect.txt @@ -25,7 +25,7 @@ cp go.mod go.mod.orig go mod tidy cmp go.mod.orig go.mod -go get -d example.com/d@v0.1.0 +go get example.com/d@v0.1.0 go list -m all ! stdout '^example.com/b ' ! stdout '^example.com/c ' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_downup_pseudo_artifact.txt b/libgo/go/cmd/go/testdata/script/mod_get_downup_pseudo_artifact.txt index c49615cecb3..b678a177b52 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_downup_pseudo_artifact.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_downup_pseudo_artifact.txt @@ -28,7 +28,7 @@ cmp go.mod.orig go.mod # When we downgrade d.2 to d.1, no dependency on e should be added # because nothing else in the module or import graph requires it. -go get -d example.net/d@v0.1.0 +go get example.net/d@v0.1.0 go list -m all stdout '^example.net/b v0.2.1-0.20210219000000-000000000000 ' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_errors.txt b/libgo/go/cmd/go/testdata/script/mod_get_errors.txt index 5c37058d1c1..7cb03ce2f1e 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_errors.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_errors.txt @@ -1,35 +1,23 @@ cp go.mod go.mod.orig -# Both 'go get' and 'go get -d' should fail, without updating go.mod, -# if the transitive dependencies of the requested package (by default, -# the package in the current directory) cannot be resolved. +# 'go get' should fail, without updating go.mod, if the transitive dependencies +# of the requested package (by default, the package in the current directory) +# cannot be resolved. ! go get stderr '^example.com/m imports\n\texample.com/badimport imports\n\texample.net/oops: cannot find module providing package example.net/oops$' cmp go.mod.orig go.mod -! go get -d -stderr '^example.com/m imports\n\texample.com/badimport imports\n\texample.net/oops: cannot find module providing package example.net/oops$' -cmp go.mod.orig go.mod - cd importsyntax -# If 'go get' fails due to a compile error (such as a syntax error), -# it should not update the go.mod file. - -! go get -stderr '^..[/\\]badimport[/\\]syntaxerror[/\\]syntaxerror.go:1:1: expected ''package'', found pack$' # TODO: An import stack would be nice. -cmp ../go.mod.orig ../go.mod - - # A syntax error in a dependency prevents the compiler from needing that -# dependency's imports, so 'go get -d' should not report an error when those +# dependency's imports, so 'go get' should not report an error when those # imports cannot be resolved: it has all of the dependencies that the compiler # needs, and the user did not request to run the compiler. -go get -d +go get cmp ../go.mod.syntax-d ../go.mod diff --git a/libgo/go/cmd/go/testdata/script/mod_get_extra.txt b/libgo/go/cmd/go/testdata/script/mod_get_extra.txt index 7efa24e87b0..083e03678e4 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_extra.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_extra.txt @@ -4,7 +4,7 @@ cp go.mod go.mod.orig # determined by explicit queries to any version other than the explicit one. # Otherwise, 'go get -u' could introduce spurious dependencies. -go get -d -u example.net/a@v0.1.0 example.net/b@v0.1.0 +go get -u example.net/a@v0.1.0 example.net/b@v0.1.0 go list -m all stdout '^example.net/a v0.1.0 ' stdout '^example.net/b v0.1.0 ' @@ -16,7 +16,7 @@ stdout '^example.net/b v0.1.0 ' cp go.mod.orig go.mod -go get -d -u example.net/a@v0.1.0 example.net/b/...@v0.1.0 +go get -u example.net/a@v0.1.0 example.net/b/...@v0.1.0 go list -m all stdout '^example.net/a v0.1.0 ' stdout '^example.net/b v0.1.0 ' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_fallback.txt b/libgo/go/cmd/go/testdata/script/mod_get_fallback.txt index 9733fa366be..35722333d6a 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_fallback.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_fallback.txt @@ -5,6 +5,11 @@ env GO111MODULE=on env GOPROXY=https://proxy.golang.org,direct env GOSUMDB=off -go get -x -v -d golang.org/x/tools/cmd/goimports +go get -x -v golang.org/x/tools/cmd/goimports stderr '# get https://proxy.golang.org/golang.org/x/tools/@v/list' ! stderr '# get https://golang.org' + +-- go.mod -- +module m + +go 1.18 diff --git a/libgo/go/cmd/go/testdata/script/mod_get_fossil.txt b/libgo/go/cmd/go/testdata/script/mod_get_fossil.txt index baad544557a..c2d42f0f596 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_fossil.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_fossil.txt @@ -18,11 +18,10 @@ env GOSUMDB=off env USER=fossiluser env FOSSIL_HOME=$WORK/home -# Attempting to get the latest version of a fossil repo. +# Attempt to get the latest version of a fossil repo. go get vcs-test.golang.org/fossil/hello.fossil ! stderr 'unexpected response from fossil info' grep 'vcs-test.golang.org/fossil/hello.fossil' go.mod -exists $GOPATH/bin/hello.fossil$GOEXE -- go.mod -- module x diff --git a/libgo/go/cmd/go/testdata/script/mod_get_go_file.txt b/libgo/go/cmd/go/testdata/script/mod_get_go_file.txt index 0c7b5dc11c5..c81e491b947 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_go_file.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_go_file.txt @@ -17,7 +17,7 @@ env GO111MODULE=on # argument has .go suffix, is a file and exists ! go get test.go -stderr 'go get test.go: arguments must be package or module paths' +stderr 'go: test.go: arguments must be package or module paths' # argument has .go suffix, doesn't exist and has no slashes ! go get test_missing.go @@ -25,7 +25,7 @@ stderr 'arguments must be package or module paths' # argument has .go suffix, is a file and exists in sub-directory ! go get test/test.go -stderr 'go get: test/test.go exists as a file, but ''go get'' requires package arguments' +stderr 'go: test/test.go exists as a file, but ''go get'' requires package arguments' # argument has .go suffix, doesn't exist and has slashes ! go get test/test_missing.go @@ -35,19 +35,19 @@ stderr 'go get: test/test.go exists as a file, but ''go get'' requires package a # argument has .go suffix, is a symlink and exists [symlink] symlink test_sym.go -> test.go [symlink] ! go get test_sym.go -[symlink] stderr 'go get test_sym.go: arguments must be package or module paths' +[symlink] stderr 'go: test_sym.go: arguments must be package or module paths' [symlink] rm test_sym.go # argument has .go suffix, is a symlink and exists in sub-directory [symlink] symlink test/test_sym.go -> test.go [symlink] ! go get test/test_sym.go -[symlink] stderr 'go get: test/test_sym.go exists as a file, but ''go get'' requires package arguments' +[symlink] stderr 'go: test/test_sym.go exists as a file, but ''go get'' requires package arguments' [symlink] rm test_sym.go # argument has .go suffix, is a directory and exists mkdir test_dir.go ! go get test_dir.go -stderr 'go get test_dir.go: arguments must be package or module paths' +stderr 'go: test_dir.go: arguments must be package or module paths' rm test_dir.go # argument has .go suffix, is a directory and exists in sub-directory @@ -58,6 +58,11 @@ mkdir test/test_dir.go rm test/test_dir.go +-- go.mod -- +module m + +go 1.18 + -- test.go -- package main func main() {println("test")} diff --git a/libgo/go/cmd/go/testdata/script/mod_get_incompatible.txt b/libgo/go/cmd/go/testdata/script/mod_get_incompatible.txt index 8000ee61481..5a7d7063718 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_incompatible.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_incompatible.txt @@ -1,15 +1,15 @@ env GO111MODULE=on -go get -d x +go get x go list -m all stdout 'rsc.io/breaker v2.0.0\+incompatible' cp go.mod2 go.mod -go get -d rsc.io/breaker@7307b30 +go get rsc.io/breaker@7307b30 go list -m all stdout 'rsc.io/breaker v2.0.0\+incompatible' -go get -d rsc.io/breaker@v2.0.0 +go get rsc.io/breaker@v2.0.0 go list -m all stdout 'rsc.io/breaker v2.0.0\+incompatible' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_indirect.txt b/libgo/go/cmd/go/testdata/script/mod_get_indirect.txt index e1cc1ab4115..fa7edf22d7b 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_indirect.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_indirect.txt @@ -27,7 +27,7 @@ grep 'golang.org/x/text v0.3.0 // indirect$' go.mod # indirect tag should be removed upon seeing direct import. cp $WORK/tmp/uselang.go x.go -go get -d +go get grep 'rsc.io/quote v1.5.2$' go.mod grep 'golang.org/x/text [v0-9a-f\.-]+$' go.mod diff --git a/libgo/go/cmd/go/testdata/script/mod_get_issue37438.txt b/libgo/go/cmd/go/testdata/script/mod_get_issue37438.txt index 38b2031d3ae..9392e73a174 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_issue37438.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_issue37438.txt @@ -6,7 +6,7 @@ # 'go get foo@requested' should resolve the requested version, # not error out on the (unrelated) latest one. -go get -d example.net/a/p@v0.2.0 +go get example.net/a/p@v0.2.0 -- go.mod -- module example diff --git a/libgo/go/cmd/go/testdata/script/mod_get_issue47979.txt b/libgo/go/cmd/go/testdata/script/mod_get_issue47979.txt new file mode 100644 index 00000000000..848ee3aa09a --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_get_issue47979.txt @@ -0,0 +1,117 @@ +# Regression test for https://golang.org/issue/47979: +# +# An argument to 'go get' that results in an upgrade to a different existing +# root should be allowed, and should not panic the 'go' command. + +cp go.mod go.mod.orig + + +# Transitive upgrades from upgraded roots should not prevent +# 'go get -u' from performing upgrades. + +cp go.mod.orig go.mod +go get -u . +cmp go.mod go.mod.want + + +# 'go get' of a specific version should allow upgrades of +# every dependency (transitively) required by that version, +# including dependencies that are pulled into the module +# graph by upgrading other root requirements +# (in this case, example.net/indirect). + +cp go.mod.orig go.mod +go get example.net/a@v0.2.0 +cmp go.mod go.mod.want + + +-- go.mod -- +module golang.org/issue47979 + +go 1.17 + +replace ( + example.net/a v0.1.0 => ./a1 + example.net/a v0.2.0 => ./a2 + example.net/indirect v0.1.0 => ./indirect1 + example.net/indirect v0.2.0 => ./indirect2 + example.net/other v0.1.0 => ./other + example.net/other v0.2.0 => ./other +) + +require ( + example.net/a v0.1.0 + example.net/other v0.1.0 +) + +require example.net/indirect v0.1.0 // indirect +-- go.mod.want -- +module golang.org/issue47979 + +go 1.17 + +replace ( + example.net/a v0.1.0 => ./a1 + example.net/a v0.2.0 => ./a2 + example.net/indirect v0.1.0 => ./indirect1 + example.net/indirect v0.2.0 => ./indirect2 + example.net/other v0.1.0 => ./other + example.net/other v0.2.0 => ./other +) + +require ( + example.net/a v0.2.0 + example.net/other v0.2.0 +) + +require example.net/indirect v0.2.0 // indirect +-- issue.go -- +package issue + +import _ "example.net/a" +-- useother/useother.go -- +package useother + +import _ "example.net/other" +-- a1/go.mod -- +module example.net/a + +go 1.17 + +require example.net/indirect v0.1.0 +-- a1/a.go -- +package a +-- a2/go.mod -- +module example.net/a + +go 1.17 + +require example.net/indirect v0.2.0 +-- a2/a.go -- +package a + +import "example.net/indirect" +-- indirect1/go.mod -- +module example.net/indirect + +go 1.17 + +require example.net/other v0.1.0 +-- indirect1/indirect.go -- +package indirect +-- indirect2/go.mod -- +module example.net/indirect + +go 1.17 + +require example.net/other v0.2.0 +-- indirect2/indirect.go -- +package indirect + +import "example.net/other" +-- other/go.mod -- +module example.net/other + +go 1.17 +-- other/other.go -- +package other diff --git a/libgo/go/cmd/go/testdata/script/mod_get_issue48511.txt b/libgo/go/cmd/go/testdata/script/mod_get_issue48511.txt new file mode 100644 index 00000000000..0ba486d35bd --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_get_issue48511.txt @@ -0,0 +1,68 @@ +# Regression test for https://golang.org/issue/48511: +# requirement minimization was accidentally replacing previous +# versions of the main module, causing dependencies to be +# spuriously dropping during requirement minimization and +# leading to an infinite loop. + +cp go.mod go.mod.orig +go mod tidy +cmp go.mod go.mod.orig + +go get -u=patch ./... +cmp go.mod go.mod.want + +-- go.mod -- +module example.net/m + +go 1.16 + +replace ( + example.net/a v0.1.0 => ./a + example.net/b v0.1.0 => ./b + example.net/b v0.1.1 => ./b + example.net/m v0.1.0 => ./m1 +) + +require example.net/a v0.1.0 +-- go.mod.want -- +module example.net/m + +go 1.16 + +replace ( + example.net/a v0.1.0 => ./a + example.net/b v0.1.0 => ./b + example.net/b v0.1.1 => ./b + example.net/m v0.1.0 => ./m1 +) + +require ( + example.net/a v0.1.0 + example.net/b v0.1.1 // indirect +) +-- m.go -- +package m + +import "example.net/a" +-- m1/go.mod -- +module example.net/m + +go 1.16 + +require example.net/b v0.1.0 +-- a/go.mod -- +module example.net/a + +go 1.16 + +require example.net/m v0.1.0 +-- a/a.go -- +package a + +import "example.net/b" +-- b/go.mod -- +module example.net/b + +go 1.16 +-- b/b.go -- +package b diff --git a/libgo/go/cmd/go/testdata/script/mod_get_latest_pseudo.txt b/libgo/go/cmd/go/testdata/script/mod_get_latest_pseudo.txt index 241a0c2f0df..00da0c31646 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_latest_pseudo.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_latest_pseudo.txt @@ -5,6 +5,6 @@ env GO111MODULE=on go mod init m -go get -d example.com/notags +go get example.com/notags go list -m all stdout '^example.com/notags v0.0.0-20190507143103-cc8cbe209b64$' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_lazy_upgrade_lazy.txt b/libgo/go/cmd/go/testdata/script/mod_get_lazy_upgrade_lazy.txt new file mode 100644 index 00000000000..3dae383de1e --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_get_lazy_upgrade_lazy.txt @@ -0,0 +1,68 @@ +# Check that 'go get -u' will upgrade a dependency (direct or indirect) +# when the main module and the dependency are both lazy. +# Verifies #47768. + +# Check that go.mod is tidy, and an upgrade is available. +cp go.mod go.mod.orig +go mod tidy +cmp go.mod go.mod.orig + +go list -m -u example.com/lazyupgrade +stdout '^example.com/lazyupgrade v0.1.0 \[v0.1.1\] => ./lazyupgrade@v0.1.0$' + +# 'go get -u' on a package that directly imports the dependency should upgrade. +go get -u ./usedirect +go list -m example.com/lazyupgrade +stdout '^example.com/lazyupgrade v0.1.1 => ./lazyupgrade@v0.1.1$' +cp go.mod.orig go.mod + +# 'go get -u' on a package that indirectly imports the dependency should upgrade. +go get -u ./useindirect +go list -m example.com/lazyupgrade +stdout '^example.com/lazyupgrade v0.1.1 => ./lazyupgrade@v0.1.1$' + +-- go.mod -- +module use + +go 1.17 + +require ( + direct v0.0.0 + example.com/lazyupgrade v0.1.0 +) + +replace ( + direct => ./direct + example.com/lazyupgrade v0.1.0 => ./lazyupgrade@v0.1.0 + example.com/lazyupgrade v0.1.1 => ./lazyupgrade@v0.1.1 +) +-- usedirect/usedirect.go -- +package use + +import _ "example.com/lazyupgrade" +-- useindirect/useindirect.go -- +package use + +import _ "direct" +-- direct/go.mod -- +module direct + +go 1.17 + +require example.com/lazyupgrade v0.1.0 +-- direct/direct.go -- +package direct + +import _ "example.com/lazyupgrade" +-- lazyupgrade@v0.1.0/go.mod -- +module example.com/lazyupgrade + +go 1.17 +-- lazyupgrade@v0.1.0/lazyupgrade.go -- +package lazyupgrade +-- lazyupgrade@v0.1.1/go.mod -- +module example.com/lazyupgrade + +go 1.17 +-- lazyupgrade@v0.1.1/lazyupgrade.go -- +package lazyupgrade diff --git a/libgo/go/cmd/go/testdata/script/mod_get_local.txt b/libgo/go/cmd/go/testdata/script/mod_get_local.txt index eb09da58b3e..4c81d16a1fe 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_local.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_local.txt @@ -7,7 +7,7 @@ cp go.mod go.mod.orig # 'go get -u' within the main module should work, even if it has a local-only name. cp go.mod.orig go.mod -go get -d -u ./... +go get -u ./... grep 'rsc.io/quote.*v1.5.2' go.mod grep 'golang.org/x/text.*v0.3.0' go.mod cp go.mod go.mod.implicitmod @@ -15,34 +15,34 @@ cp go.mod go.mod.implicitmod # 'go get -u local/...' should be equivalent to 'go get -u ./...' # (assuming no nested modules) cp go.mod.orig go.mod -go get -d -u local/... +go get -u local/... cmp go.mod go.mod.implicitmod # For the main module, @patch should be a no-op. cp go.mod.orig go.mod -go get -d -u local/...@patch +go get -u local/...@patch cmp go.mod go.mod.implicitmod -# 'go get -u -d' in the empty root of the main module should fail. -# 'go get -u -d .' should also fail. +# 'go get -u' in the empty root of the main module should fail. +# 'go get -u .' should also fail. cp go.mod.orig go.mod -! go get -u -d -! go get -u -d . +! go get -u +! go get -u . -# 'go get -u -d .' within a package in the main module updates the dependencies +# 'go get -u .' within a package in the main module updates the dependencies # of that package. cp go.mod.orig go.mod cd uselang -go get -u -d . +go get -u . cd .. grep 'rsc.io/quote.*v1.3.0' go.mod grep 'golang.org/x/text.*v0.3.0' go.mod cp go.mod go.mod.dotpkg -# 'go get -u -d' with an explicit package in the main module updates the +# 'go get -u' with an explicit package in the main module updates the # dependencies of that package. cp go.mod.orig go.mod -go get -u -d local/uselang +go get -u local/uselang cmp go.mod go.mod.dotpkg -- go.mod -- diff --git a/libgo/go/cmd/go/testdata/script/mod_get_main.txt b/libgo/go/cmd/go/testdata/script/mod_get_main.txt index 50b2fee9ae1..cddd5f70826 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_main.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_main.txt @@ -2,44 +2,44 @@ env GO111MODULE=on cp go.mod.orig go.mod # relative and absolute paths must be within the main module. -! go get -d .. -stderr '^go get: \.\. \('$WORK'[/\\]gopath\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' -! go get -d $WORK -stderr '^go get: '$WORK' is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' -! go get -d ../... -stderr '^go get: \.\./\.\.\. \('$WORK'[/\\]gopath([/\\]...)?\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' -! go get -d $WORK/... -stderr '^go get: '$WORK'[/\\]\.\.\. is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' +! go get .. +stderr '^go: \.\. \('$WORK'[/\\]gopath\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' +! go get $WORK +stderr '^go: '$WORK' is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' +! go get ../... +stderr '^go: \.\./\.\.\. \('$WORK'[/\\]gopath([/\\]...)?\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' +! go get $WORK/... +stderr '^go: '$WORK'[/\\]\.\.\. is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' # @patch and @latest within the main module refer to the current version. # The main module won't be upgraded, but missing dependencies will be added. -go get -d rsc.io/x +go get rsc.io/x grep 'rsc.io/quote v1.5.2' go.mod -go get -d rsc.io/x@upgrade +go get rsc.io/x@upgrade grep 'rsc.io/quote v1.5.2' go.mod cp go.mod.orig go.mod -go get -d rsc.io/x@patch +go get rsc.io/x@patch grep 'rsc.io/quote v1.5.2' go.mod cp go.mod.orig go.mod # Upgrading a package pattern not contained in the main module should not # attempt to upgrade the main module. -go get -d rsc.io/quote/...@v1.5.1 +go get rsc.io/quote/...@v1.5.1 grep 'rsc.io/quote v1.5.1' go.mod # The main module cannot be updated to a specific version. -! go get -d rsc.io@v0.1.0 -stderr '^go get: can''t request version "v0.1.0" of the main module \(rsc.io\)$' +! go get rsc.io@v0.1.0 +stderr '^go: can''t request version "v0.1.0" of the main module \(rsc.io\)$' # A package in the main module can't be upgraded either. -! go get -d rsc.io/x@v0.1.0 -stderr '^go get: package rsc.io/x is in the main module, so can''t request version v0.1.0$' +! go get rsc.io/x@v0.1.0 +stderr '^go: package rsc.io/x is in the main module, so can''t request version v0.1.0$' # Nor can a pattern matching packages in the main module. -! go get -d rsc.io/x/...@latest -stderr '^go get: pattern rsc.io/x/... matches package rsc.io/x in the main module, so can''t request version latest$' +! go get rsc.io/x/...@latest +stderr '^go: pattern rsc.io/x/... matches package rsc.io/x in the main module, so can''t request version latest$' -- go.mod.orig -- module rsc.io diff --git a/libgo/go/cmd/go/testdata/script/mod_get_major.txt b/libgo/go/cmd/go/testdata/script/mod_get_major.txt index 367ede9ded4..2db13180bd7 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_major.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_major.txt @@ -8,12 +8,12 @@ env GOSUMDB=off # golang.org/issue/34383: if a module path ends in a major-version suffix, # ensure that 'direct' mode can resolve the package to a module. -go get -d vcs-test.golang.org/git/v3pkg.git/v3@v3.0.0 +go get vcs-test.golang.org/git/v3pkg.git/v3@v3.0.0 go list -m vcs-test.golang.org/git/v3pkg.git/v3 stdout '^vcs-test.golang.org/git/v3pkg.git/v3 v3.0.0$' -go get -d vcs-test.golang.org/git/empty-v2-without-v1.git/v2@v2.0.0 +go get vcs-test.golang.org/git/empty-v2-without-v1.git/v2@v2.0.0 go list -m vcs-test.golang.org/git/empty-v2-without-v1.git/v2 stdout '^vcs-test.golang.org/git/empty-v2-without-v1.git/v2 v2.0.0$' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_missing_ziphash.txt b/libgo/go/cmd/go/testdata/script/mod_get_missing_ziphash.txt index 789d42d24db..5934251e4b8 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_missing_ziphash.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_missing_ziphash.txt @@ -13,7 +13,7 @@ env GOSUMDB=off cp go.sum.bug go.sum ! go build -n use stderr '^use.go:3:8: missing go.sum entry for module providing package rsc.io/quote \(imported by use\); to add:\n\tgo get use$' -go get -d use +go get use cmp go.sum go.sum.tidy go build -n use @@ -22,7 +22,7 @@ cp go.sum.bug go.sum rm $WORK/gopath/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.ziphash ! go build -n use stderr '^use.go:3:8: missing go.sum entry for module providing package rsc.io/quote \(imported by use\); to add:\n\tgo get use$' -go get -d use +go get use cmp go.sum go.sum.tidy go build -n use diff --git a/libgo/go/cmd/go/testdata/script/mod_get_moved.txt b/libgo/go/cmd/go/testdata/script/mod_get_moved.txt index 8430a737c40..ba79c8263c0 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_moved.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_moved.txt @@ -4,17 +4,17 @@ env GO111MODULE=on # A 'go get' that worked at a previous version should continue to work at that version, # even if the package was subsequently moved into a submodule. go mod init example.com/foo -go get -d example.com/split/subpkg@v1.0.0 +go get example.com/split/subpkg@v1.0.0 go list -m all stdout 'example.com/split v1.0.0' # A 'go get' that simultaneously upgrades away conflicting package defitions is not ambiguous. -go get -d example.com/split/subpkg@v1.1.0 +go get example.com/split/subpkg@v1.1.0 # A 'go get' without an upgrade should find the package. rm go.mod go mod init example.com/foo -go get -d example.com/split/subpkg +go get example.com/split/subpkg go list -m all stdout 'example.com/split/subpkg v1.1.0' @@ -23,18 +23,18 @@ stdout 'example.com/split/subpkg v1.1.0' # even if the package was subsequently moved into a parent module. rm go.mod go mod init example.com/foo -go get -d example.com/join/subpkg@v1.0.0 +go get example.com/join/subpkg@v1.0.0 go list -m all stdout 'example.com/join/subpkg v1.0.0' # A 'go get' that simultaneously upgrades away conflicting package definitions is not ambiguous. # (A wildcard pattern applies to both packages and modules, # because we define wildcard matching to apply after version resolution.) -go get -d example.com/join/subpkg/...@v1.1.0 +go get example.com/join/subpkg/...@v1.1.0 # A 'go get' without an upgrade should find the package. rm go.mod go mod init example.com/foo -go get -d example.com/join/subpkg@v1.1.0 +go get example.com/join/subpkg@v1.1.0 go list -m all stdout 'example.com/join v1.1.0' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_newcycle.txt b/libgo/go/cmd/go/testdata/script/mod_get_newcycle.txt index f71620c1bcb..18dc6503617 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_newcycle.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_newcycle.txt @@ -11,4 +11,4 @@ go mod init m cmp stderr stderr-expected -- stderr-expected -- -go get: example.com/newcycle/a@v1.0.0 requires example.com/newcycle/a@v1.0.1, not example.com/newcycle/a@v1.0.0 +go: example.com/newcycle/a@v1.0.0 requires example.com/newcycle/a@v1.0.1, not example.com/newcycle/a@v1.0.0 diff --git a/libgo/go/cmd/go/testdata/script/mod_get_none.txt b/libgo/go/cmd/go/testdata/script/mod_get_none.txt index b358f05af36..5aec209f59f 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_none.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_none.txt @@ -3,10 +3,10 @@ env GO111MODULE=on go mod init example.com/foo # 'go get bar@none' should be a no-op if module bar is not active. -go get -d example.com/bar@none +go get example.com/bar@none go list -m all ! stdout example.com/bar -go get -d example.com/bar@none +go get example.com/bar@none go list -m all ! stdout example.com/bar diff --git a/libgo/go/cmd/go/testdata/script/mod_get_nopkgs.txt b/libgo/go/cmd/go/testdata/script/mod_get_nopkgs.txt index 078e71a041c..14176a7dc87 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_nopkgs.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_nopkgs.txt @@ -6,17 +6,17 @@ cd subdir go get ./... stderr -count=1 'matched no packages' -go get -d ./... +go get ./... stderr -count=1 'matched no packages' # 'go get' on patterns that could conceivably match nested modules # should report a module resolution error. -go get -d example.net/emptysubdir/... # control case +go get example.net/emptysubdir/... # control case -! go get -d example.net/emptysubdir/subdir/... +! go get example.net/emptysubdir/subdir/... ! stderr 'matched no packages' -stderr '^go get example\.net/emptysubdir/subdir/\.\.\.: module example\.net/emptysubdir/subdir: reading http://.*: 404 Not Found\n\tserver response: 404 page not found\n\z' +stderr '^go: example\.net/emptysubdir/subdir/\.\.\.: module example\.net/emptysubdir/subdir: reading http://.*: 404 Not Found\n\tserver response: 404 page not found\n\z' # It doesn't make sense to 'go get' a path in the standard library, # since the standard library necessarily can't have unresolved imports. @@ -26,8 +26,8 @@ stderr '^go get example\.net/emptysubdir/subdir/\.\.\.: module example\.net/empt # For that case, we emit a "malformed module path" error message, # which isn't ideal either. -! go get -d builtin/... # in GOROOT/src, but contains no packages -stderr '^go get builtin/...: malformed module path "builtin": missing dot in first path element$' +! go get builtin/... # in GOROOT/src, but contains no packages +stderr '^go: builtin/...: malformed module path "builtin": missing dot in first path element$' -- go.mod -- module example.net/emptysubdir diff --git a/libgo/go/cmd/go/testdata/script/mod_get_patch.txt b/libgo/go/cmd/go/testdata/script/mod_get_patch.txt index 053ef621471..35cc276c5c3 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_patch.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_patch.txt @@ -7,8 +7,8 @@ cp go.mod go.mod.orig # example.net/b@patch refers to the patch for the version of b that was selected # at the start of 'go get', not the version after applying other changes. -! go get -d example.net/a@v0.2.0 example.net/b@patch -stderr '^go get: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$' +! go get example.net/a@v0.2.0 example.net/b@patch +stderr '^go: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$' cmp go.mod go.mod.orig @@ -18,8 +18,8 @@ cmp go.mod go.mod.orig # # TODO(#42360): Reconsider the change in defaults. -! go get -d -u=patch example.net/a@v0.2.0 example.net/b -stderr '^go get: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$' +! go get -u=patch example.net/a@v0.2.0 example.net/b +stderr '^go: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$' cmp go.mod go.mod.orig @@ -27,7 +27,7 @@ cmp go.mod go.mod.orig # applying other version changes, not the versions that were selected at the start. # However, it should not patch versions determined by explicit arguments. -go get -d -u=patch example.net/a@v0.2.0 +go get -u=patch example.net/a@v0.2.0 go list -m all stdout '^example.net/a v0.2.0 ' stdout '^example.net/b v0.2.1 ' @@ -38,7 +38,7 @@ stdout '^example.net/b v0.2.1 ' cp go.mod.orig go.mod ! go get -u=patch all -stderr '^go get: example.net/a@v0.1.1 \(matching all@patch\) requires example.net/b@v0.2.0, not example.net/b@v0.1.1 \(matching all@patch\)$' +stderr '^go: example.net/a@v0.1.1 \(matching all@patch\) requires example.net/b@v0.2.0, not example.net/b@v0.1.1 \(matching all@patch\)$' cmp go.mod go.mod.orig diff --git a/libgo/go/cmd/go/testdata/script/mod_get_patchbound.txt b/libgo/go/cmd/go/testdata/script/mod_get_patchbound.txt index 4fd1ec53e16..e4d3c491f45 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_patchbound.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_patchbound.txt @@ -5,7 +5,7 @@ go list -m all stdout '^example.net/a v0.1.0 ' stdout '^example.net/b v0.1.0 ' -go get -d -u=patch example.net/a@v0.2.0 +go get -u=patch example.net/a@v0.2.0 go list -m all stdout '^example.net/a v0.2.0 ' stdout '^example.net/b v0.1.1 ' # not v0.1.2, which requires …/a v0.3.0. diff --git a/libgo/go/cmd/go/testdata/script/mod_get_patchcycle.txt b/libgo/go/cmd/go/testdata/script/mod_get_patchcycle.txt index d1db56f935c..6600109d2da 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_patchcycle.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_patchcycle.txt @@ -6,7 +6,7 @@ # (It used to print v0.1.1 but then silently upgrade to v0.2.0.) ! go get example.net/a@patch -stderr '^go get: example.net/a@patch \(v0.1.1\) requires example.net/a@v0.2.0, not example.net/a@patch \(v0.1.1\)$' # TODO: A mention of b v0.1.0 would be nice. +stderr '^go: example.net/a@patch \(v0.1.1\) requires example.net/a@v0.2.0, not example.net/a@patch \(v0.1.1\)$' # TODO: A mention of b v0.1.0 would be nice. -- go.mod -- module example diff --git a/libgo/go/cmd/go/testdata/script/mod_get_patchmod.txt b/libgo/go/cmd/go/testdata/script/mod_get_patchmod.txt index e39d13a0f41..28277310aa1 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_patchmod.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_patchmod.txt @@ -1,5 +1,5 @@ # example.net/pkgremoved@v0.1.0 refers to a package. -go get -d example.net/pkgremoved@v0.1.0 +go get example.net/pkgremoved@v0.1.0 go list example.net/pkgremoved stdout '^example.net/pkgremoved' @@ -15,8 +15,8 @@ cp go.mod go.mod.orig # be constrained to the latest patch of its originally-selected version (v0.1.0), # not upgraded to the latest patch of the new transitive dependency. -! go get -d example.net/pkgremoved@patch example.net/other@v0.1.0 -stderr '^go get: example.net/other@v0.1.0 requires example.net/pkgremoved@v0.2.0, not example.net/pkgremoved@patch \(v0.1.1\)$' +! go get example.net/pkgremoved@patch example.net/other@v0.1.0 +stderr '^go: example.net/other@v0.1.0 requires example.net/pkgremoved@v0.2.0, not example.net/pkgremoved@patch \(v0.1.1\)$' cmp go.mod.orig go.mod @@ -24,19 +24,19 @@ cmp go.mod.orig go.mod # Package to module ... -go get -d example.net/pkgremoved@v0.3.0 +go get example.net/pkgremoved@v0.3.0 go list example.net/pkgremoved stdout 'example.net/pkgremoved' -go get -d example.net/pkgremoved@patch +go get example.net/pkgremoved@patch ! go list example.net/pkgremoved # ... and module to package. -go get -d example.net/pkgremoved@v0.4.0 +go get example.net/pkgremoved@v0.4.0 ! go list example.net/pkgremoved -go get -d example.net/pkgremoved@patch +go get example.net/pkgremoved@patch go list example.net/pkgremoved stdout 'example.net/pkgremoved' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_patterns.txt b/libgo/go/cmd/go/testdata/script/mod_get_patterns.txt index aee4374dc8a..891353fd4b9 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_patterns.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_patterns.txt @@ -5,29 +5,29 @@ env GO111MODULE=on # in the build list, we assume the pattern matches a single module # whose path is a prefix of the part of the pattern before "...". cp go.mod.orig go.mod -go get -d rsc.io/quote/... +go get rsc.io/quote/... grep 'require rsc.io/quote' go.mod cp go.mod.orig go.mod -! go get -d rsc.io/quote/x... -stderr 'go get: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x...' +! go get rsc.io/quote/x... +stderr 'go: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x...' ! grep 'require rsc.io/quote' go.mod -! go get -d rsc.io/quote/x/... -stderr 'go get: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x/...' +! go get rsc.io/quote/x/... +stderr 'go: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x/...' ! grep 'require rsc.io/quote' go.mod # If a pattern matches no packages within a module, the module should not # be upgraded, even if the module path is a prefix of the pattern. cp go.mod.orig go.mod go mod edit -require example.com/nest@v1.0.0 -go get -d example.com/nest/sub/y... +go get example.com/nest/sub/y... grep 'example.com/nest/sub v1.0.0' go.mod grep 'example.com/nest v1.0.0' go.mod # However, if the pattern matches the module path itself, the module # should be upgraded even if it contains no matching packages. -go get -d example.com/n...t +go get example.com/n...t grep 'example.com/nest v1.1.0' go.mod grep 'example.com/nest/sub v1.0.0' go.mod diff --git a/libgo/go/cmd/go/testdata/script/mod_get_pkgtags.txt b/libgo/go/cmd/go/testdata/script/mod_get_pkgtags.txt index 0c79ec71b7b..7ad3c3c7c44 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_pkgtags.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_pkgtags.txt @@ -12,10 +12,10 @@ stderr '^module example\.net/cmd provides package example\.net/cmd/tool and is r go mod edit -droprequire example.net/tools -# 'go get -d' makes a best effort to fetch those dependencies, but shouldn't +# 'go get' makes a best effort to fetch those dependencies, but shouldn't # error out if dependencies of tag-guarded files are missing. -go get -d example.net/tools@v0.1.0 +go get example.net/tools@v0.1.0 ! stderr 'no Go source files' ! go list example.net/tools @@ -48,27 +48,27 @@ stderr '^package example.net/tools: build constraints exclude all Go files in .* # 'go get' should fetch modules whose roots contain test-only packages, but # without the -t flag shouldn't error out if the test has missing dependencies. -go get -d example.net/testonly@v0.1.0 +go get example.net/testonly@v0.1.0 # With the -t flag, the test dependencies must resolve successfully. -! go get -d -t example.net/testonly@v0.1.0 +! go get -t example.net/testonly@v0.1.0 stderr '^example.net/testonly tested by\n\texample.net/testonly\.test imports\n\texample.net/missing: cannot find module providing package example.net/missing$' -# 'go get -d' should succeed for a module path that does not contain a package, +# 'go get' should succeed for a module path that does not contain a package, # but fail for a non-package subdirectory of a module. -! go get -d example.net/missing/subdir@v0.1.0 -stderr '^go get: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$' +! go get example.net/missing/subdir@v0.1.0 +stderr '^go: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$' -go get -d example.net/missing@v0.1.0 +go get example.net/missing@v0.1.0 # Getting the subdirectory should continue to fail even if the corresponding # module is already present in the build list. -! go get -d example.net/missing/subdir@v0.1.0 -stderr '^go get: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$' +! go get example.net/missing/subdir@v0.1.0 +stderr '^go: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$' -- go.mod -- diff --git a/libgo/go/cmd/go/testdata/script/mod_get_prefer_incompatible.txt b/libgo/go/cmd/go/testdata/script/mod_get_prefer_incompatible.txt index be3db42d1d9..06e2fc68602 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_prefer_incompatible.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_prefer_incompatible.txt @@ -8,7 +8,7 @@ cmp go.mod.orig go.mod grep '^example.com/incompatiblewithsub v2\.0\.0\+incompatible' go.sum ! grep '^example.com/incompatiblewithsub v1.0.0' go.sum -go get -d example.com/incompatiblewithsub/sub +go get example.com/incompatiblewithsub/sub cmp go.mod.orig go.mod ! grep '^example.com/incompatiblewithsub v1.0.0' go.sum diff --git a/libgo/go/cmd/go/testdata/script/mod_get_promote_implicit.txt b/libgo/go/cmd/go/testdata/script/mod_get_promote_implicit.txt index 9eec2013210..03f6810e354 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_promote_implicit.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_promote_implicit.txt @@ -12,7 +12,7 @@ stderr '^package m/use-indirect imports indirect-with-pkg from implicitly requir # NOTE: the hint recommends getting the imported package (tested below) since # it's more obvious and doesn't require -d. However, that adds an '// indirect' # comment on the requirement. -go get -d m/use-indirect +go get m/use-indirect cmp go.mod go.mod.use cp go.mod.orig go.mod @@ -21,7 +21,7 @@ cp go.mod.orig go.mod # know they're needed by the main module. See #43131 for the rationale. # The hint above recommends this because it's more obvious usage and doesn't # require the -d flag. -go get -d indirect-with-pkg indirect-without-pkg +go get indirect-with-pkg indirect-without-pkg cmp go.mod go.mod.indirect -- go.mod.orig -- diff --git a/libgo/go/cmd/go/testdata/script/mod_get_pseudo.txt b/libgo/go/cmd/go/testdata/script/mod_get_pseudo.txt index 582837a1665..b964ae44845 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_pseudo.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_pseudo.txt @@ -9,69 +9,69 @@ env GOSUMDB=off # We can resolve the @master branch without unshallowing the local repository # (even with older gits), so try that before we do anything else. # (This replicates https://golang.org/issue/26713 with git 2.7.4.) -go get -d github.com/rsc/legacytest@master +go get github.com/rsc/legacytest@master go list -m all stdout '^github.com/rsc/legacytest v2\.0\.1-0\.\d{14}-7303f7796364\+incompatible$' # get should include incompatible tags in "latest" calculation. go mod edit -droprequire github.com/rsc/legacytest -go get -d github.com/rsc/legacytest@latest +go get github.com/rsc/legacytest@latest go list go list -m all stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$' # v2.0.1-0.pseudo+incompatible -go get -d ...test@7303f77 +go get ...test@7303f77 go list -m all stdout '^github.com/rsc/legacytest v2\.0\.1-0\.\d{14}-7303f7796364\+incompatible$' # v2.0.0+incompatible by tag+incompatible -go get -d ...test@v2.0.0+incompatible +go get ...test@v2.0.0+incompatible go list -m all stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$' # v2.0.0+incompatible by tag -go get -d ...test@v2.0.0 +go get ...test@v2.0.0 go list -m all stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$' # v2.0.0+incompatible by hash (back on master) -go get -d ...test@d7ae1e4 +go get ...test@d7ae1e4 go list -m all stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$' # v1.2.1-0.pseudo -go get -d ...test@d2d4c3e +go get ...test@d2d4c3e go list -m all stdout '^github.com/rsc/legacytest v1\.2\.1-0\.\d{14}-d2d4c3ea6623$' # v1.2.0 -go get -d ...test@9f6f860 +go get ...test@9f6f860 go list -m all stdout '^github.com/rsc/legacytest v1\.2\.0$' # v1.1.0-pre.0.pseudo -go get -d ...test@fb3c628 +go get ...test@fb3c628 go list -m all stdout '^github.com/rsc/legacytest v1\.1\.0-pre\.0\.\d{14}-fb3c628075e3$' # v1.1.0-pre (no longer on master) -go get -d ...test@731e3b1 +go get ...test@731e3b1 go list -m all stdout '^github.com/rsc/legacytest v1\.1\.0-pre$' # v1.0.1-0.pseudo -go get -d ...test@fa4f5d6 +go get ...test@fa4f5d6 go list -m all stdout '^github.com/rsc/legacytest v1\.0\.1-0\.\d{14}-fa4f5d6a71c6$' # v1.0.0 -go get -d ...test@7fff7f3 +go get ...test@7fff7f3 go list -m all stdout '^github.com/rsc/legacytest v1\.0\.0$' # v0.0.0-pseudo -go get -d ...test@52853eb +go get ...test@52853eb go list -m all stdout '^github.com/rsc/legacytest v0\.0\.0-\d{14}-52853eb7b552$' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_pseudo_other_branch.txt b/libgo/go/cmd/go/testdata/script/mod_get_pseudo_other_branch.txt index 0fbd041f860..d085f4fa3c8 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_pseudo_other_branch.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_pseudo_other_branch.txt @@ -17,7 +17,7 @@ env GOSUMDB=off # The pseudo-version hence sorts immediately after v0.2.2 rather # than v0.2.1, even though the v0.2.2 tag is not on master. -go get -d vcs-test.golang.org/git/tagtests.git@master +go get vcs-test.golang.org/git/tagtests.git@master go list -m all stdout '^vcs-test.golang.org/git/tagtests.git v0.2.3-0\.' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_pseudo_prefix.txt b/libgo/go/cmd/go/testdata/script/mod_get_pseudo_prefix.txt index b78e6e644f7..8e6cd907f18 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_pseudo_prefix.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_pseudo_prefix.txt @@ -16,11 +16,11 @@ env GOSUMDB=off # # The pseudo-version is based on sub/v0.0.10, since v0.2.0 doesn't # contain the prefix. -go get -d vcs-test.golang.org/git/prefixtagtests.git/sub +go get vcs-test.golang.org/git/prefixtagtests.git/sub go list -m all stdout '^vcs-test.golang.org/git/prefixtagtests.git/sub v0.0.10$' -go get -d -u vcs-test.golang.org/git/prefixtagtests.git/sub@master +go get -u vcs-test.golang.org/git/prefixtagtests.git/sub@master go list -m all stdout '^vcs-test.golang.org/git/prefixtagtests.git/sub v0.0.11-0\.' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_replaced.txt b/libgo/go/cmd/go/testdata/script/mod_get_replaced.txt index d97f3f1a401..b1fc8b80563 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_replaced.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_replaced.txt @@ -6,7 +6,7 @@ env oldGOPROXY=$GOPROXY # 'go get' should resolve it to the minimum valid pseudo-version. go mod edit -replace=example.com/x=./x -go get -d example.com/x +go get example.com/x go list -m example.com/x stdout '^example.com/x v0.0.0-00010101000000-000000000000 ' @@ -15,11 +15,11 @@ stdout '^example.com/x v0.0.0-00010101000000-000000000000 ' go mod edit -replace=example.com/x@v0.1.0=./x go mod edit -replace=example.com/x@v0.2.0=./x -go get -d example.com/x +go get example.com/x go list -m example.com/x stdout '^example.com/x v0.2.0 ' -go get -d example.com/x@<v0.2.0 +go get example.com/x@<v0.2.0 go list -m example.com/x stdout '^example.com/x v0.1.0 ' @@ -30,7 +30,7 @@ env GOPROXY=off cp go.mod.orig go.mod go mod edit -replace=example.com/x=./x -go get -d example.com/x +go get example.com/x go list -m example.com/x stdout '^example.com/x v0.0.0-00010101000000-000000000000 ' @@ -39,11 +39,11 @@ stdout '^example.com/x v0.0.0-00010101000000-000000000000 ' go mod edit -replace=example.com/x@v0.1.0=./x go mod edit -replace=example.com/x@v0.2.0=./x -go get -d example.com/x +go get example.com/x go list -m example.com/x stdout '^example.com/x v0.2.0 ' -go get -d example.com/x@<v0.2.0 +go get example.com/x@<v0.2.0 go list -m example.com/x stdout '^example.com/x v0.1.0 ' @@ -57,7 +57,7 @@ cp go.mod.orig go.mod go list -versions -m rsc.io/quote stdout 'v1.3.0 v1.4.0' -go get -d rsc.io/quote@v1.3 +go get rsc.io/quote@v1.3 go list -m rsc.io/quote stdout '^rsc.io/quote v1.3.0' @@ -66,11 +66,11 @@ go mod edit -replace rsc.io/quote@v1.3.1=rsc.io/quote@v1.4.0 go list -versions -m rsc.io/quote stdout 'v1.3.0 v1.3.1 v1.4.0' -go get -d rsc.io/quote@v1.3 +go get rsc.io/quote@v1.3 go list -m rsc.io/quote stdout '^rsc.io/quote v1.3.1 ' -go get -d rsc.io/quote@>v1.3.1 +go get rsc.io/quote@>v1.3.1 go list -m rsc.io/quote stdout '^rsc.io/quote v1.4.0' @@ -81,15 +81,15 @@ cp go.mod.orig go.mod ! go list example stderr '^package example is not in GOROOT \(.*\)$' -! go get -d example -stderr '^go get: malformed module path "example": missing dot in first path element$' +! go get example +stderr '^go: malformed module path "example": missing dot in first path element$' go mod edit -replace example@v0.1.0=./example ! go list example stderr '^module example provides package example and is replaced but not required; to add it:\n\tgo get example@v0.1.0$' -go get -d example +go get example go list -m example stdout '^example v0.1.0 ' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_retract.txt b/libgo/go/cmd/go/testdata/script/mod_get_retract.txt index 560fa7bfb25..97579896667 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_retract.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_retract.txt @@ -1,7 +1,7 @@ # 'go get pkg' should not upgrade to a retracted version. cp go.mod.orig go.mod go mod edit -require example.com/retract/self/prev@v1.1.0 -go get -d example.com/retract/self/prev +go get example.com/retract/self/prev go list -m example.com/retract/self/prev stdout '^example.com/retract/self/prev v1.1.0$' @@ -9,7 +9,7 @@ stdout '^example.com/retract/self/prev v1.1.0$' # version is available. cp go.mod.orig go.mod go mod edit -require example.com/retract/self/prev@v1.9.0 -go get -d example.com/retract/self/prev +go get example.com/retract/self/prev stderr '^go: warning: example.com/retract/self/prev@v1.9.0: retracted by module author: self$' stderr '^go: to switch to the latest unretracted version, run:\n\tgo get example.com/retract/self/prev@latest\n$' go list -m example.com/retract/self/prev @@ -18,14 +18,14 @@ stdout '^example.com/retract/self/prev v1.9.0$' # 'go get pkg@latest' should downgrade from a retracted version. cp go.mod.orig go.mod go mod edit -require example.com/retract/self/prev@v1.9.0 -go get -d example.com/retract/self/prev@latest +go get example.com/retract/self/prev@latest go list -m example.com/retract/self/prev stdout '^example.com/retract/self/prev v1.1.0$' # 'go get pkg@version' should update to a specific version, even if that # version is retracted. cp go.mod.orig go.mod -go get -d example.com/retract@v1.0.0-bad +go get example.com/retract@v1.0.0-bad stderr '^go: warning: example.com/retract@v1.0.0-bad: retracted by module author: bad$' go list -m example.com/retract stdout '^example.com/retract v1.0.0-bad$' @@ -34,16 +34,16 @@ stdout '^example.com/retract v1.0.0-bad$' # version is available. cp go.mod.orig go.mod go mod edit -require example.com/retract/self/prev@v1.9.0 -go get -d -u ./use +go get -u ./use stderr '^go: warning: example.com/retract/self/prev@v1.9.0: retracted by module author: self$' go list -m example.com/retract/self/prev stdout '^example.com/retract/self/prev v1.9.0$' # 'go get' should warn if a module needed to build named packages is retracted. # 'go get' should not warn about unrelated modules. -go get -d ./empty +go get ./empty ! stderr retracted -go get -d ./use +go get ./use stderr '^go: warning: example.com/retract/self/prev@v1.9.0: retracted by module author: self$' -- go.mod.orig -- diff --git a/libgo/go/cmd/go/testdata/script/mod_get_retract_ambiguous.txt b/libgo/go/cmd/go/testdata/script/mod_get_retract_ambiguous.txt index b49ba54982b..4b4f5da03cd 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_retract_ambiguous.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_retract_ambiguous.txt @@ -1,4 +1,4 @@ -! go get -d example.com/retract/ambiguous/other +! go get example.com/retract/ambiguous/other stderr 'ambiguous import: found package example.com/retract/ambiguous/nested in multiple modules:' stderr '^go: warning: example.com/retract/ambiguous/nested@v1.9.0-bad: retracted by module author: nested modules are bad$' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_split.txt b/libgo/go/cmd/go/testdata/script/mod_get_split.txt index f4e7661f9b2..0fb22c85d39 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_split.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_split.txt @@ -4,7 +4,7 @@ cp go.mod go.mod.orig # 'go get' on a package already provided by the build list should update # the module already in the build list, not fail with an ambiguous import error. -go get -d example.net/split/nested@patch +go get example.net/split/nested@patch go list -m all stdout '^example.net/split v0.2.1 ' ! stdout '^example.net/split/nested' @@ -13,7 +13,7 @@ stdout '^example.net/split v0.2.1 ' cp go.mod.orig go.mod -go get -d example.net/split/nested/...@patch +go get example.net/split/nested/...@patch go list -m all stdout '^example.net/split v0.2.1 ' ! stdout '^example.net/split/nested' @@ -32,7 +32,7 @@ stdout '^example.net/split v0.2.1 ' cp go.mod.orig go.mod -! go get -d example.net/split/nested@v0.1.0 +! go get example.net/split/nested@v0.1.0 stderr '^example.net/split/nested: ambiguous import: found package example.net/split/nested in multiple modules:\n\texample.net/split v0.2.0 \(.*split.2[/\\]nested\)\n\texample.net/split/nested v0.1.0 \(.*nested.1\)$' # A wildcard that matches packages in some module at its selected version @@ -54,22 +54,22 @@ stderr '^example.net/split/nested: ambiguous import: found package example.net/s # # TODO(#27899): Should we instead upgrade or downgrade to an arbirary version? -! go get -d example.net/split/nested/...@v0.1.0 -stderr '^go get: example.net/split/nested/\.\.\.@v0.1.0 matches packages in example.net/split@v0.2.0 but not example.net/split@v0.1.0: specify a different version for module example.net/split$' +! go get example.net/split/nested/...@v0.1.0 +stderr '^go: example.net/split/nested/\.\.\.@v0.1.0 matches packages in example.net/split@v0.2.0 but not example.net/split@v0.1.0: specify a different version for module example.net/split$' cmp go.mod go.mod.orig # If another argument resolves the ambiguity, we should be ok again. -go get -d example.net/split@none example.net/split/nested@v0.1.0 +go get example.net/split@none example.net/split/nested@v0.1.0 go list -m all ! stdout '^example.net/split ' stdout '^example.net/split/nested v0.1.0 ' cp go.mod.orig go.mod -go get -d example.net/split@v0.3.0 example.net/split/nested@v0.1.0 +go get example.net/split@v0.3.0 example.net/split/nested@v0.1.0 go list -m all stdout '^example.net/split v0.3.0 ' stdout '^example.net/split/nested v0.1.0 ' @@ -80,14 +80,14 @@ stdout '^example.net/split/nested v0.1.0 ' # to match the pattern if possible. cp go.mod.orig go.mod -go get -d example.net/split/nested@v0.0.0 +go get example.net/split/nested@v0.0.0 -go get -d example.net/...@v0.1.0 +go get example.net/...@v0.1.0 go list -m all stdout '^example.net/split v0.1.0 ' stdout '^example.net/split/nested v0.1.0 ' -go get -d example.net/... +go get example.net/... go list -m all stdout '^example.net/split v0.3.0 ' stdout '^example.net/split/nested v0.2.0 ' @@ -96,15 +96,15 @@ stdout '^example.net/split/nested v0.2.0 ' # @none applies to all matching module paths, # regardless of whether they contain any packages. -go get -d example.net/...@none +go get example.net/...@none go list -m all ! stdout '^example.net' # Starting from no dependencies, a wildcard can resolve to an empty module with # the same prefix even if it contains no packages. -go get -d example.net/...@none -go get -d example.net/split/...@v0.1.0 +go get example.net/...@none +go get example.net/split/...@v0.1.0 go list -m all stdout '^example.net/split v0.1.0 ' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_sum_noroot.txt b/libgo/go/cmd/go/testdata/script/mod_get_sum_noroot.txt index 4f1cf032777..0d9a840e779 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_sum_noroot.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_sum_noroot.txt @@ -2,7 +2,7 @@ # it should add sums for the module's go.mod file and its content to go.sum. # Verifies golang.org/issue/41103. go mod init m -go get -d rsc.io/QUOTE +go get rsc.io/QUOTE grep '^rsc.io/QUOTE v1.5.2/go.mod ' go.sum grep '^rsc.io/QUOTE v1.5.2 ' go.sum diff --git a/libgo/go/cmd/go/testdata/script/mod_get_tags.txt b/libgo/go/cmd/go/testdata/script/mod_get_tags.txt index e9869e3f023..e4fb6c4326b 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_tags.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_tags.txt @@ -1,25 +1,14 @@ env GO111MODULE=on -[short] skip - # get should add modules needed to build packages, even if those # dependencies are in sources excluded by build tags. # All build tags are considered true except "ignore". go mod init m -go get -d . +go get . go list -m all stdout 'example.com/version v1.1.0' stdout 'rsc.io/quote v1.5.2' -[short] skip - -# Packages that are only imported in excluded files should not be built. -env GOCACHE=$WORK/gocache # Looking for compile commands, so need a clean cache. -go get -n -x . -stderr 'compile.* -p m ' -! stderr 'compile.* -p example.com/version ' -! stderr 'compile.* -p rsc.io/quote ' - -- empty.go -- package m diff --git a/libgo/go/cmd/go/testdata/script/mod_get_test.txt b/libgo/go/cmd/go/testdata/script/mod_get_test.txt index 23722bd4e4d..0fa7cd98b98 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_test.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_test.txt @@ -2,38 +2,38 @@ env GO111MODULE=on # By default, 'go get' should ignore tests cp go.mod.empty go.mod -go get -d m/a +go get m/a ! grep rsc.io/quote go.mod # 'go get -t' should consider test dependencies of the named package. cp go.mod.empty go.mod -go get -d -t m/a +go get -t m/a grep 'rsc.io/quote v1.5.2$' go.mod # 'go get -t' should not consider test dependencies of imported packages, # including packages imported from tests. cp go.mod.empty go.mod -go get -d -t m/b +go get -t m/b ! grep rsc.io/quote go.mod # 'go get -t -u' should update test dependencies of the named package. cp go.mod.empty go.mod go mod edit -require=rsc.io/quote@v1.5.1 -go get -d -t -u m/a +go get -t -u m/a grep 'rsc.io/quote v1.5.2$' go.mod # 'go get -t -u' should not add or update test dependencies # of imported packages, including packages imported from tests. cp go.mod.empty go.mod -go get -d -t -u m/b +go get -t -u m/b ! grep rsc.io/quote go.mod go mod edit -require=rsc.io/quote@v1.5.1 -go get -d -t -u m/b +go get -t -u m/b grep 'rsc.io/quote v1.5.1$' go.mod # 'go get all' should consider test dependencies with or without -t. cp go.mod.empty go.mod -go get -d all +go get all grep 'rsc.io/quote v1.5.2$' go.mod -- go.mod.empty -- diff --git a/libgo/go/cmd/go/testdata/script/mod_get_trailing_slash.txt b/libgo/go/cmd/go/testdata/script/mod_get_trailing_slash.txt index c5366935379..7b469008baf 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_trailing_slash.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_trailing_slash.txt @@ -7,16 +7,9 @@ stdout ^example.com/dotgo.go$ go list example.com/dotgo.go/ stdout ^example.com/dotgo.go$ -# go get -d should succeed in either case, with or without a version. +# go get should succeed in either case, with or without a version. # Arguments are interpreted as packages or package patterns with versions, # not source files. -go get -d example.com/dotgo.go -go get -d example.com/dotgo.go/ -go get -d example.com/dotgo.go@v1.0.0 -go get -d example.com/dotgo.go/@v1.0.0 - -# go get (without -d) should also succeed in either case. -[short] skip go get example.com/dotgo.go go get example.com/dotgo.go/ go get example.com/dotgo.go@v1.0.0 diff --git a/libgo/go/cmd/go/testdata/script/mod_get_update_unrelated_sum.txt b/libgo/go/cmd/go/testdata/script/mod_get_update_unrelated_sum.txt index 0093c0eda0c..a5651e93418 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_update_unrelated_sum.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_update_unrelated_sum.txt @@ -18,7 +18,7 @@ cmp go.sum.orig go.sum # Upgrade a module. This also upgrades rsc.io/quote, and though we didn't load # a package from it, we had the sum for its old version, so we need the # sum for the new version, too. -go get -d example.com/upgrade@v0.0.2 +go get example.com/upgrade@v0.0.2 grep '^rsc.io/quote v1.5.2 ' go.sum # The upgrade still breaks the build because the new version of quote imports @@ -34,7 +34,7 @@ cp go.sum.orig go.sum # We didn't need a sum for it before (even though we had one), so we won't # fetch a new sum. go mod edit -replace rsc.io/quote@v1.0.0=./dummy -go get -d example.com/upgrade@v0.0.2 +go get example.com/upgrade@v0.0.2 ! grep '^rsc.io/quote v1.5.2 ' go.sum cp go.mod.orig go.mod cp go.sum.orig go.sum @@ -43,7 +43,7 @@ cp go.sum.orig go.sum # Replace the new version with a directory before upgrading. # We can't get a sum for a directory. go mod edit -replace rsc.io/quote@v1.5.2=./dummy -go get -d example.com/upgrade@v0.0.2 +go get example.com/upgrade@v0.0.2 ! grep '^rsc.io/quote v1.5.2 ' go.sum cp go.mod.orig go.mod cp go.sum.orig go.sum @@ -52,7 +52,7 @@ cp go.sum.orig go.sum # Replace the new version with a different version. # We should get a sum for that version. go mod edit -replace rsc.io/quote@v1.5.2=rsc.io/quote@v1.5.1 -go get -d example.com/upgrade@v0.0.2 +go get example.com/upgrade@v0.0.2 ! grep '^rsc.io/quote v1.5.2 ' go.sum grep '^rsc.io/quote v1.5.1 ' go.sum cp go.mod.orig go.mod @@ -63,7 +63,7 @@ cp go.sum.orig go.sum # 'go get' should fail when fetching the zip. rm $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip env GOPROXY=off -! go get -d example.com/upgrade@v0.0.2 +! go get example.com/upgrade@v0.0.2 stderr '^go: upgraded rsc.io/quote v1.0.0 => v1.5.2: error finding sum for rsc.io/quote@v1.5.2: module lookup disabled by GOPROXY=off$' -- go.mod.orig -- diff --git a/libgo/go/cmd/go/testdata/script/mod_get_upgrade.txt b/libgo/go/cmd/go/testdata/script/mod_get_upgrade.txt index eeb6d6f6af6..51d5990ee17 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_upgrade.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_upgrade.txt @@ -1,35 +1,35 @@ env GO111MODULE=on -go get -d rsc.io/quote@v1.5.1 +go get rsc.io/quote@v1.5.1 go list -m all stdout 'rsc.io/quote v1.5.1' grep 'rsc.io/quote v1.5.1$' go.mod # get -u should update dependencies of the package in the current directory -go get -d -u +go get -u grep 'rsc.io/quote v1.5.2$' go.mod grep 'golang.org/x/text [v0-9a-f\.-]+ // indirect' go.mod # get -u rsc.io/sampler should update only sampler's dependencies cp go.mod-v1.5.1 go.mod -go get -d -u rsc.io/sampler +go get -u rsc.io/sampler grep 'rsc.io/quote v1.5.1$' go.mod grep 'golang.org/x/text [v0-9a-f\.-]+ // indirect' go.mod # move to a pseudo-version after any tags -go get -d rsc.io/quote@dd9747d +go get rsc.io/quote@dd9747d grep 'rsc.io/quote v0.0.0-20180628003336-dd9747d19b04' go.mod # get -u should not jump off newer pseudo-version to earlier tag -go get -d -u +go get -u grep 'rsc.io/quote v0.0.0-20180628003336-dd9747d19b04' go.mod # move to earlier pseudo-version -go get -d rsc.io/quote@e7a685a342 +go get rsc.io/quote@e7a685a342 grep 'rsc.io/quote v0.0.0-20180214005133-e7a685a342c0' go.mod # get -u should jump off earlier pseudo-version to newer tag -go get -d -u +go get -u grep 'rsc.io/quote v1.5.2' go.mod -- go.mod -- diff --git a/libgo/go/cmd/go/testdata/script/mod_get_upgrade_pseudo.txt b/libgo/go/cmd/go/testdata/script/mod_get_upgrade_pseudo.txt index f5f415aa3fa..deff9358f06 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_upgrade_pseudo.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_upgrade_pseudo.txt @@ -10,52 +10,52 @@ env GO111MODULE=on # The v0.0.0 pseudo-version is chronologically newer. # Start at v0.1.1-0.20190429073117-b5426c86b553 -go get -d example.com/pseudoupgrade@b5426c8 +go get example.com/pseudoupgrade@b5426c8 go list -m -u all stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$' # 'get -u' should not downgrade to the (lower) tagged version. -go get -d -u +go get -u go list -m -u all stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$' # 'get example.com/pseudoupgrade@upgrade' should not downgrade. -go get -d example.com/pseudoupgrade@upgrade +go get example.com/pseudoupgrade@upgrade go list -m all stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$' # 'get example.com/pseudoupgrade' should not downgrade. # This is equivalent to 'get example.com/pseudoupgrade@upgrade'. -go get -d example.com/pseudoupgrade +go get example.com/pseudoupgrade go list -m all stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$' # 'get example.com/pseudoupgrade@latest' should downgrade. # @latest should not consider the current version. -go get -d example.com/pseudoupgrade@latest +go get example.com/pseudoupgrade@latest go list -m all stdout '^example.com/pseudoupgrade v0.1.0$' # We should observe the same behavior with the newer pseudo-version. -go get -d example.com/pseudoupgrade@v0.0.0-20190430073000-30950c05d534 +go get example.com/pseudoupgrade@v0.0.0-20190430073000-30950c05d534 # 'get -u' should not downgrade to the chronologically older tagged version. -go get -d -u +go get -u go list -m -u all stdout '^example.com/pseudoupgrade v0.0.0-20190430073000-30950c05d534$' # 'get example.com/pseudoupgrade@upgrade should not downgrade. -go get -d example.com/pseudoupgrade@upgrade +go get example.com/pseudoupgrade@upgrade go list -m -u all stdout '^example.com/pseudoupgrade v0.0.0-20190430073000-30950c05d534$' # 'get example.com/pseudoupgrade' should not downgrade. -go get -d example.com/pseudoupgrade +go get example.com/pseudoupgrade go list -m -u all stdout '^example.com/pseudoupgrade v0.0.0-20190430073000-30950c05d534$' # 'get example.com/pseudoupgrade@latest' should downgrade. -go get -d example.com/pseudoupgrade@latest +go get example.com/pseudoupgrade@latest go list -m -u all stdout '^example.com/pseudoupgrade v0.1.0$' diff --git a/libgo/go/cmd/go/testdata/script/mod_get_wild.txt b/libgo/go/cmd/go/testdata/script/mod_get_wild.txt index 78c645c6b9f..06f9973e431 100644 --- a/libgo/go/cmd/go/testdata/script/mod_get_wild.txt +++ b/libgo/go/cmd/go/testdata/script/mod_get_wild.txt @@ -11,15 +11,15 @@ stdout '^example.net/a v0.1.0 ' # already in the build list, and the wildcard in the first element prevents us # from attempting to resolve a new module whose path is a prefix of the pattern. -! go get -d -u=patch example.../b@upgrade -stderr '^go get: no modules to query for example\.\.\./b@upgrade because first path element contains a wildcard$' +! go get -u=patch example.../b@upgrade +stderr '^go: no modules to query for example\.\.\./b@upgrade because first path element contains a wildcard$' # Patching . causes a patch to example.net/a, which introduces a new match # for example.net/b/..., which is itself patched and causes another upgrade to # example.net/a, which is then patched again. -go get -d -u=patch . example.../b@upgrade +go get -u=patch . example.../b@upgrade go list -m all stdout '^example.net/a v0.2.1 ' # upgraded by dependency of b and -u=patch stdout '^example.net/b v0.2.0 ' # introduced by patch of a and upgraded by wildcard diff --git a/libgo/go/cmd/go/testdata/script/mod_getmode_vendor.txt b/libgo/go/cmd/go/testdata/script/mod_getmode_vendor.txt index d3df2078b07..aaf526b2ab5 100644 --- a/libgo/go/cmd/go/testdata/script/mod_getmode_vendor.txt +++ b/libgo/go/cmd/go/testdata/script/mod_getmode_vendor.txt @@ -1,6 +1,6 @@ env GO111MODULE=on -go get -d rsc.io/quote@v1.5.1 +go get rsc.io/quote@v1.5.1 go mod vendor env GOPATH=$WORK/empty env GOPROXY=file:///nonexist @@ -11,20 +11,21 @@ stdout '^rsc.io/quote v1.5.1 .*vendor[\\/]rsc.io[\\/]quote$' stdout '^golang.org/x/text v0.0.0.* .*vendor[\\/]golang.org[\\/]x[\\/]text[\\/]language$' ! go list -mod=vendor -m rsc.io/quote@latest -stderr 'go list -m: rsc.io/quote@latest: cannot query module due to -mod=vendor' +stderr 'go: rsc.io/quote@latest: cannot query module due to -mod=vendor' ! go get -mod=vendor -u stderr 'flag provided but not defined: -mod' # Since we don't have a complete module graph, 'go list -m' queries # that require the complete graph should fail with a useful error. ! go list -mod=vendor -m all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' ! go list -mod=vendor -m ... -stderr 'go list -m: can''t match module patterns using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t match module patterns using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' -- go.mod -- module x +go 1.16 -- x.go -- package x import _ "rsc.io/quote" diff --git a/libgo/go/cmd/go/testdata/script/mod_getx.txt b/libgo/go/cmd/go/testdata/script/mod_getx.txt index ccb8d1375aa..b3d06c17c85 100644 --- a/libgo/go/cmd/go/testdata/script/mod_getx.txt +++ b/libgo/go/cmd/go/testdata/script/mod_getx.txt @@ -8,7 +8,12 @@ env GOSUMDB=off # 'go get -x' should log URLs with an HTTP or HTTPS scheme. # A bug had caused us to log schemeless URLs instead. -go get -x -d golang.org/x/text@v0.1.0 +go get -x golang.org/x/text@v0.1.0 stderr '^# get https://golang.org/x/text\?go-get=1$' stderr '^# get https://golang.org/x/text\?go-get=1: 200 OK \([0-9.]+s\)$' ! stderr '^# get //.*' + +-- go.mod -- +module m + +go 1.18 diff --git a/libgo/go/cmd/go/testdata/script/mod_go_version_missing.txt b/libgo/go/cmd/go/testdata/script/mod_go_version_missing.txt index d704816729b..2159a1e4c0e 100644 --- a/libgo/go/cmd/go/testdata/script/mod_go_version_missing.txt +++ b/libgo/go/cmd/go/testdata/script/mod_go_version_missing.txt @@ -27,7 +27,7 @@ cmp go.mod go.mod.orig ! go list -mod=vendor all ! stderr '^go: inconsistent vendoring' -stderr 'cannot find package "\." in:\n\t.*[/\\]vendor[/\\]example.com[/\\]badedit$' +stderr 'cannot find package "vendor/example.com/badedit" in:\n\t.*[/\\]vendor[/\\]example.com[/\\]badedit$' # When we set -mod=mod, the go version should be updated immediately, # to the current version, converting the requirements from eager to lazy. diff --git a/libgo/go/cmd/go/testdata/script/mod_gomodcache.txt b/libgo/go/cmd/go/testdata/script/mod_gomodcache.txt index 74a3c79622f..bafa5876241 100644 --- a/libgo/go/cmd/go/testdata/script/mod_gomodcache.txt +++ b/libgo/go/cmd/go/testdata/script/mod_gomodcache.txt @@ -5,7 +5,7 @@ env GO111MODULE=on env GOMODCACHE=$WORK/modcache go env GOMODCACHE stdout $WORK[/\\]modcache -go get -d rsc.io/quote@v1.0.0 +go get rsc.io/quote@v1.0.0 exists $WORK/modcache/cache/download/rsc.io/quote/@v/v1.0.0.info grep '{"Version":"v1.0.0","Time":"2018-02-14T00:45:20Z"}' $WORK/modcache/cache/download/rsc.io/quote/@v/v1.0.0.info @@ -18,7 +18,7 @@ exists $WORK/modcache/cache/download/sumdb env GOMODCACHE= go env GOMODCACHE stdout $GOPATH[/\\]pkg[/\\]mod -go get -d rsc.io/quote@v1.0.0 +go get rsc.io/quote@v1.0.0 exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.0.0.info grep '{"Version":"v1.0.0","Time":"2018-02-14T00:45:20Z"}' $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.0.0.info @@ -31,11 +31,18 @@ env GOPATH= go env GOMODCACHE stdout $HOME[/\\]go[/\\]pkg[/\\]mod -# If GOMODCACHE isn't set and GOPATH starts with the path list separator, it's an error. +# If GOMODCACHE isn't set and GOPATH starts with the path list separator, +# GOMODCACHE is empty and any command that needs it errors out. env GOMODCACHE= env GOPATH=${:}$WORK/this/is/ignored -! go env GOMODCACHE -stderr 'missing \$GOPATH' + +go env GOMODCACHE +stdout '^$' +! stdout . +! stderr . + +! go mod download rsc.io/quote@v1.0.0 +stderr '^go: module cache not found: neither GOMODCACHE nor GOPATH is set$' # If GOMODCACHE isn't set and GOPATH has multiple elements only the first is used. env GOMODCACHE= diff --git a/libgo/go/cmd/go/testdata/script/mod_gonoproxy.txt b/libgo/go/cmd/go/testdata/script/mod_gonoproxy.txt index 204786969f5..d42d668f679 100644 --- a/libgo/go/cmd/go/testdata/script/mod_gonoproxy.txt +++ b/libgo/go/cmd/go/testdata/script/mod_gonoproxy.txt @@ -7,16 +7,16 @@ env dbname=localhost.localdev/sumdb # disagree with sumdb fails cp go.mod.orig go.mod env GOSUMDB=$sumdb' '$proxy/sumdb-wrong -! go get -d rsc.io/quote +! go get rsc.io/quote stderr 'SECURITY ERROR' # GONOSUMDB bypasses sumdb, for rsc.io/quote, rsc.io/sampler, golang.org/x/text env GONOSUMDB='*/quote,*/*mple*,golang.org/x' -go get -d rsc.io/quote +go get rsc.io/quote rm go.sum env GOPRIVATE='*/quote,*/*mple*,golang.org/x' env GONOPROXY=none # that is, proxy all despite GOPRIVATE -go get -d rsc.io/quote +go get rsc.io/quote # Download .info files needed for 'go list -m all' later. # TODO(#42723): either 'go list -m' should not read these files, @@ -26,27 +26,27 @@ stdout '^golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c$' # When GOPROXY is not empty but contains no entries, an error should be reported. env GOPROXY=',' -! go get -d golang.org/x/text -stderr '^go get golang.org/x/text: GOPROXY list is not the empty string, but contains no entries$' +! go get golang.org/x/text +stderr '^go: golang.org/x/text: GOPROXY list is not the empty string, but contains no entries$' # When GOPROXY=off, fetching modules not matched by GONOPROXY fails. env GONOPROXY=*/fortune env GOPROXY=off -! go get -d golang.org/x/text -stderr '^go get golang.org/x/text: module lookup disabled by GOPROXY=off$' +! go get golang.org/x/text +stderr '^go: golang.org/x/text: module lookup disabled by GOPROXY=off$' # GONOPROXY bypasses proxy [!net] skip [!exec:git] skip env GOPRIVATE=none env GONOPROXY='*/fortune' -! go get -d rsc.io/fortune # does not exist in real world, only on test proxy +! go get rsc.io/fortune # does not exist in real world, only on test proxy stderr 'git ls-remote' env GOSUMDB= env GONOPROXY= env GOPRIVATE='*/x' -go get -d golang.org/x/text +go get golang.org/x/text go list -m all ! stdout 'text.*v0.0.0-2017' # should not have the version from the proxy diff --git a/libgo/go/cmd/go/testdata/script/mod_gopkg_unstable.txt b/libgo/go/cmd/go/testdata/script/mod_gopkg_unstable.txt index 5ad9106378f..beba3e7b02c 100644 --- a/libgo/go/cmd/go/testdata/script/mod_gopkg_unstable.txt +++ b/libgo/go/cmd/go/testdata/script/mod_gopkg_unstable.txt @@ -1,7 +1,7 @@ env GO111MODULE=on cp go.mod.empty go.mod -go get -d gopkg.in/dummy.v2-unstable +go get gopkg.in/dummy.v2-unstable cp x.go.txt x.go cp go.mod.empty go.mod @@ -12,7 +12,7 @@ go list env GOPROXY=direct env GOSUMDB=off -go get -d gopkg.in/macaroon-bakery.v2-unstable/bakery +go get gopkg.in/macaroon-bakery.v2-unstable/bakery go list -m all stdout 'gopkg.in/macaroon-bakery.v2-unstable v2.0.0-[0-9]+-[0-9a-f]+$' diff --git a/libgo/go/cmd/go/testdata/script/mod_import.txt b/libgo/go/cmd/go/testdata/script/mod_import.txt index 28358b5b0c5..07714e92c72 100644 --- a/libgo/go/cmd/go/testdata/script/mod_import.txt +++ b/libgo/go/cmd/go/testdata/script/mod_import.txt @@ -1,7 +1,7 @@ env GO111MODULE=on # latest rsc.io/quote should be v1.5.2 not v1.5.3-pre1 -go get -d +go get go list -m all stdout 'rsc.io/quote v1.5.2' diff --git a/libgo/go/cmd/go/testdata/script/mod_import_v1suffix.txt b/libgo/go/cmd/go/testdata/script/mod_import_v1suffix.txt index a4294504661..75b3374bca0 100644 --- a/libgo/go/cmd/go/testdata/script/mod_import_v1suffix.txt +++ b/libgo/go/cmd/go/testdata/script/mod_import_v1suffix.txt @@ -1,6 +1,6 @@ env GO111MODULE=on -! go get -d example.com/invalidpath/v1 +! go get example.com/invalidpath/v1 ! go install . -- go.mod -- diff --git a/libgo/go/cmd/go/testdata/script/mod_in_testdata_dir.txt b/libgo/go/cmd/go/testdata/script/mod_in_testdata_dir.txt index 66f79faa6d9..866f7841b9f 100644 --- a/libgo/go/cmd/go/testdata/script/mod_in_testdata_dir.txt +++ b/libgo/go/cmd/go/testdata/script/mod_in_testdata_dir.txt @@ -9,7 +9,7 @@ cd $WORK/testdata go mod init testdata.tld/foo # Getting a package within that module should resolve its dependencies. -go get -d +go get grep 'rsc.io/quote' go.mod # Tidying the module should preserve those dependencies. diff --git a/libgo/go/cmd/go/testdata/script/mod_init_invalid_major.txt b/libgo/go/cmd/go/testdata/script/mod_init_invalid_major.txt new file mode 100644 index 00000000000..ae93e70d630 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_init_invalid_major.txt @@ -0,0 +1,82 @@ +env GO111MODULE=on +env GOFLAGS=-mod=mod + +! go mod init example.com/user/repo/v0 +stderr '(?s)^go: invalid module path "example.com/user/repo/v0": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$' + +! go mod init example.com/user/repo/v02 +stderr '(?s)^go: invalid module path "example.com/user/repo/v02": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$' + +! go mod init example.com/user/repo/v023 +stderr '(?s)^go: invalid module path "example.com/user/repo/v023": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v23$' + +! go mod init example.com/user/repo/v1 +stderr '(?s)^go: invalid module path "example.com/user/repo/v1": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$' + +! go mod init example.com/user/repo/v2.0 +stderr '(?s)^go: invalid module path "example.com/user/repo/v2.0": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$' + +! go mod init example.com/user/repo/v2.1.4 +stderr '(?s)^go: invalid module path "example.com/user/repo/v2.1.4": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$' + +! go mod init example.com/user/repo/v3.5 +stderr '(?s)^go: invalid module path "example.com/user/repo/v3.5": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v3$' + +! go mod init example.com/user/repo/v4.1.4 +stderr '(?s)^go: invalid module path "example.com/user/repo/v4.1.4": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v4$' + +! go mod init example.com/user/repo/v.2.3 +stderr '(?s)^go: invalid module path "example.com/user/repo/v.2.3": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$' + +! go mod init example.com/user/repo/v.5.3 +stderr '(?s)^go: invalid module path "example.com/user/repo/v.5.3": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v5$' + +! go mod init gopkg.in/pkg +stderr '(?s)^go: invalid module path "gopkg.in/pkg": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/pkg.v1$' + +! go mod init gopkg.in/user/pkg +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$' + +! go mod init gopkg.in/user/pkg/v0 +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg/v0": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$' + +! go mod init gopkg.in/user/pkg/v1 +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg/v1": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$' + +! go mod init gopkg.in/user/pkg/v2 +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg/v2": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v2$' + +! go mod init gopkg.in/user/pkg.v +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$' + +! go mod init gopkg.in/user/pkg.v0.1 +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v0.1": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$' + +! go mod init gopkg.in/user/pkg.v.1 +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v.1": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$' + +! go mod init gopkg.in/user/pkg.v01 +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v01": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$' + +! go mod init gopkg.in/user/pkg.v.2.3 +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v.2.3": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v2$' + +# module paths with a trailing dot are rejected as invalid import paths +! go mod init example.com/user/repo/v2. +stderr '(?s)^go: malformed module path "example.com/user/repo/v2.": trailing dot in path element$' + +! go mod init example.com/user/repo/v2.. +stderr '(?s)^go: malformed module path "example.com/user/repo/v2..": trailing dot in path element$' + +! go mod init gopkg.in/user/pkg.v.2. +stderr '(?s)^go: malformed module path "gopkg.in/user/pkg.v.2.": trailing dot in path element$' + +! go mod init gopkg.in/user/pkg.v.2.. +stderr '(?s)^go: malformed module path "gopkg.in/user/pkg.v.2..": trailing dot in path element$' + +# module paths with spaces are also rejected +! go mod init 'foo bar' +stderr '(?s)^go: malformed module path "foo bar": invalid char '' ''$' + +! go mod init 'foo bar baz' +stderr '(?s)^go: malformed module path "foo bar baz": invalid char '' ''$' diff --git a/libgo/go/cmd/go/testdata/script/mod_install_pkg_version.txt b/libgo/go/cmd/go/testdata/script/mod_install_pkg_version.txt index 367d34ba603..59d5c0a4e81 100644 --- a/libgo/go/cmd/go/testdata/script/mod_install_pkg_version.txt +++ b/libgo/go/cmd/go/testdata/script/mod_install_pkg_version.txt @@ -16,7 +16,7 @@ env GO111MODULE=auto cd m cp go.mod go.mod.orig ! go list -m all -stderr '^go list -m: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$' +stderr '^go: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$' go install example.com/cmd/a@latest cmp go.mod go.mod.orig exists $GOPATH/bin/a$GOEXE @@ -70,7 +70,7 @@ go mod edit -require=rsc.io/fortune@v1.0.0 stderr '^missing go\.sum entry for module providing package rsc\.io/fortune; to add:\n\tgo mod download rsc\.io/fortune$' ! go install -mod=readonly ../../pkg/mod/rsc.io/fortune@v1.0.0 stderr '^missing go\.sum entry for module providing package rsc\.io/fortune; to add:\n\tgo mod download rsc\.io/fortune$' -go get -d rsc.io/fortune@v1.0.0 +go get rsc.io/fortune@v1.0.0 go install -mod=readonly $GOPATH/pkg/mod/rsc.io/fortune@v1.0.0 exists $GOPATH/bin/fortune$GOEXE cd .. @@ -81,15 +81,15 @@ env GO111MODULE=auto # 'go install pkg@version' reports errors for meta packages, std packages, # and directories. ! go install std@v1.0.0 -stderr '^go install: std@v1.0.0: argument must be a package path, not a meta-package$' +stderr '^go: std@v1.0.0: argument must be a package path, not a meta-package$' ! go install fmt@v1.0.0 -stderr '^go install: fmt@v1.0.0: argument must not be a package in the standard library$' +stderr '^go: fmt@v1.0.0: argument must not be a package in the standard library$' ! go install example.com//cmd/a@v1.0.0 -stderr '^go install: example.com//cmd/a@v1.0.0: argument must be a clean package path$' +stderr '^go: example.com//cmd/a@v1.0.0: argument must be a clean package path$' ! go install example.com/cmd/a@v1.0.0 ./x@v1.0.0 -stderr '^go install: ./x@v1.0.0: argument must be a package path, not a relative path$' +stderr '^go: ./x@v1.0.0: argument must be a package path, not a relative path$' ! go install example.com/cmd/a@v1.0.0 $GOPATH/src/x@v1.0.0 -stderr '^go install: '$WORK'[/\\]gopath/src/x@v1.0.0: argument must be a package path, not an absolute path$' +stderr '^go: '$WORK'[/\\]gopath/src/x@v1.0.0: argument must be a package path, not an absolute path$' ! go install example.com/cmd/a@v1.0.0 cmd/...@v1.0.0 stderr '^package cmd/go not provided by module example.com/cmd@v1.0.0$' @@ -106,7 +106,7 @@ stdout '^example.com/cmd v1.0.0$' env GO111MODULE=auto ! go install example.com/cmd/a@v1.0.0 example.com/cmd/b@latest -stderr '^go install: example.com/cmd/b@latest: all arguments must have the same version \(@v1.0.0\)$' +stderr '^go: example.com/cmd/b@latest: all arguments must have the same version \(@v1.0.0\)$' # 'go install pkg@version' should report an error if the arguments are in @@ -125,7 +125,7 @@ stderr '^package example.com/cmd/err is not a main package$' mkdir tmp cd tmp go mod init m -go get -d example.com/cmd@v1.0.0 +go get example.com/cmd@v1.0.0 ! go build example.com/cmd/... stderr 'err[/\\]err.go:3:9: undefined: DoesNotCompile$' cd .. @@ -137,7 +137,7 @@ rm $GOPATH/bin # If a wildcard matches no packages, we should see a warning. ! go install example.com/cmd/nomatch...@v1.0.0 -stderr '^go install: example.com/cmd/nomatch\.\.\.@v1.0.0: module example.com/cmd@v1.0.0 found, but does not contain packages matching example.com/cmd/nomatch\.\.\.$' +stderr '^go: example.com/cmd/nomatch\.\.\.@v1.0.0: module example.com/cmd@v1.0.0 found, but does not contain packages matching example.com/cmd/nomatch\.\.\.$' go install example.com/cmd/a@v1.0.0 example.com/cmd/nomatch...@v1.0.0 stderr '^go: warning: "example.com/cmd/nomatch\.\.\." matched no packages$' @@ -159,7 +159,7 @@ cmp stderr exclude-err # 'go install pkg@version' should report an error if the module requires a # higher version of itself. ! go install example.com/cmd/a@v1.0.0-newerself -stderr '^go install: example.com/cmd/a@v1.0.0-newerself: version constraints conflict:\n\texample.com/cmd@v1.0.0-newerself requires example.com/cmd@v1.0.0, but example.com/cmd@v1.0.0-newerself is requested$' +stderr '^go: example.com/cmd/a@v1.0.0-newerself: version constraints conflict:\n\texample.com/cmd@v1.0.0-newerself requires example.com/cmd@v1.0.0, but example.com/cmd@v1.0.0-newerself is requested$' # 'go install pkg@version' will only match a retracted version if it's @@ -192,12 +192,12 @@ package main func main() {} -- replace-err -- -go install: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace): +go: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace): The go.mod file for the module providing named packages contains one or more replace directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module. -- exclude-err -- -go install: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude): +go: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude): The go.mod file for the module providing named packages contains one or more exclude directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module. diff --git a/libgo/go/cmd/go/testdata/script/mod_install_versioned.txt b/libgo/go/cmd/go/testdata/script/mod_install_versioned.txt index c6bce418b4f..627a9a81b0b 100644 --- a/libgo/go/cmd/go/testdata/script/mod_install_versioned.txt +++ b/libgo/go/cmd/go/testdata/script/mod_install_versioned.txt @@ -1,11 +1,11 @@ env GO111MODULE=on -go get -d rsc.io/fortune +go get rsc.io/fortune go list -f '{{.Target}}' rsc.io/fortune ! stdout fortune@v1 stdout 'fortune(\.exe)?$' -go get -d rsc.io/fortune/v2 +go get rsc.io/fortune/v2 go list -f '{{.Target}}' rsc.io/fortune/v2 ! stdout v2 stdout 'fortune(\.exe)?$' diff --git a/libgo/go/cmd/go/testdata/script/mod_internal.txt b/libgo/go/cmd/go/testdata/script/mod_internal.txt index 687269d18f6..787b21f379c 100644 --- a/libgo/go/cmd/go/testdata/script/mod_internal.txt +++ b/libgo/go/cmd/go/testdata/script/mod_internal.txt @@ -3,34 +3,34 @@ env GO111MODULE=on # golang.org/x/internal should be importable from other golang.org/x modules. go mod edit -module=golang.org/x/anything -go get -d . +go get . # ...and their tests... go test stdout PASS # ...but that should not leak into other modules. -go get -d ./baddep +go get ./baddep ! go build ./baddep stderr golang.org[/\\]notx[/\\]useinternal stderr 'use of internal package golang.org/x/.* not allowed' # Internal packages in the standard library should not leak into modules. -go get -d ./fromstd +go get ./fromstd ! go build ./fromstd stderr 'use of internal package internal/testenv not allowed' # Dependencies should be able to use their own internal modules... go mod edit -module=golang.org/notx -go get -d ./throughdep +go get ./throughdep # ... but other modules should not, even if they have transitive dependencies. -go get -d . +go get . ! go build . stderr 'use of internal package golang.org/x/.* not allowed' # And transitive dependencies still should not leak. -go get -d ./baddep +go get ./baddep ! go build ./baddep stderr golang.org[/\\]notx[/\\]useinternal stderr 'use of internal package golang.org/x/.* not allowed' @@ -38,17 +38,17 @@ stderr 'use of internal package golang.org/x/.* not allowed' # Replacing an internal module should keep it internal to the same paths. go mod edit -module=golang.org/notx go mod edit -replace golang.org/x/internal=./replace/golang.org/notx/internal -go get -d ./throughdep +go get ./throughdep -go get -d ./baddep +go get ./baddep ! go build ./baddep stderr golang.org[/\\]notx[/\\]useinternal stderr 'use of internal package golang.org/x/.* not allowed' go mod edit -replace golang.org/x/internal=./vendor/golang.org/x/internal -go get -d ./throughdep +go get ./throughdep -go get -d ./baddep +go get ./baddep ! go build ./baddep stderr golang.org[/\\]notx[/\\]useinternal stderr 'use of internal package golang.org/x/.* not allowed' diff --git a/libgo/go/cmd/go/testdata/script/mod_invalid_path.txt b/libgo/go/cmd/go/testdata/script/mod_invalid_path.txt index 333a3ffa35c..667b76e340a 100644 --- a/libgo/go/cmd/go/testdata/script/mod_invalid_path.txt +++ b/libgo/go/cmd/go/testdata/script/mod_invalid_path.txt @@ -29,9 +29,9 @@ stdout '^example.com/dotname/.dot$' go list ./use stdout '^example.com/dotname/use$' ! go list -m example.com/dotname/.dot@latest -stderr '^go list -m: example.com/dotname/.dot@latest: malformed module path "example.com/dotname/.dot": leading dot in path element$' -go get -d example.com/dotname/.dot -go get -d example.com/dotname/use +stderr '^go: example.com/dotname/.dot@latest: malformed module path "example.com/dotname/.dot": leading dot in path element$' +go get example.com/dotname/.dot +go get example.com/dotname/use go mod tidy -- mod/go.mod -- diff --git a/libgo/go/cmd/go/testdata/script/mod_invalid_path_dotname.txt b/libgo/go/cmd/go/testdata/script/mod_invalid_path_dotname.txt index 85934332d14..484c208f0f7 100644 --- a/libgo/go/cmd/go/testdata/script/mod_invalid_path_dotname.txt +++ b/libgo/go/cmd/go/testdata/script/mod_invalid_path_dotname.txt @@ -3,19 +3,19 @@ # 'go get' works with no version query. cp go.mod.empty go.mod -go get -d example.com/dotname/.dot +go get example.com/dotname/.dot go list -m example.com/dotname stdout '^example.com/dotname v1.0.0$' # 'go get' works with a version query. cp go.mod.empty go.mod -go get -d example.com/dotname/.dot@latest +go get example.com/dotname/.dot@latest go list -m example.com/dotname stdout '^example.com/dotname v1.0.0$' # 'go get' works on an importing package. cp go.mod.empty go.mod -go get -d . +go get . go list -m example.com/dotname stdout '^example.com/dotname v1.0.0$' diff --git a/libgo/go/cmd/go/testdata/script/mod_invalid_path_plus.txt b/libgo/go/cmd/go/testdata/script/mod_invalid_path_plus.txt index 51dbf936888..dd59eb1feda 100644 --- a/libgo/go/cmd/go/testdata/script/mod_invalid_path_plus.txt +++ b/libgo/go/cmd/go/testdata/script/mod_invalid_path_plus.txt @@ -4,16 +4,16 @@ # 'go list' accepts package paths with pluses. cp go.mod.orig go.mod -go get -d example.net/cmd +go get example.net/cmd go list example.net/cmd/x++ # 'go list -m' rejects module paths with pluses. ! go list -versions -m 'example.net/bad++' -stderr '^go list -m: malformed module path "example.net/bad\+\+": invalid char ''\+''$' +stderr '^go: malformed module path "example.net/bad\+\+": invalid char ''\+''$' # 'go get' accepts package paths with pluses. cp go.mod.orig go.mod -go get -d example.net/cmd/x++ +go get example.net/cmd/x++ go list -m example.net/cmd stdout '^example.net/cmd v0.0.0-00010101000000-000000000000 => ./cmd$' diff --git a/libgo/go/cmd/go/testdata/script/mod_invalid_version.txt b/libgo/go/cmd/go/testdata/script/mod_invalid_version.txt index 6846a792a5d..428b8aa60e6 100644 --- a/libgo/go/cmd/go/testdata/script/mod_invalid_version.txt +++ b/libgo/go/cmd/go/testdata/script/mod_invalid_version.txt @@ -19,7 +19,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "14c0d48ead0c" invalid: must be of the form v1.2.3' +stderr 'go: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "14c0d48ead0c" invalid: must be of the form v1.2.3' cd .. go list -m golang.org/x/text stdout 'golang.org/x/text v0.1.1-0.20170915032832-14c0d48ead0c' @@ -30,14 +30,14 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c: invalid version: missing golang.org/x/text/unicode/go.mod at revision 14c0d48ead0c' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c: invalid version: missing golang.org/x/text/unicode/go.mod at revision 14c0d48ead0c' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c: invalid version: missing golang.org/x/text/unicode/go.mod at revision 14c0d48ead0c' # However, arguments to 'go get' can name packages above the root. cp go.mod.orig go.mod -go get -d golang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c +go get golang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c go list -m golang.org/x/text/... stdout 'golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c' ! stdout 'golang.org/x/text/unicode' @@ -47,7 +47,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v2.1.1-0.20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2' +stderr 'go: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2' cd .. ! go list -m golang.org/x/text stderr $WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2' @@ -57,27 +57,27 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0 cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(14c0d48ead0c\)' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(expected 14c0d48ead0c\)' cd .. ! go list -m golang.org/x/text -stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(14c0d48ead0c\)' +stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(expected 14c0d48ead0c\)' # A pseudo-version with more than 12 digits of SHA-1 prefix is invalid. cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(14c0d48ead0c\)' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(expected 14c0d48ead0c\)' cd .. ! go list -m golang.org/x/text -stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(14c0d48ead0c\)' +stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(expected 14c0d48ead0c\)' # A pseudo-version that does not match the commit timestamp is invalid. cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)' @@ -87,7 +87,7 @@ stderr 'golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-v go mod edit -replace golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c=golang.org/x/text@14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)' cd .. go list -m golang.org/x/text stdout 'golang.org/x/text v0.1.1-0.20190915032832-14c0d48ead0c => golang.org/x/text v0.1.1-0.20170915032832-14c0d48ead0c' @@ -97,7 +97,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c: invalid pseudo-version: preceding tag \(v1.999.998\) not found' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c: invalid pseudo-version: preceding tag \(v1.999.998\) not found' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c: invalid pseudo-version: preceding tag \(v1.999.998\) not found' @@ -109,7 +109,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c: invalid pseudo-version: major version without preceding tag must be v0, not v1' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c: invalid pseudo-version: major version without preceding tag must be v0, not v1' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c: invalid pseudo-version: major version without preceding tag must be v0, not v1' @@ -120,7 +120,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' @@ -130,7 +130,7 @@ stderr 'golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-v go mod edit -replace golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c=golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' cd .. go list -m golang.org/x/text stdout 'golang.org/x/text v0.0.0-0.20170915032832-14c0d48ead0c => golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c' @@ -141,10 +141,10 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v1.999999.0 go mod edit -replace golang.org/x/text@v1.999999.0=golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c cd outside -! go get -d golang.org/x/text@upgrade +! go get golang.org/x/text@upgrade stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.999999.0: reading golang.org/x/text/go.mod at revision v1.999999.0: unknown revision v1.999999.0' cd .. -go get -d golang.org/x/text@upgrade +go get golang.org/x/text@upgrade go list -m golang.org/x/text stdout 'golang.org/x/text v1.999999.0 => golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c' @@ -153,7 +153,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c: invalid pseudo-version: revision 14c0d48ead0c is not a descendent of preceding tag \(v0.2.0\)' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c: invalid pseudo-version: revision 14c0d48ead0c is not a descendent of preceding tag \(v0.2.0\)' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c: invalid pseudo-version: revision 14c0d48ead0c is not a descendent of preceding tag \(v0.2.0\)' @@ -163,7 +163,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac: invalid pseudo-version: tag \(v0.2.0\) found on revision c4d099d611ac is already canonical, so should not be replaced with a pseudo-version derived from that tag' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac: invalid pseudo-version: tag \(v0.2.0\) found on revision c4d099d611ac is already canonical, so should not be replaced with a pseudo-version derived from that tag' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac: invalid pseudo-version: tag \(v0.2.0\) found on revision c4d099d611ac is already canonical, so should not be replaced with a pseudo-version derived from that tag' @@ -173,7 +173,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c+incompatible cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c\+incompatible: invalid version: \+incompatible suffix not allowed: major version v0 is compatible' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c\+incompatible: invalid version: \+incompatible suffix not allowed: major version v0 is compatible' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c\+incompatible: invalid version: \+incompatible suffix not allowed: major version v0 is compatible' @@ -194,7 +194,7 @@ cp go.mod.orig go.mod go mod edit -require github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d+incompatible cd outside ! go list -m github.com/pierrec/lz4 -stderr 'go list -m: example.com@v0.0.0 requires\n\tgithub.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required' +stderr 'go: example.com@v0.0.0 requires\n\tgithub.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required' cd .. ! go list -m github.com/pierrec/lz4 stderr 'github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required' @@ -214,15 +214,15 @@ stdout 'github.com/pierrec/lz4 v2.0.4-0.20180826165652-dbe9298ce099\+incompatibl # to the equivalent +incompatible version, not a pseudo-version with a different # major version. cp go.mod.orig go.mod -go get -d github.com/pierrec/lz4@v2.0.5 +go get github.com/pierrec/lz4@v2.0.5 go list -m github.com/pierrec/lz4 stdout 'github.com/pierrec/lz4 v2.0.5\+incompatible' # 'go get' for a mismatched major version with a go.mod file should error out, # not resolve to a pseudo-version with a different major version. cp go.mod.orig go.mod -! go get -d github.com/pierrec/lz4@v2.0.8 -stderr 'go get: github.com/pierrec/lz4@v2.0.8: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2' +! go get github.com/pierrec/lz4@v2.0.8 +stderr 'go: github.com/pierrec/lz4@v2.0.8: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2' # An invalid +incompatible suffix for a canonical version should error out, # not resolve to a pseudo-version. diff --git a/libgo/go/cmd/go/testdata/script/mod_issue35317.txt b/libgo/go/cmd/go/testdata/script/mod_issue35317.txt index b1852ab0319..92416a54e47 100644 --- a/libgo/go/cmd/go/testdata/script/mod_issue35317.txt +++ b/libgo/go/cmd/go/testdata/script/mod_issue35317.txt @@ -5,4 +5,4 @@ env GO111MODULE=on [short] skip go mod init example.com -go get -d golang.org/x/text@v0.3.0 golang.org/x/internal@v0.1.0 golang.org/x/exp@none +go get golang.org/x/text@v0.3.0 golang.org/x/internal@v0.1.0 golang.org/x/exp@none diff --git a/libgo/go/cmd/go/testdata/script/mod_lazy_downgrade.txt b/libgo/go/cmd/go/testdata/script/mod_lazy_downgrade.txt index 2f815fef22f..eb69d2eb8f8 100644 --- a/libgo/go/cmd/go/testdata/script/mod_lazy_downgrade.txt +++ b/libgo/go/cmd/go/testdata/script/mod_lazy_downgrade.txt @@ -27,7 +27,7 @@ stdout '^example.com/c v0.2.0 ' # Downgrading c should also downgrade the b that requires it. -go get -d example.com/c@v0.1.0 +go get example.com/c@v0.1.0 go list -m all stdout '^example.com/a v0.1.0 ' stdout '^example.com/b v0.2.0 ' @@ -35,7 +35,7 @@ stdout '^example.com/c v0.1.0 ' # Removing c entirely should also remove the a and b that require it. -go get -d example.com/c@none +go get example.com/c@none go list -m all ! stdout '^example.com/a ' ! stdout '^example.com/b ' @@ -53,7 +53,7 @@ stdout '^example.com/a v0.1.0 ' stdout '^example.com/b v0.3.0 ' stdout '^example.com/c v0.2.0 ' -go get -d example.com/c@v0.1.0 +go get example.com/c@v0.1.0 go list -m all stdout '^example.com/a v0.1.0 ' stdout '^example.com/b v0.2.0 ' @@ -63,7 +63,7 @@ stdout '^example.com/c v0.1.0 ' # is still tracked, and it will still be downgraded away if we remove c. # ('go get' never makes a root into a non-root. Only 'go mod tidy' does that.) -go get -d example.com/c@none +go get example.com/c@none go list -m all ! stdout '^example.com/a ' ! stdout '^example.com/b ' @@ -84,7 +84,7 @@ stdout '^example.com/a v0.1.0 ' stdout '^example.com/b v0.3.0 ' stdout '^example.com/c v0.2.0 ' -go get -d example.com/c@v0.1.0 example.com/b@v0.1.0 +go get example.com/c@v0.1.0 example.com/b@v0.1.0 go list -m all stdout '^example.com/a v0.1.0 ' stdout '^example.com/b v0.1.0 ' @@ -96,7 +96,7 @@ stdout '^example.com/a v0.1.0 ' stdout '^example.com/b v0.1.0 ' ! stdout '^example.com/c ' -go get -d example.com/c@none +go get example.com/c@none go list -m all stdout '^example.com/a v0.1.0' stdout '^example.com/b v0.1.0' diff --git a/libgo/go/cmd/go/testdata/script/mod_lazy_import_allmod.txt b/libgo/go/cmd/go/testdata/script/mod_lazy_import_allmod.txt index 97718c4513b..60d4187b117 100644 --- a/libgo/go/cmd/go/testdata/script/mod_lazy_import_allmod.txt +++ b/libgo/go/cmd/go/testdata/script/mod_lazy_import_allmod.txt @@ -66,7 +66,7 @@ stdout '^b v0.1.0 ' ! stdout '^c ' # After adding a new direct import of b/y, -# the existing verison of b should be promoted to a root, +# the existing version of b should be promoted to a root, # bringing the version of c required by b into the build list. cp m.go.new m.go diff --git a/libgo/go/cmd/go/testdata/script/mod_lazy_new_import.txt b/libgo/go/cmd/go/testdata/script/mod_lazy_new_import.txt index 4272a52de1c..520d8459cc1 100644 --- a/libgo/go/cmd/go/testdata/script/mod_lazy_new_import.txt +++ b/libgo/go/cmd/go/testdata/script/mod_lazy_new_import.txt @@ -7,7 +7,7 @@ # \ # ---- a/y (new) ---- c # -# Where a/x and x/y are disjoint packages, but both contained in module a. +# Where a/x and a/y are disjoint packages, but both contained in module a. # # The module dependency graph initially looks like: # diff --git a/libgo/go/cmd/go/testdata/script/mod_list.txt b/libgo/go/cmd/go/testdata/script/mod_list.txt index 239c7caa4a2..06316cc335e 100644 --- a/libgo/go/cmd/go/testdata/script/mod_list.txt +++ b/libgo/go/cmd/go/testdata/script/mod_list.txt @@ -39,8 +39,8 @@ stdout '^module nonexist: not a known dependency$' stdout '^module rsc.io/quote/buggy: not a known dependency$' ! go list -m nonexist rsc.io/quote/buggy -stderr '^go list -m: module nonexist: not a known dependency' -stderr '^go list -m: module rsc.io/quote/buggy: not a known dependency' +stderr '^go: module nonexist: not a known dependency' +stderr '^go: module rsc.io/quote/buggy: not a known dependency' # Module loader does not interfere with list -e (golang.org/issue/24149). go list -e -f '{{.Error.Err}}' database diff --git a/libgo/go/cmd/go/testdata/script/mod_list_command_line_arguments.txt b/libgo/go/cmd/go/testdata/script/mod_list_command_line_arguments.txt new file mode 100644 index 00000000000..fd99ae84b2e --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_list_command_line_arguments.txt @@ -0,0 +1,35 @@ +# The command-line-arguments package does not belong to a module... +cd a +go list -f '{{.Module}}' ../b/b.go +stdout '^<nil>$' + +# ... even if the arguments are sources from that module +go list -f '{{.Module}}' a.go +stdout '^<nil>$' + +[short] skip + +# check that the version of command-line-arguments doesn't include a module +go build -o a.exe a.go +go version -m a.exe +stdout '^\tpath\tcommand-line-arguments$' +stdout '^\tdep\ta\t\(devel\)\t$' +! stdout mod + +-- a/go.mod -- +module a +go 1.17 +-- a/a.go -- +package main + +import "a/dep" + +func main() { + dep.D() +} +-- a/dep/dep.go -- +package dep + +func D() {} +-- b/b.go -- +package b
\ No newline at end of file diff --git a/libgo/go/cmd/go/testdata/script/mod_list_compiled_concurrent.txt b/libgo/go/cmd/go/testdata/script/mod_list_compiled_concurrent.txt index b08713dcfd7..896bbab9fcc 100644 --- a/libgo/go/cmd/go/testdata/script/mod_list_compiled_concurrent.txt +++ b/libgo/go/cmd/go/testdata/script/mod_list_compiled_concurrent.txt @@ -1,6 +1,7 @@ env GO111MODULE=on [short] skip +[!cgo] skip # Regression test for golang.org/issue/29667: # spurious 'failed to cache compiled Go files' errors. diff --git a/libgo/go/cmd/go/testdata/script/mod_list_deprecated.txt b/libgo/go/cmd/go/testdata/script/mod_list_deprecated.txt index f0ecbba2cea..ee985cccbf3 100644 --- a/libgo/go/cmd/go/testdata/script/mod_list_deprecated.txt +++ b/libgo/go/cmd/go/testdata/script/mod_list_deprecated.txt @@ -20,7 +20,7 @@ stdout '^in example.com/deprecated/a@v1.9.0$' # This works even if we use an old version that does not have the deprecation # message in its go.mod file. -go get -d example.com/deprecated/a@v1.0.0 +go get example.com/deprecated/a@v1.0.0 ! grep Deprecated: $WORK/gopath/pkg/mod/cache/download/example.com/deprecated/a/@v/v1.0.0.mod go list -m -u -f {{.Deprecated}} example.com/deprecated/a stdout '^in example.com/deprecated/a@v1.9.0$' diff --git a/libgo/go/cmd/go/testdata/script/mod_list_dir.txt b/libgo/go/cmd/go/testdata/script/mod_list_dir.txt index 1adab8f027d..7ad65ffbc73 100644 --- a/libgo/go/cmd/go/testdata/script/mod_list_dir.txt +++ b/libgo/go/cmd/go/testdata/script/mod_list_dir.txt @@ -3,7 +3,7 @@ # go list with path to directory should work # populate go.sum -go get -d +go get env GO111MODULE=off go list -f '{{.ImportPath}}' $GOROOT/src/math @@ -20,7 +20,7 @@ go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/quote@v1.5.2 stdout '^rsc.io/quote$' go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/sampler@v1.3.0 stdout '^rsc.io/sampler$' -go get -d rsc.io/sampler@v1.3.1 +go get rsc.io/sampler@v1.3.1 go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/sampler@v1.3.1 stdout '^rsc.io/sampler$' ! go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/sampler@v1.3.0 diff --git a/libgo/go/cmd/go/testdata/script/mod_list_direct.txt b/libgo/go/cmd/go/testdata/script/mod_list_direct.txt index 62a472f475e..9b7a04c5044 100644 --- a/libgo/go/cmd/go/testdata/script/mod_list_direct.txt +++ b/libgo/go/cmd/go/testdata/script/mod_list_direct.txt @@ -10,7 +10,7 @@ env GOSUMDB=off # For a while, (*modfetch.codeRepo).Stat was not checking for a go.mod file, # which would produce a hard error at the subsequent call to GoMod. -go get -d +go get -- go.mod -- module example.com diff --git a/libgo/go/cmd/go/testdata/script/mod_list_replace_dir.txt b/libgo/go/cmd/go/testdata/script/mod_list_replace_dir.txt index f2f2d2b2bb2..eac5ca7dd32 100644 --- a/libgo/go/cmd/go/testdata/script/mod_list_replace_dir.txt +++ b/libgo/go/cmd/go/testdata/script/mod_list_replace_dir.txt @@ -3,7 +3,7 @@ # Verifies golang.org/issue/29548 # Populate go.sum and download dependencies. -go get -d +go get # Ensure v1.5.2 is also in the cache so we can list it. go mod download rsc.io/quote@v1.5.2 diff --git a/libgo/go/cmd/go/testdata/script/mod_list_retract.txt b/libgo/go/cmd/go/testdata/script/mod_list_retract.txt index 4b133485152..b7147aa1824 100644 --- a/libgo/go/cmd/go/testdata/script/mod_list_retract.txt +++ b/libgo/go/cmd/go/testdata/script/mod_list_retract.txt @@ -101,7 +101,9 @@ module example.com/use go 1.15 require example.com/retract v1.0.0-bad - +-- go.sum -- +example.com/retract v1.0.0-bad h1:liAW69rbtjY67x2CcNzat668L/w+YGgNX3lhJsWIJis= +example.com/retract v1.0.0-bad/go.mod h1:0DvGGofJ9hr1q63cBrOY/jSY52OwhRGA0K47NE80I5Y= -- use.go -- package use diff --git a/libgo/go/cmd/go/testdata/script/mod_list_sums.txt b/libgo/go/cmd/go/testdata/script/mod_list_sums.txt index 86c528f8290..6c2f57c2b2d 100644 --- a/libgo/go/cmd/go/testdata/script/mod_list_sums.txt +++ b/libgo/go/cmd/go/testdata/script/mod_list_sums.txt @@ -29,4 +29,4 @@ stderr '^go: updates to go.sum needed, disabled by -mod=readonly$' # # TODO(#41297): This should not be an error either. ! go list -m -mod=readonly -versions rsc.io/sampler -stderr '^go list -m: rsc\.io/quote@v1\.5\.1: missing go\.sum entry; to add it:\n\tgo mod download rsc\.io/quote$' +stderr '^go: rsc\.io/quote@v1\.5\.1: missing go\.sum entry; to add it:\n\tgo mod download rsc\.io/quote$' diff --git a/libgo/go/cmd/go/testdata/script/mod_list_update_nolatest.txt b/libgo/go/cmd/go/testdata/script/mod_list_update_nolatest.txt index c6bbbb04ec7..7eebe266dbb 100644 --- a/libgo/go/cmd/go/testdata/script/mod_list_update_nolatest.txt +++ b/libgo/go/cmd/go/testdata/script/mod_list_update_nolatest.txt @@ -26,7 +26,7 @@ stdout '^example.com/nolatest v0.0.0$' # If proxy returns an invalid response, we should see an error. env GOPROXY=$testproxy/invalid ! go list -m -u example.com/nolatest -stderr '^go list -m: loading module retractions for example.com/nolatest@v0.0.0: invalid response from proxy "[^"]*": invalid character ''i'' looking for beginning of value$' +stderr '^go: loading module retractions for example.com/nolatest@v0.0.0: invalid response from proxy "[^"]*": invalid character ''i'' looking for beginning of value$' -- go.mod -- module m diff --git a/libgo/go/cmd/go/testdata/script/mod_list_upgrade_pseudo.txt b/libgo/go/cmd/go/testdata/script/mod_list_upgrade_pseudo.txt index b983bec73db..8e51dfcd2aa 100644 --- a/libgo/go/cmd/go/testdata/script/mod_list_upgrade_pseudo.txt +++ b/libgo/go/cmd/go/testdata/script/mod_list_upgrade_pseudo.txt @@ -12,11 +12,11 @@ env GO111MODULE=on # The latest pseudo-version is semantically higher than the latest tag. # 'list -u' should not suggest a lower version as an upgrade. -go get -d example.com/pseudoupgrade@b5426c8 +go get example.com/pseudoupgrade@b5426c8 go list -m -u all stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$' -go get -d example.com/pseudoupgrade@v0.0.0-20190430073000-30950c05d534 +go get example.com/pseudoupgrade@v0.0.0-20190430073000-30950c05d534 go list -m -u all stdout '^example.com/pseudoupgrade v0.0.0-20190430073000-30950c05d534$' diff --git a/libgo/go/cmd/go/testdata/script/mod_load_badchain.txt b/libgo/go/cmd/go/testdata/script/mod_load_badchain.txt index eb464ab0d3b..be2a4bc1db2 100644 --- a/libgo/go/cmd/go/testdata/script/mod_load_badchain.txt +++ b/libgo/go/cmd/go/testdata/script/mod_load_badchain.txt @@ -10,13 +10,13 @@ go mod download example.com/badchain/b@v1.1.0 go mod download example.com/badchain/c@v1.1.0 # Try to update example.com/badchain/a (and its dependencies). -! go get -d example.com/badchain/a +! go get example.com/badchain/a cmp stderr update-a-expected cmp go.mod go.mod.orig # Try to update the main module. This updates everything, including # modules that aren't direct requirements, so the error stack is shorter. -! go get -d -u ./... +! go get -u ./... cmp stderr update-main-expected cmp go.mod go.mod.orig @@ -69,17 +69,17 @@ import ( func Test(t *testing.T) {} -- update-main-expected -- -go get: example.com/badchain/c@v1.1.0: parsing go.mod: +go: example.com/badchain/c@v1.1.0: parsing go.mod: module declares its path as: badchain.example.com/c but was required as: example.com/badchain/c -- update-a-expected -- -go get: example.com/badchain/a@v1.1.0 requires +go: example.com/badchain/a@v1.1.0 requires example.com/badchain/b@v1.1.0 requires example.com/badchain/c@v1.1.0: parsing go.mod: module declares its path as: badchain.example.com/c but was required as: example.com/badchain/c -- list-expected -- -go list -m: example.com/badchain/a@v1.1.0 requires +go: example.com/badchain/a@v1.1.0 requires example.com/badchain/b@v1.1.0 requires example.com/badchain/c@v1.1.0: parsing go.mod: module declares its path as: badchain.example.com/c diff --git a/libgo/go/cmd/go/testdata/script/mod_load_badzip.txt b/libgo/go/cmd/go/testdata/script/mod_load_badzip.txt index 65374d2a6d1..58160b4d442 100644 --- a/libgo/go/cmd/go/testdata/script/mod_load_badzip.txt +++ b/libgo/go/cmd/go/testdata/script/mod_load_badzip.txt @@ -1,7 +1,7 @@ # Zip files with unexpected file names inside should be rejected. env GO111MODULE=on -! go get -d rsc.io/badzip +! go get rsc.io/badzip stderr 'zip for rsc.io/badzip@v1.0.0 has unexpected file rsc.io/badzip@v1.0.0.txt' ! grep rsc.io/badzip go.mod diff --git a/libgo/go/cmd/go/testdata/script/mod_missing_repo.txt b/libgo/go/cmd/go/testdata/script/mod_missing_repo.txt index 8dae85fa88d..b91a8dbedac 100644 --- a/libgo/go/cmd/go/testdata/script/mod_missing_repo.txt +++ b/libgo/go/cmd/go/testdata/script/mod_missing_repo.txt @@ -9,7 +9,7 @@ env GO111MODULE=on env GOPROXY=direct env GOSUMDB=off -! go get -d vcs-test.golang.org/go/missingrepo/missingrepo-git +! go mod download vcs-test.golang.org/go/missingrepo/missingrepo-git@latest stderr 'vcs-test.golang.org/go/missingrepo/missingrepo-git: git ls-remote .*: exit status .*' -go get -d vcs-test.golang.org/go/missingrepo/missingrepo-git/notmissing +go mod download vcs-test.golang.org/go/missingrepo/missingrepo-git/notmissing@latest diff --git a/libgo/go/cmd/go/testdata/script/mod_multirepo.txt b/libgo/go/cmd/go/testdata/script/mod_multirepo.txt index 0f335a11f0f..bbefb78d90a 100644 --- a/libgo/go/cmd/go/testdata/script/mod_multirepo.txt +++ b/libgo/go/cmd/go/testdata/script/mod_multirepo.txt @@ -7,7 +7,7 @@ go list -deps -f {{.Dir}} # v2 import should use a downloaded module # both without an explicit go.mod entry ... cp tmp/use_v2.go x.go -go get -d . +go get . go list -deps -f {{.Dir}} stdout 'pkg[\\/]mod[\\/]rsc.io[\\/]quote[\\/]v2@v2.0.1$' diff --git a/libgo/go/cmd/go/testdata/script/mod_no_gopath.txt b/libgo/go/cmd/go/testdata/script/mod_no_gopath.txt new file mode 100644 index 00000000000..ed91f5d42e5 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_no_gopath.txt @@ -0,0 +1,15 @@ +# https://golang.org/issue/43938: 'go build' should succeed +# if GOPATH and the variables needed for its default value +# are all unset but not relevant to the specific command. + +env HOME='' +env home='' +env GOPATH='' + +go list -deps main.go +stdout '^io$' + +-- main.go -- +package main + +import _ "io" diff --git a/libgo/go/cmd/go/testdata/script/mod_outside.txt b/libgo/go/cmd/go/testdata/script/mod_outside.txt index 33341f7d4b3..f88e2ae7efa 100644 --- a/libgo/go/cmd/go/testdata/script/mod_outside.txt +++ b/libgo/go/cmd/go/testdata/script/mod_outside.txt @@ -123,30 +123,30 @@ stderr '^go: go.mod file not found in current directory or any parent directory; stderr '^go: go.mod file not found in current directory or any parent directory; see ''go help modules''$' -# 'go get' without arguments implicitly operates on the main module, and thus -# should fail. +# 'go get' has no go.mod file to update outside a module and should fail. ! go get -stderr '^go: go.mod file not found in current directory or any parent directory; see ''go help modules''$' +stderr '^go: go.mod file not found in current directory or any parent directory.$' +stderr '^\t''go get'' is no longer supported outside a module.$' ! go get -u -stderr '^go: go.mod file not found in current directory or any parent directory; see ''go help modules''$' +stderr '^go: go.mod file not found in current directory or any parent directory.$' +stderr '^\t''go get'' is no longer supported outside a module.$' ! go get -u ./needmod -stderr '^go: go.mod file not found in current directory or any parent directory; see ''go help modules''$' - -# 'go get -u all' upgrades the transitive import graph of the main module, -# which is empty. +stderr '^go: go.mod file not found in current directory or any parent directory.$' +stderr '^\t''go get'' is no longer supported outside a module.$' ! go get -u all -stderr '^go get: cannot match "all": go.mod file not found in current directory or any parent directory; see ''go help modules''$' - -# 'go get' should check the proposed module graph for consistency, -# even though we won't write it anywhere. -! go get -d example.com/printversion@v1.0.0 example.com/version@none -stderr '^go get: example.com/printversion@v1.0.0 requires example.com/version@v1.0.0, not example.com/version@none$' +stderr '^go: go.mod file not found in current directory or any parent directory.$' +stderr '^\t''go get'' is no longer supported outside a module.$' +! go get example.com/printversion@v1.0.0 example.com/version@none +stderr '^go: go.mod file not found in current directory or any parent directory.$' +stderr '^\t''go get'' is no longer supported outside a module.$' -# 'go get -d' should download and extract the source code needed to build the requested version. -rm -r $GOPATH/pkg/mod/example.com -go get -d example.com/printversion@v1.0.0 -exists $GOPATH/pkg/mod/example.com/printversion@v1.0.0 -exists $GOPATH/pkg/mod/example.com/version@v1.0.0 +# 'go get' should not download anything. +go clean -modcache +! go get example.com/printversion@v1.0.0 +stderr '^go: go.mod file not found in current directory or any parent directory.$' +stderr '^\t''go get'' is no longer supported outside a module.$' +! exists $GOPATH/pkg/mod/example.com/printversion@v1.0.0 +! exists $GOPATH/pkg/mod/example.com/version@v1.0.0 # 'go build' without arguments implicitly operates on the current directory, and should fail. @@ -196,7 +196,7 @@ exists $GOPATH/bin/printversion$GOEXE # 'go install' should fail if a package argument must be resolved to a module. ! go install example.com/printversion -stderr '^go install: version is required when current directory is not in a module\n\tTry ''go install example.com/printversion@latest'' to install the latest version$' +stderr '^go: ''go install'' requires a version when current directory is not in a module\n\tTry ''go install example.com/printversion@latest'' to install the latest version$' # 'go install' should fail if a source file imports a package that must be # resolved to a module. @@ -223,35 +223,11 @@ go fmt needmod/needmod.go # The remainder of the test checks dependencies by linking and running binaries. -# 'go get' of a binary without a go.mod should install the requested version, -# resolving outside dependencies to the latest available versions. -go get example.com/printversion@v0.1.0 -exec ../bin/printversion -stdout 'path is example.com/printversion' -stdout 'main is example.com/printversion v0.1.0' -stdout 'using example.com/version v1.1.0' - -# 'go get' of a versioned binary should build and install the latest version -# using its minimal required modules, ignoring replacements and exclusions. -go get example.com/printversion -exec ../bin/printversion -stdout 'path is example.com/printversion' -stdout 'main is example.com/printversion v1.0.0' -stdout 'using example.com/version v1.0.0' - -# 'go get -u=patch' should patch dependencies before installing, -# again ignoring replacements and exclusions. -go get -u=patch example.com/printversion@v1.0.0 -exec ../bin/printversion -stdout 'path is example.com/printversion' -stdout 'main is example.com/printversion v1.0.0' -stdout 'using example.com/version v1.0.1' - # 'go run' should work with file arguments if they don't import anything # outside std. go run ./stdonly/stdonly.go stdout 'path is command-line-arguments$' -stdout 'main is command-line-arguments \(devel\)' +stdout 'main is $' # 'go generate' should work with file arguments. [exec:touch] go generate ./needmod/needmod.go diff --git a/libgo/go/cmd/go/testdata/script/mod_overlay.txt b/libgo/go/cmd/go/testdata/script/mod_overlay.txt index 86ab04bd3cf..da35be6a196 100644 --- a/libgo/go/cmd/go/testdata/script/mod_overlay.txt +++ b/libgo/go/cmd/go/testdata/script/mod_overlay.txt @@ -20,7 +20,7 @@ go list -deps -overlay overlay.json . # Overlaid go.mod is not rewritten by 'go get'. cd $WORK/gopath/src/get-doesnt-add-dep cp $WORK/overlay/get_doesnt_add_dep_go_mod $WORK/want_go_mod -! go get -d -overlay overlay.json . +! go get -overlay overlay.json . stderr '^go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay$' cmp $WORK/overlay/get_doesnt_add_dep_go_mod $WORK/want_go_mod @@ -30,17 +30,17 @@ cmp $WORK/overlay/get_doesnt_add_dep_go_mod $WORK/want_go_mod # the correct go.sum is used with the overlay, 'go get .' should # not report a security error. cd $WORK/gopath/src/overlay-sum-used -! go get -d . +! go get . stderr 'SECURITY ERROR' ! go mod verify stderr 'SECURITY ERROR' -go get -d -overlay overlay.json . +go get -overlay overlay.json . go mod verify -overlay overlay.json # Overlaid go.sum is not rewritten. # Copy an incomplete file to the overlay file, and expect an error # attempting to update the file cp incomplete-sum-file $WORK/overlay/overlay-sum-used-correct-sums -! go get -d -overlay overlay.json . +! go get -overlay overlay.json . stderr '^go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay$' cmp incomplete-sum-file $WORK/overlay/overlay-sum-used-correct-sums ! go mod tidy -overlay overlay.json @@ -55,7 +55,7 @@ cd $WORK/gopath/src/overlay-and-dash-modfile go list -modfile=alternate.mod -overlay overlay.json . stdout 'found.the/module' # Even with -modfile, overlaid files can't be opened for write. -! go get -modfile=alternate.mod -overlay overlay.json -d rsc.io/quote +! go get -modfile=alternate.mod -overlay overlay.json rsc.io/quote stderr '^go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay$' # Carving out a module by adding an overlaid go.mod file @@ -77,11 +77,11 @@ go list -overlay overlay.json all ! stdout ^carve2$ stdout ^carve2/nomod$ # Editing go.mod file fails because overlay is read only -! go get -overlay overlay.json -d rsc.io/quote +! go get -overlay overlay.json rsc.io/quote stderr '^go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay$' ! grep rsc.io/quote $WORK/overlay/carve2-nomod-go.mod # Editing go.mod file succeeds because we use -modfile to redirect to same file -go get -overlay overlay.json -modfile $WORK/overlay/carve2-nomod-go.mod -d rsc.io/quote +go get -overlay overlay.json -modfile $WORK/overlay/carve2-nomod-go.mod rsc.io/quote grep rsc.io/quote $WORK/overlay/carve2-nomod-go.mod -- no-go-mod/file.go -- diff --git a/libgo/go/cmd/go/testdata/script/mod_permissions.txt b/libgo/go/cmd/go/testdata/script/mod_permissions.txt index 2d32dcd10fd..77e2508cb7f 100644 --- a/libgo/go/cmd/go/testdata/script/mod_permissions.txt +++ b/libgo/go/cmd/go/testdata/script/mod_permissions.txt @@ -12,7 +12,7 @@ chmod 0640 go.mod chmod 0604 go.sum go mod edit -module=golang.org/issue/34634 -go get -d +go get cmp go.mod go.mod.want cmp go.sum go.sum.want diff --git a/libgo/go/cmd/go/testdata/script/mod_prefer_compatible.txt b/libgo/go/cmd/go/testdata/script/mod_prefer_compatible.txt index 1b408c3e9e9..8e88997a3c3 100644 --- a/libgo/go/cmd/go/testdata/script/mod_prefer_compatible.txt +++ b/libgo/go/cmd/go/testdata/script/mod_prefer_compatible.txt @@ -24,7 +24,7 @@ go list -m github.com/russross/blackfriday@upgrade stdout '^github.com/russross/blackfriday v1\.' ! go list -m github.com/russross/blackfriday@patch -stderr '^go list -m: github.com/russross/blackfriday@patch: can''t query version "patch" of module github.com/russross/blackfriday: no existing version is required$' +stderr '^go: github.com/russross/blackfriday@patch: can''t query version "patch" of module github.com/russross/blackfriday: no existing version is required$' # If we're fetching directly from version control, ignored +incompatible # versions should also be omitted by 'go list'. diff --git a/libgo/go/cmd/go/testdata/script/mod_proxy_invalid.txt b/libgo/go/cmd/go/testdata/script/mod_proxy_invalid.txt index 6427cc1527a..63980b839e7 100644 --- a/libgo/go/cmd/go/testdata/script/mod_proxy_invalid.txt +++ b/libgo/go/cmd/go/testdata/script/mod_proxy_invalid.txt @@ -2,7 +2,7 @@ env GO111MODULE=on env GOPROXY=$GOPROXY/invalid ! go list -m rsc.io/quote@latest -stderr '^go list -m: module rsc.io/quote: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$' +stderr '^go: module rsc.io/quote: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$' ! go list -m rsc.io/quote@1.5.2 -stderr '^go list -m: rsc.io/quote@1.5.2: invalid version: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$' +stderr '^go: rsc.io/quote@1.5.2: invalid version: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$' diff --git a/libgo/go/cmd/go/testdata/script/mod_proxy_list.txt b/libgo/go/cmd/go/testdata/script/mod_proxy_list.txt index 89129f4fe23..849cf2c4764 100644 --- a/libgo/go/cmd/go/testdata/script/mod_proxy_list.txt +++ b/libgo/go/cmd/go/testdata/script/mod_proxy_list.txt @@ -3,34 +3,34 @@ env proxy=$GOPROXY # Proxy that can't serve should fail. env GOPROXY=$proxy/404 -! go get -d rsc.io/quote@v1.0.0 +! go get rsc.io/quote@v1.0.0 stderr '404 Not Found' # get should walk down the proxy list past 404 and 410 responses. env GOPROXY=$proxy/404,$proxy/410,$proxy -go get -d rsc.io/quote@v1.1.0 +go get rsc.io/quote@v1.1.0 # get should not walk past other 4xx errors if proxies are separated with ','. env GOPROXY=$proxy/403,$proxy -! go get -d rsc.io/quote@v1.2.0 +! go get rsc.io/quote@v1.2.0 stderr 'reading.*/403/rsc.io/.*: 403 Forbidden' # get should not walk past non-4xx errors if proxies are separated with ','. env GOPROXY=$proxy/500,$proxy -! go get -d rsc.io/quote@v1.3.0 +! go get rsc.io/quote@v1.3.0 stderr 'reading.*/500/rsc.io/.*: 500 Internal Server Error' # get should walk past other 4xx errors if proxies are separated with '|'. env GOPROXY=$proxy/403|https://0.0.0.0|$proxy -go get -d rsc.io/quote@v1.2.0 +go get rsc.io/quote@v1.2.0 # get should walk past non-4xx errors if proxies are separated with '|'. env GOPROXY=$proxy/500|https://0.0.0.0|$proxy -go get -d rsc.io/quote@v1.3.0 +go get rsc.io/quote@v1.3.0 # get should return the final error if that's all we have. env GOPROXY=$proxy/404,$proxy/410 -! go get -d rsc.io/quote@v1.4.0 +! go get rsc.io/quote@v1.4.0 stderr 'reading.*/410/rsc.io/.*: 410 Gone' -- go.mod -- diff --git a/libgo/go/cmd/go/testdata/script/mod_query.txt b/libgo/go/cmd/go/testdata/script/mod_query.txt index a75f86ed7c5..3758732504d 100644 --- a/libgo/go/cmd/go/testdata/script/mod_query.txt +++ b/libgo/go/cmd/go/testdata/script/mod_query.txt @@ -25,7 +25,7 @@ go list -m rsc.io/quote@<v1.5.4 stdout 'rsc.io/quote v1.5.2$' ! go list -m rsc.io/quote@>v1.5.3 -stderr 'go list -m: module rsc.io/quote: no matching versions for query ">v1.5.3"' +stderr 'go: module rsc.io/quote: no matching versions for query ">v1.5.3"' go list -m -e -f '{{.Error.Err}}' rsc.io/quote@>v1.5.3 stdout 'no matching versions for query ">v1.5.3"' diff --git a/libgo/go/cmd/go/testdata/script/mod_query_empty.txt b/libgo/go/cmd/go/testdata/script/mod_query_empty.txt index f8b6e3e97ed..1c39eae5744 100644 --- a/libgo/go/cmd/go/testdata/script/mod_query_empty.txt +++ b/libgo/go/cmd/go/testdata/script/mod_query_empty.txt @@ -7,14 +7,14 @@ go mod download example.com/join@v1.1.0 # reading that version should cause 'go get' to fail. env GOPROXY=file:///$WORK/badproxy cp go.mod.orig go.mod -! go get -d example.com/join/subpkg -stderr 'go get: example.com/join/subpkg@v0.0.0-20190624000000-123456abcdef: .*' +! go get example.com/join/subpkg +stderr 'go: example.com/join/subpkg@v0.0.0-20190624000000-123456abcdef: .*' # If @v/list is empty, the 'go' command should still try to resolve # other module paths. env GOPROXY=file:///$WORK/emptysub cp go.mod.orig go.mod -go get -d example.com/join/subpkg +go get example.com/join/subpkg go list -m example.com/join/... ! stdout 'example.com/join/subpkg' stdout 'example.com/join v1.1.0' @@ -23,7 +23,7 @@ stdout 'example.com/join v1.1.0' # that version is treated as nonexistent. env GOPROXY=file:///$WORK/notfound cp go.mod.orig go.mod -go get -d example.com/join/subpkg +go get example.com/join/subpkg go list -m example.com/join/... ! stdout 'example.com/join/subpkg' stdout 'example.com/join v1.1.0' @@ -39,8 +39,8 @@ stdout 'example.com/join v1.1.0' env GOPROXY=file:///$WORK/gatekeeper chmod 0000 $WORK/gatekeeper/example.com/join/subpkg/@latest cp go.mod.orig go.mod -! go get -d example.com/join/subpkg -stderr 'go get: module example.com/join/subpkg: (invalid response from proxy ".+": invalid character .+|reading file://.*/gatekeeper/example.com/join/subpkg/@latest: .+)' +! go get example.com/join/subpkg +stderr 'go: module example.com/join/subpkg: (invalid response from proxy ".+": invalid character .+|reading file://.*/gatekeeper/example.com/join/subpkg/@latest: .+)' -- go.mod.orig -- module example.com/othermodule diff --git a/libgo/go/cmd/go/testdata/script/mod_query_exclude.txt b/libgo/go/cmd/go/testdata/script/mod_query_exclude.txt index b0019694119..f76b20c6d88 100644 --- a/libgo/go/cmd/go/testdata/script/mod_query_exclude.txt +++ b/libgo/go/cmd/go/testdata/script/mod_query_exclude.txt @@ -18,17 +18,17 @@ stdout '^rsc.io/quote v1.5.1$' # get excluded version cp go.exclude.mod go.exclude.mod.orig -! go get -modfile=go.exclude.mod -d rsc.io/quote@v1.5.0 -stderr '^go get: rsc.io/quote@v1.5.0: excluded by go.mod$' +! go get -modfile=go.exclude.mod rsc.io/quote@v1.5.0 +stderr '^go: rsc.io/quote@v1.5.0: excluded by go.mod$' # get non-excluded version cp go.exclude.mod.orig go.exclude.mod -go get -modfile=go.exclude.mod -d rsc.io/quote@v1.5.1 +go get -modfile=go.exclude.mod rsc.io/quote@v1.5.1 stderr 'rsc.io/quote v1.5.1' # get query with excluded version cp go.exclude.mod.orig go.exclude.mod -go get -modfile=go.exclude.mod -d rsc.io/quote@>=v1.5 +go get -modfile=go.exclude.mod rsc.io/quote@>=v1.5 go list -modfile=go.exclude.mod -m ...quote stdout 'rsc.io/quote v1.5.[1-9]' diff --git a/libgo/go/cmd/go/testdata/script/mod_query_main.txt b/libgo/go/cmd/go/testdata/script/mod_query_main.txt index 39e5841a9cf..2a2fa42318a 100644 --- a/libgo/go/cmd/go/testdata/script/mod_query_main.txt +++ b/libgo/go/cmd/go/testdata/script/mod_query_main.txt @@ -6,9 +6,9 @@ go mod download rsc.io/quote@latest # 'go mod download' will not download @upgrade or @patch, since they always # resolve to the main module. go mod download rsc.io/quote@upgrade -stderr '^go mod download: skipping argument rsc.io/quote@upgrade that resolves to the main module$' +stderr '^go: skipping download of rsc.io/quote@upgrade that resolves to the main module$' go mod download rsc.io/quote@patch -stderr '^go mod download: skipping argument rsc.io/quote@patch that resolves to the main module$' +stderr '^go: skipping download of rsc.io/quote@patch that resolves to the main module$' # 'go list -m' can show a version of the main module. go list -m rsc.io/quote@5d9f230b @@ -31,11 +31,11 @@ stdout '^rsc.io/quote$' # 'go get' will not attempt to upgrade the main module to any specific version. # See also: mod_get_main.txt. ! go get rsc.io/quote@5d9f230b -stderr '^go get: can''t request version "5d9f230b" of the main module \(rsc.io/quote\)$' +stderr '^go: can''t request version "5d9f230b" of the main module \(rsc.io/quote\)$' ! go get rsc.io/quote@v1.5.2 -stderr '^go get: can''t request version "v1.5.2" of the main module \(rsc.io/quote\)$' +stderr '^go: can''t request version "v1.5.2" of the main module \(rsc.io/quote\)$' ! go get rsc.io/quote@latest -stderr '^go get: can''t request version "latest" of the main module \(rsc.io/quote\)$' +stderr '^go: can''t request version "latest" of the main module \(rsc.io/quote\)$' -- go.mod -- module rsc.io/quote diff --git a/libgo/go/cmd/go/testdata/script/mod_readonly.txt b/libgo/go/cmd/go/testdata/script/mod_readonly.txt index d05ad2a3174..9e950c38982 100644 --- a/libgo/go/cmd/go/testdata/script/mod_readonly.txt +++ b/libgo/go/cmd/go/testdata/script/mod_readonly.txt @@ -19,7 +19,7 @@ cmp go.mod go.mod.empty env GOFLAGS=-mod=readonly # update go.mod - go get allowed -go get -d rsc.io/quote +go get rsc.io/quote grep rsc.io/quote go.mod # update go.mod - go mod tidy allowed @@ -41,7 +41,7 @@ go list -m all # -mod=readonly should reject inconsistent go.mod files # (ones that would be rewritten). -go get -d rsc.io/sampler@v1.2.0 +go get rsc.io/sampler@v1.2.0 go mod edit -require rsc.io/quote@v1.5.2 cp go.mod go.mod.inconsistent ! go list @@ -81,7 +81,7 @@ stderr '^x.go:2:8: no required module provides package rsc.io/quote; to add it:\ stderr '^x.go:2:8: no required module provides package rsc.io/quote; to add it:\n\tgo get rsc.io/quote$' # However, if we didn't see an import from the main module, we should suggest -# 'go get -d' instead, because we don't know whether 'go mod tidy' would add it. +# 'go get' instead, because we don't know whether 'go mod tidy' would add it. ! go list rsc.io/quote stderr '^no required module provides package rsc.io/quote; to add it:\n\tgo get rsc.io/quote$' diff --git a/libgo/go/cmd/go/testdata/script/mod_replace.txt b/libgo/go/cmd/go/testdata/script/mod_replace.txt index a0a367fb1db..26b15518d94 100644 --- a/libgo/go/cmd/go/testdata/script/mod_replace.txt +++ b/libgo/go/cmd/go/testdata/script/mod_replace.txt @@ -42,7 +42,7 @@ stdout 'Concurrency is not parallelism.' # indicate the replacement module. cp go.mod.orig go.mod go mod edit -replace=rsc.io/quote/v3=./local/rsc.io/quote/v3 -! go get -d rsc.io/quote/v3/missing-package +! go get rsc.io/quote/v3/missing-package stderr 'module rsc.io/quote/v3@upgrade found \(v3.0.0, replaced by ./local/rsc.io/quote/v3\), but does not contain package' # The reported Dir and GoMod for a replaced module should be accurate. diff --git a/libgo/go/cmd/go/testdata/script/mod_replace_gopkgin.txt b/libgo/go/cmd/go/testdata/script/mod_replace_gopkgin.txt index d24f37b7880..df752d9716e 100644 --- a/libgo/go/cmd/go/testdata/script/mod_replace_gopkgin.txt +++ b/libgo/go/cmd/go/testdata/script/mod_replace_gopkgin.txt @@ -35,7 +35,7 @@ go list -m gopkg.in/src-d/go-git.v4 # A mismatched gopkg.in path should not be able to replace a different major version. cd ../3-to-gomod-4 ! go list -m gopkg.in/src-d/go-git.v3 -stderr '^go list -m: gopkg\.in/src-d/go-git\.v3@v3\.2\.0 \(replaced by gopkg\.in/src-d/go-git\.v3@v3\.0\.0-20190801152248-0d1a009cbb60\): version "v3\.0\.0-20190801152248-0d1a009cbb60" invalid: go\.mod has non-\.\.\.\.v3 module path "gopkg\.in/src-d/go-git\.v4" at revision 0d1a009cbb60$' +stderr '^go: gopkg\.in/src-d/go-git\.v3@v3\.2\.0 \(replaced by gopkg\.in/src-d/go-git\.v3@v3\.0\.0-20190801152248-0d1a009cbb60\): version "v3\.0\.0-20190801152248-0d1a009cbb60" invalid: go\.mod has non-\.\.\.\.v3 module path "gopkg\.in/src-d/go-git\.v4" at revision 0d1a009cbb60$' -- 4-to-4/go.mod -- module golang.org/issue/34254 diff --git a/libgo/go/cmd/go/testdata/script/mod_replace_import.txt b/libgo/go/cmd/go/testdata/script/mod_replace_import.txt index 2add31f71c1..7bf3a86fed8 100644 --- a/libgo/go/cmd/go/testdata/script/mod_replace_import.txt +++ b/libgo/go/cmd/go/testdata/script/mod_replace_import.txt @@ -6,7 +6,7 @@ cp go.mod go.mod.orig cmp go.mod go.mod.orig # 'go list' should resolve imports using replacements. -go get -d +go get go list all stdout 'example.com/a/b$' stdout 'example.com/x/v3$' diff --git a/libgo/go/cmd/go/testdata/script/mod_replace_readonly.txt b/libgo/go/cmd/go/testdata/script/mod_replace_readonly.txt index d950d78bd3c..5c1226b15ef 100644 --- a/libgo/go/cmd/go/testdata/script/mod_replace_readonly.txt +++ b/libgo/go/cmd/go/testdata/script/mod_replace_readonly.txt @@ -10,7 +10,7 @@ cp go.mod go.mod.orig go mod edit -replace rsc.io/quote=./quote ! go list rsc.io/quote stderr '^module rsc.io/quote provides package rsc.io/quote and is replaced but not required; to add it:\n\tgo get rsc.io/quote$' -go get -d rsc.io/quote +go get rsc.io/quote cmp go.mod go.mod.latest go list rsc.io/quote cp go.mod.orig go.mod @@ -19,7 +19,7 @@ cp go.mod.orig go.mod go mod edit -replace rsc.io/quote@v1.0.0-doesnotexist=./quote ! go list rsc.io/quote stderr '^module rsc.io/quote provides package rsc.io/quote and is replaced but not required; to add it:\n\tgo get rsc.io/quote@v1.0.0-doesnotexist$' -go get -d rsc.io/quote@v1.0.0-doesnotexist +go get rsc.io/quote@v1.0.0-doesnotexist cmp go.mod go.mod.specific go list rsc.io/quote cp go.mod.orig go.mod diff --git a/libgo/go/cmd/go/testdata/script/mod_retention.txt b/libgo/go/cmd/go/testdata/script/mod_retention.txt index 7a371b18068..9d30026459a 100644 --- a/libgo/go/cmd/go/testdata/script/mod_retention.txt +++ b/libgo/go/cmd/go/testdata/script/mod_retention.txt @@ -39,12 +39,14 @@ go list -mod=mod all cmp go.mod go.mod.tidy # "// indirect" comments should be added if appropriate. +# TODO(#42504): add case for 'go list -mod=mod -tags=any all' when -tags=any +# is supported. Only a command that loads "all" without build constraints +# (except "ignore") has enough information to add "// indirect" comments. +# 'go mod tidy' and 'go mod vendor' are the only commands that do that, +# but 'go mod vendor' cannot write go.mod. cp go.mod.toodirect go.mod go list all cmp go.mod go.mod.toodirect -go mod vendor # loads everything, so adds "// indirect" comments. -cmp go.mod go.mod.tidy -rm -r vendor # Redundant requirements should be preserved... @@ -81,14 +83,14 @@ require ( package x import _ "rsc.io/quote" -- go.mod.crlf -- -module m
-
-go 1.14
-
-require (
- rsc.io/quote v1.5.2
- rsc.io/testonly v1.0.0 // indirect
-)
+module m + +go 1.14 + +require ( + rsc.io/quote v1.5.2 + rsc.io/testonly v1.0.0 // indirect +) -- go.mod.unsorted -- module m @@ -139,10 +141,10 @@ module m go $goversion +require rsc.io/quote v1.5.2 + require ( - rsc.io/quote v1.5.2 + golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect rsc.io/sampler v1.3.0 // indirect rsc.io/testonly v1.0.0 // indirect ) - -require golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect diff --git a/libgo/go/cmd/go/testdata/script/mod_retract.txt b/libgo/go/cmd/go/testdata/script/mod_retract.txt index 4f95ece8d7a..37aae4896d0 100644 --- a/libgo/go/cmd/go/testdata/script/mod_retract.txt +++ b/libgo/go/cmd/go/testdata/script/mod_retract.txt @@ -17,7 +17,7 @@ exists $GOPATH/pkg/mod/cache/download/example.com/retract/@v/v1.0.0-bad.mod # Importing a package from a module with a retracted latest version will # select the latest non-retracted version. -go get -d ./use_self_prev +go get ./use_self_prev go list -m example.com/retract/self/prev stdout '^example.com/retract/self/prev v1.1.0$' exists $GOPATH/pkg/mod/cache/download/example.com/retract/self/prev/@v/v1.9.0.mod diff --git a/libgo/go/cmd/go/testdata/script/mod_retract_fix_version.txt b/libgo/go/cmd/go/testdata/script/mod_retract_fix_version.txt index e45758b6270..9ae49f53ab3 100644 --- a/libgo/go/cmd/go/testdata/script/mod_retract_fix_version.txt +++ b/libgo/go/cmd/go/testdata/script/mod_retract_fix_version.txt @@ -15,7 +15,7 @@ cmp go.mod go.mod.want # If a retracted version doesn't match the module's major version suffx, # an error should be reported. ! go mod edit -retract=v3.0.1 -stderr '^go mod: -retract=v3.0.1: version "v3.0.1" invalid: should be v2, not v3$' +stderr '^go: -retract=v3.0.1: version "v3.0.1" invalid: should be v2, not v3$' cp go.mod.mismatch-v2 go.mod ! go list -m all stderr 'go.mod:3: retract rsc.io/quote/v2: version "v3.0.1" invalid: should be v2, not v3$' diff --git a/libgo/go/cmd/go/testdata/script/mod_retract_incompatible.txt b/libgo/go/cmd/go/testdata/script/mod_retract_incompatible.txt index 61538e80244..5d095325292 100644 --- a/libgo/go/cmd/go/testdata/script/mod_retract_incompatible.txt +++ b/libgo/go/cmd/go/testdata/script/mod_retract_incompatible.txt @@ -6,10 +6,10 @@ go mod init m # Request a +incompatible version retracted in v1.0.0. -go get -d example.com/retract/incompatible@v2.0.0+incompatible +go get example.com/retract/incompatible@v2.0.0+incompatible stderr '^go: warning: example.com/retract/incompatible@v2.0.0\+incompatible: retracted by module author$' # We should still see a warning if the +incompatible was previously in the # build list. -go get -d example.com/retract/incompatible@v2.0.0+incompatible +go get example.com/retract/incompatible@v2.0.0+incompatible stderr '^go: warning: example.com/retract/incompatible@v2.0.0\+incompatible: retracted by module author$' diff --git a/libgo/go/cmd/go/testdata/script/mod_retract_pseudo_base.txt b/libgo/go/cmd/go/testdata/script/mod_retract_pseudo_base.txt index eb00e8405c0..27c2b670658 100644 --- a/libgo/go/cmd/go/testdata/script/mod_retract_pseudo_base.txt +++ b/libgo/go/cmd/go/testdata/script/mod_retract_pseudo_base.txt @@ -24,12 +24,12 @@ stdout '^vcs-test.golang.org/git/retract-pseudo.git v0.0.0-20201009173747-64c061 # A retracted version is a valid base. Retraction should not validate existing # pseudo-versions, nor should it turn invalid pseudo-versions valid. -go get -d vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-713affd19d7b +go get vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-713affd19d7b go list -m vcs-test.golang.org/git/retract-pseudo.git stdout '^vcs-test.golang.org/git/retract-pseudo.git v1.0.1-0.20201009173747-713affd19d7b$' -! go get -d vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371 -stderr '^go get: vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371: invalid pseudo-version: tag \(v1.0.0\) found on revision 64c061ed4371 is already canonical, so should not be replaced with a pseudo-version derived from that tag$' +! go get vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371 +stderr '^go: vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371: invalid pseudo-version: tag \(v1.0.0\) found on revision 64c061ed4371 is already canonical, so should not be replaced with a pseudo-version derived from that tag$' -- retract-pseudo.sh -- #!/bin/bash diff --git a/libgo/go/cmd/go/testdata/script/mod_retract_rationale.txt b/libgo/go/cmd/go/testdata/script/mod_retract_rationale.txt index 823c384e488..92e9b7d6ea5 100644 --- a/libgo/go/cmd/go/testdata/script/mod_retract_rationale.txt +++ b/libgo/go/cmd/go/testdata/script/mod_retract_rationale.txt @@ -1,5 +1,5 @@ # When there is no rationale, 'go get' should print a hard-coded message. -go get -d example.com/retract/rationale@v1.0.0-empty +go get example.com/retract/rationale@v1.0.0-empty stderr '^go: warning: example.com/retract/rationale@v1.0.0-empty: retracted by module author$' # 'go list' should print the same hard-coded message. @@ -8,7 +8,7 @@ stdout '^\[retracted by module author\]$' # When there is a multi-line message, 'go get' should print the first line. -go get -d example.com/retract/rationale@v1.0.0-multiline1 +go get example.com/retract/rationale@v1.0.0-multiline1 stderr '^go: warning: example.com/retract/rationale@v1.0.0-multiline1: retracted by module author: short description$' ! stderr 'detail' @@ -18,7 +18,7 @@ cmp stdout multiline # 'go get' output should be the same whether the retraction appears at top-level # or in a block. -go get -d example.com/retract/rationale@v1.0.0-multiline2 +go get example.com/retract/rationale@v1.0.0-multiline2 stderr '^go: warning: example.com/retract/rationale@v1.0.0-multiline2: retracted by module author: short description$' ! stderr 'detail' @@ -28,7 +28,7 @@ cmp stdout multiline # 'go get' should omit long messages. -go get -d example.com/retract/rationale@v1.0.0-long +go get example.com/retract/rationale@v1.0.0-long stderr '^go: warning: example.com/retract/rationale@v1.0.0-long: retracted by module author: \(message omitted: too long\)' # 'go list' should show the full message. @@ -37,7 +37,7 @@ stdout '^\[lo{500}ng\]$' # 'go get' should omit messages with unprintable characters. -go get -d example.com/retract/rationale@v1.0.0-unprintable +go get example.com/retract/rationale@v1.0.0-unprintable stderr '^go: warning: example.com/retract/rationale@v1.0.0-unprintable: retracted by module author: \(message omitted: contains non-printable characters\)' # 'go list' should show the full message. @@ -61,9 +61,9 @@ go list -m -retracted -f '{{range .Retracted}}{{.}},{{end}}' example.com/retract stdout '^single version,degenerate range,$' # 'go get' will only report the first retraction to avoid being too verbose. -go get -d example.com/retract/rationale@v1.0.0-order +go get example.com/retract/rationale@v1.0.0-order stderr '^go: warning: example.com/retract/rationale@v1.0.0-order: retracted by module author: degenerate range$' -go get -d example.com/retract/rationale@v1.0.1-order +go get example.com/retract/rationale@v1.0.1-order stderr '^go: warning: example.com/retract/rationale@v1.0.1-order: retracted by module author: single version$' -- go.mod -- diff --git a/libgo/go/cmd/go/testdata/script/mod_retract_rename.txt b/libgo/go/cmd/go/testdata/script/mod_retract_rename.txt index f54742c5232..38986f333f6 100644 --- a/libgo/go/cmd/go/testdata/script/mod_retract_rename.txt +++ b/libgo/go/cmd/go/testdata/script/mod_retract_rename.txt @@ -1,5 +1,5 @@ # Populate go.sum. -go get -d +go get # 'go list -m -retracted' should load retractions, even if the version # containing retractions has a different module path. @@ -9,11 +9,11 @@ go list -m -retracted -f '{{with .Retracted}}retracted{{end}}' example.com/retra go list -m -u -f '{{with .Retracted}}retracted{{end}}' example.com/retract/rename # 'go get' should warn about the retracted version. -go get -d +go get stderr '^go: warning: example.com/retract/rename@v1.0.0-bad: retracted by module author: bad$' # We can't upgrade, since this latest version has a different module path. -! go get -d example.com/retract/rename +! go get example.com/retract/rename stderr 'module declares its path as: example.com/retract/newname' -- go.mod -- diff --git a/libgo/go/cmd/go/testdata/script/mod_retract_replace.txt b/libgo/go/cmd/go/testdata/script/mod_retract_replace.txt index 9cd714739ab..788968f420e 100644 --- a/libgo/go/cmd/go/testdata/script/mod_retract_replace.txt +++ b/libgo/go/cmd/go/testdata/script/mod_retract_replace.txt @@ -2,7 +2,7 @@ # obtain retractions from the replacement. # Populate go.sum. -go get -d +go get # The latest version, v1.9.0, is not available on the proxy. go list -m -retracted example.com/retract/missingmod diff --git a/libgo/go/cmd/go/testdata/script/mod_run_nonmain.txt b/libgo/go/cmd/go/testdata/script/mod_run_nonmain.txt index 036755d2d1a..8435fc05b49 100644 --- a/libgo/go/cmd/go/testdata/script/mod_run_nonmain.txt +++ b/libgo/go/cmd/go/testdata/script/mod_run_nonmain.txt @@ -7,7 +7,7 @@ stderr '^package example.net/nonmain is not a main package$' ! go run ./... stderr '^go: warning: "\./\.\.\." matched only non-main packages$' -stderr '^go run: no packages loaded from \./\.\.\.$' +stderr '^go: no packages loaded from \./\.\.\.$' -- go.mod -- module example.net/nonmain diff --git a/libgo/go/cmd/go/testdata/script/mod_run_pkg_version.txt b/libgo/go/cmd/go/testdata/script/mod_run_pkg_version.txt index e921fab5085..c3a218d553d 100644 --- a/libgo/go/cmd/go/testdata/script/mod_run_pkg_version.txt +++ b/libgo/go/cmd/go/testdata/script/mod_run_pkg_version.txt @@ -21,7 +21,7 @@ env GO111MODULE=on cd m cp go.mod go.mod.orig ! go list -m all -stderr '^go list -m: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$' +stderr '^go: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$' go run example.com/cmd/a@v1.0.0 stdout '^a@v1.0.0$' cmp go.mod go.mod.orig @@ -92,12 +92,12 @@ package main func main() {} -- replace-err -- -go run: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace): +go: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace): The go.mod file for the module providing named packages contains one or more replace directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module. -- exclude-err -- -go run: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude): +go: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude): The go.mod file for the module providing named packages contains one or more exclude directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module. diff --git a/libgo/go/cmd/go/testdata/script/mod_skip_write.txt b/libgo/go/cmd/go/testdata/script/mod_skip_write.txt new file mode 100644 index 00000000000..9fdb6fc1212 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_skip_write.txt @@ -0,0 +1,92 @@ +# Commands used to debug the module graph should not write go.mod or go.sum +# or report errors when those files need to be updated. + +# Everything's okay initially. +go list -m all + +# Downgrading sampler makes go.mod inconsistent, but 'go mod graph', +# 'go mod verify', and 'go mod why' still work. +cp go.mod go.mod.orig +go mod edit -require=rsc.io/sampler@v1.2.0 +cp go.mod go.mod.edit +! go list -m all +stderr 'updates to go.mod needed' + +go mod graph +cmp stdout graph.want +cmp go.mod go.mod.edit + +go mod verify +stdout '^all modules verified$' +cmp go.mod go.mod.edit + +go mod why rsc.io/sampler +cmp stdout why.want +cmp go.mod go.mod.edit + +go mod why -m rsc.io/sampler +cmp stdout why.want +cmp go.mod go.mod.edit + +cp go.mod.orig go.mod + +# Removing go.sum breaks other commands, but 'go mod graph' and +# 'go mod why' still work. +rm go.sum +! go list -m all +stderr 'missing go.sum entry' + +go mod graph +cmp stdout graph.want +! exists go.sum + +go mod verify +stdout '^all modules verified$' +! exists go.sum + +go mod why rsc.io/sampler +cmp stdout why.want +! exists go.sum + +go mod why -m rsc.io/sampler +cmp stdout why.want +! exists go.sum + +-- go.mod -- +module m + +go 1.18 + +require rsc.io/quote v1.5.2 + +require ( + golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect + rsc.io/sampler v1.3.0 // indirect + rsc.io/testonly v1.0.0 // indirect +) +-- go.sum -- +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:pvCbr/wm8HzDD3fVywevekufpn6tCGPY3spdHeZJEsw= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +rsc.io/quote v1.5.2 h1:3fEykkD9k7lYzXqCYrwGAf7iNhbk4yCjHmKBN9td4L0= +rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/sampler v1.2.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/testonly v1.0.0 h1:K/VWHdO+Jv7woUXG0GzVNx1czBXUt3Ib1deaMn+xk64= +rsc.io/testonly v1.0.0/go.mod h1:OqmGbIFOcF+XrFReLOGZ6BhMM7uMBiQwZsyNmh74SzY= +-- use.go -- +package use + +import _ "rsc.io/quote" +-- graph.want -- +m golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c +m rsc.io/quote@v1.5.2 +m rsc.io/sampler@v1.3.0 +m rsc.io/testonly@v1.0.0 +rsc.io/quote@v1.5.2 rsc.io/sampler@v1.3.0 +rsc.io/sampler@v1.3.0 golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c +-- why.want -- +# rsc.io/sampler +m +rsc.io/quote +rsc.io/sampler diff --git a/libgo/go/cmd/go/testdata/script/mod_sum_lookup.txt b/libgo/go/cmd/go/testdata/script/mod_sum_lookup.txt index e0219213800..7513f7f49f3 100644 --- a/libgo/go/cmd/go/testdata/script/mod_sum_lookup.txt +++ b/libgo/go/cmd/go/testdata/script/mod_sum_lookup.txt @@ -8,7 +8,7 @@ go list -e -mod=mod -tags=ignore ./noexist # When an import is resolved successfully, we should only save hashes for # the module that provides the package, not for other modules looked up. # Verifies golang.org/issue/31580. -go get -d ./exist +go get ./exist grep '^example.com/join v1.1.0 h1:' go.sum ! grep '^example.com/join/subpkg' go.sum cp go.sum go.list.sum diff --git a/libgo/go/cmd/go/testdata/script/mod_sum_readonly.txt b/libgo/go/cmd/go/testdata/script/mod_sum_readonly.txt index 113f13ea390..57c5bbeefdf 100644 --- a/libgo/go/cmd/go/testdata/script/mod_sum_readonly.txt +++ b/libgo/go/cmd/go/testdata/script/mod_sum_readonly.txt @@ -4,7 +4,7 @@ env GO111MODULE=on # When a sum is needed to load the build list, we get an error for the # specific module. The .mod file is not downloaded, and go.sum is not written. ! go list -m all -stderr '^go list -m: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' +stderr '^go: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' ! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod ! exists go.sum @@ -12,7 +12,7 @@ stderr '^go list -m: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo # we should see the same error. cp go.sum.h2only go.sum ! go list -m all -stderr '^go list -m: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' +stderr '^go: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' ! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod cmp go.sum go.sum.h2only rm go.sum @@ -21,7 +21,7 @@ rm go.sum cp go.mod go.mod.orig go mod edit -replace rsc.io/quote@v1.5.2=rsc.io/quote@v1.5.1 ! go list -m all -stderr '^go list -m: rsc.io/quote@v1.5.2 \(replaced by rsc.io/quote@v1.5.1\): missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' +stderr '^go: rsc.io/quote@v1.5.2 \(replaced by rsc.io/quote@v1.5.1\): missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' ! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.1.mod ! exists go.sum cp go.mod.orig go.mod diff --git a/libgo/go/cmd/go/testdata/script/mod_sum_replaced.txt b/libgo/go/cmd/go/testdata/script/mod_sum_replaced.txt index b03982d9cff..6c322a00d60 100644 --- a/libgo/go/cmd/go/testdata/script/mod_sum_replaced.txt +++ b/libgo/go/cmd/go/testdata/script/mod_sum_replaced.txt @@ -1,7 +1,7 @@ env GO111MODULE=on -# After 'go get -d', the go.sum file should contain the sum for the module. -go get -d rsc.io/quote@v1.5.0 +# After 'go get', the go.sum file should contain the sum for the module. +go get rsc.io/quote@v1.5.0 grep 'rsc.io/quote v1.5.0' go.sum # If we replace the module and run 'go mod tidy', we should get a sum for the replacement. diff --git a/libgo/go/cmd/go/testdata/script/mod_sumdb.txt b/libgo/go/cmd/go/testdata/script/mod_sumdb.txt index fa3483c5cb1..d06db4ae692 100644 --- a/libgo/go/cmd/go/testdata/script/mod_sumdb.txt +++ b/libgo/go/cmd/go/testdata/script/mod_sumdb.txt @@ -8,13 +8,13 @@ env dbname=localhost.localdev/sumdb # (this also populates tiles on the sumdb server). cp go.mod.orig go.mod env GOSUMDB=$sumdb' '$proxy/sumdb-wrong -! go get -d rsc.io/quote -stderr 'go get: rsc.io/quote@v1.5.2: verifying module: checksum mismatch' +! go get rsc.io/quote +stderr 'go: rsc.io/quote@v1.5.2: verifying module: checksum mismatch' stderr 'downloaded: h1:3fEy' stderr 'localhost.localdev/sumdb: h1:wrong' stderr 'SECURITY ERROR\nThis download does NOT match the one reported by the checksum server.' -! go get -d rsc.io/sampler -! go get -d golang.org/x/text +! go get rsc.io/sampler +! go get golang.org/x/text go mod edit -require rsc.io/quote@v1.5.2 ! go mod tidy @@ -26,14 +26,14 @@ rm go.sum # switching to truthful sumdb detects timeline inconsistency cp go.mod.orig go.mod env GOSUMDB=$sumdb -! go get -d rsc.io/fortune +! go get rsc.io/fortune stderr 'SECURITY ERROR\ngo.sum database server misbehavior detected!' stderr 'proof of misbehavior:' # removing the cached wrong tree head and cached tiles clears the bad data rm $GOPATH/pkg/sumdb/$dbname/latest go clean -modcache -go get -d rsc.io/fortune +go get rsc.io/fortune -- go.mod.orig -- module m diff --git a/libgo/go/cmd/go/testdata/script/mod_sumdb_cache.txt b/libgo/go/cmd/go/testdata/script/mod_sumdb_cache.txt index 1b38475fb5e..063fd20964f 100644 --- a/libgo/go/cmd/go/testdata/script/mod_sumdb_cache.txt +++ b/libgo/go/cmd/go/testdata/script/mod_sumdb_cache.txt @@ -7,40 +7,40 @@ env GOPROXY GONOPROXY GOSUMDB GONOSUMDB cp go.mod.orig go.mod rm go.sum env GOPROXY=$proxy/sumdb-503 -! go get -d rsc.io/quote +! go get rsc.io/quote stderr 503 # fetch through working proxy is OK cp go.mod.orig go.mod rm go.sum env GOPROXY=$proxy -go get -d rsc.io/quote +go get rsc.io/quote # repeated fetch works entirely from cache, does not consult sumdb cp go.mod.orig go.mod rm go.sum env GOPROXY=$proxy/sumdb-503 -go get -d rsc.io/quote +go get rsc.io/quote rm go.sum # fetch specific module can work without proxy, using cache or go.sum cp go.mod.orig go.mod rm go.sum env GOPROXY=off -go get -d rsc.io/quote@v1.5.2 # using cache +go get rsc.io/quote@v1.5.2 # using cache rm $GOPATH/pkg/mod/cache/download/sumdb/localhost.localdev/sumdb/lookup/rsc.io/quote@v1.5.2 -go get -d rsc.io/quote@v1.5.2 # using go.sum +go get rsc.io/quote@v1.5.2 # using go.sum # fetch fails once we lose access to both cache and go.sum rm go.sum env GOPROXY=$proxy/sumdb-504 -! go get -d rsc.io/quote@v1.5.2 +! go get rsc.io/quote@v1.5.2 stderr 504 # GOINSECURE does not bypass checksum lookup env GOINSECURE=rsc.io env GOPROXY=$proxy/sumdb-504 -! go get -d rsc.io/quote@v1.5.2 +! go get rsc.io/quote@v1.5.2 stderr 504 -- go.mod.orig -- diff --git a/libgo/go/cmd/go/testdata/script/mod_sumdb_file_path.txt b/libgo/go/cmd/go/testdata/script/mod_sumdb_file_path.txt index 22fcbf3de84..a834c4800dc 100644 --- a/libgo/go/cmd/go/testdata/script/mod_sumdb_file_path.txt +++ b/libgo/go/cmd/go/testdata/script/mod_sumdb_file_path.txt @@ -12,15 +12,15 @@ env GOPATH=$WORK/gopath1 # It comes from the sumweb package, which isn't yet producing structured errors. [windows] env GOPROXY=file:///$WORK/sumproxy,https://proxy.golang.org [!windows] env GOPROXY=file://$WORK/sumproxy,https://proxy.golang.org -! go get -d golang.org/x/text@v0.3.2 -stderr '^go get: golang.org/x/text@v0.3.2: verifying module: golang.org/x/text@v0.3.2: reading file://.*/sumdb/sum.golang.org/lookup/golang.org/x/text@v0.3.2: (no such file or directory|.*cannot find the path specified.*)' +! go get golang.org/x/text@v0.3.2 +stderr '^go: golang.org/x/text@v0.3.2: verifying module: golang.org/x/text@v0.3.2: reading file://.*/sumdb/sum.golang.org/lookup/golang.org/x/text@v0.3.2: (no such file or directory|.*cannot find the path specified.*)' # If the proxy does not claim to support the database, # checksum verification should fall through to the next proxy, # and downloading should succeed. [windows] env GOPROXY=file:///$WORK/emptyproxy,https://proxy.golang.org [!windows] env GOPROXY=file://$WORK/emptyproxy,https://proxy.golang.org -go get -d golang.org/x/text@v0.3.2 +go get golang.org/x/text@v0.3.2 # After a successful sumdb lookup, the lookup can be repeated # using the download cache as a proxy. @@ -29,7 +29,7 @@ cp supported $GOPATH/pkg/mod/cache/download/sumdb/sum.golang.org/supported [!windows] env GOPROXY=file://$WORK/gopath1/pkg/mod/cache/download,file://$WORK/sumproxy env GOPATH=$WORK/gopath2 rm go.sum -go get -d -x -v golang.org/x/text@v0.3.2 +go get -x -v golang.org/x/text@v0.3.2 # Once the checksum is present in the go.sum file, # an empty file-based sumdb can be used in conjunction with @@ -38,10 +38,10 @@ grep golang.org/x/text go.sum env GOPATH=$WORK/gopath3 [windows] env GOPROXY=file:///$WORK/sumproxy [!windows] env GOPROXY=file://$WORK/sumproxy -! go get -d golang.org/x/text@v0.3.2 +! go get golang.org/x/text@v0.3.2 [windows] env GOPROXY=file:///$WORK/sumproxy,https://proxy.golang.org [!windows] env GOPROXY=file://$WORK/sumproxy,https://proxy.golang.org -go get -d golang.org/x/text@v0.3.2 +go get golang.org/x/text@v0.3.2 -- supported -- diff --git a/libgo/go/cmd/go/testdata/script/mod_sumdb_golang.txt b/libgo/go/cmd/go/testdata/script/mod_sumdb_golang.txt index becd88b52e7..a48a5ba1b04 100644 --- a/libgo/go/cmd/go/testdata/script/mod_sumdb_golang.txt +++ b/libgo/go/cmd/go/testdata/script/mod_sumdb_golang.txt @@ -16,7 +16,7 @@ stdout '^sum.golang.org$' env GOSUMDB=sum.golang.org env GOPROXY=direct -go get -d rsc.io/quote@v1.5.2 +go get rsc.io/quote@v1.5.2 cp go.sum saved.sum diff --git a/libgo/go/cmd/go/testdata/script/mod_sumdb_proxy.txt b/libgo/go/cmd/go/testdata/script/mod_sumdb_proxy.txt index 70b8e3fc446..194c0c92e57 100644 --- a/libgo/go/cmd/go/testdata/script/mod_sumdb_proxy.txt +++ b/libgo/go/cmd/go/testdata/script/mod_sumdb_proxy.txt @@ -5,14 +5,14 @@ env GOPROXY GONOPROXY GOSUMDB GONOSUMDB # basic fetch (through proxy) works cp go.mod.orig go.mod -go get -d rsc.io/fortune@v1.0.0 # note: must use test proxy, does not exist in real world +go get rsc.io/fortune@v1.0.0 # note: must use test proxy, does not exist in real world rm $GOPATH/pkg/mod/cache/download/sumdb # rm sumdb cache but NOT package download cache rm go.sum # can fetch by explicit URL cp go.mod.orig go.mod env GOSUMDB=$sumdb' '$proxy/sumdb-direct -go get -d rsc.io/fortune@v1.0.0 +go get rsc.io/fortune@v1.0.0 rm $GOPATH/pkg/mod/cache/download/sumdb rm go.sum @@ -21,7 +21,7 @@ rm go.sum cp go.mod.orig go.mod env GOSUMDB=$sumdb env GOPROXY=direct -! go get -d rsc.io/fortune@v1.0.0 +! go get rsc.io/fortune@v1.0.0 stderr 'verifying module: rsc.io/fortune@v1.0.0: .*: no such host localhost.localdev' rm $GOPATH/pkg/mod/cache/download/sumdb rm go.sum @@ -30,7 +30,7 @@ rm go.sum cp go.mod.orig go.mod env GOSUMDB=$sumdb env GOPROXY=$proxy/sumdb-404 -! go get -d rsc.io/fortune@v1.0.0 +! go get rsc.io/fortune@v1.0.0 stderr 'verifying.*localhost.localdev' rm $GOPATH/pkg/mod/cache/download/sumdb rm go.sum @@ -39,7 +39,7 @@ rm go.sum cp go.mod.orig go.mod env GOSUMDB=$sumdb env GOPROXY=$proxy/sumdb-503 -! go get -d rsc.io/fortune@v1.0.0 +! go get rsc.io/fortune@v1.0.0 stderr '503 Service Unavailable' rm $GOPATH/pkg/mod/cache/download/sumdb rm go.sum @@ -48,7 +48,7 @@ rm go.sum cp go.mod.orig go.mod env GOSUMDB=$sumdb env GOPROXY=$proxy/sumdb-404,$proxy/sumdb-503 -! go get -d rsc.io/fortune@v1.0.0 +! go get rsc.io/fortune@v1.0.0 stderr '503 Service Unavailable' rm $GOPATH/pkg/mod/cache/download/sumdb rm go.sum @@ -57,7 +57,7 @@ rm go.sum cp go.mod.orig go.mod env GOSUMDB=$sumdb env GOPROXY=$proxy/sumdb-503|https://0.0.0.0|$proxy -go get -d rsc.io/fortune@v1.0.0 +go get rsc.io/fortune@v1.0.0 rm $GOPATH/pkg/mod/cache/download/sumdb rm go.sum diff --git a/libgo/go/cmd/go/testdata/script/mod_symlink.txt b/libgo/go/cmd/go/testdata/script/mod_symlink.txt index dbc23fb8f09..0604e1a4c4f 100644 --- a/libgo/go/cmd/go/testdata/script/mod_symlink.txt +++ b/libgo/go/cmd/go/testdata/script/mod_symlink.txt @@ -1,12 +1,12 @@ env GO111MODULE=on [!symlink] skip -# 'go get -d' should resolve modules of imported packages. -go get -d +# 'go get' should resolve modules of imported packages. +go get go list -deps -f '{{.Module}}' . stdout golang.org/x/text -go get -d ./subpkg +go get ./subpkg go list -deps -f '{{.Module}}' ./subpkg stdout golang.org/x/text diff --git a/libgo/go/cmd/go/testdata/script/mod_tidy_compat.txt b/libgo/go/cmd/go/testdata/script/mod_tidy_compat.txt index e6edef5ee3b..18b297da60e 100644 --- a/libgo/go/cmd/go/testdata/script/mod_tidy_compat.txt +++ b/libgo/go/cmd/go/testdata/script/mod_tidy_compat.txt @@ -20,7 +20,7 @@ env MODFMT='{{with .Module}}{{.Path}} {{.Version}}{{end}}' # + ---- example.net/lazy v0.1.0 ---- example.com/version v1.0.1 # # Go 1.17 avoids loading the go.mod file for example.com/version v1.0.1 -# (because it is lower than the verison explicitly required by m, +# (because it is lower than the version explicitly required by m, # and the module that requires it — m — specifies 'go 1.17'). # # That go.mod file happens not to affect the final 1.16 module graph anyway, @@ -50,7 +50,7 @@ cmp stdout m_all.txt go mod edit -go=1.16 ! go list -m all -stderr '^go list -m: example.net/lazy@v0.1.0 requires\n\texample.com/version@v1.0.1: missing go.sum entry; to add it:\n\tgo mod download example.com/version$' +stderr '^go: example.net/lazy@v0.1.0 requires\n\texample.com/version@v1.0.1: missing go.sum entry; to add it:\n\tgo mod download example.com/version$' -- go.mod -- diff --git a/libgo/go/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt b/libgo/go/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt index c544cb7413f..a45de5ad8cd 100644 --- a/libgo/go/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt +++ b/libgo/go/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt @@ -62,7 +62,7 @@ cmp stdout all-m.txt go mod edit -go=1.16 ! go list -m all -stderr '^go list -m: example\.net/indirect@v0\.1\.0 requires\n\texample\.net/ambiguous@v0\.1\.0: missing go\.sum entry; to add it:\n\tgo mod download example\.net/ambiguous\n' +stderr '^go: example\.net/indirect@v0\.1\.0 requires\n\texample\.net/ambiguous@v0\.1\.0: missing go\.sum entry; to add it:\n\tgo mod download example\.net/ambiguous\n' -- go.mod -- @@ -72,10 +72,9 @@ go 1.17 replace example.net/indirect v0.1.0 => ./indirect -require ( - example.net/ambiguous/nested v0.1.0 // indirect - example.net/indirect v0.1.0 -) +require example.net/indirect v0.1.0 + +require example.net/ambiguous/nested v0.1.0 // indirect -- all-m.txt -- example.com/m example.net/ambiguous v0.1.0 diff --git a/libgo/go/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt b/libgo/go/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt index ea9e42e87e2..11313f144c3 100644 --- a/libgo/go/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt +++ b/libgo/go/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt @@ -97,10 +97,9 @@ replace ( example.net/requireincompatible v0.1.0 => ./requireincompatible ) -require ( - example.com/retract/incompatible v1.0.0 // indirect - example.net/lazy v0.1.0 -) +require example.net/lazy v0.1.0 + +require example.com/retract/incompatible v1.0.0 // indirect -- incompatible.go -- package incompatible diff --git a/libgo/go/cmd/go/testdata/script/mod_tidy_convergence.txt b/libgo/go/cmd/go/testdata/script/mod_tidy_convergence.txt index 09c46f764bf..be0a8e9b8c5 100644 --- a/libgo/go/cmd/go/testdata/script/mod_tidy_convergence.txt +++ b/libgo/go/cmd/go/testdata/script/mod_tidy_convergence.txt @@ -79,7 +79,7 @@ cmp go.mod go.mod.tidye # succeed and remain stable. y.1 does not upgrade x, and can therefore be used # with it. -go get -d example.net/x@v0.1.0 example.net/y@v0.1.0 +go get example.net/x@v0.1.0 example.net/y@v0.1.0 go mod tidy cmp go.mod go.mod.postget @@ -96,7 +96,7 @@ cmp go.mod go.mod.tidye stderr '^go: found example\.net/y in example\.net/y v0.2.0$' stderr '^example\.net/m imports\n\texample\.net/x: package example\.net/x provided by example\.net/x at latest version v0\.1\.0 but not at required version v0\.2\.0-pre$' -go get -d example.net/x@v0.1.0 example.net/y@v0.1.0 +go get example.net/x@v0.1.0 example.net/y@v0.1.0 go mod tidy cmp go.mod go.mod.postget-117 diff --git a/libgo/go/cmd/go/testdata/script/mod_tidy_convergence_loop.txt b/libgo/go/cmd/go/testdata/script/mod_tidy_convergence_loop.txt index 3c4d3244d5d..99599e551a1 100644 --- a/libgo/go/cmd/go/testdata/script/mod_tidy_convergence_loop.txt +++ b/libgo/go/cmd/go/testdata/script/mod_tidy_convergence_loop.txt @@ -94,7 +94,7 @@ cmp go.mod go.mod.orig # packages simultaneously over-upgrades all of the dependencies, and 'go mod # tidy' treats "no package can be added" as a terminal state. -go get -d example.net/w@v0.2.0-pre example.net/x@v0.2.0-pre example.net/y@v0.2.0-pre example.net/z@v0.2.0-pre +go get example.net/w@v0.2.0-pre example.net/x@v0.2.0-pre example.net/y@v0.2.0-pre example.net/z@v0.2.0-pre go mod tidy -e cmp go.mod go.mod.postget go mod tidy -e @@ -154,7 +154,7 @@ cmp go.mod go.mod.117 # As in the eager case, for the lazy module the fully-upgraded dependency graph # becomes empty, and the empty graph is stable. -go get -d example.net/w@v0.2.0-pre example.net/x@v0.2.0-pre example.net/y@v0.2.0-pre example.net/z@v0.2.0-pre +go get example.net/w@v0.2.0-pre example.net/x@v0.2.0-pre example.net/y@v0.2.0-pre example.net/z@v0.2.0-pre go mod tidy -e cmp go.mod go.mod.postget go mod tidy -e diff --git a/libgo/go/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt b/libgo/go/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt new file mode 100644 index 00000000000..8b508c7ea8a --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt @@ -0,0 +1,58 @@ +# Verifies golang.org/issue/47738. + +# In this test, the user has rewritten their imports to use rsc.io/quote/v3, +# but their go.mod still requires rsc.io/quote@v1.5.2, and they indirectly +# require rsc.io/quote@v1.5.1 but don't import anything from it. +go list -m -f '{{.Path}}@{{.Version}}{{if .Indirect}} indirect{{end}}' all +stdout '^rsc.io/quote@v1.5.2$' +! stdout 'rsc.io/quote/v3' +go list -e all +! stdout '^rsc.io/quote$' + +# 'go mod tidy' should preserve the requirement on rsc.io/quote but mark it +# indirect. This prevents a downgrade to v1.5.1, which could introduce +# an ambiguity. +go mod tidy +go list -m -f '{{.Path}}@{{.Version}}{{if .Indirect}} indirect{{end}}' all +stdout '^rsc.io/quote@v1.5.2 indirect$' +stdout '^rsc.io/quote/v3@v3.0.0$' + +-- go.mod -- +module use + +go 1.16 + +require ( + old-indirect v0.0.0 + rsc.io/quote v1.5.2 +) + +replace old-indirect v0.0.0 => ./old-indirect +-- go.sum -- +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:pvCbr/wm8HzDD3fVywevekufpn6tCGPY3spdHeZJEsw= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +rsc.io/quote v1.5.1/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/quote v1.5.2 h1:3fEykkD9k7lYzXqCYrwGAf7iNhbk4yCjHmKBN9td4L0= +rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +-- use.go -- +package use + +import ( + _ "old-indirect/empty" + + _ "rsc.io/quote/v3" +) +-- old-indirect/empty/empty.go -- +package empty +-- old-indirect/go.mod -- +module old-indirect + +go 1.16 + +require rsc.io/quote v1.5.1 +-- old-indirect/go.sum -- +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/libgo/go/cmd/go/testdata/script/mod_tidy_error.txt b/libgo/go/cmd/go/testdata/script/mod_tidy_error.txt index 395537b1a71..51fc65fa7a8 100644 --- a/libgo/go/cmd/go/testdata/script/mod_tidy_error.txt +++ b/libgo/go/cmd/go/testdata/script/mod_tidy_error.txt @@ -10,8 +10,8 @@ stderr '^issue27063 imports\n\tissue27063/other imports\n\tother.example.com/non ! go mod vendor ! stderr 'package nonexist is not in GOROOT' -stderr '^issue27063 imports\n\tnonexist.example.com: cannot find module providing package nonexist.example.com' -stderr '^issue27063 imports\n\tissue27063/other imports\n\tother.example.com/nonexist: cannot find module providing package other.example.com/nonexist' +stderr '^issue27063 imports\n\tnonexist.example.com: no required module provides package nonexist.example.com; to add it:\n\tgo get nonexist.example.com$' +stderr '^issue27063 imports\n\tissue27063/other imports\n\tother.example.com/nonexist: no required module provides package other.example.com/nonexist; to add it:\n\tgo get other.example.com/nonexist$' -- go.mod -- module issue27063 diff --git a/libgo/go/cmd/go/testdata/script/mod_tidy_replace.txt b/libgo/go/cmd/go/testdata/script/mod_tidy_replace.txt index 297f6a6a45c..274f3bf5090 100644 --- a/libgo/go/cmd/go/testdata/script/mod_tidy_replace.txt +++ b/libgo/go/cmd/go/testdata/script/mod_tidy_replace.txt @@ -35,7 +35,7 @@ grep 'golang.org/x/text' go.mod # 'go get' and 'go mod tidy' should follow the requirements of the replacements, # not the originals, even if that results in a set of versions that are # misleading or redundant without those replacements. -go get -d rsc.io/sampler@v1.2.0 +go get rsc.io/sampler@v1.2.0 go mod tidy go list -m all stdout 'rsc.io/quote/v3 v3.0.0' diff --git a/libgo/go/cmd/go/testdata/script/mod_tidy_sum.txt b/libgo/go/cmd/go/testdata/script/mod_tidy_sum.txt index c583f560586..5a15818543e 100644 --- a/libgo/go/cmd/go/testdata/script/mod_tidy_sum.txt +++ b/libgo/go/cmd/go/testdata/script/mod_tidy_sum.txt @@ -1,12 +1,12 @@ env GO111MODULE=on # go.sum should list directly used modules and dependencies -go get -d rsc.io/quote@v1.5.2 +go get rsc.io/quote@v1.5.2 go mod tidy grep rsc.io/sampler go.sum # go.sum should not normally lose old entries -go get -d rsc.io/quote@v1.0.0 +go get rsc.io/quote@v1.0.0 grep 'rsc.io/quote v1.0.0' go.sum grep 'rsc.io/quote v1.5.2' go.sum grep rsc.io/sampler go.sum diff --git a/libgo/go/cmd/go/testdata/script/mod_tidy_too_new.txt b/libgo/go/cmd/go/testdata/script/mod_tidy_too_new.txt index b9c53b510da..8c34a997c95 100644 --- a/libgo/go/cmd/go/testdata/script/mod_tidy_too_new.txt +++ b/libgo/go/cmd/go/testdata/script/mod_tidy_too_new.txt @@ -9,7 +9,7 @@ cp go.mod go.mod.orig # would look like. ! go mod tidy -stderr 'go mod tidy: go.mod file indicates go 2000.0, but maximum supported version is '$goversion'$' +stderr 'go: go.mod file indicates go 2000.0, but maximum version supported by tidy is '$goversion'$' cmp go.mod go.mod.orig @@ -18,7 +18,7 @@ cmp go.mod go.mod.orig cp go.mod.orig go.mod go mod tidy -e -stderr 'go mod tidy: go.mod file indicates go 2000.0, but maximum supported version is '$goversion'$' +stderr 'go: go.mod file indicates go 2000.0, but maximum version supported by tidy is '$goversion'$' cmp go.mod go.mod.tidy diff --git a/libgo/go/cmd/go/testdata/script/mod_upgrade_patch.txt b/libgo/go/cmd/go/testdata/script/mod_upgrade_patch.txt index 8b34f8bf27d..79a9808ef03 100644 --- a/libgo/go/cmd/go/testdata/script/mod_upgrade_patch.txt +++ b/libgo/go/cmd/go/testdata/script/mod_upgrade_patch.txt @@ -2,7 +2,7 @@ env GO111MODULE=on [short] skip # Initially, we are at v1.0.0 for all dependencies. -go get -d +go get cp go.mod go.mod.orig go list -m all stdout '^patch.example.com/direct v1.0.0' @@ -10,15 +10,15 @@ stdout '^patch.example.com/indirect v1.0.0' ! stdout '^patch.example.com/depofdirectpatch' # @patch should be rejected for modules not already in the build list. -! go get -d patch.example.com/depofdirectpatch@patch -stderr '^go get: can''t query version "patch" of module patch.example.com/depofdirectpatch: no existing version is required$' +! go get patch.example.com/depofdirectpatch@patch +stderr '^go: can''t query version "patch" of module patch.example.com/depofdirectpatch: no existing version is required$' cmp go.mod.orig go.mod # get -u=patch, with no arguments, should patch-update all dependencies # of the package in the current directory, pulling in transitive dependencies # and also patching those. cp go.mod.orig go.mod -go get -d -u=patch +go get -u=patch go list -m all stdout '^patch.example.com/direct v1.0.1' stdout '^patch.example.com/indirect v1.0.1' @@ -26,7 +26,7 @@ stdout '^patch.example.com/depofdirectpatch v1.0.0' # 'get all@patch' should patch the modules that provide packages in 'all'. cp go.mod.orig go.mod -go get -d all@patch +go get all@patch go list -m all stdout '^patch.example.com/direct v1.0.1' stdout '^patch.example.com/indirect v1.0.1' @@ -37,14 +37,14 @@ stdout '^patch.example.com/depofdirectpatch v1.0.0' cp go.mod.orig go.mod go mod edit -droprequire=patch.example.com/direct cp go.mod go.mod.dropped -! go get -d all@patch -stderr '^go get all@patch: can''t query version "patch" of module patch.example.com/direct: no existing version is required$' +! go get all@patch +stderr '^go: all@patch: can''t query version "patch" of module patch.example.com/direct: no existing version is required$' cmp go.mod.dropped go.mod # Requesting the direct dependency with -u=patch but without an explicit version # should patch-update it and its dependencies. cp go.mod.orig go.mod -go get -d -u=patch patch.example.com/direct +go get -u=patch patch.example.com/direct go list -m all stdout '^patch.example.com/direct v1.0.1' stdout '^patch.example.com/indirect v1.0.1' @@ -52,7 +52,7 @@ stdout '^patch.example.com/depofdirectpatch v1.0.0' # Requesting only the indirect dependency should not update the direct one. cp go.mod.orig go.mod -go get -d -u=patch patch.example.com/indirect +go get -u=patch patch.example.com/indirect go list -m all stdout '^patch.example.com/direct v1.0.0' stdout '^patch.example.com/indirect v1.0.1' @@ -61,7 +61,7 @@ stdout '^patch.example.com/indirect v1.0.1' # @patch should apply only to the specific module, # but the result must reflect its upgraded requirements. cp go.mod.orig go.mod -go get -d patch.example.com/direct@patch +go get patch.example.com/direct@patch go list -m all stdout '^patch.example.com/direct v1.0.1' stdout '^patch.example.com/indirect v1.0.0' @@ -69,7 +69,7 @@ stdout '^patch.example.com/depofdirectpatch v1.0.0' # An explicit @patch should override a general -u. cp go.mod.orig go.mod -go get -d -u patch.example.com/direct@patch +go get -u patch.example.com/direct@patch go list -m all stdout '^patch.example.com/direct v1.0.1' stdout '^patch.example.com/indirect v1.1.0' @@ -77,7 +77,7 @@ stdout '^patch.example.com/depofdirectpatch v1.0.0' # An explicit @latest should override a general -u=patch. cp go.mod.orig go.mod -go get -d -u=patch patch.example.com/direct@latest +go get -u=patch patch.example.com/direct@latest go list -m all stdout '^patch.example.com/direct v1.1.0' stdout '^patch.example.com/indirect v1.0.1' @@ -86,16 +86,16 @@ stdout '^patch.example.com/indirect v1.0.1' # Standard library packages cannot be upgraded explicitly. cp go.mod.orig go.mod ! go get cmd/vet@patch -stderr 'go get: can''t request explicit version "patch" of standard library package cmd/vet$' +stderr 'go: can''t request explicit version "patch" of standard library package cmd/vet$' # However, standard-library packages without explicit versions are fine. -go get -d -u=patch -d cmd/go +go get -u=patch cmd/go # We can upgrade to a new version of a module with no root package. -go get -d example.com/noroot@v1.0.0 +go get example.com/noroot@v1.0.0 go list -m all stdout '^example.com/noroot v1.0.0$' -go get -d example.com/noroot@patch +go get example.com/noroot@patch go list -m all stdout '^example.com/noroot v1.0.1$' diff --git a/libgo/go/cmd/go/testdata/script/mod_vcs_missing.txt b/libgo/go/cmd/go/testdata/script/mod_vcs_missing.txt index f8be43cf4c6..9e6e371927b 100644 --- a/libgo/go/cmd/go/testdata/script/mod_vcs_missing.txt +++ b/libgo/go/cmd/go/testdata/script/mod_vcs_missing.txt @@ -5,7 +5,7 @@ env GO111MODULE=on env GOPROXY=direct cd empty -! go get -d launchpad.net/gocheck +! go get launchpad.net/gocheck stderr '"bzr": executable file not found' cd .. diff --git a/libgo/go/cmd/go/testdata/script/mod_vendor.txt b/libgo/go/cmd/go/testdata/script/mod_vendor.txt index 2622916f614..a2727ddf7f3 100644 --- a/libgo/go/cmd/go/testdata/script/mod_vendor.txt +++ b/libgo/go/cmd/go/testdata/script/mod_vendor.txt @@ -40,15 +40,15 @@ stdout '^v1.0.0 $' # -mod=vendor should cause 'go list' flags that look up versions to fail. ! go list -mod=vendor -versions -m x -stderr '^go list -m: can''t determine available versions using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$' +stderr '^go: can''t determine available versions using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$' ! go list -mod=vendor -u -m x -stderr '^go list -m: can''t determine available upgrades using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$' +stderr '^go: can''t determine available upgrades using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$' # 'go list -mod=vendor -m' on a transitive dependency that does not # provide vendored packages should give a helpful error rather than # 'not a known dependency'. ! go list -mod=vendor -f '{{.Version}} {{.Dir}}' -m diamondright -stderr 'go list -m: module diamondright: can''t resolve module using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: module diamondright: can''t resolve module using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' # 'go list -mod=mod' should report packages outside the import graph, # but 'go list -mod=vendor' should error out for them. @@ -82,6 +82,48 @@ exists vendor/mysite/myname/mypkg/LICENSE.txt ! exists vendor/x/x2 ! exists vendor/x/x2/LICENSE +# 'go mod vendor' should work with an alternative vendor directory if the -o flag is provided. +go mod vendor -v -o alternative-vendor-dir +exists alternative-vendor-dir/modules.txt +exists alternative-vendor-dir/a/foo/LICENSE + +# 'go mod vendor' should interpret paths relative to the current working directory when the -o flag is provided. +mkdir dir1 +mkdir dir2 + +cd dir1 +go mod vendor -v -o relative-vendor-dir + +go mod vendor -v -o ../dir2/relative-vendor-dir + +cd .. +exists dir1/relative-vendor-dir/modules.txt +exists dir1/relative-vendor-dir/a/foo/LICENSE +exists dir2/relative-vendor-dir/modules.txt +exists dir2/relative-vendor-dir/a/foo/LICENSE + +# 'go mod vendor' should fall back to the default 'vendor' directory when an empty argument is passed to the -o flag +# the same behavior should be exhibited both on the module root directory, as well as nested subdirectories + +go mod vendor -v -o '' +exists vendor/modules.txt + +env GOFLAGS=-o=foo +go mod vendor -v -o '' +exists vendor/modules.txt +env GOFLAGS='' + +mkdir -p nested/dir +cd nested/dir +go mod vendor -v -o '' +! exists vendor/ +exists ../../vendor/modules.txt +cd ../.. + +# 'go mod vendor' should work with absolute paths as well +go mod vendor -v -o $WORK/tmp/absolute-vendor-dir +exists $WORK/tmp/absolute-vendor-dir/modules.txt + [short] stop # 'go build' and 'go test' using vendored packages should succeed. diff --git a/libgo/go/cmd/go/testdata/script/mod_vendor_auto.txt b/libgo/go/cmd/go/testdata/script/mod_vendor_auto.txt index b0ea907206a..3cace73a896 100644 --- a/libgo/go/cmd/go/testdata/script/mod_vendor_auto.txt +++ b/libgo/go/cmd/go/testdata/script/mod_vendor_auto.txt @@ -17,10 +17,10 @@ stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]printversion$' stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]version$' ! go list -m all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' ! go list -m -f '{{.Dir}}' all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' # An explicit -mod=mod should force the vendor directory to be ignored. env GOFLAGS=-mod=mod @@ -105,10 +105,10 @@ stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]version$' # ...but 'go list -m' should continue to fail, this time without # referring to a -mod default that the user didn't set. ! go list -m all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' ! go list -m -f '{{.Dir}}' all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' # 'go mod init' should work if there is already a GOPATH-mode vendor directory @@ -177,7 +177,7 @@ stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]version$' # 'go get' should update from the network or module cache, # even if a vendor directory is present. -go get -d example.com/version@v1.1.0 +go get example.com/version@v1.1.0 ! go list -f {{.Dir}} -tags tools all stderr '^go: inconsistent vendoring' diff --git a/libgo/go/cmd/go/testdata/script/mod_vendor_build.txt b/libgo/go/cmd/go/testdata/script/mod_vendor_build.txt index 3b8eec0119b..4efda55e08f 100644 --- a/libgo/go/cmd/go/testdata/script/mod_vendor_build.txt +++ b/libgo/go/cmd/go/testdata/script/mod_vendor_build.txt @@ -10,7 +10,7 @@ stdout rsc.io/sampler ! grep 'rsc.io/sampler v1.3.0' go.mod # update to v1.3.1, now indirect in go.mod. -go get -d rsc.io/sampler@v1.3.1 +go get rsc.io/sampler@v1.3.1 grep 'rsc.io/sampler v1.3.1 // indirect' go.mod cp go.mod go.mod.good diff --git a/libgo/go/cmd/go/testdata/script/mod_vendor_embed.txt b/libgo/go/cmd/go/testdata/script/mod_vendor_embed.txt index be114159a1f..b14fd991564 100644 --- a/libgo/go/cmd/go/testdata/script/mod_vendor_embed.txt +++ b/libgo/go/cmd/go/testdata/script/mod_vendor_embed.txt @@ -6,11 +6,11 @@ cmp vendor/example.com/a/subdir/test/xtest/embed.txt a/subdir/test/xtest/embed.t cd broken_no_matching_files ! go mod vendor -stderr 'go mod vendor: pattern foo.txt: no matching files found' +stderr 'go: pattern foo.txt: no matching files found' cd ../broken_bad_pattern ! go mod vendor -stderr 'go mod vendor: pattern ../foo.txt: invalid pattern syntax' +stderr 'go: pattern ../foo.txt: invalid pattern syntax' # matchPotentialSourceFile prunes out tests and unbuilt code. # Make sure that they are vendored if they are embedded files. diff --git a/libgo/go/cmd/go/testdata/script/mod_vendor_goversion.txt b/libgo/go/cmd/go/testdata/script/mod_vendor_goversion.txt index aa4cb41171a..9e3618a2184 100644 --- a/libgo/go/cmd/go/testdata/script/mod_vendor_goversion.txt +++ b/libgo/go/cmd/go/testdata/script/mod_vendor_goversion.txt @@ -3,7 +3,6 @@ [short] skip - # Control case: without a vendor directory, need117 builds and bad114 doesn't. go build example.net/need117 @@ -26,7 +25,8 @@ go mod vendor ! grep 1.17 vendor/modules.txt ! go build example.net/need117 -stderr '^vendor[/\\]example\.net[/\\]need117[/\\]need117.go:5:18: .*\n\tconversion of slices to array pointers only supported as of -lang=go1\.17' +stderr '^vendor[/\\]example\.net[/\\]need117[/\\]need117.go:5:1[89]:' +stderr 'conversion of slices to array pointers requires go1\.17 or later' ! grep 1.13 vendor/modules.txt go build example.net/bad114 diff --git a/libgo/go/cmd/go/testdata/script/mod_vendor_redundant_requirement.txt b/libgo/go/cmd/go/testdata/script/mod_vendor_redundant_requirement.txt new file mode 100644 index 00000000000..3f6f5c5276b --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/mod_vendor_redundant_requirement.txt @@ -0,0 +1,29 @@ +# 'go list -mod=vendor' should succeed even when go.mod contains redundant +# requirements. Verifies #47565. +go list -mod=vendor + +-- go.mod -- +module m + +go 1.17 + +require example.com/m v0.0.0 +require example.com/m v0.0.0 + +replace example.com/m v0.0.0 => ./m +-- m/go.mod -- +module example.com/m + +go 1.17 +-- m/m.go -- +package m +-- use.go -- +package use + +import _ "example.com/m" +-- vendor/example.com/m/m.go -- +package m +-- vendor/modules.txt -- +# example.com/m v0.0.0 => ./m +## explicit; go 1.17 +example.com/m diff --git a/libgo/go/cmd/go/testdata/script/mod_vendor_replace.txt b/libgo/go/cmd/go/testdata/script/mod_vendor_replace.txt index 0c1c1d22f5b..1820af62ad8 100644 --- a/libgo/go/cmd/go/testdata/script/mod_vendor_replace.txt +++ b/libgo/go/cmd/go/testdata/script/mod_vendor_replace.txt @@ -36,7 +36,6 @@ module example.com/replace require rsc.io/quote/v3 v3.0.0 replace rsc.io/quote/v3 => ./local/not-rsc.io/quote/v3 - -- imports.go -- package replace @@ -64,3 +63,7 @@ require ( not-rsc.io/quote/v3 v3.0.0 ) replace not-rsc.io/quote/v3 => rsc.io/quote/v3 v3.0.0 +-- multiple-paths/go.sum -- +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +rsc.io/quote/v3 v3.0.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/libgo/go/cmd/go/testdata/script/mod_vendor_trimpath.txt b/libgo/go/cmd/go/testdata/script/mod_vendor_trimpath.txt index 5451aa773c8..d9d9d988974 100644 --- a/libgo/go/cmd/go/testdata/script/mod_vendor_trimpath.txt +++ b/libgo/go/cmd/go/testdata/script/mod_vendor_trimpath.txt @@ -29,8 +29,12 @@ stdout '^example.com/stack@v1.0.0/stack.go$' -- go.mod -- module example.com/main -require example.com/stack v1.0.0 +go 1.17 +require example.com/stack v1.0.0 +-- go.sum -- +example.com/stack v1.0.0 h1:IEDLeew5NytZ8vrgCF/QVem3H3SR3QMttdu9HfJvk9I= +example.com/stack v1.0.0/go.mod h1:7wFEbaV5e5O7wJ8aBdqQOR//UXppm/pwnwziMKViuI4= -- main.go -- package main diff --git a/libgo/go/cmd/go/testdata/script/mod_vendor_unused_only.txt b/libgo/go/cmd/go/testdata/script/mod_vendor_unused_only.txt index 839c6453cf8..accd9f373de 100644 --- a/libgo/go/cmd/go/testdata/script/mod_vendor_unused_only.txt +++ b/libgo/go/cmd/go/testdata/script/mod_vendor_unused_only.txt @@ -12,6 +12,8 @@ module example.com/m go 1.14 require example.com v1.0.0 // indirect +-- go.sum -- +example.com v1.0.0/go.mod h1:WRiieAqDBb1hVdDXLLdxNtCDWNfehn7FWyPC5Oz2vB4= -- go1.14-modules.txt -- # example.com v1.0.0 ## explicit diff --git a/libgo/go/cmd/go/testdata/script/mod_verify.txt b/libgo/go/cmd/go/testdata/script/mod_verify.txt index b5106659a9a..018709e33b4 100644 --- a/libgo/go/cmd/go/testdata/script/mod_verify.txt +++ b/libgo/go/cmd/go/testdata/script/mod_verify.txt @@ -39,11 +39,6 @@ stderr 'go.mod: checksum mismatch' # go.sum should be created and updated automatically. rm go.sum -go mod graph -exists go.sum -grep '^rsc.io/quote v1.1.0/go.mod ' go.sum -! grep '^rsc.io/quote v1.1.0 ' go.sum - go mod tidy grep '^rsc.io/quote v1.1.0/go.mod ' go.sum grep '^rsc.io/quote v1.1.0 ' go.sum @@ -59,7 +54,7 @@ go mod verify # Packages below module root should not be mentioned in go.sum. rm go.sum go mod edit -droprequire rsc.io/quote -go get -d rsc.io/quote/buggy +go get rsc.io/quote/buggy grep '^rsc.io/quote v1.5.2/go.mod ' go.sum ! grep buggy go.sum diff --git a/libgo/go/cmd/go/testdata/script/modfile_flag.txt b/libgo/go/cmd/go/testdata/script/modfile_flag.txt index 0ad08808178..398e523a9ce 100644 --- a/libgo/go/cmd/go/testdata/script/modfile_flag.txt +++ b/libgo/go/cmd/go/testdata/script/modfile_flag.txt @@ -24,6 +24,11 @@ stdout '^go.alt.mod$' go mod edit -require rsc.io/quote@v1.5.2 grep rsc.io/quote go.alt.mod +# 'go list -m' should add sums to the alternate go.sum. +go list -m -mod=mod all +grep '^rsc.io/quote v1.5.2/go.mod ' go.alt.sum +! grep '^rsc.io/quote v1.5.2 ' go.alt.sum + # other 'go mod' commands should work. 'go mod vendor' is tested later. go mod download rsc.io/quote go mod graph @@ -41,7 +46,7 @@ go list -mod=mod . grep rsc.io/quote go.alt.mod go build -n -mod=mod . go test -n -mod=mod . -go get -d rsc.io/quote +go get rsc.io/quote # 'go mod vendor' should work. @@ -73,7 +78,7 @@ cmp go.mod go.mod.orig cmp go.sum go.sum.orig -# If the altnernate mod file does not have a ".mod" suffix, an error +# If the alternate mod file does not have a ".mod" suffix, an error # should be reported. cp go.alt.mod goaltmod ! go mod tidy -modfile=goaltmod diff --git a/libgo/go/cmd/go/testdata/script/run_dirs.txt b/libgo/go/cmd/go/testdata/script/run_dirs.txt index 538a6ac6f39..bd5cfbe3fb2 100644 --- a/libgo/go/cmd/go/testdata/script/run_dirs.txt +++ b/libgo/go/cmd/go/testdata/script/run_dirs.txt @@ -1,11 +1,21 @@ cd rundir ! go run x.go sub/sub.go -stderr 'named files must all be in one directory; have ./ and sub/' +stderr 'named files must all be in one directory; have . and sub' ! go run sub/sub.go x.go -stderr 'named files must all be in one directory; have sub/ and ./' +stderr 'named files must all be in one directory; have sub and .' + +cd ../ +go run rundir/foo.go ./rundir/bar.go +stderr 'hello world' -- rundir/sub/sub.go -- package main -- rundir/x.go -- package main +-- rundir/foo.go -- +package main +func main() { println(msg) } +-- rundir/bar.go -- +package main +const msg = "hello world" diff --git a/libgo/go/cmd/go/testdata/script/run_wildcard.txt b/libgo/go/cmd/go/testdata/script/run_wildcard.txt index 72036d1d8db..3e7e7b7e42f 100644 --- a/libgo/go/cmd/go/testdata/script/run_wildcard.txt +++ b/libgo/go/cmd/go/testdata/script/run_wildcard.txt @@ -4,4 +4,4 @@ env GO111MODULE=off # go run x/... should not panic when directory x doesn't exist. ! go run nonexistent/... -stderr '^go run: no packages loaded from nonexistent/...$' +stderr '^go: no packages loaded from nonexistent/...$' diff --git a/libgo/go/cmd/go/testdata/script/test_benchmark_1x.txt b/libgo/go/cmd/go/testdata/script/test_benchmark_1x.txt new file mode 100644 index 00000000000..b1d4c39c16f --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_benchmark_1x.txt @@ -0,0 +1,37 @@ +# Test that -benchtime 1x only runs a total of 1 loop iteration. +# See golang.org/issue/32051. + +go test -run ^$ -bench . -benchtime 1x + +-- go.mod -- +module bench + +go 1.16 +-- x_test.go -- +package bench + +import ( + "fmt" + "os" + "testing" +) + +var called = false + +func TestMain(m *testing.M) { + m.Run() + if !called { + fmt.Println("benchmark never called") + os.Exit(1) + } +} + +func Benchmark(b *testing.B) { + if b.N > 1 { + b.Fatalf("called with b.N=%d; want b.N=1 only", b.N) + } + if called { + b.Fatal("called twice") + } + called = true +} diff --git a/libgo/go/cmd/go/testdata/script/test_build_failure.txt b/libgo/go/cmd/go/testdata/script/test_build_failure.txt index 8d13634c8c4..e8c984f272c 100644 --- a/libgo/go/cmd/go/testdata/script/test_build_failure.txt +++ b/libgo/go/cmd/go/testdata/script/test_build_failure.txt @@ -3,7 +3,7 @@ ! go test -x coverbad ! stderr '[\\/]coverbad\.test( |$)' # 'go test' should not claim to have run the test. stderr 'undefined: g' -stderr 'undefined: j' +[cgo] stderr 'undefined: j' -- go.mod -- module coverbad diff --git a/libgo/go/cmd/go/testdata/script/test_cache_inputs.txt b/libgo/go/cmd/go/testdata/script/test_cache_inputs.txt index d694a309947..3705c700d10 100644 --- a/libgo/go/cmd/go/testdata/script/test_cache_inputs.txt +++ b/libgo/go/cmd/go/testdata/script/test_cache_inputs.txt @@ -108,6 +108,12 @@ go test testcache -run=Benchtime -bench=Benchtime -benchtime=1x go test testcache -run=Benchtime -bench=Benchtime -benchtime=1x ! stdout '\(cached\)' +# golang.org/issue/47355: that includes the `-failfast` argument. +go test testcache -run=TestOSArgs -failfast +! stdout '\(cached\)' +go test testcache -run=TestOSArgs -failfast +stdout '\(cached\)' + # Executables within GOROOT and GOPATH should affect caching, # even if the test does not stat them explicitly. diff --git a/libgo/go/cmd/go/testdata/script/test_fail_newline.txt b/libgo/go/cmd/go/testdata/script/test_fail_newline.txt new file mode 100644 index 00000000000..43cee565a19 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fail_newline.txt @@ -0,0 +1,65 @@ +[short] skip + +# In package list mode, output is buffered. +# Check that a newline is printed after the buffer's contents. +cd fail +! go test . +! stderr . +stdout '^exitcode=1\n' +stdout '^FAIL\s+example/fail' + +# In local directory mode output is streamed, so we don't know +# whether the test printed anything at all, so we print the exit code +# (just in case it failed without emitting any output at all), +# and that happens to add the needed newline as well. +! go test +! stderr . +stdout '^exitcode=1exit status 1\n' +stdout '^FAIL\s+example/fail' + +# In package list mode, if the test passes the 'ok' message appears +# on its own line. +cd ../skip +go test -v . +! stderr . +stdout '^skipping\n' +stdout '^ok\s+example/skip' + +# If the output is streamed and the test passes, we can't tell whether it ended +# in a partial line, and don't want to emit any extra output in the +# overwhelmingly common case that it did not. +# (In theory we could hook the 'os' package to report whether output +# was emitted and whether it ended in a newline, but that seems too invasive.) +go test +! stderr . +stdout '^skippingok\s+example/skip' + + +-- go.mod -- +module example + +go 1.18 +-- fail/fail_test.go -- +package fail + +import ( + "os" + "testing" +) + +func TestMain(m *testing.M) { + os.Stderr.WriteString("exitcode=1") + os.Exit(1) +} +-- skip/skip_test.go -- +package skip + +import ( + "os" + "testing" +) + +func TestMain(m *testing.M) { + os.Stderr.WriteString("skipping") + os.Exit(0) +} diff --git a/libgo/go/cmd/go/testdata/script/test_flag.txt b/libgo/go/cmd/go/testdata/script/test_flag.txt index 0142b3f308f..d168cfe6a8c 100644 --- a/libgo/go/cmd/go/testdata/script/test_flag.txt +++ b/libgo/go/cmd/go/testdata/script/test_flag.txt @@ -9,13 +9,13 @@ go test -count=1 -custom -args -v=7 # However, it should be an error to use custom flags when -i or -c are used, # since we know for sure that no test binary will run at all. ! go test -i -custom -stderr '^go test: unknown flag -custom cannot be used with -i$' +stderr '^go: unknown flag -custom cannot be used with -i$' ! go test -c -custom -stderr '^go test: unknown flag -custom cannot be used with -c$' +stderr '^go: unknown flag -custom cannot be used with -c$' # The same should apply even if -c or -i come after a custom flag. ! go test -custom -c -stderr '^go test: unknown flag -custom cannot be used with -c$' +stderr '^go: unknown flag -custom cannot be used with -c$' -- go.mod -- module m diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz.txt b/libgo/go/cmd/go/testdata/script/test_fuzz.txt new file mode 100644 index 00000000000..3e048e00c55 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz.txt @@ -0,0 +1,498 @@ +[!fuzz] skip + +# Test that running a fuzz target that returns without failing or calling +# f.Fuzz fails and causes a non-zero exit status. +! go test noop_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test that fuzzing a fuzz target that returns without failing or calling +# f.Fuzz fails and causes a non-zero exit status. +! go test -fuzz=Fuzz -fuzztime=1x noop_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test that calling f.Error in a fuzz target causes a non-zero exit status. +! go test -fuzz=Fuzz -fuzztime=1x error_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test that calling f.Fatal in a fuzz target causes a non-zero exit status. +! go test fatal_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test that successful test exits cleanly. +go test success_fuzz_test.go +stdout ^ok +! stdout FAIL + +# Test that successful fuzzing exits cleanly. +go test -fuzz=Fuzz -fuzztime=1x success_fuzz_test.go +stdout ok +! stdout FAIL + +# Test that calling f.Fatal while fuzzing causes a non-zero exit status. +! go test -fuzz=Fuzz -fuzztime=1x fatal_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test error with seed corpus in f.Fuzz +! go test -run FuzzError fuzz_add_test.go +! stdout ^ok +stdout FAIL +stdout 'error here' + +[short] stop + +# Test that calling panic(nil) in a fuzz target causes a non-zero exit status. +! go test panic_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test that skipped test exits cleanly. +go test skipped_fuzz_test.go +stdout ok +! stdout FAIL + +# Test that f.Fatal within f.Fuzz panics +! go test fatal_fuzz_fn_fuzz_test.go +! stdout ^ok +! stdout 'fatal here' +stdout FAIL +stdout 'fuzz target' + +# Test that f.Error within f.Fuzz panics +! go test error_fuzz_fn_fuzz_test.go +! stdout ^ok +! stdout 'error here' +stdout FAIL +stdout 'fuzz target' + +# Test that f.Fail within f.Fuzz panics +! go test fail_fuzz_fn_fuzz_test.go +! stdout ^ok +stdout FAIL +stdout 'fuzz target' + +# Test that f.Skip within f.Fuzz panics +! go test skip_fuzz_fn_fuzz_test.go +! stdout ^ok +! stdout 'skip here' +stdout FAIL +stdout 'fuzz target' + +# Test that f.Skipped within f.Fuzz panics +! go test skipped_fuzz_fn_fuzz_test.go +! stdout ^ok +! stdout 'f.Skipped is' +stdout FAIL +stdout 'fuzz target' +stdout 't.Skipped is false' + +# Test that runtime.Goexit within the fuzz function is an error. +! go test goexit_fuzz_fn_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test that a call to f.Fatal after the Fuzz func is executed. +! go test fatal_after_fuzz_func_fuzz_test.go +! stdout ok +stdout FAIL + +# Test that missing *T in f.Fuzz causes a non-zero exit status. +! go test incomplete_fuzz_call_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test that a panic in the Cleanup func is executed. +! go test cleanup_fuzz_test.go +! stdout ^ok +stdout FAIL +stdout 'failed some precondition' + +# Test success with seed corpus in f.Fuzz +go test -run FuzzPass fuzz_add_test.go +stdout ok +! stdout FAIL +! stdout 'off by one error' + +# Test fatal with seed corpus in f.Fuzz +! go test -run FuzzFatal fuzz_add_test.go +! stdout ^ok +stdout FAIL +stdout 'fatal here' + +# Test panic with seed corpus in f.Fuzz +! go test -run FuzzPanic fuzz_add_test.go +! stdout ^ok +stdout FAIL +stdout 'off by one error' + +# Test panic(nil) with seed corpus in f.Fuzz +! go test -run FuzzNilPanic fuzz_add_test.go +! stdout ^ok +stdout FAIL + +# Test panic with unsupported seed corpus +! go test -run FuzzUnsupported fuzz_add_test.go +! stdout ^ok +stdout FAIL + +# Test panic with different number of args to f.Add +! go test -run FuzzAddDifferentNumber fuzz_add_test.go +! stdout ^ok +stdout FAIL + +# Test panic with different type of args to f.Add +! go test -run FuzzAddDifferentType fuzz_add_test.go +! stdout ^ok +stdout FAIL + +# Test that the wrong type given with f.Add will fail. +! go test -run FuzzWrongType fuzz_add_test.go +! stdout ^ok +stdout '\[string int\], want \[\[\]uint8 int8\]' +stdout FAIL + +# Test fatal with testdata seed corpus +! go test -run FuzzFail corpustesting/fuzz_testdata_corpus_test.go +! stdout ^ok +stdout FAIL +stdout 'fatal here' + +# Test pass with testdata seed corpus +go test -run FuzzPass corpustesting/fuzz_testdata_corpus_test.go +stdout ok +! stdout FAIL +! stdout 'fatal here' + +# Test pass with testdata and f.Add seed corpus +go test -run FuzzPassString corpustesting/fuzz_testdata_corpus_test.go +stdout ok +! stdout FAIL + +# Fuzzing pass with testdata and f.Add seed corpus (skip running tests first) +go test -run=None -fuzz=FuzzPassString corpustesting/fuzz_testdata_corpus_test.go -fuzztime=10x +stdout ok +! stdout FAIL + +# Fuzzing pass with testdata and f.Add seed corpus +go test -run=FuzzPassString -fuzz=FuzzPassString corpustesting/fuzz_testdata_corpus_test.go -fuzztime=10x +stdout ok +! stdout FAIL + +# Test panic with malformed seed corpus +! go test -run FuzzFail corpustesting/fuzz_testdata_corpus_test.go +! stdout ^ok +stdout FAIL + +# Test pass with file in other nested testdata directory +go test -run FuzzInNestedDir corpustesting/fuzz_testdata_corpus_test.go +stdout ok +! stdout FAIL +! stdout 'fatal here' + +# Test fails with file containing wrong type +! go test -run FuzzWrongType corpustesting/fuzz_testdata_corpus_test.go +! stdout ^ok +stdout FAIL + +-- noop_fuzz_test.go -- +package noop_fuzz + +import "testing" + +func Fuzz(f *testing.F) {} + +-- error_fuzz_test.go -- +package error_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Error("error in target") +} + +-- fatal_fuzz_test.go -- +package fatal_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Fatal("fatal in target") +} + +-- panic_fuzz_test.go -- +package panic_fuzz + +import "testing" + +func FuzzPanic(f *testing.F) { + panic(nil) +} + +-- success_fuzz_test.go -- +package success_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Fuzz(func (*testing.T, []byte) {}) +} + +-- skipped_fuzz_test.go -- +package skipped_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Skip() +} + +-- fatal_fuzz_fn_fuzz_test.go -- +package fatal_fuzz_fn_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + f.Fatal("fatal here") + }) +} + +-- error_fuzz_fn_fuzz_test.go -- +package error_fuzz_fn_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + f.Error("error here") + }) +} + +-- fail_fuzz_fn_fuzz_test.go -- +package skip_fuzz_fn_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + f.Fail() + }) +} + +-- skip_fuzz_fn_fuzz_test.go -- +package skip_fuzz_fn_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + f.Skip("skip here") + }) +} + +-- skipped_fuzz_fn_fuzz_test.go -- +package skipped_fuzz_fn_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + t.Logf("t.Skipped is %t\n", t.Skipped()) + t.Logf("f.Skipped is %t\n", f.Skipped()) + }) +} + +-- goexit_fuzz_fn_fuzz_test.go -- +package goexit_fuzz_fn_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + runtime.Goexit() + }) +} + +-- fatal_after_fuzz_func_fuzz_test.go -- +package fatal_after_fuzz_func_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) { + // no-op + }) + f.Fatal("this shouldn't be called") +} + +-- incomplete_fuzz_call_fuzz_test.go -- +package incomplete_fuzz_call_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Fuzz(func(b []byte) { + // this is missing *testing.T as the first param, so should panic + }) +} + +-- cleanup_fuzz_test.go -- +package cleanup_fuzz_test + +import "testing" + +func Fuzz(f *testing.F) { + f.Cleanup(func() { + panic("failed some precondition") + }) + f.Fuzz(func(t *testing.T, b []byte) { + // no-op + }) +} + +-- fuzz_add_test.go -- +package fuzz_add + +import "testing" + +func add(f *testing.F) { + f.Helper() + f.Add([]byte("123")) + f.Add([]byte("12345")) + f.Add([]byte("")) +} + +func FuzzPass(f *testing.F) { + add(f) + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) == -1 { + t.Fatal("fatal here") // will not be executed + } + }) +} + +func FuzzError(f *testing.F) { + add(f) + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) == 3 { + t.Error("error here") + } + }) +} + +func FuzzFatal(f *testing.F) { + add(f) + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) == 0 { + t.Fatal("fatal here") + } + }) +} + +func FuzzPanic(f *testing.F) { + add(f) + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) == 5 { + panic("off by one error") + } + }) +} + +func FuzzNilPanic(f *testing.F) { + add(f) + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) == 3 { + panic(nil) + } + }) +} + +func FuzzUnsupported(f *testing.F) { + m := make(map[string]bool) + f.Add(m) + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzAddDifferentNumber(f *testing.F) { + f.Add([]byte("a")) + f.Add([]byte("a"), []byte("b")) + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzAddDifferentType(f *testing.F) { + f.Add(false) + f.Add(1234) + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzWrongType(f *testing.F) { + f.Add("hello", 50) + f.Fuzz(func(*testing.T, []byte, int8) {}) +} + +-- corpustesting/fuzz_testdata_corpus_test.go -- +package fuzz_testdata_corpus + +import "testing" + +func fuzzFn(f *testing.F) { + f.Helper() + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) == "12345" { + t.Fatal("fatal here") + } + }) +} + +func FuzzFail(f *testing.F) { + fuzzFn(f) +} + +func FuzzPass(f *testing.F) { + fuzzFn(f) +} + +func FuzzPassString(f *testing.F) { + f.Add("some seed corpus") + f.Fuzz(func(*testing.T, string) {}) +} + +func FuzzPanic(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) {}) +} + +func FuzzInNestedDir(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) {}) +} + +func FuzzWrongType(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) {}) +} + +-- corpustesting/testdata/fuzz/FuzzFail/1 -- +go test fuzz v1 +[]byte("12345") +-- corpustesting/testdata/fuzz/FuzzPass/1 -- +go test fuzz v1 +[]byte("00000") +-- corpustesting/testdata/fuzz/FuzzPassString/1 -- +go test fuzz v1 +string("hello") +-- corpustesting/testdata/fuzz/FuzzPanic/1 -- +malformed +-- corpustesting/testdata/fuzz/FuzzInNestedDir/anotherdir/1 -- +go test fuzz v1 +[]byte("12345") +-- corpustesting/testdata/fuzz/FuzzWrongType/1 -- +go test fuzz v1 +int("00000") diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_cache.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_cache.txt new file mode 100644 index 00000000000..552966b06bd --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_cache.txt @@ -0,0 +1,97 @@ +[!fuzz-instrumented] skip + +[short] skip +env GOCACHE=$WORK/cache + +# Fuzz cache should not exist after a regular test run. +go test . +exists $GOCACHE +! exists $GOCACHE/fuzz + +# Fuzzing should write interesting values to the cache. +go test -fuzz=FuzzY -fuzztime=100x . +go run ./contains_files $GOCACHE/fuzz/example.com/y/FuzzY + +# 'go clean -cache' should not delete the fuzz cache. +go clean -cache +exists $GOCACHE/fuzz + +# 'go clean -fuzzcache' should delete the fuzz cache but not the build cache. +go list -f {{.Stale}} ./empty +stdout true +go install ./empty +go list -f {{.Stale}} ./empty +stdout false +go clean -fuzzcache +! exists $GOCACHE/fuzz +go list -f {{.Stale}} ./empty +stdout false + +# Fuzzing indicates that one new interesting value was found with an empty +# corpus, and the total size of the cache is now 1. +go clean -fuzzcache +go test -fuzz=FuzzEmpty -fuzztime=10000x . +stdout 'new interesting: 1' +stdout 'total: 1' + +# Fuzzing again with a small fuzztime does not find any other interesting +# values but still indicates that the cache size is 1. +go test -fuzz=FuzzEmpty -fuzztime=2x . +stdout 'new interesting: 0' +stdout 'total: 1' + +-- go.mod -- +module example.com/y + +go 1.16 +-- y_test.go -- +package y + +import ( + "io" + "testing" +) + +func FuzzEmpty(f *testing.F) { + f.Fuzz(func (*testing.T, []byte) {}) +} + +func FuzzY(f *testing.F) { + f.Add([]byte("y")) + f.Fuzz(func(t *testing.T, b []byte) { Y(io.Discard, b) }) +} +-- y.go -- +package y + +import ( + "bytes" + "io" +) + +func Y(w io.Writer, b []byte) { + if !bytes.Equal(b, []byte("y")) { + w.Write([]byte("not equal")) + } +} +-- empty/empty.go -- +package empty +-- contains_files/contains_files.go -- +package main + +import ( + "fmt" + "path/filepath" + "io/ioutil" + "os" +) + +func main() { + infos, err := ioutil.ReadDir(filepath.Clean(os.Args[1])) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + if len(infos) == 0 { + os.Exit(1) + } +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_chatty.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_chatty.txt new file mode 100644 index 00000000000..d07fe50f95e --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_chatty.txt @@ -0,0 +1,102 @@ +[!fuzz] skip +[short] skip + +# Run chatty fuzz targets with an error. +! go test -v chatty_error_fuzz_test.go +! stdout '^ok' +stdout 'FAIL' +stdout 'error in target' + +# Run chatty fuzz targets with a fatal. +! go test -v chatty_fatal_fuzz_test.go +! stdout '^ok' +stdout 'FAIL' +stdout 'fatal in target' + +# Run chatty fuzz target with a panic +! go test -v chatty_panic_fuzz_test.go +! stdout ^ok +stdout FAIL +stdout 'this is bad' + +# Run skipped chatty fuzz targets. +go test -v chatty_skipped_fuzz_test.go +stdout ok +stdout SKIP +! stdout FAIL + +# Run successful chatty fuzz targets. +go test -v chatty_fuzz_test.go +stdout ok +stdout PASS +stdout 'all good here' +! stdout FAIL + +# Fuzz successful chatty fuzz target that includes a separate unit test. +go test -v chatty_with_test_fuzz_test.go -fuzz=Fuzz -fuzztime=1x +stdout ok +stdout PASS +! stdout FAIL +stdout -count=1 'all good here' +# Verify that the unit test is only run once. +stdout -count=1 'logged foo' + +-- chatty_error_fuzz_test.go -- +package chatty_error_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Error("error in target") +} + +-- chatty_fatal_fuzz_test.go -- +package chatty_fatal_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Fatal("fatal in target") +} + +-- chatty_panic_fuzz_test.go -- +package chatty_panic_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + panic("this is bad") +} + +-- chatty_skipped_fuzz_test.go -- +package chatty_skipped_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Skip() +} + +-- chatty_fuzz_test.go -- +package chatty_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Log("all good here") + f.Fuzz(func(*testing.T, []byte) {}) +} + +-- chatty_with_test_fuzz_test.go -- +package chatty_with_test_fuzz + +import "testing" + +func TestFoo(t *testing.T) { + t.Log("logged foo") +} + +func Fuzz(f *testing.F) { + f.Log("all good here") + f.Fuzz(func(*testing.T, []byte) {}) +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_cleanup.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_cleanup.txt new file mode 100644 index 00000000000..b65022bd74e --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_cleanup.txt @@ -0,0 +1,66 @@ +[!fuzz] skip +[short] skip + +# Cleanup should run after F.Skip. +go test -run=FuzzTargetSkip +stdout cleanup + +# Cleanup should run after F.Fatal. +! go test -run=FuzzTargetFatal +stdout cleanup + +# Cleanup should run after an unexpected runtime.Goexit. +! go test -run=FuzzTargetGoexit +stdout cleanup + +# Cleanup should run after panic. +! go test -run=FuzzTargetPanic +stdout cleanup + +# Cleanup should run in fuzz function on seed corpus. +go test -v -run=FuzzFunction +stdout '(?s)inner.*outer' + +# TODO(jayconrod): test cleanup while fuzzing. For now, the worker process's +# stdout and stderr is connected to the coordinator's, but it should eventually +# be connected to os.DevNull, so we wouldn't see t.Log output. + +-- go.mod -- +module cleanup + +go 1.15 +-- cleanup_test.go -- +package cleanup + +import ( + "runtime" + "testing" +) + +func FuzzTargetSkip(f *testing.F) { + f.Cleanup(func() { f.Log("cleanup") }) + f.Skip() +} + +func FuzzTargetFatal(f *testing.F) { + f.Cleanup(func() { f.Log("cleanup") }) + f.Fatal() +} + +func FuzzTargetGoexit(f *testing.F) { + f.Cleanup(func() { f.Log("cleanup") }) + runtime.Goexit() +} + +func FuzzTargetPanic(f *testing.F) { + f.Cleanup(func() { f.Log("cleanup") }) + panic("oh no") +} + +func FuzzFunction(f *testing.F) { + f.Add([]byte{0}) + f.Cleanup(func() { f.Log("outer") }) + f.Fuzz(func(t *testing.T, b []byte) { + t.Cleanup(func() { t.Logf("inner") }) + }) +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_cov.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_cov.txt new file mode 100644 index 00000000000..05b634889f3 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_cov.txt @@ -0,0 +1,33 @@ +# Test that coverage instrumentation is working. Without the instrumentation +# it is _extremely_ unlikely that the fuzzer would produce this particular +# input in any reasonable amount of time. + +[short] skip +[!fuzz-instrumented] skip + +! go test -fuzz=FuzzCov +! stderr 'cov instrumentation working' + +-- go.mod -- +module test + +-- cov_test.go -- +package cov + +import "testing" + +func FuzzCov(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) == 8 && + b[0] == 'h' && + b[1] == 'e' && + b[2] == 'l' && + b[3] == 'l' && + b[4] == 'o' && + b[5] == ' ' && + b[6] == ':' && + b[7] == ')' { + panic("cov instrumentation working") + } + }) +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_deadline.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_deadline.txt new file mode 100644 index 00000000000..5ba76a3d4fe --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_deadline.txt @@ -0,0 +1,35 @@ +[!fuzz] skip +[short] skip + +# The fuzz function should be able to detect whether -timeout +# was set with T.Deadline. Note there is no F.Deadline, and +# there is no timeout while fuzzing, even if -fuzztime is set. +go test -run=FuzzDeadline -wantdeadline=true # -timeout defaults to 10m +go test -run=FuzzDeadline -timeout=0 -wantdeadline=false +! go test -run=FuzzDeadline -timeout=1s -wantdeadline=false +go test -run=FuzzDeadline -timeout=1s -wantdeadline=true +go test -fuzz=FuzzDeadline -timeout=0 -fuzztime=1s -wantdeadline=false +go test -fuzz=FuzzDeadline -timeout=0 -fuzztime=100x -wantdeadline=false + +-- go.mod -- +module fuzz + +go 1.16 +-- fuzz_deadline_test.go -- +package fuzz_test + +import ( + "flag" + "testing" +) + +var wantDeadline = flag.Bool("wantdeadline", false, "whether the test should have a deadline") + +func FuzzDeadline(f *testing.F) { + f.Add("run once") + f.Fuzz(func (t *testing.T, _ string) { + if _, hasDeadline := t.Deadline(); hasDeadline != *wantDeadline { + t.Fatalf("function got %v; want %v", hasDeadline, *wantDeadline) + } + }) +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_fuzztime.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_fuzztime.txt new file mode 100644 index 00000000000..56d94a4bcf5 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_fuzztime.txt @@ -0,0 +1,121 @@ +[!fuzz] skip +[short] skip + +# There are no seed values, so 'go test' should finish quickly. +go test + +# Fuzzing should exit 0 after fuzztime, even if timeout is short. +go test -timeout=3s -fuzz=FuzzFast -fuzztime=5s + +# We should see the same behavior when invoking the test binary directly. +go test -c +exec ./fuzz.test$GOEXE -test.timeout=3s -test.fuzz=FuzzFast -test.fuzztime=5s -test.parallel=1 -test.fuzzcachedir=$WORK/cache + +# Timeout should not cause inputs to be written as crashers. +! exists testdata/fuzz + +env GOCACHE=$WORK/tmp + +# When we use fuzztime with an "x" suffix, it runs a specific number of times. +# This fuzz function creates a file with a unique name ($pid.$count) on each +# run. We count the files to find the number of runs. +mkdir count +go test -fuzz=FuzzTestCount -fuzztime=1000x -fuzzminimizetime=1x +go run check_file_count.go count 1000 + +# When we use fuzzminimizetime with an "x" suffix, it runs a specific number of +# times while minimizing. This fuzz function creates a file with a unique name +# ($pid.$count) on each run once the first crash has been found. That means that +# there should be one file for each execution of the fuzz function during +# minimization, so we count these to determine how many times minimization was +# run. +mkdir minimizecount +! go test -fuzz=FuzzMinimizeCount -fuzzminimizetime=3x -parallel=1 +go run check_file_count.go minimizecount 3 + +-- go.mod -- +module fuzz + +go 1.16 +-- fuzz_fast_test.go -- +package fuzz_test + +import "testing" + +func FuzzFast(f *testing.F) { + f.Fuzz(func (*testing.T, []byte) {}) +} +-- fuzz_count_test.go -- +package fuzz + +import ( + "fmt" + "os" + "testing" +) + +func FuzzTestCount(f *testing.F) { + pid := os.Getpid() + n := 0 + f.Fuzz(func(t *testing.T, _ []byte) { + name := fmt.Sprintf("count/%v.%d", pid, n) + if err := os.WriteFile(name, nil, 0666); err != nil { + t.Fatal(err) + } + n++ + }) +} +-- fuzz_minimize_count_test.go -- +package fuzz + +import ( + "bytes" + "fmt" + "os" + "testing" +) + +func FuzzMinimizeCount(f *testing.F) { + pid := os.Getpid() + n := 0 + seed := bytes.Repeat([]byte("a"), 357) + f.Add(seed) + crashFound := false + f.Fuzz(func(t *testing.T, b []byte) { + if crashFound { + name := fmt.Sprintf("minimizecount/%v.%d", pid, n) + if err := os.WriteFile(name, nil, 0666); err != nil { + t.Fatal(err) + } + n++ + } + if !bytes.Equal(b, seed) { // this should happen right away + crashFound = true + t.Error("minimize this!") + } + }) +} +-- check_file_count.go -- +// +build ignore + +package main + +import ( + "fmt" + "os" + "strconv" +) + +func main() { + dir, err := os.ReadDir(os.Args[1]) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + got := len(dir) + want, _ := strconv.Atoi(os.Args[2]) + if got != want { + fmt.Fprintf(os.Stderr, "got %d files; want %d\n", got, want) + os.Exit(1) + } +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_io_error.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_io_error.txt new file mode 100644 index 00000000000..1a0aa6427e8 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_io_error.txt @@ -0,0 +1,101 @@ +# Test that when the coordinator experiences an I/O error communicating +# with a worker, the coordinator stops the worker and reports the error. +# The coordinator should not record a crasher. +# +# We simulate an I/O error in the test by writing garbage to fuzz_out. +# This is unlikely, but possible. It's difficult to simulate interruptions +# due to ^C and EOF errors which are more common. We don't report those. +[short] skip +[!fuzz] skip + +# If the I/O error occurs before F.Fuzz is called, the coordinator should +# stop the worker and say that. +! go test -fuzz=FuzzClosePipeBefore -parallel=1 +stdout '\s*fuzzing process terminated without fuzzing:' +! stdout 'communicating with fuzzing process' +! exists testdata + +# If the I/O error occurs after F.Fuzz is called (unlikely), just exit. +# It's hard to distinguish this case from the worker being interrupted by ^C +# or exiting with status 0 (which it should do when interrupted by ^C). +! go test -fuzz=FuzzClosePipeAfter -parallel=1 +stdout '^\s*communicating with fuzzing process: invalid character ''!'' looking for beginning of value$' +! exists testdata + +-- go.mod -- +module test + +go 1.17 +-- io_error_test.go -- +package io_error + +import ( + "flag" + "testing" + "time" +) + +func isWorker() bool { + f := flag.Lookup("test.fuzzworker") + if f == nil { + return false + } + get, ok := f.Value.(flag.Getter) + if !ok { + return false + } + return get.Get() == interface{}(true) +} + +func FuzzClosePipeBefore(f *testing.F) { + if isWorker() { + sendGarbageToCoordinator(f) + time.Sleep(3600 * time.Second) // pause until coordinator terminates the process + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzClosePipeAfter(f *testing.F) { + f.Fuzz(func(t *testing.T, _ []byte) { + if isWorker() { + sendGarbageToCoordinator(t) + time.Sleep(3600 * time.Second) // pause until coordinator terminates the process + } + }) +} +-- io_error_windows_test.go -- +package io_error + +import ( + "fmt" + "os" + "testing" +) + +func sendGarbageToCoordinator(tb testing.TB) { + v := os.Getenv("GO_TEST_FUZZ_WORKER_HANDLES") + var fuzzInFD, fuzzOutFD uintptr + if _, err := fmt.Sscanf(v, "%x,%x", &fuzzInFD, &fuzzOutFD); err != nil { + tb.Fatalf("parsing GO_TEST_FUZZ_WORKER_HANDLES: %v", err) + } + f := os.NewFile(fuzzOutFD, "fuzz_out") + if _, err := f.Write([]byte("!!")); err != nil { + tb.Fatalf("writing fuzz_out: %v", err) + } +} +-- io_error_notwindows_test.go -- +// +build !windows + +package io_error + +import ( + "os" + "testing" +) + +func sendGarbageToCoordinator(tb testing.TB) { + f := os.NewFile(4, "fuzz_out") + if _, err := f.Write([]byte("!!")); err != nil { + tb.Fatalf("writing fuzz_out: %v", err) + } +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_match.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_match.txt new file mode 100644 index 00000000000..dbf987605f3 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_match.txt @@ -0,0 +1,38 @@ +[!fuzz] skip + +# Matches only fuzz targets to test. +go test standalone_fuzz_test.go +! stdout '^ok.*\[no tests to run\]' +stdout '^ok' + +# Matches only for fuzzing. +go test -fuzz Fuzz -fuzztime 1x standalone_fuzz_test.go +! stdout '^ok.*\[no tests to run\]' +stdout '^ok' + +# Matches none for fuzzing but will run the fuzz target as a test. +go test -fuzz ThisWillNotMatch -fuzztime 1x standalone_fuzz_test.go +! stdout '^ok.*no tests to run' +stdout '^ok' +stdout 'no fuzz tests to fuzz' + +[short] stop + +# Matches only fuzz targets to test with -run. +go test -run Fuzz standalone_fuzz_test.go +! stdout '^ok.*\[no tests to run\]' +stdout '^ok' + +# Matches no fuzz targets. +go test -run ThisWillNotMatch standalone_fuzz_test.go +stdout '^ok.*no tests to run' +! stdout 'no fuzz tests to fuzz' + +-- standalone_fuzz_test.go -- +package standalone_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Fuzz(func (*testing.T, []byte) {}) +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_minimize.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_minimize.txt new file mode 100644 index 00000000000..a6dc3f19534 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_minimize.txt @@ -0,0 +1,203 @@ +[!fuzz] skip +[short] skip + +# We clean the fuzz cache during this test. Don't clean the user's cache. +env GOCACHE=$WORK/gocache + +# Test that fuzzminimizetime cannot be negative seconds +! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x -fuzzminimizetime=-1ms . +! stdout '^ok' +! stdout 'contains a non-zero byte' +stdout 'invalid duration' +stdout FAIL + +# Test that fuzzminimizetime cannot be negative times +! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x -fuzzminimizetime=-1x . +! stdout '^ok' +! stdout 'contains a non-zero byte' +stdout 'invalid count' +stdout FAIL + +# Test that fuzzminimizetime can be zero seconds, and minimization is disabled +! go test -fuzz=FuzzMinimizeZeroDurationSet -run=FuzzMinimizeZeroDurationSet -fuzztime=10000x -fuzzminimizetime=0s . +! stdout '^ok' +! stdout 'minimizing' +stdout 'there was an Error' +stdout FAIL + +# Test that fuzzminimizetime can be zero times, and minimization is disabled +! go test -fuzz=FuzzMinimizeZeroLimitSet -run=FuzzMinimizeZeroLimitSet -fuzztime=10000x -fuzzminimizetime=0x . +! stdout '^ok' +! stdout 'minimizing' +stdout -count=1 'there was an Error' +stdout FAIL + +# Test that minimization is working for recoverable errors. +! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x . +! stdout '^ok' +stdout 'got the minimum size!' +# The error message that was printed should be for the one written to testdata. +stdout 'contains a non-zero byte of length 50' +stdout FAIL + +# Check that the bytes written to testdata are of length 50 (the minimum size) +go run ./check_testdata FuzzMinimizerRecoverable 50 + +# Test that re-running the minimized value causes a crash. +! go test -run=FuzzMinimizerRecoverable . +rm testdata + +# Test that minimization is working for recoverable errors. Run it with -v this +# time to ensure the command line output still looks right. +! go test -v -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x . +! stdout '^ok' +stdout 'got the minimum size!' +# The error message that was printed should be for the one written to testdata. +stdout 'contains a non-zero byte of length 50' +stdout FAIL + +# Check that the bytes written to testdata are of length 50 (the minimum size) +go run ./check_testdata FuzzMinimizerRecoverable 50 + +# Test that re-running the minimized value causes a crash. +! go test -run=FuzzMinimizerRecoverable . +rm testdata + +# Test that minimization doesn't run for non-recoverable errors. +! go test -fuzz=FuzzMinimizerNonrecoverable -run=FuzzMinimizerNonrecoverable -fuzztime=10000x . +! stdout '^ok' +! stdout 'minimizing' +stdout -count=1 '^\s+fuzzing process hung or terminated unexpectedly: exit status 99' +stdout FAIL + +# Check that re-running the value causes a crash. +! go test -run=FuzzMinimizerNonrecoverable . +rm testdata + +# Clear the fuzzing cache. There may already be minimized inputs that would +# interfere with the next stage of the test. +go clean -fuzzcache + +# Test that minimization can be cancelled by fuzzminimizetime and the latest +# crash will still be logged and written to testdata. +! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=100x -fuzzminimizetime=1x . +! stdout '^ok' +stdout 'testdata[/\\]fuzz[/\\]FuzzMinimizerRecoverable[/\\]' +! stdout 'got the minimum size!' # it shouldn't have had enough time to minimize it +stdout FAIL + +# Test that re-running the unminimized value causes a crash. +! go test -run=FuzzMinimizerRecoverable . + +# TODO(jayconrod,katiehockman): add a test which verifies that the right bytes +# are written to testdata in the case of an interrupt during minimization. + +-- go.mod -- +module example.com/y + +go 1.16 +-- y_test.go -- +package y + +import ( + "os" + "testing" +) + +func FuzzMinimizeZeroDurationSet(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) > 5 { + t.Errorf("there was an Error") + } + }) +} + +func FuzzMinimizeZeroLimitSet(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) > 5 { + t.Errorf("there was an Error") + } + }) +} + +func FuzzMinimizerRecoverable(f *testing.F) { + f.Add(make([]byte, 100)) + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) < 50 { + // Make sure that b is large enough that it can be minimized + return + } + // Given the randomness of the mutations, this should allow the + // minimizer to trim down the value a bit. + for _, n := range b { + if n != 0 { + if len(b) == 50 { + t.Log("got the minimum size!") + } + t.Fatalf("contains a non-zero byte of length %d", len(b)) + } + } + }) +} + +func FuzzMinimizerNonrecoverable(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) { + os.Exit(99) + }) +} +-- empty/empty.go -- +package empty +-- check_testdata/check_testdata.go -- +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" +) + +func main() { + target := os.Args[1] + numBytes, err := strconv.Atoi(os.Args[2]) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + // Open the file in testdata (there should only be one) + dir := fmt.Sprintf("testdata/fuzz/%s", target) + files, err := ioutil.ReadDir(dir) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + if len(files) != 1 { + fmt.Fprintf(os.Stderr, "expected one file, got %d", len(files)) + os.Exit(1) + } + got, err := ioutil.ReadFile(filepath.Join(dir, files[0].Name())) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + // Trim the newline at the end of the file + got = bytes.TrimSpace(got) + + // Make sure that there were exactly 100 bytes written to the corpus entry + prefix := []byte("[]byte(") + i := bytes.Index(got, prefix) + gotBytes := got[i+len(prefix) : len(got)-1] + s, err := strconv.Unquote(string(gotBytes)) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + if want, got := numBytes, len(s); want != got { + fmt.Fprintf(os.Stderr, "want %d bytes, got %d\n", want, got) + os.Exit(1) + } +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt new file mode 100644 index 00000000000..5d0de17f6bc --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt @@ -0,0 +1,251 @@ +[short] skip +[!fuzz-instrumented] skip + +# Test that when an interesting value is discovered (one that expands coverage), +# the fuzzing engine minimizes it before writing it to the cache. +# +# The program below starts with a seed value of length 100, but more coverage +# will be found for any value other than the seed. We should end with a value +# in the cache of length 1 (the minimizer currently does not produce empty +# strings). check_cache.go confirms that. +# +# We would like to verify that ALL values in the cache were minimized to a +# length of 1, but this isn't always possible when new coverage is found in +# functions called by testing or internal/fuzz in the background. + +go test -c -fuzz=. # Build using shared build cache for speed. +env GOCACHE=$WORK/gocache +exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinCache -test.fuzztime=1000x +go run check_cache/check_cache.go $GOCACHE/fuzz/FuzzMinCache + +go test -c -fuzz=. # Build using shared build cache for speed. +env GOCACHE=$WORK/gocache + +# Test that minimization occurs for a crash that appears while minimizing a +# newly found interesting input. There must be only one worker for this test to +# be flaky like we want. +! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerCrashInMinimization -test.run=FuzzMinimizerCrashInMinimization -test.fuzztime=10000x -test.parallel=1 +! stdout '^ok' +stdout -count=1 'got the minimum size!' +stdout -count=1 'flaky failure' +stdout FAIL +# Check that the input written to testdata will reproduce the error, and is the +# smallest possible. +go run check_testdata/check_testdata.go FuzzMinimizerCrashInMinimization 50 + +# Test that a nonrecoverable error that occurs while minimizing an interesting +# input is reported correctly. +! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerNonrecoverableCrashInMinimization -test.run=FuzzMinimizerNonrecoverableCrashInMinimization -test.fuzztime=10000x -test.parallel=1 +! stdout '^ok' +stdout -count=1 'fuzzing process hung or terminated unexpectedly while minimizing' +stdout -count=1 'EOF' +stdout FAIL +# Check that the input written to testdata will reproduce the error. +go run check_testdata/check_testdata.go FuzzMinimizerNonrecoverableCrashInMinimization 100 + +-- go.mod -- +module fuzz + +go 1.17 +-- y.go -- +package fuzz + +import ( + "bytes" + "io" +) + +func Y(w io.Writer, s string) { + if !bytes.Equal([]byte(s), []byte("y")) { + w.Write([]byte("not equal")) + } +} +-- fuzz_test.go -- +package fuzz + +import ( + "bytes" + "io" + "os" + "strings" + "testing" + "unicode/utf8" +) + +func FuzzMinimizerCrashInMinimization(f *testing.F) { + seed := strings.Repeat("A", 1000) + f.Add(seed) + i := 3 + f.Fuzz(func(t *testing.T, s string) { + if len(s) < 50 || len(s) > 1100 { + // Make sure that b is large enough that it can be minimized + return + } + if s != seed { + // This should hit a new edge, and the interesting input + // should attempt minimization + Y(io.Discard, s) + } + if i > 0 { + // Don't let it fail right away. + i-- + } else if utf8.RuneCountInString(s) == len(s) && len(s) <= 100 { + // Make sure this only fails if the number of bytes in the + // marshaled string is the same as the unmarshaled string, + // so that we can check the length of the testdata file. + t.Error("flaky failure") + if len(s) == 50 { + t.Error("got the minimum size!") + } + } + }) +} + +func FuzzMinimizerNonrecoverableCrashInMinimization(f *testing.F) { + seed := strings.Repeat("A", 1000) + f.Add(seed) + i := 3 + f.Fuzz(func(t *testing.T, s string) { + if len(s) < 50 || len(s) > 1100 { + return + } + if s != seed { + Y(io.Discard, s) + } + if i > 0 { + i-- + } else if utf8.RuneCountInString(s) == len(s) && len(s) <= 100 { + os.Exit(19) + } + }) +} + +func FuzzMinCache(f *testing.F) { + seed := bytes.Repeat([]byte("a"), 20) + f.Add(seed) + f.Fuzz(func(t *testing.T, buf []byte) { + if bytes.Equal(buf, seed) { + return + } + if n := sum(buf); n < 0 { + t.Error("sum cannot be negative") + } + }) +} + +func sum(buf []byte) int { + n := 0 + for _, b := range buf { + n += int(b) + } + return n +} +-- check_testdata/check_testdata.go -- +//go:build ignore +// +build ignore + +// check_testdata.go checks that the string written +// is not longer than the provided length. +package main + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" +) + +func main() { + wantLen, err := strconv.Atoi(os.Args[2]) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + testName := os.Args[1] + dir := filepath.Join("testdata/fuzz", testName) + + files, err := ioutil.ReadDir(dir) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + if len(files) == 0 { + fmt.Fprintf(os.Stderr, "expect at least one failure to be written to testdata\n") + os.Exit(1) + } + + fname := files[0].Name() + contents, err := ioutil.ReadFile(filepath.Join(dir, fname)) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + contentsLen := len(contents) - len(`go test fuzz v1 +string("") +`) + if got, want := contentsLen, wantLen; got > want { + fmt.Fprintf(os.Stderr, "expect length <= %d, got %d\n", want, got) + os.Exit(1) + } + fmt.Fprintf(os.Stderr, "%s\n", contents) +} + +-- check_cache/check_cache.go -- +//go:build ignore +// +build ignore + +// check_cache.go checks that each file in the cached corpus has a []byte +// of length at most 1. This verifies that at least one cached input is minimized. +package main + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "regexp" + "strconv" +) + +func main() { + dir := os.Args[1] + ents, err := os.ReadDir(dir) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + for _, ent := range ents { + name := filepath.Join(dir, ent.Name()) + if good, err := checkCacheFile(name); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } else if good { + os.Exit(0) + } + } + fmt.Fprintln(os.Stderr, "no cached inputs were minimized") + os.Exit(1) +} + +func checkCacheFile(name string) (good bool, err error) { + data, err := os.ReadFile(name) + if err != nil { + return false, err + } + for _, line := range bytes.Split(data, []byte("\n")) { + m := valRe.FindSubmatch(line) + if m == nil { + continue + } + if s, err := strconv.Unquote(string(m[1])); err != nil { + return false, err + } else if len(s) <= 1 { + return true, nil + } + } + return false, nil +} + +var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`) diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_modcache.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_modcache.txt new file mode 100644 index 00000000000..c0f18ea3c07 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_modcache.txt @@ -0,0 +1,58 @@ +# This test demonstrates the fuzz corpus behavior for packages outside of the main module. +# (See https://golang.org/issue/48495.) + +[short] skip + +# Set -modcacherw so that the test behaves the same regardless of whether the +# module cache is writable. (For example, on some platforms it can always be +# written if the user is running as root.) At one point, a failing fuzz test +# in a writable module cache would corrupt module checksums in the cache. +env GOFLAGS=-modcacherw + + +# When the upstream module has no test corpus, running 'go test' should succeed, +# but 'go test -fuzz=.' should error out before running the test. +# (It should NOT corrupt the module cache by writing out new fuzz inputs, +# even if the cache is writable.) + +go get -t example.com/fuzzfail@v0.1.0 +go test example.com/fuzzfail + +! go test -fuzz=. example.com/fuzzfail +! stdout . +stderr '^cannot use -fuzz flag on package outside the main module$' + +go mod verify + + +# If the module does include a test corpus, 'go test' (without '-fuzz') should +# load that corpus and run the fuzz tests against it, but 'go test -fuzz=.' +# should continue to be rejected. + +go get -t example.com/fuzzfail@v0.2.0 + +! go test example.com/fuzzfail +stdout '^\s*fuzzfail_test\.go:7: oops:' + +! go test -fuzz=. example.com/fuzzfail +! stdout . +stderr '^cannot use -fuzz flag on package outside the main module$' + +go mod verify + + +# Packages in 'std' cannot be fuzzed when the corresponding GOROOT module is not +# the main module — either the failures would not be recorded or the behavior of +# the 'std' tests would change globally. + +! go test -fuzz . encoding/json +stderr '^cannot use -fuzz flag on package outside the main module$' + +! go test -fuzz . cmd/buildid +stderr '^cannot use -fuzz flag on package outside the main module$' + + +-- go.mod -- +module example.com/m + +go 1.18 diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_multiple.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_multiple.txt new file mode 100644 index 00000000000..1ec4985613f --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_multiple.txt @@ -0,0 +1,49 @@ +# This test checks that 'go test' prints a reasonable error when fuzzing is +# enabled, and multiple package or multiple fuzz targets match. +# TODO(#46312): support fuzzing multiple targets in multiple packages. + +[!fuzz] skip +[short] skip + +# With fuzzing disabled, multiple targets can be tested. +go test ./... + +# With fuzzing enabled, at most one package may be tested, +# even if only one package contains fuzz targets. +! go test -fuzz=. ./... +stderr '^cannot use -fuzz flag with multiple packages$' +! go test -fuzz=. ./zero ./one +stderr '^cannot use -fuzz flag with multiple packages$' +go test -fuzz=. -fuzztime=1x ./one + +# With fuzzing enabled, at most one target in the same package may match. +! go test -fuzz=. ./two +stdout '^testing: will not fuzz, -fuzz matches more than one fuzz test: \[FuzzOne FuzzTwo\]$' +go test -fuzz=FuzzTwo -fuzztime=1x ./two + +-- go.mod -- +module fuzz + +go 1.18 +-- zero/zero.go -- +package zero +-- one/one_test.go -- +package one + +import "testing" + +func FuzzOne(f *testing.F) { + f.Fuzz(func(*testing.T, []byte) {}) +} +-- two/two_test.go -- +package two + +import "testing" + +func FuzzOne(f *testing.F) { + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzTwo(f *testing.F) { + f.Fuzz(func(*testing.T, []byte) {}) +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_mutate_crash.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_mutate_crash.txt new file mode 100644 index 00000000000..99bae1daf07 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_mutate_crash.txt @@ -0,0 +1,323 @@ +[!fuzz] skip + +# Tests that a crash caused by a mutator-discovered input writes the bad input +# to testdata, and fails+reports correctly. This tests the end-to-end behavior +# of the mutator finding a crash while fuzzing, adding it as a regression test +# to the seed corpus in testdata, and failing the next time the test is run. + +[short] skip + +# Running the seed corpus for all of the targets should pass the first +# time, since nothing in the seed corpus will cause a crash. +go test + +# Running the fuzzer should find a crashing input quickly. +! go test -fuzz=FuzzWithBug -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithBug[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzWithBug + +# Now, the failing bytes should have been added to the seed corpus for +# the target, and should fail when run without fuzzing. +! go test +stdout 'FuzzWithBug/[a-f0-9]{64}' +stdout 'this input caused a crash!' + +! go test -run=FuzzWithNilPanic -fuzz=FuzzWithNilPanic -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithNilPanic[/\\]' +stdout 'runtime.Goexit' +go run check_testdata.go FuzzWithNilPanic + +! go test -run=FuzzWithGoexit -fuzz=FuzzWithGoexit -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithGoexit[/\\]' +stdout 'runtime.Goexit' +go run check_testdata.go FuzzWithGoexit + +! go test -run=FuzzWithFail -fuzz=FuzzWithFail -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithFail[/\\]' +go run check_testdata.go FuzzWithFail + +! go test -run=FuzzWithLogFail -fuzz=FuzzWithLogFail -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithLogFail[/\\]' +stdout 'logged something' +go run check_testdata.go FuzzWithLogFail + +! go test -run=FuzzWithErrorf -fuzz=FuzzWithErrorf -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithErrorf[/\\]' +stdout 'errorf was called here' +go run check_testdata.go FuzzWithErrorf + +! go test -run=FuzzWithFatalf -fuzz=FuzzWithFatalf -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithFatalf[/\\]' +stdout 'fatalf was called here' +go run check_testdata.go FuzzWithFatalf + +! go test -run=FuzzWithBadExit -fuzz=FuzzWithBadExit -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithBadExit[/\\]' +stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status' +go run check_testdata.go FuzzWithBadExit + +! go test -run=FuzzDeadlock -fuzz=FuzzDeadlock -fuzztime=100x -fuzzminimizetime=0x +stdout 'testdata[/\\]fuzz[/\\]FuzzDeadlock[/\\]' +stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status' +go run check_testdata.go FuzzDeadlock + +# Running the fuzzer should find a crashing input quickly for fuzzing two types. +! go test -run=FuzzWithTwoTypes -fuzz=FuzzWithTwoTypes -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithTwoTypes[/\\]' +stdout 'these inputs caused a crash!' +go run check_testdata.go FuzzWithTwoTypes + +# Running the fuzzer should find a crashing input quickly for an integer. +! go test -run=FuzzInt -fuzz=FuzzInt -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzInt[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzInt + +! go test -run=FuzzUint -fuzz=FuzzUint -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzUint[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzUint + +# Running the fuzzer should find a crashing input quickly for a bool. +! go test -run=FuzzBool -fuzz=FuzzBool -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzBool[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzBool + +# Running the fuzzer should find a crashing input quickly for a float. +! go test -run=FuzzFloat -fuzz=FuzzFloat -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzFloat[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzFloat + +# Running the fuzzer should find a crashing input quickly for a byte. +! go test -run=FuzzByte -fuzz=FuzzByte -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzByte[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzByte + +# Running the fuzzer should find a crashing input quickly for a rune. +! go test -run=FuzzRune -fuzz=FuzzRune -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzRune[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzRune + +# Running the fuzzer should find a crashing input quickly for a string. +! go test -run=FuzzString -fuzz=FuzzString -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzString[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzString + +-- go.mod -- +module m + +go 1.16 +-- fuzz_crash_test.go -- +package fuzz_crash + +import ( + "os" + "runtime" + "testing" +) + +func FuzzWithBug(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + panic("this input caused a crash!") + } + }) +} + +func FuzzWithNilPanic(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + panic(nil) + } + }) +} + +func FuzzWithGoexit(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + runtime.Goexit() + } + }) +} + +func FuzzWithFail(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + t.Fail() + } + }) +} + +func FuzzWithLogFail(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + t.Log("logged something") + t.Fail() + } + }) +} + +func FuzzWithErrorf(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + t.Errorf("errorf was called here") + } + }) +} + +func FuzzWithFatalf(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + t.Fatalf("fatalf was called here") + } + }) +} + +func FuzzWithBadExit(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + os.Exit(1) + } + }) +} + +func FuzzDeadlock(f *testing.F) { + f.Add(int(0)) + f.Fuzz(func(t *testing.T, n int) { + if n != 0 { + select {} + } + }) +} + +func FuzzWithTwoTypes(f *testing.F) { + f.Fuzz(func(t *testing.T, a, b []byte) { + if len(a) > 0 && len(b) > 0 { + panic("these inputs caused a crash!") + } + }) +} + +func FuzzInt(f *testing.F) { + f.Add(0) + f.Fuzz(func(t *testing.T, a int) { + if a != 0 { + panic("this input caused a crash!") + } + }) +} + +func FuzzUint(f *testing.F) { + f.Add(uint(0)) + f.Fuzz(func(t *testing.T, a uint) { + if a != 0 { + panic("this input caused a crash!") + } + }) +} + +func FuzzBool(f *testing.F) { + f.Add(false) + f.Fuzz(func(t *testing.T, a bool) { + if a { + panic("this input caused a crash!") + } + }) +} + +func FuzzFloat(f *testing.F) { + f.Fuzz(func(t *testing.T, a float64) { + if a != float64(int64(a)) { + // It has a decimal, so it was mutated by division + panic("this input caused a crash!") + } + }) +} + +func FuzzByte(f *testing.F) { + f.Add(byte(0)) + f.Fuzz(func(t *testing.T, a byte) { + if a != 0 { + panic("this input caused a crash!") + } + }) +} + +func FuzzRune(f *testing.F) { + f.Add(rune(0)) + f.Fuzz(func(t *testing.T, a rune) { + if a != 0 { + panic("this input caused a crash!") + } + }) +} + +func FuzzString(f *testing.F) { + f.Add("") + f.Fuzz(func(t *testing.T, a string) { + if a != "" { + panic("this input caused a crash!") + } + }) +} + +-- check_testdata.go -- +// +build ignore + +package main + +import ( + "bytes" + "crypto/sha256" + "fmt" + "io/ioutil" + "os" + "path/filepath" +) + +func main() { + target := os.Args[1] + dir := filepath.Join("testdata/fuzz", target) + + files, err := ioutil.ReadDir(dir) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + if len(files) == 0 { + fmt.Fprintf(os.Stderr, "expect at least one new mutation to be written to testdata\n") + os.Exit(1) + } + + fname := files[0].Name() + contents, err := ioutil.ReadFile(filepath.Join(dir, fname)) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + if bytes.Equal(contents, []byte("aa")) { + fmt.Fprintf(os.Stderr, "newly written testdata entry was not mutated\n") + os.Exit(1) + } + // The hash of the bytes in the file should match the filename. + h := []byte(fmt.Sprintf("%x", sha256.Sum256(contents))) + if !bytes.Equal([]byte(fname), h) { + fmt.Fprintf(os.Stderr, "hash of bytes %q does not match filename %q\n", h, fname) + os.Exit(1) + } +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_mutate_fail.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_mutate_fail.txt new file mode 100644 index 00000000000..b5eab173494 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_mutate_fail.txt @@ -0,0 +1,102 @@ +[!fuzz] skip + +# Check that if a worker does not call F.Fuzz or calls F.Fail first, +# 'go test' exits non-zero and no crasher is recorded. + +[short] skip + +! go test -fuzz=FuzzReturn +! exists testdata + +! go test -fuzz=FuzzSkip +! exists testdata + +! go test -fuzz=FuzzFail +! exists testdata + +! go test -fuzz=FuzzPanic +! exists testdata + +! go test -fuzz=FuzzNilPanic +! exists testdata + +! go test -fuzz=FuzzGoexit +! exists testdata + +! go test -fuzz=FuzzExit +! exists testdata + +-- go.mod -- +module m + +go 1.17 +-- fuzz_fail_test.go -- +package fuzz_fail + +import ( + "flag" + "os" + "runtime" + "testing" +) + +func isWorker() bool { + f := flag.Lookup("test.fuzzworker") + if f == nil { + return false + } + get, ok := f.Value.(flag.Getter) + if !ok { + return false + } + return get.Get() == interface{}(true) +} + +func FuzzReturn(f *testing.F) { + if isWorker() { + return + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzSkip(f *testing.F) { + if isWorker() { + f.Skip() + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzFail(f *testing.F) { + if isWorker() { + f.Fail() + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzPanic(f *testing.F) { + if isWorker() { + panic("nope") + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzNilPanic(f *testing.F) { + if isWorker() { + panic(nil) + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzGoexit(f *testing.F) { + if isWorker() { + runtime.Goexit() + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzExit(f *testing.F) { + if isWorker() { + os.Exit(99) + } + f.Fuzz(func(*testing.T, []byte) {}) +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_mutator.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_mutator.txt new file mode 100644 index 00000000000..76b86488ad8 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_mutator.txt @@ -0,0 +1,165 @@ +[!fuzz] skip + +# Test basic fuzzing mutator behavior. +# +# fuzz_test.go has two fuzz targets (FuzzA, FuzzB) which both add a seed value. +# Each fuzz function writes the input to a log file. The coordinator and worker +# use separate log files. check_logs.go verifies that the coordinator only +# tests seed values and the worker tests mutated values on the fuzz target. + +[short] skip + +go test -fuzz=FuzzA -fuzztime=100x -parallel=1 -log=fuzz +go run check_logs.go fuzz fuzz.worker + +# TODO(b/181800488): remove -parallel=1, here and below. For now, when a +# crash is found, all workers keep running, wasting resources and reducing +# the number of executions available to the minimizer, increasing flakiness. + +# Test that the mutator is good enough to find several unique mutations. +! go test -fuzz=FuzzMutator -parallel=1 -fuzztime=100x mutator_test.go +! stdout '^ok' +stdout FAIL +stdout 'mutator found enough unique mutations' + +-- go.mod -- +module m + +go 1.16 +-- fuzz_test.go -- +package fuzz_test + +import ( + "flag" + "fmt" + "os" + "testing" +) + +var ( + logPath = flag.String("log", "", "path to log file") + logFile *os.File +) + +func TestMain(m *testing.M) { + flag.Parse() + var err error + logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) + if os.IsExist(err) { + *logPath += ".worker" + logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) + } + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + os.Exit(m.Run()) +} + +func FuzzA(f *testing.F) { + f.Add([]byte("seed")) + f.Fuzz(func(t *testing.T, b []byte) { + fmt.Fprintf(logFile, "FuzzA %q\n", b) + }) +} + +func FuzzB(f *testing.F) { + f.Add([]byte("seed")) + f.Fuzz(func(t *testing.T, b []byte) { + fmt.Fprintf(logFile, "FuzzB %q\n", b) + }) +} + +-- check_logs.go -- +// +build ignore + +package main + +import ( + "bufio" + "bytes" + "fmt" + "io" + "os" + "strings" +) + +func main() { + coordPath, workerPath := os.Args[1], os.Args[2] + + coordLog, err := os.Open(coordPath) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + defer coordLog.Close() + if err := checkCoordLog(coordLog); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + workerLog, err := os.Open(workerPath) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + defer workerLog.Close() + if err := checkWorkerLog(workerLog); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func checkCoordLog(r io.Reader) error { + b, err := io.ReadAll(r) + if err != nil { + return err + } + if string(bytes.TrimSpace(b)) != `FuzzB "seed"` { + return fmt.Errorf("coordinator: did not test FuzzB seed") + } + return nil +} + +func checkWorkerLog(r io.Reader) error { + scan := bufio.NewScanner(r) + var sawAMutant bool + for scan.Scan() { + line := scan.Text() + if !strings.HasPrefix(line, "FuzzA ") { + return fmt.Errorf("worker: tested something other than target: %s", line) + } + if strings.TrimPrefix(line, "FuzzA ") != `"seed"` { + sawAMutant = true + } + } + if err := scan.Err(); err != nil && err != bufio.ErrTooLong { + return err + } + if !sawAMutant { + return fmt.Errorf("worker: did not test any mutants") + } + return nil +} +-- mutator_test.go -- +package fuzz_test + +import ( + "testing" +) + +// TODO(katiehockman): re-work this test once we have a better fuzzing engine +// (ie. more mutations, and compiler instrumentation) +func FuzzMutator(f *testing.F) { + // TODO(katiehockman): simplify this once we can dedupe crashes (e.g. + // replace map with calls to panic, and simply count the number of crashes + // that were added to testdata) + crashes := make(map[string]bool) + // No seed corpus initiated + f.Fuzz(func(t *testing.T, b []byte) { + crashes[string(b)] = true + if len(crashes) >= 10 { + panic("mutator found enough unique mutations") + } + }) +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_mutator_repeat.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_mutator_repeat.txt new file mode 100644 index 00000000000..3764dcb9157 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_mutator_repeat.txt @@ -0,0 +1,74 @@ +# TODO(jayconrod): support shared memory on more platforms. +[!darwin] [!linux] [!windows] skip + +# Verify that the fuzzing engine records the actual crashing input, even when +# a worker process terminates without communicating the crashing input back +# to the coordinator. + +[short] skip + +# Start fuzzing. The worker crashes after 100 iterations. +# The fuzz function writes the crashing input to "want" before exiting. +# The fuzzing engine reconstructs the crashing input and saves it to testdata. +! exists want +! go test -fuzz=. -parallel=1 -fuzztime=110x -fuzzminimizetime=10x -v +stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status' +stdout 'Failing input written to testdata' + +# Run the fuzz target without fuzzing. The fuzz function is called with the +# crashing input in testdata. The test passes if that input is identical to +# the one saved in "want". +exists want +go test -want=want + +-- go.mod -- +module fuzz + +go 1.17 +-- fuzz_test.go -- +package fuzz + +import ( + "bytes" + "flag" + "os" + "testing" +) + +var wantFlag = flag.String("want", "", "file containing previous crashing input") + +func FuzzRepeat(f *testing.F) { + i := 0 + f.Fuzz(func(t *testing.T, b []byte) { + i++ + if i == 100 { + f, err := os.OpenFile("want", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666) + if err != nil { + // Couldn't create the file. Return without crashing, and try + // again. + i-- + t.Skip(err) + } + if _, err := f.Write(b); err != nil { + // We already created the file, so if we failed to write it + // there's not much we can do. The test will fail anyway, but + // at least make sure the error is logged to stdout. + t.Fatal(err) + } + if err := f.Close(); err != nil { + t.Fatal(err) + } + os.Exit(1) // crash without communicating + } + + if *wantFlag != "" { + want, err := os.ReadFile(*wantFlag) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(want, b) { + t.Fatalf("inputs are not equal!\n got: %q\nwant:%q", b, want) + } + } + }) +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_non_crash_signal.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_non_crash_signal.txt new file mode 100644 index 00000000000..1051292fcb8 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_non_crash_signal.txt @@ -0,0 +1,75 @@ +# NOTE: this test is skipped on Windows, since there's no concept of signals. +# When a process terminates another process, it provides an exit code. +[windows] skip +[!fuzz] skip +[short] skip + +# FuzzNonCrash sends itself a signal that does not appear to be a crash. +# We should not save a crasher. +! go test -fuzz=FuzzNonCrash +! exists testdata +! stdout unreachable +! stderr unreachable +stdout 'fuzzing process terminated by unexpected signal; no crash will be recorded: signal: terminated' + +# FuzzKill sends itself a signal that cannot be caught by the worker process +# and does not appear to be a crash. +# We should not save a crasher. +! go test -fuzz=FuzzKill +! exists testdata +! stdout unreachable +! stderr unreachable +stdout 'fuzzing process terminated by unexpected signal; no crash will be recorded: signal: killed' + +# FuzzCrash sends itself a signal that looks like a crash. +# We should save a crasher. +! go test -fuzz=FuzzCrash +exists testdata/fuzz/FuzzCrash +stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status' + +-- go.mod -- +module test + +go 1.17 +-- fuzz_posix_test.go -- +// +build darwin freebsd linux + +package fuzz + +import ( + "syscall" + "testing" +) + +func FuzzNonCrash(f *testing.F) { + f.Fuzz(func(*testing.T, bool) { + pid := syscall.Getpid() + if err := syscall.Kill(pid, syscall.SIGTERM); err != nil { + panic(err) + } + // signal may not be received immediately. Wait for it. + select{} + }) +} + +func FuzzKill(f *testing.F) { + f.Fuzz(func(*testing.T, bool) { + pid := syscall.Getpid() + if err := syscall.Kill(pid, syscall.SIGKILL); err != nil { + panic(err) + } + // signal may not be received immediately. Wait for it. + select{} + }) +} + +func FuzzCrash(f *testing.F) { + f.Fuzz(func(*testing.T, bool) { + pid := syscall.Getpid() + if err := syscall.Kill(pid, syscall.SIGILL); err != nil { + panic(err) + } + // signal may not be received immediately. Wait for it. + select{} + }) +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_parallel.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_parallel.txt new file mode 100644 index 00000000000..e6325208d00 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_parallel.txt @@ -0,0 +1,66 @@ +[!fuzz] skip +[short] skip + +# When running seed inputs, T.Parallel should let multiple inputs run in +# parallel. +go test -run=FuzzSeed + +# When fuzzing, T.Parallel should be safe to call, but it should have no effect. +# We just check that it doesn't hang, which would be the most obvious +# failure mode. +# TODO(jayconrod): check for the string "after T.Parallel". It's not printed +# by 'go test', so we can't distinguish that crasher from some other panic. +! go test -run=FuzzMutate -fuzz=FuzzMutate +exists testdata/fuzz/FuzzMutate + +# Testdata should now contain a corpus entry which will fail FuzzMutate. +# Run the test without fuzzing, setting -parallel to different values to make +# sure it fails, and doesn't hang. +! go test -run=FuzzMutate -parallel=1 +! go test -run=FuzzMutate -parallel=2 +! go test -run=FuzzMutate -parallel=4 + +-- go.mod -- +module fuzz_parallel + +go 1.17 +-- fuzz_parallel_test.go -- +package fuzz_parallel + +import ( + "sort" + "sync" + "testing" +) + +func FuzzSeed(f *testing.F) { + for _, v := range [][]byte{{'a'}, {'b'}, {'c'}} { + f.Add(v) + } + + var mu sync.Mutex + var before, after []byte + f.Cleanup(func() { + sort.Slice(after, func(i, j int) bool { return after[i] < after[j] }) + got := string(before) + string(after) + want := "abcabc" + if got != want { + f.Fatalf("got %q; want %q", got, want) + } + }) + + f.Fuzz(func(t *testing.T, b []byte) { + before = append(before, b...) + t.Parallel() + mu.Lock() + after = append(after, b...) + mu.Unlock() + }) +} + +func FuzzMutate(f *testing.F) { + f.Fuzz(func(t *testing.T, _ []byte) { + t.Parallel() + t.Error("after T.Parallel") + }) +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_profile_flags.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_profile_flags.txt new file mode 100644 index 00000000000..5434c723ad2 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_profile_flags.txt @@ -0,0 +1,38 @@ +[!fuzz] skip + +! go test -fuzz=FuzzTrivial -coverprofile=prof +! stdout . +stderr '^cannot use -coverprofile flag with -fuzz flag$' + +! go test -fuzz=FuzzTrivial -blockprofile=prof +! stdout . +stderr '^cannot use -blockprofile flag with -fuzz flag$' + +! go test -fuzz=FuzzTrivial -cpuprofile=prof +! stdout . +stderr '^cannot use -cpuprofile flag with -fuzz flag$' + +! go test -fuzz=FuzzTrivial -memprofile=prof +! stdout . +stderr '^cannot use -memprofile flag with -fuzz flag$' + +! go test -fuzz=FuzzTrivial -mutexprofile=prof +! stdout . +stderr '^cannot use -mutexprofile flag with -fuzz flag$' + +! go test -fuzz=FuzzTrivial -trace=prof +! stdout . +stderr '^cannot use -trace flag with -fuzz flag$' + +-- go.mod -- +module example + +go 1.18 +-- fuzz_test.go -- +package example + +import "testing" + +func FuzzTrivial(f *testing.F) { + f.Fuzz(func(t *testing.T, _ []byte) {}) +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_run.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_run.txt new file mode 100644 index 00000000000..99a4413d322 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_run.txt @@ -0,0 +1,143 @@ +[!fuzz] skip +[short] skip +env GOCACHE=$WORK/cache + +# Tests which verify the behavior and command line output when +# running a fuzz target as a unit test. + +# Tests without -run. + +! go test +stdout FAIL +stdout 'error here' + +! go test -v +stdout FAIL +stdout 'error here' +stdout '=== RUN FuzzFoo/thisfails' +stdout '--- FAIL: FuzzFoo/thisfails' +stdout '=== RUN FuzzFoo/thispasses' +stdout '--- PASS: FuzzFoo/thispasses' + +# Tests where -run matches all seed corpora. + +! go test -run FuzzFoo/this +stdout FAIL +stdout 'error here' +! stdout 'no tests to run' + +! go test -run /this +stdout FAIL +stdout 'error here' +! stdout 'no tests to run' + +! go test -v -run FuzzFoo/this +stdout FAIL +stdout 'error here' +stdout '=== RUN FuzzFoo/thisfails' +stdout '--- FAIL: FuzzFoo/thisfails' +stdout '=== RUN FuzzFoo/thispasses' +stdout '--- PASS: FuzzFoo/thispasses' +! stdout 'no tests to run' + +! go test -v -run /this +stdout FAIL +stdout 'error here' +stdout '=== RUN FuzzFoo/thisfails' +stdout '--- FAIL: FuzzFoo/thisfails' +stdout '=== RUN FuzzFoo/thispasses' +stdout '--- PASS: FuzzFoo/thispasses' +! stdout 'no tests to run' + +# Tests where -run only matches one seed corpus which passes. + +go test -run FuzzFoo/thispasses +stdout ok +! stdout 'no tests to run' + +go test -run /thispasses +stdout ok +! stdout 'no tests to run' + +# Same tests in verbose mode +go test -v -run FuzzFoo/thispasses +stdout '=== RUN FuzzFoo/thispasses' +stdout '--- PASS: FuzzFoo/thispasses' +! stdout '=== RUN FuzzFoo/thisfails' +! stdout 'no tests to run' + +go test -v -run /thispasses +stdout '=== RUN FuzzFoo/thispasses' +stdout '--- PASS: FuzzFoo/thispasses' +! stdout '=== RUN FuzzFoo/thisfails' +! stdout 'no tests to run' + +# Tests where -run only matches one seed corpus which fails. + +! go test -run FuzzFoo/thisfails +stdout FAIL +stdout 'error here' +! stdout 'no tests to run' + +! go test -run /thisfails +stdout FAIL +stdout 'error here' +! stdout 'no tests to run' + +! go test -v -run FuzzFoo/thisfails +stdout 'error here' +stdout '=== RUN FuzzFoo/thisfails' +stdout '--- FAIL: FuzzFoo/thisfails' +! stdout '=== RUN FuzzFoo/thispasses' +! stdout 'no tests to run' + +! go test -v -run /thisfails +stdout 'error here' +stdout '=== RUN FuzzFoo/thisfails' +stdout '--- FAIL: FuzzFoo/thisfails' +! stdout '=== RUN FuzzFoo/thispasses' +! stdout 'no tests to run' + +# Tests where -run doesn't match any seed corpora. + +go test -run FuzzFoo/nomatch +stdout ok + +go test -run /nomatch +stdout ok + +go test -v -run FuzzFoo/nomatch +stdout '=== RUN FuzzFoo' +stdout '--- PASS: FuzzFoo' +stdout ok +! stdout 'no tests to run' + +go test -v -run /nomatch +stdout '=== RUN FuzzFoo' +stdout '--- PASS: FuzzFoo' +stdout ok +! stdout 'no tests to run' + +-- go.mod -- +module example.com/x + +go 1.16 +-- x_test.go -- +package x + +import "testing" + +func FuzzFoo(f *testing.F) { + f.Add("this is fine") + f.Fuzz(func(t *testing.T, s string) { + if s == "fails" { + t.Error("error here") + } + }) +} +-- testdata/fuzz/FuzzFoo/thisfails -- +go test fuzz v1 +string("fails") +-- testdata/fuzz/FuzzFoo/thispasses -- +go test fuzz v1 +string("passes") diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_seed_corpus.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_seed_corpus.txt new file mode 100644 index 00000000000..57c8a8ba659 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_seed_corpus.txt @@ -0,0 +1,203 @@ +[!fuzz-instrumented] skip +[short] skip +env GOCACHE=$WORK/cache + +# Test that fuzzing a target with a failure in f.Add prints the crash +# and doesn't write anything to testdata/fuzz +! go test -fuzz=FuzzWithAdd -run=FuzzWithAdd -fuzztime=1x +! stdout ^ok +! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithAdd[/\\]' +stdout FAIL + +# Test that fuzzing a target with a sucess in f.Add and a fuzztime of only +# 1 does not produce a crash. +go test -fuzz=FuzzWithGoodAdd -run=FuzzWithGoodAdd -fuzztime=1x +stdout ok +! stdout FAIL + +# Test that fuzzing a target with a failure in testdata/fuzz prints the crash +# and doesn't write anything to testdata/fuzz +! go test -fuzz=FuzzWithTestdata -run=FuzzWithTestdata -fuzztime=1x +! stdout ^ok +! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithTestdata[/\\]' +stdout 'failure while testing seed corpus entry: FuzzWithTestdata/1' +stdout FAIL + +# Test that fuzzing a target with no seed corpus or cache finds a crash, prints +# it, and write it to testdata +! go test -fuzz=FuzzWithNoCache -run=FuzzWithNoCache -fuzztime=1x +! stdout ^ok +stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithNoCache[/\\]' +stdout FAIL + +# Write a crashing input to the cache +mkdir $GOCACHE/fuzz/example.com/x/FuzzWithCache +cp cache-file $GOCACHE/fuzz/example.com/x/FuzzWithCache/1 + +# Test that fuzzing a target with a failure in the cache prints the crash +# and writes this as a "new" crash to testdata/fuzz +! go test -fuzz=FuzzWithCache -run=FuzzWithCache -fuzztime=1x +! stdout ^ok +stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithCache[/\\]' +stdout FAIL + +# Write a crashing input to the cache +mkdir $GOCACHE/fuzz/example.com/x/FuzzWithMinimizableCache +cp cache-file-bytes $GOCACHE/fuzz/example.com/x/FuzzWithMinimizableCache/1 + +# Test that fuzzing a target with a failure in the cache minimizes it and writes +# the new crash to testdata/fuzz +! go test -fuzz=FuzzWithMinimizableCache -run=FuzzWithMinimizableCache -fuzztime=10000x +! stdout ^ok +stdout 'gathering baseline coverage' +stdout 'got the minimum size!' +stdout 'contains a non-zero byte of length 10' +stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithMinimizableCache[/\\]' +stdout FAIL +# Make sure this crash didn't come from fuzzing +# (the log line that states fuzzing began shouldn't have printed) +! stdout 'execs' + +# Clear the fuzz cache and make sure it's gone +go clean -fuzzcache +! exists $GOCACHE/fuzz + +# The tests below should operate the exact same as the previous tests. If -fuzz +# is enabled, then whatever target is going to be fuzzed shouldn't be run by +# anything other than the workers. + +# Test that fuzzing a target (with -run=None set) with a failure in f.Add prints +# the crash and doesn't write anything to testdata/fuzz -fuzztime=1x +! go test -fuzz=FuzzWithAdd -run=None +! stdout ^ok +! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithAdd[/\\]' +stdout FAIL + +# Test that fuzzing a target (with -run=None set) with a sucess in f.Add and a +# fuzztime of only 1 does not produce a crash. +go test -fuzz=FuzzWithGoodAdd -run=None -fuzztime=1x +stdout ok +! stdout FAIL + +# Test that fuzzing a target (with -run=None set) with a failure in +# testdata/fuzz prints the crash and doesn't write anything to testdata/fuzz +! go test -fuzz=FuzzWithTestdata -run=None -fuzztime=1x +! stdout ^ok +! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithTestdata[/\\]' +stdout FAIL + +# Write a crashing input to the cache +mkdir $GOCACHE/fuzz/example.com/x/FuzzRunNoneWithCache +cp cache-file $GOCACHE/fuzz/example.com/x/FuzzRunNoneWithCache/1 + +# Test that fuzzing a target (with -run=None set) with a failure in the cache +# prints the crash and writes this as a "new" crash to testdata/fuzz +! go test -fuzz=FuzzRunNoneWithCache -run=None -fuzztime=1x +! stdout ^ok +stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzRunNoneWithCache[/\\]' +stdout FAIL + +# Clear the fuzz cache and make sure it's gone +go clean -fuzzcache +! exists $GOCACHE/fuzz + +# The tests below should operate the exact same way for the previous tests with +# a seed corpus (namely, they should still fail). However, the binary is built +# without instrumentation, so this should be a "testing only" run which executes +# the seed corpus before attempting to fuzz. + +go test -c +! exec ./x.test$GOEXE -test.fuzz=FuzzWithAdd -test.run=FuzzWithAdd -test.fuzztime=1x -test.fuzzcachedir=$WORK/cache +! stdout ^ok +! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithAdd[/\\]' +stdout FAIL +stderr warning + +go test -c +! exec ./x.test$GOEXE -test.fuzz=FuzzWithTestdata -test.run=FuzzWithTestdata -test.fuzztime=1x -test.fuzzcachedir=$WORK/cache +! stdout ^ok +! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithTestdata[/\\]' +stdout FAIL +stderr warning + +-- go.mod -- +module example.com/x + +go 1.16 +-- x_test.go -- +package x + +import "testing" + +func FuzzWithAdd(f *testing.F) { + f.Add(10) + f.Fuzz(func(t *testing.T, i int) { + if i == 10 { + t.Error("bad thing here") + } + }) +} + +func FuzzWithGoodAdd(f *testing.F) { + f.Add(10) + f.Fuzz(func(t *testing.T, i int) { + if i != 10 { + t.Error("bad thing here") + } + }) +} + +func FuzzWithTestdata(f *testing.F) { + f.Fuzz(func(t *testing.T, i int) { + if i == 10 { + t.Error("bad thing here") + } + }) +} + +func FuzzWithNoCache(f *testing.F) { + f.Fuzz(func(t *testing.T, i int) { + t.Error("bad thing here") + }) +} + +func FuzzWithCache(f *testing.F) { + f.Fuzz(func(t *testing.T, i int) { + if i == 10 { + t.Error("bad thing here") + } + }) +} + +func FuzzWithMinimizableCache(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) < 10 { + return + } + for _, n := range b { + if n != 0 { + if len(b) == 10 { + t.Log("got the minimum size!") + } + t.Fatalf("contains a non-zero byte of length %d", len(b)) + } + } + }) +} + +func FuzzRunNoneWithCache(f *testing.F) { + f.Fuzz(func(t *testing.T, i int) { + if i == 10 { + t.Error("bad thing here") + } + }) +} +-- testdata/fuzz/FuzzWithTestdata/1 -- +go test fuzz v1 +int(10) +-- cache-file -- +go test fuzz v1 +int(10) +-- cache-file-bytes -- +go test fuzz v1 +[]byte("11111111111111111111") diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_setenv.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_setenv.txt new file mode 100644 index 00000000000..2924569de11 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_setenv.txt @@ -0,0 +1,45 @@ +[!fuzz] skip +[short] skip + +go test -fuzz=FuzzA -fuzztime=100x fuzz_setenv_test.go + +-- fuzz_setenv_test.go -- +package fuzz + +import ( + "flag" + "os" + "testing" +) + +func FuzzA(f *testing.F) { + if s := os.Getenv("TEST_FUZZ_SETENV_A"); isWorker() && s == "" { + f.Fatal("environment variable not set") + } else if !isWorker() && s != "" { + f.Fatal("environment variable already set") + } + f.Setenv("TEST_FUZZ_SETENV_A", "A") + if os.Getenv("TEST_FUZZ_SETENV_A") == "" { + f.Fatal("Setenv did not set environment variable") + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzB(f *testing.F) { + if os.Getenv("TEST_FUZZ_SETENV_A") != "" { + f.Fatal("environment variable not cleared after FuzzA") + } + f.Skip() +} + +func isWorker() bool { + f := flag.Lookup("test.fuzzworker") + if f == nil { + return false + } + get, ok := f.Value.(flag.Getter) + if !ok { + return false + } + return get.Get() == interface{}(true) +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_test_race.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_test_race.txt new file mode 100644 index 00000000000..9d39cd684e7 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_test_race.txt @@ -0,0 +1,39 @@ +# Test that when both race detection and coverage instrumentation are enabled, +# and seed values are being executed, the race detector isn't mistakenly +# triggered. + +[short] skip +[!fuzz] skip +[!race] skip + +# Test with coverage instrumentation enabled (-fuzz) and race instrumentation +# but without actually fuzzing the target (by using a non-matching pattern) +go test -fuzz=xxx -race -v +! stderr 'race detected during execution of test' + +# Test with just race instrumentation enabled +go test -race -v +! stderr 'race detected during execution of test' + +# Test with coverage and race instrumentation enabled, and a matching fuzz +# pattern +go test -fuzz=FuzzRace -race -v -fuzztime=200x +! stderr 'race detected during execution of test' + +-- go.mod -- +module test + +-- race_test.go -- +package race + +import "testing" + +func FuzzRace(f *testing.F) { + for i := 0; i < 100; i++ { + f.Add(i) + } + + f.Fuzz(func(t *testing.T, i int) { + t.Parallel() + }) +} diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_unsupported.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_unsupported.txt new file mode 100644 index 00000000000..1ed0b8a6f75 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_fuzz_unsupported.txt @@ -0,0 +1,18 @@ +[fuzz] skip + +! go test -fuzz=. -fuzztime=1x +! stdout . +stderr '^-fuzz flag is not supported on '$GOOS'/'$GOARCH'$' + +-- go.mod -- +module example + +go 1.18 +-- fuzz_test.go -- +package example + +import "testing" + +func FuzzTrivial(f *testing.F) { + f.Fuzz(func(t *testing.T, _ []byte) {}) +} diff --git a/libgo/go/cmd/go/testdata/script/test_issue45477.txt b/libgo/go/cmd/go/testdata/script/test_issue45477.txt new file mode 100644 index 00000000000..f435b6a6f43 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/test_issue45477.txt @@ -0,0 +1,12 @@ +[short] skip # links and runs a test binary + +go test -v . + +-- go.mod -- +module example.com/pkg_test + +-- pkg.go -- +package pkg_test + +-- pkg_test.go -- +package pkg_test diff --git a/libgo/go/cmd/go/testdata/script/test_race_install.txt b/libgo/go/cmd/go/testdata/script/test_race_install.txt index 8b1f343a325..a1d47a7dd3c 100644 --- a/libgo/go/cmd/go/testdata/script/test_race_install.txt +++ b/libgo/go/cmd/go/testdata/script/test_race_install.txt @@ -15,4 +15,4 @@ go 1.16 -- pkg/pkg.go -- package p -- stderr.txt -- -go test: -i flag is deprecated +go: -i flag is deprecated diff --git a/libgo/go/cmd/go/testdata/script/test_relative_cmdline.txt b/libgo/go/cmd/go/testdata/script/test_relative_cmdline.txt index 2f9c80fe4d5..37aa3b1ec77 100644 --- a/libgo/go/cmd/go/testdata/script/test_relative_cmdline.txt +++ b/libgo/go/cmd/go/testdata/script/test_relative_cmdline.txt @@ -1,6 +1,7 @@ # Relative imports in command line package # Run tests outside GOPATH. +env GO111MODULE=off env GOPATH=$WORK/tmp go test ./testimport/p.go ./testimport/p_test.go ./testimport/x_test.go @@ -47,4 +48,4 @@ func TestF1(t *testing.T) { if F() != p2.F() { t.Fatal(F()) } -}
\ No newline at end of file +} diff --git a/libgo/go/cmd/go/testdata/script/test_vet.txt b/libgo/go/cmd/go/testdata/script/test_vet.txt index 5af26b54f91..6151f912ae0 100644 --- a/libgo/go/cmd/go/testdata/script/test_vet.txt +++ b/libgo/go/cmd/go/testdata/script/test_vet.txt @@ -16,6 +16,22 @@ go test -vet=off p1.go ! stderr '[\\/]vet.*-shift' stdout '\[no test files\]' +# ensure all runs non-default vet +! go test -vet=all ./vetall/... +stderr 'using resp before checking for errors' + +# Test issue #47309 +! go test -vet=bools,xyz ./vetall/... +stderr '-vet argument must be a supported analyzer' + +# Test with a single analyzer +! go test -vet=httpresponse ./vetall/... +stderr 'using resp before checking for errors' + +# Test with a list of analyzers +go test -vet=atomic,bools,nilfunc ./vetall/... +stdout 'm/vetall.*\[no tests to run\]' + # Test issue #22890 go test m/vetcycle stdout 'm/vetcycle.*\[no test files\]' @@ -51,6 +67,21 @@ import "fmt" func F() { fmt.Printf("%d") // oops } +-- vetall/p.go -- +package p + +import "net/http" + +func F() { + resp, err := http.Head("example.com") + defer resp.Body.Close() + if err != nil { + panic(err) + } + // (defer statement belongs here) +} +-- vetall/p_test.go -- +package p -- vetcycle/p.go -- package p diff --git a/libgo/go/cmd/go/testdata/script/vendor_list_issue11977.txt b/libgo/go/cmd/go/testdata/script/vendor_list_issue11977.txt index ce2e29f99a7..cdab33c0892 100644 --- a/libgo/go/cmd/go/testdata/script/vendor_list_issue11977.txt +++ b/libgo/go/cmd/go/testdata/script/vendor_list_issue11977.txt @@ -2,7 +2,7 @@ [!exec:git] skip env GO111MODULE=off -go get -d github.com/rsc/go-get-issue-11864 +go get github.com/rsc/go-get-issue-11864 go list -f '{{join .TestImports "\n"}}' github.com/rsc/go-get-issue-11864/t stdout 'go-get-issue-11864/vendor/vendor.org/p' diff --git a/libgo/go/cmd/go/testdata/script/version.txt b/libgo/go/cmd/go/testdata/script/version.txt index 8615a4aac59..adca7af7a9d 100644 --- a/libgo/go/cmd/go/testdata/script/version.txt +++ b/libgo/go/cmd/go/testdata/script/version.txt @@ -16,11 +16,18 @@ stdout '^go version' env GOFLAGS= env GO111MODULE=on -# Skip the builds below if we are running in short mode. + +# Check that very basic version lookup succeeds. +go build empty.go +go version empty$GOEXE +[cgo] go build -ldflags=-linkmode=external empty.go +[cgo] go version empty$GOEXE + +# Skip the remaining builds if we are running in short mode. [short] skip # Check that 'go version' and 'go version -m' work on a binary built in module mode. -go get -d rsc.io/fortune +go get rsc.io/fortune go build -o fortune.exe rsc.io/fortune go version fortune.exe stdout '^fortune.exe: .+' @@ -28,6 +35,13 @@ go version -m fortune.exe stdout '^\tpath\trsc.io/fortune' stdout '^\tmod\trsc.io/fortune\tv1.0.0' +# Check the build info of a binary built from $GOROOT/src/cmd +go build -o test2json.exe cmd/test2json +go version -m test2json.exe +stdout '^test2json.exe: .+' +stdout '^\tpath\tcmd/test2json$' +! stdout 'mod' + # Repeat the test with -buildmode=pie. [!buildmode:pie] stop go build -buildmode=pie -o external.exe rsc.io/fortune @@ -50,3 +64,7 @@ stdout '^\tmod\trsc.io/fortune\tv1.0.0' -- go.mod -- module m + +-- empty.go -- +package main +func main(){} diff --git a/libgo/go/cmd/go/testdata/script/version_build_settings.txt b/libgo/go/cmd/go/testdata/script/version_build_settings.txt new file mode 100644 index 00000000000..dc9e67681ea --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/version_build_settings.txt @@ -0,0 +1,70 @@ +[short] skip + +# Compiler name is always added. +go build +go version -m m$GOEXE +stdout '^\tbuild\t-compiler=gc$' +stdout '^\tbuild\tGOOS=' +stdout '^\tbuild\tGOARCH=' +[amd64] stdout '^\tbuild\tGOAMD64=' +! stdout asmflags|gcflags|ldflags|gccgoflags + +# Toolchain flags are added if present. +# The raw flags are included, with package patterns if specified. +go build -asmflags=example.com/m=-D=FOO=bar +go version -m m$GOEXE +stdout '^\tbuild\t-asmflags=example\.com/m=-D=FOO=bar$' + +go build -gcflags=example.com/m=-N +go version -m m$GOEXE +stdout '^\tbuild\t-gcflags=example\.com/m=-N$' + +go build -ldflags=example.com/m=-w +go version -m m$GOEXE +stdout '^\tbuild\t-ldflags=example\.com/m=-w$' + +# gccgoflags are not added when gc is used, and vice versa. +# TODO: test gccgo. +go build -gccgoflags=all=UNUSED +go version -m m$GOEXE +! stdout gccgoflags + +# Build and tool tags are added but not release tags. +# "race" is included with build tags but not "cgo". +go build -tags=a,b +go version -m m$GOEXE +stdout '^\tbuild\t-tags=a,b$' +[race] go build -race +[race] go version -m m$GOEXE +[race] ! stdout '^\tbuild\t-tags=' +[race] stdout '^\tbuild\t-race=true$' + +# CGO flags are separate settings. +# CGO_ENABLED is always present. +# Other flags are added if CGO_ENABLED is true. +env CGO_ENABLED=0 +go build +go version -m m$GOEXE +stdout '^\tbuild\tCGO_ENABLED=0$' +! stdout CGO_CPPFLAGS|CGO_CFLAGS|CGO_CXXFLAGS|CGO_LDFLAGS +[cgo] env CGO_ENABLED=1 +[cgo] env CGO_CPPFLAGS=-DFROM_CPPFLAGS=1 +[cgo] env CGO_CFLAGS=-DFROM_CFLAGS=1 +[cgo] env CGO_CXXFLAGS=-DFROM_CXXFLAGS=1 +[cgo] env CGO_LDFLAGS=-L/extra/dir/does/not/exist +[cgo] go build +[cgo] go version -m m$GOEXE +[cgo] stdout '^\tbuild\tCGO_ENABLED=1$' +[cgo] stdout '^\tbuild\tCGO_CPPFLAGS=-DFROM_CPPFLAGS=1$' +[cgo] stdout '^\tbuild\tCGO_CFLAGS=-DFROM_CFLAGS=1$' +[cgo] stdout '^\tbuild\tCGO_CXXFLAGS=-DFROM_CXXFLAGS=1$' +[cgo] stdout '^\tbuild\tCGO_LDFLAGS=-L/extra/dir/does/not/exist$' + +-- go.mod -- +module example.com/m + +go 1.18 +-- m.go -- +package main + +func main() {} diff --git a/libgo/go/cmd/go/testdata/script/version_buildvcs_bzr.txt b/libgo/go/cmd/go/testdata/script/version_buildvcs_bzr.txt new file mode 100644 index 00000000000..85db9bab6df --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/version_buildvcs_bzr.txt @@ -0,0 +1,107 @@ +# This test checks that VCS information is stamped into Go binaries by default, +# controlled with -buildvcs. This test focuses on Bazaar specifics. +# The Git test covers common functionality. + +[!exec:bzr] skip +[short] skip +env GOBIN=$WORK/gopath/bin +env oldpath=$PATH +env HOME=$WORK +cd repo/a +exec bzr whoami 'J.R. Gopher <gopher@golang.org>' + +# If there's no local repository, there's no VCS info. +go install +go version -m $GOBIN/a$GOEXE +! stdout bzrrevision +rm $GOBIN/a$GOEXE + +# If there is a repository, but it can't be used for some reason, +# there should be an error. It should hint about -buildvcs=false. +cd .. +mkdir .bzr +env PATH=$WORK${/}fakebin${:}$oldpath +chmod 0755 $WORK/fakebin/bzr +! exec bzr help +cd a +! go install +stderr '^error obtaining VCS status: exit status 1\n\tUse -buildvcs=false to disable VCS stamping.$' +rm $GOBIN/a$GOEXE +cd .. +env PATH=$oldpath +rm .bzr + +# If there is an empty repository in a parent directory, only "modified" is tagged. +exec bzr init +cd a +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs=bzr$' +! stdout vcs.revision +! stdout vcs.time +stdout '^\tbuild\tvcs.modified=true$' +cd .. + +# Revision and commit time are tagged for repositories with commits. +exec bzr add a README +exec bzr commit -m 'initial commit' +cd a +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs=bzr$' +stdout '^\tbuild\tvcs.revision=' +stdout '^\tbuild\tvcs.time=' +stdout '^\tbuild\tvcs.modified=false$' +rm $GOBIN/a$GOEXE + +# Building an earlier commit should still build clean. +cp ../../outside/empty.txt ../NEWS +exec bzr add ../NEWS +exec bzr commit -m 'add NEWS' +exec bzr update -r1 +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs=bzr$' +stdout '^\tbuild\tvcs.revision=' +stdout '^\tbuild\tvcs.time=' +stdout '^\tbuild\tvcs.modified=false$' + +# Building with -buildvcs=false suppresses the info. +go install -buildvcs=false +go version -m $GOBIN/a$GOEXE +! stdout vcs.revision +rm $GOBIN/a$GOEXE + +# An untracked file is shown as modified, even if it isn't part of the build. +cp ../../outside/empty.txt . +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.modified=true$' +rm empty.txt +rm $GOBIN/a$GOEXE + +# An edited file is shown as modified, even if it isn't part of the build. +cp ../../outside/empty.txt ../README +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.modified=true$' +exec bzr revert ../README +rm $GOBIN/a$GOEXE + +-- $WORK/fakebin/bzr -- +#!/bin/sh +exit 1 +-- $WORK/fakebin/bzr.bat -- +exit 1 +-- repo/README -- +Far out in the uncharted backwaters of the unfashionable end of the western +spiral arm of the Galaxy lies a small, unregarded yellow sun. +-- repo/a/go.mod -- +module example.com/a + +go 1.18 +-- repo/a/a.go -- +package main + +func main() {} +-- outside/empty.txt -- diff --git a/libgo/go/cmd/go/testdata/script/version_buildvcs_fossil.txt b/libgo/go/cmd/go/testdata/script/version_buildvcs_fossil.txt new file mode 100644 index 00000000000..720306868bc --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/version_buildvcs_fossil.txt @@ -0,0 +1,93 @@ +# This test checks that VCS information is stamped into Go binaries by default, +# controlled with -buildvcs. This test focuses on Fossil specifics. +# The Git test covers common functionality. + +# "fossil" is the Fossil file server on Plan 9. +[plan9] skip +[!exec:fossil] skip +[short] skip +env GOBIN=$WORK/gopath/bin +env oldpath=$PATH +env HOME=$WORK +env USER=gopher +[!windows] env fslckout=.fslckout +[windows] env fslckout=_FOSSIL_ +exec pwd +exec fossil init repo.fossil +cd repo/a + +# If there's no local repository, there's no VCS info. +go install +go version -m $GOBIN/a$GOEXE +! stdout vcs.revision +rm $GOBIN/a$GOEXE + +# If there is a repository, but it can't be used for some reason, +# there should be an error. It should hint about -buildvcs=false. +cd .. +mkdir $fslckout +env PATH=$WORK${/}fakebin${:}$oldpath +chmod 0755 $WORK/fakebin/fossil +! exec fossil help +cd a +! go install +stderr '^error obtaining VCS status: exit status 1\n\tUse -buildvcs=false to disable VCS stamping.$' +rm $GOBIN/a$GOEXE +cd .. +env PATH=$oldpath +rm $fslckout + +# Revision and commit time are tagged for repositories with commits. +exec fossil open ../repo.fossil -f +exec fossil add a README +exec fossil commit -m 'initial commit' +cd a +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs=fossil\n' +stdout '^\tbuild\tvcs.revision=' +stdout '^\tbuild\tvcs.time=' +stdout '^\tbuild\tvcs.modified=false$' +rm $GOBIN/a$GOEXE + +# Building with -buildvcs=false suppresses the info. +go install -buildvcs=false +go version -m $GOBIN/a$GOEXE +! stdout vcs.revision +rm $GOBIN/a$GOEXE + +# An untracked file is shown as modified, even if it isn't part of the build. +cp ../../outside/empty.txt . +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs=fossil\n' +stdout '^\tbuild\tvcs.modified=true$' +rm empty.txt +rm $GOBIN/a$GOEXE + +# An edited file is shown as modified, even if it isn't part of the build. +cp ../../outside/empty.txt ../README +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs=fossil\n' +stdout '^\tbuild\tvcs.modified=true$' +exec fossil revert ../README +rm $GOBIN/a$GOEXE + +-- $WORK/fakebin/fossil -- +#!/bin/sh +exit 1 +-- $WORK/fakebin/fossil.bat -- +exit 1 +-- repo/README -- +Far out in the uncharted backwaters of the unfashionable end of the western +spiral arm of the Galaxy lies a small, unregarded yellow sun. +-- repo/a/go.mod -- +module example.com/a + +go 1.18 +-- repo/a/a.go -- +package main + +func main() {} +-- outside/empty.txt -- diff --git a/libgo/go/cmd/go/testdata/script/version_buildvcs_git.txt b/libgo/go/cmd/go/testdata/script/version_buildvcs_git.txt new file mode 100644 index 00000000000..86d1de06df4 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/version_buildvcs_git.txt @@ -0,0 +1,164 @@ +# This test checks that VCS information is stamped into Go binaries by default, +# controlled with -buildvcs. This test focuses on Git. Other tests focus on +# other VCS tools but may not cover common functionality. + +[!exec:git] skip +[short] skip +env GOBIN=$WORK/gopath/bin +env oldpath=$PATH +cd repo/a + +# If there's no local repository, there's no VCS info. +go install +go version -m $GOBIN/a$GOEXE +! stdout vcs.revision +rm $GOBIN/a$GOEXE + +# If there is a repository, but it can't be used for some reason, +# there should be an error. It should hint about -buildvcs=false. +# Also ensure that multiple errors are collected by "go list -e". +cd .. +mkdir .git +env PATH=$WORK${/}fakebin${:}$oldpath +chmod 0755 $WORK/fakebin/git +! exec git help +cd a +! go install +stderr '^error obtaining VCS status: exit status 1\n\tUse -buildvcs=false to disable VCS stamping.$' +go list -e -f '{{.ImportPath}}: {{.Error}}' ./... +stdout -count=1 '^example\.com/a: error obtaining VCS status' +stdout -count=1 '^example\.com/a/library: <nil>' +stdout -count=1 '^example\.com/a/othermain: error obtaining VCS status' +cd .. +env PATH=$oldpath +rm .git + +# If there is an empty repository in a parent directory, only "uncommitted" is tagged. +exec git init +exec git config user.email gopher@golang.org +exec git config user.name 'J.R. Gopher' +cd a +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs=git$' +stdout '^\tbuild\tvcs.modified=true$' +! stdout vcs.revision +! stdout vcs.time +rm $GOBIN/a$GOEXE + +# Revision and commit time are tagged for repositories with commits. +exec git add -A +exec git commit -m 'initial commit' +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.revision=' +stdout '^\tbuild\tvcs.time=' +stdout '^\tbuild\tvcs.modified=false$' +rm $GOBIN/a$GOEXE + +# Building with -buildvcs=false suppresses the info. +go install -buildvcs=false +go version -m $GOBIN/a$GOEXE +! stdout vcs.revision +rm $GOBIN/a$GOEXE + +# An untracked file is shown as uncommitted, even if it isn't part of the build. +cp ../../outside/empty.txt . +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.modified=true$' +rm empty.txt +rm $GOBIN/a$GOEXE + +# An edited file is shown as uncommitted, even if it isn't part of the build. +cp ../../outside/empty.txt ../README +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.modified=true$' +exec git checkout ../README +rm $GOBIN/a$GOEXE + +# If the build doesn't include any packages from the repository, +# there should be no VCS info. +go install example.com/cmd/a@v1.0.0 +go version -m $GOBIN/a$GOEXE +! stdout vcs.revision +rm $GOBIN/a$GOEXE + +go mod edit -require=example.com/c@v0.0.0 +go mod edit -replace=example.com/c@v0.0.0=../../outside/c +go install example.com/c +go version -m $GOBIN/c$GOEXE +! stdout vcs.revision +rm $GOBIN/c$GOEXE +exec git checkout go.mod + +# If the build depends on a package in the repository, but it's not in the +# main module, there should be no VCS info. +go mod edit -require=example.com/b@v0.0.0 +go mod edit -replace=example.com/b@v0.0.0=../b +go mod edit -require=example.com/d@v0.0.0 +go mod edit -replace=example.com/d@v0.0.0=../../outside/d +go install example.com/d +go version -m $GOBIN/d$GOEXE +! stdout vcs.revision +exec git checkout go.mod +rm $GOBIN/d$GOEXE + +# If we're loading multiple main packages, +# but they share the same VCS repository, +# we only need to execute VCS status commands once. +go list -x ./... +stdout -count=3 '^example.com' +stderr -count=1 '^git status' +stderr -count=1 '^git show' + +-- $WORK/fakebin/git -- +#!/bin/sh +exit 1 +-- $WORK/fakebin/git.bat -- +exit 1 +-- repo/README -- +Far out in the uncharted backwaters of the unfashionable end of the western +spiral arm of the Galaxy lies a small, unregarded yellow sun. +-- repo/a/go.mod -- +module example.com/a + +go 1.18 +-- repo/a/a.go -- +package main + +func main() {} +-- repo/a/library/f.go -- +package library +-- repo/a/othermain/f.go -- +package main + +func main() {} +-- repo/b/go.mod -- +module example.com/b + +go 1.18 +-- repo/b/b.go -- +package b +-- outside/empty.txt -- +-- outside/c/go.mod -- +module example.com/c + +go 1.18 +-- outside/c/main.go -- +package main + +func main() {} +-- outside/d/go.mod -- +module example.com/d + +go 1.18 + +require example.com/b v0.0.0 +-- outside/d/main.go -- +package main + +import _ "example.com/b" + +func main() {} diff --git a/libgo/go/cmd/go/testdata/script/version_buildvcs_git_gpg.txt b/libgo/go/cmd/go/testdata/script/version_buildvcs_git_gpg.txt new file mode 100644 index 00000000000..dcf97d7c447 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/version_buildvcs_git_gpg.txt @@ -0,0 +1,105 @@ +# This test checks that VCS information is stamped into Go binaries even when +# the current commit is signed and the use has configured git to display commit +# signatures. + +[!exec:git] skip +[!exec:gpg] skip +[short] skip +env GOBIN=$GOPATH/bin +env GNUPGHOME=$WORK/.gpupg +mkdir $GNUPGHOME +chmod 0700 $GNUPGHOME + +# Create GPG key +exec gpg --batch --passphrase '' --quick-generate-key gopher@golang.org +exec gpg --list-secret-keys --with-colons gopher@golang.org +cp stdout keyinfo.txt +go run extract_key_id.go keyinfo.txt +cp stdout keyid.txt + +# Initialize repo +cd repo/ +exec git init +exec git config user.email gopher@golang.org +exec git config user.name 'J.R. Gopher' +exec git config --add log.showSignature true +go run ../configure_signing_key.go ../keyid.txt + +# Create signed commit +cd a +exec git add -A +exec git commit -m 'initial commit' --gpg-sign +exec git log + +# Verify commit signature does not interfere with versioning +go install +go version -m $GOBIN/a +stdout '^\tbuild\tvcs\.revision=' +stdout '^\tbuild\tvcs\.time=' +stdout '^\tbuild\tvcs\.modified=false$' + +-- repo/README -- +Far out in the uncharted backwaters of the unfashionable end of the western +spiral arm of the Galaxy lies a small, unregarded yellow sun. +-- repo/a/go.mod -- +module example.com/a + +go 1.18 +-- repo/a/a.go -- +package main + +func main() {} + +-- extract_key_id.go -- +package main + +import "fmt" +import "io/ioutil" +import "os" +import "strings" + +func main() { + err := run(os.Args[1]) + if err != nil { + panic(err) + } +} + +func run(keyInfoFilePath string) error { + contents, err := ioutil.ReadFile(keyInfoFilePath) + if err != nil { + return err + } + lines := strings.Split(string(contents), "\n") + for _, line := range lines { + fields := strings.Split(line, ":") + if fields[0] == "sec" { + fmt.Print(fields[4]) + return nil + } + } + return fmt.Errorf("key ID not found in: %s", keyInfoFilePath) +} + +-- configure_signing_key.go -- +package main + +import "io/ioutil" +import "os" +import "os/exec" + +func main() { + err := run(os.Args[1]) + if err != nil { + panic(err) + } +} + +func run(keyIdFilePath string) error { + keyId, err := ioutil.ReadFile(keyIdFilePath) + if err != nil { + return err + } + gitCmd := exec.Command("git", "config", "user.signingKey", string(keyId)) + return gitCmd.Run() +} diff --git a/libgo/go/cmd/go/testdata/script/version_buildvcs_hg.txt b/libgo/go/cmd/go/testdata/script/version_buildvcs_hg.txt new file mode 100644 index 00000000000..fbbd886102e --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/version_buildvcs_hg.txt @@ -0,0 +1,91 @@ +# This test checks that VCS information is stamped into Go binaries by default, +# controlled with -buildvcs. This test focuses on Mercurial specifics. +# The Git test covers common functionality. + +[!exec:hg] skip +[short] skip +env GOBIN=$WORK/gopath/bin +env oldpath=$PATH +cd repo/a + +# If there's no local repository, there's no VCS info. +go install +go version -m $GOBIN/a$GOEXE +! stdout hgrevision +rm $GOBIN/a$GOEXE + +# If there is a repository, but it can't be used for some reason, +# there should be an error. It should hint about -buildvcs=false. +cd .. +mkdir .hg +env PATH=$WORK${/}fakebin${:}$oldpath +chmod 0755 $WORK/fakebin/hg +! exec hg help +cd a +! go install +stderr '^error obtaining VCS status: exit status 1\n\tUse -buildvcs=false to disable VCS stamping.$' +rm $GOBIN/a$GOEXE +cd .. +env PATH=$oldpath +rm .hg + +# If there is an empty repository in a parent directory, only "uncommitted" is tagged. +exec hg init +cd a +go install +go version -m $GOBIN/a$GOEXE +! stdout vcs.revision +! stdout vcs.time +stdout '^\tbuild\tvcs.modified=true$' +cd .. + +# Revision and commit time are tagged for repositories with commits. +exec hg add a README +exec hg commit -m 'initial commit' +cd a +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.revision=' +stdout '^\tbuild\tvcs.time=' +stdout '^\tbuild\tvcs.modified=false$' +rm $GOBIN/a$GOEXE + +# Building with -buildvcs=false suppresses the info. +go install -buildvcs=false +go version -m $GOBIN/a$GOEXE +! stdout hgrevision +rm $GOBIN/a$GOEXE + +# An untracked file is shown as uncommitted, even if it isn't part of the build. +cp ../../outside/empty.txt . +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.modified=true$' +rm empty.txt +rm $GOBIN/a$GOEXE + +# An edited file is shown as uncommitted, even if it isn't part of the build. +cp ../../outside/empty.txt ../README +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.modified=true$' +exec hg revert ../README +rm $GOBIN/a$GOEXE + +-- $WORK/fakebin/hg -- +#!/bin/sh +exit 1 +-- $WORK/fakebin/hg.bat -- +exit 1 +-- repo/README -- +Far out in the uncharted backwaters of the unfashionable end of the western +spiral arm of the Galaxy lies a small, unregarded yellow sun. +-- repo/a/go.mod -- +module example.com/a + +go 1.18 +-- repo/a/a.go -- +package main + +func main() {} +-- outside/empty.txt -- diff --git a/libgo/go/cmd/go/testdata/script/version_buildvcs_nested.txt b/libgo/go/cmd/go/testdata/script/version_buildvcs_nested.txt new file mode 100644 index 00000000000..08d4c92bafb --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/version_buildvcs_nested.txt @@ -0,0 +1,51 @@ +[!exec:git] skip +[!exec:hg] skip +[short] skip +env GOFLAGS=-n + +# Create a root module in a root Git repository. +mkdir root +cd root +go mod init example.com/root +exec git init + +# Nesting repositories in parent directories are ignored, as the current +# directory main package, and containing main module are in the same repository. +# This is an error in GOPATH mode (to prevent VCS injection), but for modules, +# we assume users have control over repositories they've checked out. +mkdir hgsub +cd hgsub +exec hg init +cp ../../main.go main.go +! go build +stderr '^error obtaining VCS status: main module is in repository ".*root" but current directory is in repository ".*hgsub"$' +stderr '^\tUse -buildvcs=false to disable VCS stamping.$' +go build -buildvcs=false +go mod init example.com/root/hgsub +go build +cd .. + +# It's an error to build a package from a nested Git repository if the package +# is in a separate repository from the current directory or from the module +# root directory. +mkdir gitsub +cd gitsub +exec git init +exec git config user.name 'J.R.Gopher' +exec git config user.email 'gopher@golang.org' +cp ../../main.go main.go +! go build +stderr '^error obtaining VCS status: main module is in repository ".*root" but current directory is in repository ".*gitsub"$' +go build -buildvcs=false +go mod init example.com/root/gitsub +exec git commit --allow-empty -m empty # status commands fail without this +go build +rm go.mod +cd .. +! go build ./gitsub +stderr '^error obtaining VCS status: main package is in repository ".*gitsub" but current directory is in repository ".*root"$' +go build -buildvcs=false -o=gitsub${/} ./gitsub + +-- main.go -- +package main +func main() {} diff --git a/libgo/go/cmd/go/testdata/script/version_replace.txt b/libgo/go/cmd/go/testdata/script/version_replace.txt index ec98f4e3f3a..82b8504458b 100644 --- a/libgo/go/cmd/go/testdata/script/version_replace.txt +++ b/libgo/go/cmd/go/testdata/script/version_replace.txt @@ -1,7 +1,7 @@ [short] skip go mod download example.com/printversion@v0.1.0 example.com/printversion@v1.0.0 -go get -d example.com/printversion@v0.1.0 +go get example.com/printversion@v0.1.0 go install example.com/printversion go run example.com/printversion diff --git a/libgo/go/cmd/go/testdata/script/work.txt b/libgo/go/cmd/go/testdata/script/work.txt new file mode 100644 index 00000000000..cbb3746a69a --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work.txt @@ -0,0 +1,147 @@ +! go work init doesnotexist +stderr 'go: creating workspace file: no go.mod file exists in directory doesnotexist' +go env GOWORK +! stdout . + +go work init ./a ./b +cmp go.work go.work.want +go env GOWORK +stdout '^'$WORK'(\\|/)gopath(\\|/)src(\\|/)go.work$' + +! go run example.com/b +stderr 'a(\\|/)a.go:4:8: no required module provides package rsc.io/quote; to add it:\n\tcd '$WORK(\\|/)gopath(\\|/)src(\\|/)a'\n\tgo get rsc.io/quote' +cd a +go get rsc.io/quote +go env GOMOD # go env GOMOD reports the module in a single module context +stdout $GOPATH(\\|/)src(\\|/)a(\\|/)go.mod +cd .. +go run example.com/b +stdout 'Hello, world.' + +# And try from a different directory +cd c +go run example.com/b +stdout 'Hello, world.' +cd $GOPATH/src + +go list all # all includes both modules +stdout 'example.com/a' +stdout 'example.com/b' + +# -mod can only be set to readonly in workspace mode +go list -mod=readonly all +! go list -mod=mod all +stderr '^go: -mod may only be set to readonly when in workspace mode' +go list -mod=mod -workfile=off all + +# Test that duplicates in the use list return an error +cp go.work go.work.backup +cp go.work.dup go.work +! go run example.com/b +stderr 'reading go.work: path .* appears multiple times in workspace' +cp go.work.backup go.work + +cp go.work.d go.work +go run example.com/d + +# Test that we don't run into "newRequirements called with unsorted roots" +# panic with unsorted main modules. +cp go.work.backwards go.work +go run example.com/d + +# Test that command-line-arguments work inside and outside modules. +# This exercises the code that determines which module command-line-arguments +# belongs to. +go list ./b/main.go +go build -n -workfile=off -o foo foo.go +go build -n -o foo foo.go + +-- go.work.dup -- +go 1.18 + +use ( + a + b + ../src/a +) +-- go.work.want -- +go 1.18 + +use ( + ./a + ./b +) +-- go.work.d -- +go 1.18 + +use ( + a + b + d +) +-- a/go.mod -- + +module example.com/a + +-- a/a.go -- +package a + +import "fmt" +import "rsc.io/quote" + +func HelloFromA() { + fmt.Println(quote.Hello()) +} + +-- b/go.mod -- + +module example.com/b + +-- b/main.go -- +package main + +import "example.com/a" + +func main() { + a.HelloFromA() +} +-- b/lib/hello.go -- +package lib + +import "example.com/a" + +func Hello() { + a.HelloFromA() +} + +-- c/README -- +Create this directory so we can cd to +it and make sure paths are interpreted +relative to the go.work, not the cwd. +-- d/go.mod -- +module example.com/d + +-- d/main.go -- +package main + +import "example.com/b/lib" + +func main() { + lib.Hello() +} + +-- go.work.backwards -- +go 1.18 + +use ( + d + b + a +) + +-- foo.go -- +package main +import "fmt" +func main() { + fmt.Println("Hello, World") +} diff --git a/libgo/go/cmd/go/testdata/script/work_build_no_modules.txt b/libgo/go/cmd/go/testdata/script/work_build_no_modules.txt new file mode 100644 index 00000000000..c9859b437e2 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_build_no_modules.txt @@ -0,0 +1,13 @@ +! go build . +stderr 'go: no modules were found in the current workspace; see ''go help work''' + +-- go.work -- +go 1.18 +-- go.mod -- +go 1.18 + +module foo +-- foo.go -- +package main + +func main() {}
\ No newline at end of file diff --git a/libgo/go/cmd/go/testdata/script/work_edit.txt b/libgo/go/cmd/go/testdata/script/work_edit.txt new file mode 100644 index 00000000000..fd04bbda6e8 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_edit.txt @@ -0,0 +1,161 @@ +# Test editing go.work files. + +go work init m +cmp go.work go.work.want_initial + +go work edit -use n +cmp go.work go.work.want_use_n + +go work edit -go 1.18 +cmp go.work go.work.want_go_118 + +go work edit -dropuse m +cmp go.work go.work.want_dropuse_m + +go work edit -replace=x.1@v1.3.0=y.1@v1.4.0 -replace='x.1@v1.4.0 = ../z' +cmp go.work go.work.want_add_replaces + +go work edit -use n -use ../a -use /b -use c -use c +cmp go.work go.work.want_multiuse + +go work edit -dropuse /b -dropuse n +cmp go.work go.work.want_multidropuse + +go work edit -dropreplace='x.1@v1.4.0' +cmp go.work go.work.want_dropreplace + +go work edit -print -go 1.19 -use b -dropuse c -replace 'x.1@v1.4.0 = ../z' -dropreplace x.1 -dropreplace x.1@v1.3.0 +cmp stdout go.work.want_print + +go work edit -json -go 1.19 -use b -dropuse c -replace 'x.1@v1.4.0 = ../z' -dropreplace x.1 -dropreplace x.1@v1.3.0 +cmp stdout go.work.want_json + +go work edit -print -fmt -workfile $GOPATH/src/unformatted +cmp stdout formatted + +-- m/go.mod -- +module m + +go 1.18 +-- go.work.want_initial -- +go 1.18 + +use ./m +-- go.work.want_use_n -- +go 1.18 + +use ( + ./m + ./n +) +-- go.work.want_go_118 -- +go 1.18 + +use ( + ./m + ./n +) +-- go.work.want_dropuse_m -- +go 1.18 + +use ./n +-- go.work.want_add_replaces -- +go 1.18 + +use ./n + +replace ( + x.1 v1.3.0 => y.1 v1.4.0 + x.1 v1.4.0 => ../z +) +-- go.work.want_multiuse -- +go 1.18 + +use ( + ../a + ./c + ./n + /b +) + +replace ( + x.1 v1.3.0 => y.1 v1.4.0 + x.1 v1.4.0 => ../z +) +-- go.work.want_multidropuse -- +go 1.18 + +use ( + ../a + ./c +) + +replace ( + x.1 v1.3.0 => y.1 v1.4.0 + x.1 v1.4.0 => ../z +) +-- go.work.want_dropreplace -- +go 1.18 + +use ( + ../a + ./c +) + +replace x.1 v1.3.0 => y.1 v1.4.0 +-- go.work.want_print -- +go 1.19 + +use ( + ../a + ./b +) + +replace x.1 v1.4.0 => ../z +-- go.work.want_json -- +{ + "Go": "1.19", + "Use": [ + { + "DiskPath": "../a" + }, + { + "DiskPath": "./b" + } + ], + "Replace": [ + { + "Old": { + "Path": "x.1", + "Version": "v1.4.0" + }, + "New": { + "Path": "../z" + } + } + ] +} +-- unformatted -- +go 1.18 + use ( + a + b + c + ) + replace ( + x.1 v1.3.0 => y.1 v1.4.0 + x.1 v1.4.0 => ../z + ) +-- formatted -- +go 1.18 + +use ( + a + b + c +) + +replace ( + x.1 v1.3.0 => y.1 v1.4.0 + x.1 v1.4.0 => ../z +)
\ No newline at end of file diff --git a/libgo/go/cmd/go/testdata/script/work_env.txt b/libgo/go/cmd/go/testdata/script/work_env.txt new file mode 100644 index 00000000000..ec3d3be3ed7 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_env.txt @@ -0,0 +1,24 @@ +go env GOWORK +stdout '^'$GOPATH'[\\/]src[\\/]go.work$' +go env +stdout '^(set )?GOWORK="?'$GOPATH'[\\/]src[\\/]go.work"?$' + +cd .. +go env GOWORK +! stdout . +go env +stdout 'GOWORK=("")?' + +cd src +go env GOWORK +stdout 'go.work' + +! go env -w GOWORK=off +stderr '^go: GOWORK cannot be modified$' + +-- go.work -- +go 1.18 + +use a +-- a/go.mod -- +module example.com/a diff --git a/libgo/go/cmd/go/testdata/script/work_install_submodule.txt b/libgo/go/cmd/go/testdata/script/work_install_submodule.txt new file mode 100644 index 00000000000..3d1171736d9 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_install_submodule.txt @@ -0,0 +1,36 @@ +# This is a regression test for golang.org/issue/50036 +# Don't check sums for other modules in the workspace. + +cd m/sub +go install -n + +-- go.work -- +go 1.18 + +use ( + ./m + ./m/sub +) +-- m/go.mod -- +module example.com/m + +go 1.18 + +-- m/m.go -- +package m + +func M() {} +-- m/sub/go.mod -- +module example.com/m/sub + +go 1.18 + +require example.com/m v1.0.0 +-- m/sub/main.go -- +package main + +import "example.com/m" + +func main() { + m.M() +} diff --git a/libgo/go/cmd/go/testdata/script/work_prune.txt b/libgo/go/cmd/go/testdata/script/work_prune.txt new file mode 100644 index 00000000000..7e2ae4e6ce4 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_prune.txt @@ -0,0 +1,104 @@ +# This test makes sure workspace mode's handling of the module graph +# is compatible with module pruning. The graph we load from either of +# the workspace modules should be the same, even if their graphs +# don't overlap. +# +# This is the module graph in the test: +# +# example.com/a -> example.com/b v1.0.0 -> example.com/q v1.1.0 +# example.com/p -> example.com/q v1.0.0 +# +# If we didn't load the whole graph and didn't load the dependencies of b +# when loading p, we would end up loading q v1.0.0, rather than v1.1.0, +# which is selected by MVS. +# TODO(#48331): We currently load the wrong version of q. Fix this. + +go list -m -f '{{.Version}}' example.com/q +stdout '^v1.1.0$' + +-- go.work -- +go 1.18 + +use ( + ./a + ./p +) +-- a/go.mod -- +module example.com/a + +go 1.18 + +require example.com/b v1.0.0 + +replace example.com/b v1.0.0 => ../b +-- a/foo.go -- +package main + +import "example.com/b" + +func main() { + b.B() +} +-- b/go.mod -- +module example.com/b + +go 1.18 + +require example.com/q v1.1.0 + +replace example.com/q v1.0.0 => ../q1_0_0 +replace example.com/q v1.1.0 => ../q1_1_0 +-- b/b.go -- +package b + +func B() { +} +-- b/b_test.go -- +package b + +import "example.com/q" + +func TestB() { + q.PrintVersion() +} +-- p/go.mod -- +module example.com/p + +go 1.18 + +require example.com/q v1.0.0 + +replace example.com/q v1.0.0 => ../q1_0_0 +replace example.com/q v1.1.0 => ../q1_1_0 +-- p/main.go -- +package main + +import "example.com/q" + +func main() { + q.PrintVersion() +} +-- q1_0_0/go.mod -- +module example.com/q + +go 1.18 +-- q1_0_0/q.go -- +package q + +import "fmt" + +func PrintVersion() { + fmt.Println("version 1.0.0") +} +-- q1_1_0/go.mod -- +module example.com/q + +go 1.18 +-- q1_1_0/q.go -- +package q + +import "fmt" + +func PrintVersion() { + fmt.Println("version 1.1.0") +} diff --git a/libgo/go/cmd/go/testdata/script/work_prune_all.txt b/libgo/go/cmd/go/testdata/script/work_prune_all.txt new file mode 100644 index 00000000000..a7ad9c04aff --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_prune_all.txt @@ -0,0 +1,176 @@ +# This test makes sure workspace mode's handling of the module graph +# is compatible with module pruning. The graph we load from either of +# the workspace modules should be the same, even if their graphs +# don't overlap. +# +# This is the module graph in the test: +# +# example.com/p -> example.com/q v1.0.0 +# example.com/a -> example.com/b v1.0.0 -> example.com/q v1.1.0 -> example.com/w v1.0.0 -> example.com/x v1.0.0 -> example.com/y v1.0.0 +# |-> example.com/z v1.0.0 |-> example.com/z v1.1.0 +# |-> example.com/q v1.0.5 -> example.com/r v1.0.0 +# If we didn't load the whole graph and didn't load the dependencies of b +# when loading p, we would end up loading q v1.0.0, rather than v1.1.0, +# which is selected by MVS. + +go list -m all +stdout 'example.com/w v1.0.0' +stdout 'example.com/q v1.1.0' +stdout 'example.com/z v1.1.0' +stdout 'example.com/x v1.0.0' +! stdout 'example.com/r' +! stdout 'example.com/y' + +-- go.work -- +go 1.18 + +use ( + ./a + ./p +) + +replace example.com/b v1.0.0 => ./b +replace example.com/q v1.0.0 => ./q1_0_0 +replace example.com/q v1.0.5 => ./q1_0_5 +replace example.com/q v1.1.0 => ./q1_1_0 +replace example.com/r v1.0.0 => ./r +replace example.com/w v1.0.0 => ./w +replace example.com/x v1.0.0 => ./x +replace example.com/y v1.0.0 => ./y +replace example.com/z v1.0.0 => ./z1_0_0 +replace example.com/z v1.1.0 => ./z1_1_0 + +-- a/go.mod -- +module example.com/a + +go 1.18 + +require example.com/b v1.0.0 +require example.com/z v1.0.0 +-- a/foo.go -- +package main + +import "example.com/b" + +func main() { + b.B() +} +-- b/go.mod -- +module example.com/b + +go 1.18 + +require example.com/q v1.1.0 +-- b/b.go -- +package b + +func B() { +} +-- p/go.mod -- +module example.com/p + +go 1.18 + +require example.com/q v1.0.0 + +replace example.com/q v1.0.0 => ../q1_0_0 +replace example.com/q v1.1.0 => ../q1_1_0 +-- p/main.go -- +package main + +import "example.com/q" + +func main() { + q.PrintVersion() +} +-- q1_0_0/go.mod -- +module example.com/q + +go 1.18 +-- q1_0_0/q.go -- +package q + +import "fmt" + +func PrintVersion() { + fmt.Println("version 1.0.0") +} +-- q1_0_5/go.mod -- +module example.com/q + +go 1.18 + +require example.com/r v1.0.0 +-- q1_0_5/q.go -- +package q + +import _ "example.com/r" +-- q1_1_0/go.mod -- +module example.com/q + +require example.com/w v1.0.0 +require example.com/z v1.1.0 + +go 1.18 +-- q1_1_0/q.go -- +package q + +import _ "example.com/w" +import _ "example.com/z" + +import "fmt" + +func PrintVersion() { + fmt.Println("version 1.1.0") +} +-- r/go.mod -- +module example.com/r + +go 1.18 + +require example.com/r v1.0.0 +-- r/r.go -- +package r +-- w/go.mod -- +module example.com/w + +go 1.18 + +require example.com/x v1.0.0 +-- w/w.go -- +package w +-- w/w_test.go -- +package w + +import _ "example.com/x" +-- x/go.mod -- +module example.com/x + +go 1.18 +-- x/x.go -- +package x +-- x/x_test.go -- +package x +import _ "example.com/y" +-- y/go.mod -- +module example.com/y + +go 1.18 +-- y/y.go -- +package y +-- z1_0_0/go.mod -- +module example.com/z + +go 1.18 + +require example.com/q v1.0.5 +-- z1_0_0/z.go -- +package z + +import _ "example.com/q" +-- z1_1_0/go.mod -- +module example.com/z + +go 1.18 +-- z1_1_0/z.go -- +package z diff --git a/libgo/go/cmd/go/testdata/script/work_regression_hang.txt b/libgo/go/cmd/go/testdata/script/work_regression_hang.txt new file mode 100644 index 00000000000..a7661b68ad4 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_regression_hang.txt @@ -0,0 +1,71 @@ +# This test makes checks against a regression of a bug in the Go command +# where the module loader hung forever because all main module dependencies +# kept workspace pruning instead of adopting the pruning in their go.mod +# files, and the loader kept adding dependencies on the queue until they +# were either pruned or unpruned, never breaking a module dependency cycle. +# +# This is the module graph in the test: +# +# /-------------------------\ +# | | +# V | +# example.com/a -> example.com/b v1.0.0 -> example.com/c v1.1.0 + +go list -m -f '{{.Version}}' example.com/c + +-- go.work -- +go 1.16 + +use ( + ./a +) +-- a/go.mod -- +module example.com/a + +go 1.18 + +require example.com/b v1.0.0 + +replace example.com/b v1.0.0 => ../b +replace example.com/c v1.0.0 => ../c +-- a/foo.go -- +package main + +import "example.com/b" + +func main() { + b.B() +} +-- b/go.mod -- +module example.com/b + +go 1.18 + +require example.com/c v1.0.0 +-- b/b.go -- +package b + +func B() { +} +-- b/cmd/main.go -- +package main + +import "example.com/c" + +func main() { + c.C() +} +-- c/go.mod -- +module example.com/c + +go 1.18 + +require example.com/b v1.0.0 +-- c/c.go -- +package c + +import "example.com/b" + +func C() { + b.B() +}
\ No newline at end of file diff --git a/libgo/go/cmd/go/testdata/script/work_replace.txt b/libgo/go/cmd/go/testdata/script/work_replace.txt new file mode 100644 index 00000000000..81268e50697 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_replace.txt @@ -0,0 +1,55 @@ +# Support replace statement in go.work file + +# Replacement in go.work file, and none in go.mod file. +go list -m example.com/dep +stdout 'example.com/dep v1.0.0 => ./dep' + +# Wildcard replacement in go.work file overrides version replacement in go.mod +# file. +go list -m example.com/other +stdout 'example.com/other v1.0.0 => ./other2' + +-- go.work -- +use m + +replace example.com/dep => ./dep +replace example.com/other => ./other2 + +-- m/go.mod -- +module example.com/m + +require example.com/dep v1.0.0 +require example.com/other v1.0.0 + +replace example.com/other v1.0.0 => ./other +-- m/m.go -- +package m + +import "example.com/dep" +import "example.com/other" + +func F() { + dep.G() + other.H() +} +-- dep/go.mod -- +module example.com/dep +-- dep/dep.go -- +package dep + +func G() { +} +-- other/go.mod -- +module example.com/other +-- other/dep.go -- +package other + +func G() { +} +-- other2/go.mod -- +module example.com/other +-- other2/dep.go -- +package other + +func G() { +}
\ No newline at end of file diff --git a/libgo/go/cmd/go/testdata/script/work_replace_conflict.txt b/libgo/go/cmd/go/testdata/script/work_replace_conflict.txt new file mode 100644 index 00000000000..81d1fcb0435 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_replace_conflict.txt @@ -0,0 +1,53 @@ +# Conflicting replaces in workspace modules returns error that suggests +# overriding it in the go.work file. + +! go list -m example.com/dep +stderr 'go: conflicting replacements for example.com/dep@v1.0.0:\n\t./dep1\n\t./dep2\nuse "go work edit -replace example.com/dep@v1.0.0=\[override\]" to resolve' +go work edit -replace example.com/dep@v1.0.0=./dep1 +go list -m example.com/dep +stdout 'example.com/dep v1.0.0 => ./dep1' + +-- foo -- +-- go.work -- +use m +use n +-- m/go.mod -- +module example.com/m + +require example.com/dep v1.0.0 +replace example.com/dep v1.0.0 => ./dep1 +-- m/m.go -- +package m + +import "example.com/dep" + +func F() { + dep.G() +} +-- n/go.mod -- +module example.com/n + +require example.com/dep v1.0.0 +replace example.com/dep v1.0.0 => ./dep2 +-- n/n.go -- +package n + +import "example.com/dep" + +func F() { + dep.G() +} +-- dep1/go.mod -- +module example.com/dep +-- dep1/dep.go -- +package dep + +func G() { +} +-- dep2/go.mod -- +module example.com/dep +-- dep2/dep.go -- +package dep + +func G() { +} diff --git a/libgo/go/cmd/go/testdata/script/work_replace_conflict_override.txt b/libgo/go/cmd/go/testdata/script/work_replace_conflict_override.txt new file mode 100644 index 00000000000..c62084bee69 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_replace_conflict_override.txt @@ -0,0 +1,57 @@ +# Conflicting workspace module replaces can be overridden by a replace in the +# go.work file. + +go list -m example.com/dep +stdout 'example.com/dep v1.0.0 => ./dep3' + +-- go.work -- +use m +use n +replace example.com/dep => ./dep3 +-- m/go.mod -- +module example.com/m + +require example.com/dep v1.0.0 +replace example.com/dep => ./dep1 +-- m/m.go -- +package m + +import "example.com/dep" + +func F() { + dep.G() +} +-- n/go.mod -- +module example.com/n + +require example.com/dep v1.0.0 +replace example.com/dep => ./dep2 +-- n/n.go -- +package n + +import "example.com/dep" + +func F() { + dep.G() +} +-- dep1/go.mod -- +module example.com/dep +-- dep1/dep.go -- +package dep + +func G() { +} +-- dep2/go.mod -- +module example.com/dep +-- dep2/dep.go -- +package dep + +func G() { +} +-- dep3/go.mod -- +module example.com/dep +-- dep3/dep.go -- +package dep + +func G() { +} diff --git a/libgo/go/cmd/go/testdata/script/work_replace_main_module.txt b/libgo/go/cmd/go/testdata/script/work_replace_main_module.txt new file mode 100644 index 00000000000..b213764280e --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_replace_main_module.txt @@ -0,0 +1,45 @@ +# Ensure that replaces of the main module in workspace modules +# are ignored, and replaces in the go.work file are disallowed. +# This tests against an issue where requirements of the +# main module were being ignored because the main module +# was replaced in a transitive dependency with another +# version. + +go list example.com/dep + +cp replace_main_module.go.work go.work +! go list example.com/dep +stderr 'go: workspace module example.com/mainmoda is replaced at all versions in the go.work file. To fix, remove the replacement from the go.work file or specify the version at which to replace the module.' + +-- replace_main_module.go.work -- +go 1.18 +use ( + ./mainmoda + ./mainmodb +) +replace example.com/mainmoda => ../mainmodareplacement +-- go.work -- +go 1.18 +use ( + ./mainmoda + ./mainmodb +) +-- mainmoda/go.mod -- +module example.com/mainmoda + +go 1.18 + +require example.com/dep v1.0.0 +replace example.com/dep => ../dep + +-- dep/go.mod -- +module example.com/dep +-- dep/dep.go -- +package dep +-- mainmodb/go.mod -- +module example.com/mainmodb +go 1.18 +replace example.com/mainmoda => ../mainmodareplacement +-- mainmodareplacement/go.mod -- +module example.com/mainmoda +go 1.18
\ No newline at end of file diff --git a/libgo/go/cmd/go/testdata/script/work_sum.txt b/libgo/go/cmd/go/testdata/script/work_sum.txt new file mode 100644 index 00000000000..19dbb90507a --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_sum.txt @@ -0,0 +1,34 @@ +# Test adding sums to go.work.sum when sum isn't in go.mod. + +go run . +cmp go.work.sum want.sum + +-- want.sum -- +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:pvCbr/wm8HzDD3fVywevekufpn6tCGPY3spdHeZJEsw= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +rsc.io/quote v1.5.2 h1:3fEykkD9k7lYzXqCYrwGAf7iNhbk4yCjHmKBN9td4L0= +rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +-- go.work -- +go 1.18 + +use . +-- go.mod -- +go 1.18 + +module example.com/hi + +require "rsc.io/quote" v1.5.2 +-- go.sum -- +rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +-- main.go -- +package main + +import ( + "fmt" + "rsc.io/quote" +) + +func main() { + fmt.Println(quote.Hello()) +}
\ No newline at end of file diff --git a/libgo/go/cmd/go/testdata/script/work_sum_mismatch.txt b/libgo/go/cmd/go/testdata/script/work_sum_mismatch.txt new file mode 100644 index 00000000000..9e9474304e5 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_sum_mismatch.txt @@ -0,0 +1,61 @@ +# Test mismatched sums in go.sum files + +! go run ./a +cmpenv stderr want-error + +-- want-error -- +verifying rsc.io/sampler@v1.3.0/go.mod: checksum mismatch + downloaded: h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= + $WORK${/}gopath${/}src${/}a${/}go.sum: h1:U1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= + +SECURITY ERROR +This download does NOT match an earlier download recorded in go.sum. +The bits may have been replaced on the origin server, or an attacker may +have intercepted the download attempt. + +For more information, see 'go help module-auth'. +-- go.work -- +go 1.18 + +use ./a +use ./b +-- a/go.mod -- +go 1.18 + +module example.com/hi + +require "rsc.io/quote" v1.5.2 +-- a/go.sum -- +rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII= +rsc.io/sampler v1.3.0/go.mod h1:U1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +-- a/main.go -- +package main + +import ( + "fmt" + "rsc.io/quote" +) + +func main() { + fmt.Println(quote.Hello()) +} +-- b/go.mod -- +go 1.18 + +module example.com/hi + +require "rsc.io/quote" v1.5.2 +-- b/go.sum -- +rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +-- b/main.go -- +package main + +import ( + "fmt" + "rsc.io/quote" +) + +func main() { + fmt.Println(quote.Hello()) +}
\ No newline at end of file diff --git a/libgo/go/cmd/go/testdata/script/work_sync.txt b/libgo/go/cmd/go/testdata/script/work_sync.txt new file mode 100644 index 00000000000..69167d4cc14 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_sync.txt @@ -0,0 +1,119 @@ +go work sync +cmp a/go.mod a/want_go.mod +cmp b/go.mod b/want_go.mod + +-- go.work -- +go 1.18 + +use ( + ./a + ./b +) + +-- a/go.mod -- +go 1.18 + +module example.com/a + +require ( + example.com/p v1.0.0 + example.com/q v1.1.0 + example.com/r v1.0.0 +) + +replace ( + example.com/p => ../p + example.com/q => ../q + example.com/r => ../r +) +-- a/want_go.mod -- +go 1.18 + +module example.com/a + +require ( + example.com/p v1.1.0 + example.com/q v1.1.0 +) + +replace ( + example.com/p => ../p + example.com/q => ../q + example.com/r => ../r +) +-- a/a.go -- +package a + +import ( + "example.com/p" + "example.com/q" +) + +func Foo() { + p.P() + q.Q() +} +-- b/go.mod -- +go 1.18 + +module example.com/b + +require ( + example.com/p v1.1.0 + example.com/q v1.0.0 +) + +replace ( + example.com/p => ../p + example.com/q => ../q +) +-- b/want_go.mod -- +go 1.18 + +module example.com/b + +require ( + example.com/p v1.1.0 + example.com/q v1.1.0 +) + +replace ( + example.com/p => ../p + example.com/q => ../q +) +-- b/b.go -- +package b + +import ( + "example.com/p" + "example.com/q" +) + +func Foo() { + p.P() + q.Q() +} +-- p/go.mod -- +go 1.18 + +module example.com/p +-- p/p.go -- +package p + +func P() {} +-- q/go.mod -- +go 1.18 + +module example.com/q +-- q/q.go -- +package q + +func Q() {} +-- r/go.mod -- +go 1.18 + +module example.com/r +-- r/q.go -- +package r + +func R() {}
\ No newline at end of file diff --git a/libgo/go/cmd/go/testdata/script/work_sync_irrelevant_dependency.txt b/libgo/go/cmd/go/testdata/script/work_sync_irrelevant_dependency.txt new file mode 100644 index 00000000000..072323d15da --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_sync_irrelevant_dependency.txt @@ -0,0 +1,119 @@ +# Test of go work sync in a workspace in which some dependency needed by `a` +# appears at a lower version in the build list of `b`, but is not needed at all +# by `b` (so it should not be upgraded within b). +# +# a -> p 1.1 +# b -> q 1.0 -(through a test dependency)-> p 1.0 +go work sync +cmp a/go.mod a/want_go.mod +cmp b/go.mod b/want_go.mod + +-- go.work -- +go 1.18 + +use ( + ./a + ./b +) + +-- a/go.mod -- +go 1.18 + +module example.com/a + +require ( + example.com/p v1.1.0 +) + +replace ( + example.com/p => ../p +) +-- a/want_go.mod -- +go 1.18 + +module example.com/a + +require ( + example.com/p v1.1.0 +) + +replace ( + example.com/p => ../p +) +-- a/a.go -- +package a + +import ( + "example.com/p" +) + +func Foo() { + p.P() +} +-- b/go.mod -- +go 1.18 + +module example.com/b + +require ( + example.com/q v1.0.0 +) + +replace ( + example.com/q => ../q +) +-- b/want_go.mod -- +go 1.18 + +module example.com/b + +require ( + example.com/q v1.0.0 +) + +replace ( + example.com/q => ../q +) +-- b/b.go -- +package b + +import ( + "example.com/q" +) + +func Foo() { + q.Q() +} +-- p/go.mod -- +go 1.18 + +module example.com/p +-- p/p.go -- +package p + +func P() {} +-- q/go.mod -- +go 1.18 + +module example.com/q + +require ( + example.com/p v1.0.0 +) + +replace ( + example.com/p => ../p +) +-- q/q.go -- +package q + +func Q() { +} +-- q/q_test.go -- +package q + +import example.com/p + +func TestQ(t *testing.T) { + p.P() +}
\ No newline at end of file diff --git a/libgo/go/cmd/go/testdata/script/work_sync_missing_module.txt b/libgo/go/cmd/go/testdata/script/work_sync_missing_module.txt new file mode 100644 index 00000000000..0018c733ee7 --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_sync_missing_module.txt @@ -0,0 +1,12 @@ +# Ensure go work sync works without any modules in go.work. +go work sync + +# Ensure go work sync works even without a go.mod file. +rm go.mod +go work sync + +-- go.work -- +go 1.18 +-- go.mod -- +go 1.18 +module foo diff --git a/libgo/go/cmd/go/testdata/script/work_sync_relevant_dependency.txt b/libgo/go/cmd/go/testdata/script/work_sync_relevant_dependency.txt new file mode 100644 index 00000000000..d7997027d9d --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_sync_relevant_dependency.txt @@ -0,0 +1,106 @@ +# Test of go work sync in a workspace in which some dependency in the build +# list of 'b' (but not otherwise needed by `b`, so not seen when lazy loading +# occurs) actually is relevant to `a`. +# +# a -> p 1.0 +# b -> q 1.1 -> p 1.1 +go work sync +cmp a/go.mod a/want_go.mod +cmp b/go.mod b/want_go.mod + +-- go.work -- +go 1.18 + +use ( + ./a + ./b +) + +-- a/go.mod -- +go 1.18 + +module example.com/a + +require ( + example.com/p v1.0.0 +) + +replace ( + example.com/p => ../p +) +-- a/want_go.mod -- +go 1.18 + +module example.com/a + +require example.com/p v1.1.0 + +replace example.com/p => ../p +-- a/a.go -- +package a + +import ( + "example.com/p" +) + +func Foo() { + p.P() +} +-- b/go.mod -- +go 1.18 + +module example.com/b + +require ( + example.com/q v1.1.0 +) + +replace ( + example.com/q => ../q +) +-- b/want_go.mod -- +go 1.18 + +module example.com/b + +require ( + example.com/q v1.1.0 +) + +replace ( + example.com/q => ../q +) +-- b/b.go -- +package b + +import ( + "example.com/q" +) + +func Foo() { + q.Q() +} +-- p/go.mod -- +go 1.18 + +module example.com/p +-- p/p.go -- +package p + +func P() {} +-- q/go.mod -- +go 1.18 + +module example.com/q + +require example.com/p v1.1.0 + +replace example.com/p => ../p +-- q/q.go -- +package q + +import example.com/p + +func Q() { + p.P() +} diff --git a/libgo/go/cmd/go/testdata/script/work_sync_sum.txt b/libgo/go/cmd/go/testdata/script/work_sync_sum.txt new file mode 100644 index 00000000000..656fd31379e --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_sync_sum.txt @@ -0,0 +1,40 @@ +# Test that the sum file data state is properly reset between modules in +# go work sync so that the sum file that's written is correct. +# Exercises the fix to #50038. + +cp b/go.sum b/go.sum.want + +# As a sanity check, verify b/go.sum is tidy. +cd b +go mod tidy +cd .. +cmp b/go.sum b/go.sum.want + +# Run go work sync and verify it doesn't change b/go.sum. +go work sync +cmp b/go.sum b/go.sum.want + +-- b/go.sum -- +rsc.io/quote v1.0.0 h1:kQ3IZQzPTiDJxSZI98YaWgxFEhlNdYASHvh+MplbViw= +rsc.io/quote v1.0.0/go.mod h1:v83Ri/njykPcgJltBc/gEkJTmjTsNgtO1Y7vyIK1CQA= +-- go.work -- +go 1.18 +use ( + ./a + ./b +) +replace example.com/c => ./c +-- a/go.mod -- +module example.com/a +go 1.18 +require rsc.io/fortune v1.0.0 +-- a/a.go -- +package a +import "rsc.io/fortune" +-- b/go.mod -- +module example.com/b +go 1.18 +require rsc.io/quote v1.0.0 +-- b/b.go -- +package b +import _ "rsc.io/quote" diff --git a/libgo/go/cmd/go/testdata/script/work_use.txt b/libgo/go/cmd/go/testdata/script/work_use.txt new file mode 100644 index 00000000000..f5ea89c900e --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_use.txt @@ -0,0 +1,32 @@ +go work use -r foo +cmp go.work go.want_work_r + +go work use other +cmp go.work go.want_work_other +-- go.work -- +go 1.18 + +use ( + foo + foo/bar // doesn't exist +) +-- go.want_work_r -- +go 1.18 + +use ( + foo + foo/bar/baz +) +-- go.want_work_other -- +go 1.18 + +use ( + foo + foo/bar/baz + other +) +-- foo/go.mod -- +module foo +-- foo/bar/baz/go.mod -- +module baz +-- other/go.mod -- diff --git a/libgo/go/cmd/go/testdata/script/work_why_download_graph.txt b/libgo/go/cmd/go/testdata/script/work_why_download_graph.txt new file mode 100644 index 00000000000..7964c914a2c --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_why_download_graph.txt @@ -0,0 +1,59 @@ +# Test go mod download, why, and graph work in workspace mode. +# TODO(bcmills): clarify the interaction with #44435 + +go mod download rsc.io/quote +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.info +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.0.info +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.0.mod + +go mod download +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.info +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.0.info +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.0.mod + +go mod why rsc.io/quote +stdout '# rsc.io/quote\nexample.com/a\nrsc.io/quote' + +go mod graph +stdout 'example.com/a rsc.io/quote@v1.5.2\nexample.com/b example.com/c@v1.0.0\nrsc.io/quote@v1.5.2 rsc.io/sampler@v1.3.0\nrsc.io/sampler@v1.3.0 golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c' + +-- go.work -- +go 1.18 + +use ( + ./a + ./b +) +-- a/go.mod -- +go 1.18 + +module example.com/a + +require "rsc.io/quote" v1.5.2 +-- a/main.go -- +package main + +import ( + "fmt" + "rsc.io/quote" +) + +func main() { + fmt.Println(quote.Hello()) +} +-- b/go.mod -- +go 1.18 + +module example.com/b + +require example.com/c v1.0.0 +replace example.com/c => ../c +-- c/go.mod -- +go 1.18 + +module example.com/c + diff --git a/libgo/go/cmd/go/testdata/script/work_workfile.txt b/libgo/go/cmd/go/testdata/script/work_workfile.txt new file mode 100644 index 00000000000..b62918147ef --- /dev/null +++ b/libgo/go/cmd/go/testdata/script/work_workfile.txt @@ -0,0 +1,21 @@ +! go list -workfile=stop.work a # require absolute path +! stderr panic +! go list -workfile=doesnotexist a +! stderr panic + +go list -n -workfile=$GOPATH/src/stop.work a +go build -n -workfile=$GOPATH/src/stop.work a +go test -n -workfile=$GOPATH/src/stop.work a + +-- stop.work -- +go 1.18 + +use ./a +-- a/a.go -- +package a +-- a/a_test.go -- +package a +-- a/go.mod -- +module a + +go 1.18
\ No newline at end of file diff --git a/libgo/go/cmd/go/testdata/testterminal18153/terminal_test.go b/libgo/go/cmd/go/testdata/testterminal18153/terminal_test.go index 71493efe983..34ee580c0e9 100644 --- a/libgo/go/cmd/go/testdata/testterminal18153/terminal_test.go +++ b/libgo/go/cmd/go/testdata/testterminal18153/terminal_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build linux // +build linux // This test is run by src/cmd/dist/test.go (cmd_go_test_terminal), diff --git a/libgo/go/cmd/gofmt/gofmt.go b/libgo/go/cmd/gofmt/gofmt.go index b3c120daab4..51f6e652d9a 100644 --- a/libgo/go/cmd/gofmt/gofmt.go +++ b/libgo/go/cmd/gofmt/gofmt.go @@ -6,6 +6,7 @@ package main import ( "bytes" + "context" "flag" "fmt" "go/ast" @@ -22,6 +23,8 @@ import ( "strings" "cmd/internal/diff" + + "golang.org/x/sync/semaphore" ) var ( @@ -50,17 +53,10 @@ const ( ) var ( - fileSet = token.NewFileSet() // per process FileSet - exitCode = 0 - rewrite func(*ast.File) *ast.File + rewrite func(*token.FileSet, *ast.File) *ast.File parserMode parser.Mode ) -func report(err error) { - scanner.PrintError(os.Stderr, err) - exitCode = 2 -} - func usage() { fmt.Fprintf(os.Stderr, "usage: gofmt [flags] [path ...]\n") flag.PrintDefaults() @@ -76,41 +72,211 @@ func initParserMode() { func isGoFile(f fs.DirEntry) bool { // ignore non-Go files name := f.Name() - return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") + return !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") && !f.IsDir() +} + +// A sequencer performs concurrent tasks that may write output, but emits that +// output in a deterministic order. +type sequencer struct { + maxWeight int64 + sem *semaphore.Weighted // weighted by input bytes (an approximate proxy for memory overhead) + prev <-chan *reporterState // 1-buffered +} + +// newSequencer returns a sequencer that allows concurrent tasks up to maxWeight +// and writes tasks' output to out and err. +func newSequencer(maxWeight int64, out, err io.Writer) *sequencer { + sem := semaphore.NewWeighted(maxWeight) + prev := make(chan *reporterState, 1) + prev <- &reporterState{out: out, err: err} + return &sequencer{ + maxWeight: maxWeight, + sem: sem, + prev: prev, + } +} + +// exclusive is a weight that can be passed to a sequencer to cause +// a task to be executed without any other concurrent tasks. +const exclusive = -1 + +// Add blocks until the sequencer has enough weight to spare, then adds f as a +// task to be executed concurrently. +// +// If the weight is either negative or larger than the sequencer's maximum +// weight, Add blocks until all other tasks have completed, then the task +// executes exclusively (blocking all other calls to Add until it completes). +// +// f may run concurrently in a goroutine, but its output to the passed-in +// reporter will be sequential relative to the other tasks in the sequencer. +// +// If f invokes a method on the reporter, execution of that method may block +// until the previous task has finished. (To maximize concurrency, f should +// avoid invoking the reporter until it has finished any parallelizable work.) +// +// If f returns a non-nil error, that error will be reported after f's output +// (if any) and will cause a nonzero final exit code. +func (s *sequencer) Add(weight int64, f func(*reporter) error) { + if weight < 0 || weight > s.maxWeight { + weight = s.maxWeight + } + if err := s.sem.Acquire(context.TODO(), weight); err != nil { + // Change the task from "execute f" to "report err". + weight = 0 + f = func(*reporter) error { return err } + } + + r := &reporter{prev: s.prev} + next := make(chan *reporterState, 1) + s.prev = next + + // Start f in parallel: it can run until it invokes a method on r, at which + // point it will block until the previous task releases the output state. + go func() { + if err := f(r); err != nil { + r.Report(err) + } + next <- r.getState() // Release the next task. + s.sem.Release(weight) + }() +} + +// AddReport prints an error to s after the output of any previously-added +// tasks, causing the final exit code to be nonzero. +func (s *sequencer) AddReport(err error) { + s.Add(0, func(*reporter) error { return err }) +} + +// GetExitCode waits for all previously-added tasks to complete, then returns an +// exit code for the sequence suitable for passing to os.Exit. +func (s *sequencer) GetExitCode() int { + c := make(chan int, 1) + s.Add(0, func(r *reporter) error { + c <- r.ExitCode() + return nil + }) + return <-c +} + +// A reporter reports output, warnings, and errors. +type reporter struct { + prev <-chan *reporterState + state *reporterState +} + +// reporterState carries the state of a reporter instance. +// +// Only one reporter at a time may have access to a reporterState. +type reporterState struct { + out, err io.Writer + exitCode int +} + +// getState blocks until any prior reporters are finished with the reporter +// state, then returns the state for manipulation. +func (r *reporter) getState() *reporterState { + if r.state == nil { + r.state = <-r.prev + } + return r.state } +// Warnf emits a warning message to the reporter's error stream, +// without changing its exit code. +func (r *reporter) Warnf(format string, args ...any) { + fmt.Fprintf(r.getState().err, format, args...) +} + +// Write emits a slice to the reporter's output stream. +// +// Any error is returned to the caller, and does not otherwise affect the +// reporter's exit code. +func (r *reporter) Write(p []byte) (int, error) { + return r.getState().out.Write(p) +} + +// Report emits a non-nil error to the reporter's error stream, +// changing its exit code to a nonzero value. +func (r *reporter) Report(err error) { + if err == nil { + panic("Report with nil error") + } + st := r.getState() + scanner.PrintError(st.err, err) + st.exitCode = 2 +} + +func (r *reporter) ExitCode() int { + return r.getState().exitCode +} + +// If info == nil, we are formatting stdin instead of a file. // If in == nil, the source is the contents of the file with the given filename. -func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error { - var perm fs.FileMode = 0644 +func processFile(filename string, info fs.FileInfo, in io.Reader, r *reporter) error { if in == nil { - f, err := os.Open(filename) + var err error + in, err = os.Open(filename) if err != nil { return err } - defer f.Close() - fi, err := f.Stat() + } + + // Compute the file's size and read its contents with minimal allocations. + // + // If the size is unknown (or bogus, or overflows an int), fall back to + // a size-independent ReadAll. + var src []byte + size := -1 + if info != nil && info.Mode().IsRegular() && int64(int(info.Size())) == info.Size() { + size = int(info.Size()) + } + if size+1 > 0 { + // If we have the FileInfo from filepath.WalkDir, use it to make + // a buffer of the right size and avoid ReadAll's reallocations. + // + // We try to read size+1 bytes so that we can detect modifications: if we + // read more than size bytes, then the file was modified concurrently. + // (If that happens, we could, say, append to src to finish the read, or + // proceed with a truncated buffer — but the fact that it changed at all + // indicates a possible race with someone editing the file, so we prefer to + // stop to avoid corrupting it.) + src = make([]byte, size+1) + n, err := io.ReadFull(in, src) + if err != nil && err != io.ErrUnexpectedEOF { + return err + } + if n < size { + return fmt.Errorf("error: size of %s changed during reading (from %d to %d bytes)", filename, size, n) + } else if n > size { + return fmt.Errorf("error: size of %s changed during reading (from %d to >=%d bytes)", filename, size, len(src)) + } + src = src[:n] + } else { + // The file is not known to be regular, so we don't have a reliable size for it. + var err error + src, err = io.ReadAll(in) if err != nil { return err } - in = f - perm = fi.Mode().Perm() } - src, err := io.ReadAll(in) - if err != nil { - return err + fileSet := token.NewFileSet() + fragmentOk := false + if info == nil { + // If we are formatting stdin, we accept a program fragment in lieu of a + // complete source file. + fragmentOk = true } - - file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, stdin) + file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, fragmentOk) if err != nil { return err } if rewrite != nil { if sourceAdj == nil { - file = rewrite(file) + file = rewrite(fileSet, file) } else { - fmt.Fprintf(os.Stderr, "warning: rewrite ignored for incomplete programs\n") + r.Warnf("warning: rewrite ignored for incomplete programs\n") } } @@ -128,10 +294,14 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error if !bytes.Equal(src, res) { // formatting has changed if *list { - fmt.Fprintln(out, filename) + fmt.Fprintln(r, filename) } if *write { + if info == nil { + panic("-w should not have been allowed with stdin") + } // make a temporary backup before overwriting original + perm := info.Mode().Perm() bakname, err := backupFile(filename+".", src, perm) if err != nil { return err @@ -151,45 +321,42 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error if err != nil { return fmt.Errorf("computing diff: %s", err) } - fmt.Fprintf(out, "diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename)) - out.Write(data) + fmt.Fprintf(r, "diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename)) + r.Write(data) } } if !*list && !*write && !*doDiff { - _, err = out.Write(res) + _, err = r.Write(res) } return err } -func visitFile(path string, f fs.DirEntry, err error) error { - if err != nil || !isGoFile(f) { - return err - } - if err := processFile(path, nil, os.Stdout, false); err != nil { - report(err) - } - return nil -} - func main() { + // Arbitrarily limit in-flight work to 2MiB times the number of threads. + // + // The actual overhead for the parse tree and output will depend on the + // specifics of the file, but this at least keeps the footprint of the process + // roughly proportional to GOMAXPROCS. + maxWeight := (2 << 20) * int64(runtime.GOMAXPROCS(0)) + s := newSequencer(maxWeight, os.Stdout, os.Stderr) + // call gofmtMain in a separate function // so that it can use defer and have them // run before the exit. - gofmtMain() - os.Exit(exitCode) + gofmtMain(s) + os.Exit(s.GetExitCode()) } -func gofmtMain() { +func gofmtMain(s *sequencer) { flag.Usage = usage flag.Parse() if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { - fmt.Fprintf(os.Stderr, "creating cpu profile: %s\n", err) - exitCode = 2 + s.AddReport(fmt.Errorf("creating cpu profile: %s", err)) return } defer f.Close() @@ -203,34 +370,67 @@ func gofmtMain() { args := flag.Args() if len(args) == 0 { if *write { - fmt.Fprintln(os.Stderr, "error: cannot use -w with standard input") - exitCode = 2 + s.AddReport(fmt.Errorf("error: cannot use -w with standard input")) return } - if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil { - report(err) - } + s.Add(0, func(r *reporter) error { + return processFile("<standard input>", nil, os.Stdin, r) + }) return } for _, arg := range args { switch info, err := os.Stat(arg); { case err != nil: - report(err) + s.AddReport(err) case !info.IsDir(): // Non-directory arguments are always formatted. - if err := processFile(arg, nil, os.Stdout, false); err != nil { - report(err) - } + arg := arg + s.Add(fileWeight(arg, info), func(r *reporter) error { + return processFile(arg, info, nil, r) + }) default: // Directories are walked, ignoring non-Go files. - if err := filepath.WalkDir(arg, visitFile); err != nil { - report(err) + err := filepath.WalkDir(arg, func(path string, f fs.DirEntry, err error) error { + if err != nil || !isGoFile(f) { + return err + } + info, err := f.Info() + if err != nil { + s.AddReport(err) + return nil + } + s.Add(fileWeight(path, info), func(r *reporter) error { + return processFile(path, info, nil, r) + }) + return nil + }) + if err != nil { + s.AddReport(err) } } } } +func fileWeight(path string, info fs.FileInfo) int64 { + if info == nil { + return exclusive + } + if info.Mode().Type() == fs.ModeSymlink { + var err error + info, err = os.Stat(path) + if err != nil { + return exclusive + } + } + if !info.Mode().IsRegular() { + // For non-regular files, FileInfo.Size is system-dependent and thus not a + // reliable indicator of weight. + return exclusive + } + return info.Size() +} + func diffWithReplaceTempFile(b1, b2 []byte, filename string) ([]byte, error) { data, err := diff.Diff("gofmt", b1, b2) if len(data) > 0 { diff --git a/libgo/go/cmd/gofmt/gofmt_test.go b/libgo/go/cmd/gofmt/gofmt_test.go index f0d3f8780f4..676c5b43ede 100644 --- a/libgo/go/cmd/gofmt/gofmt_test.go +++ b/libgo/go/cmd/gofmt/gofmt_test.go @@ -54,13 +54,15 @@ func gofmtFlags(filename string, maxLines int) string { return "" } -var typeParamsEnabled = false - func runTest(t *testing.T, in, out string) { // process flags *simplifyAST = false *rewriteRule = "" - stdin := false + info, err := os.Lstat(in) + if err != nil { + t.Error(err) + return + } for _, flag := range strings.Split(gofmtFlags(in, 20), " ") { elts := strings.SplitN(flag, "=", 2) name := elts[0] @@ -77,12 +79,7 @@ func runTest(t *testing.T, in, out string) { *simplifyAST = true case "-stdin": // fake flag - pretend input is from stdin - stdin = true - case "-G": - // fake flag - test is for generic code - if !typeParamsEnabled { - return - } + info = nil default: t.Errorf("unrecognized flag name: %s", name) } @@ -91,11 +88,17 @@ func runTest(t *testing.T, in, out string) { initParserMode() initRewrite() - var buf bytes.Buffer - err := processFile(in, nil, &buf, stdin) - if err != nil { - t.Error(err) - return + const maxWeight = 2 << 20 + var buf, errBuf bytes.Buffer + s := newSequencer(maxWeight, &buf, &errBuf) + s.Add(fileWeight(in, info), func(r *reporter) error { + return processFile(in, info, nil, r) + }) + if errBuf.Len() > 0 { + t.Logf("%q", errBuf.Bytes()) + } + if s.GetExitCode() != 0 { + t.Fail() } expected, err := os.ReadFile(out) diff --git a/libgo/go/cmd/gofmt/gofmt_typeparams_test.go b/libgo/go/cmd/gofmt/gofmt_typeparams_test.go deleted file mode 100644 index 10641a77cb2..00000000000 --- a/libgo/go/cmd/gofmt/gofmt_typeparams_test.go +++ /dev/null @@ -1,12 +0,0 @@ -// 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. - -//go:build typeparams -// +build typeparams - -package main - -func init() { - typeParamsEnabled = true -} diff --git a/libgo/go/cmd/gofmt/rewrite.go b/libgo/go/cmd/gofmt/rewrite.go index bab22e04cda..a98c6a0cd9b 100644 --- a/libgo/go/cmd/gofmt/rewrite.go +++ b/libgo/go/cmd/gofmt/rewrite.go @@ -28,7 +28,9 @@ func initRewrite() { } pattern := parseExpr(f[0], "pattern") replace := parseExpr(f[1], "replacement") - rewrite = func(p *ast.File) *ast.File { return rewriteFile(pattern, replace, p) } + rewrite = func(fset *token.FileSet, p *ast.File) *ast.File { + return rewriteFile(fset, pattern, replace, p) + } } // parseExpr parses s as an expression. @@ -54,7 +56,7 @@ func dump(msg string, val reflect.Value) { */ // rewriteFile applies the rewrite rule 'pattern -> replace' to an entire file. -func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File { +func rewriteFile(fileSet *token.FileSet, pattern, replace ast.Expr, p *ast.File) *ast.File { cmap := ast.NewCommentMap(fileSet, p, p.Comments) m := make(map[string]reflect.Value) pat := reflect.ValueOf(pattern) @@ -290,7 +292,7 @@ func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) } return v - case reflect.Ptr: + case reflect.Pointer: v := reflect.New(p.Type()).Elem() if elem := p.Elem(); elem.IsValid() { v.Set(subst(m, elem, pos).Addr()) diff --git a/libgo/go/cmd/internal/buildid/buildid_test.go b/libgo/go/cmd/internal/buildid/buildid_test.go index e832f9987e5..f04e328046c 100644 --- a/libgo/go/cmd/internal/buildid/buildid_test.go +++ b/libgo/go/cmd/internal/buildid/buildid_test.go @@ -103,7 +103,7 @@ func TestFindAndHash(t *testing.T) { id[i] = byte(i) } numError := 0 - errorf := func(msg string, args ...interface{}) { + errorf := func(msg string, args ...any) { t.Errorf(msg, args...) if numError++; numError > 20 { t.Logf("stopping after too many errors") @@ -177,3 +177,11 @@ func TestExcludedReader(t *testing.T) { } } } + +func TestEmptyID(t *testing.T) { + r := strings.NewReader("aha!") + matches, hash, err := FindAndHash(r, "", 1000) + if matches != nil || hash != ([32]byte{}) || err == nil || !strings.Contains(err.Error(), "no id") { + t.Errorf("FindAndHash: want nil, [32]byte{}, no id specified, got %v, %v, %v", matches, hash, err) + } +} diff --git a/libgo/go/cmd/internal/buildid/rewrite.go b/libgo/go/cmd/internal/buildid/rewrite.go index a7928959c48..becc0782424 100644 --- a/libgo/go/cmd/internal/buildid/rewrite.go +++ b/libgo/go/cmd/internal/buildid/rewrite.go @@ -22,6 +22,9 @@ func FindAndHash(r io.Reader, id string, bufSize int) (matches []int64, hash [32 if bufSize == 0 { bufSize = 31 * 1024 // bufSize+little will likely fit in 32 kB } + if len(id) == 0 { + return nil, [32]byte{}, fmt.Errorf("buildid.FindAndHash: no id specified") + } if len(id) > bufSize { return nil, [32]byte{}, fmt.Errorf("buildid.FindAndHash: buffer too small") } @@ -148,7 +151,7 @@ func (r *excludedReader) Read(p []byte) (int, error) { return n, err } -func findMachoCodeSignature(r interface{}) (*macho.File, codesign.CodeSigCmd, bool) { +func findMachoCodeSignature(r any) (*macho.File, codesign.CodeSigCmd, bool) { ra, ok := r.(io.ReaderAt) if !ok { return nil, codesign.CodeSigCmd{}, false diff --git a/libgo/go/cmd/internal/objabi/flag.go b/libgo/go/cmd/internal/objabi/flag.go index e41fc570b08..f75c054fcb6 100644 --- a/libgo/go/cmd/internal/objabi/flag.go +++ b/libgo/go/cmd/internal/objabi/flag.go @@ -13,6 +13,8 @@ import ( "io/ioutil" "log" "os" + "reflect" + "sort" "strconv" "strings" ) @@ -202,3 +204,165 @@ func DecodeArg(arg string) string { } return b.String() } + +type debugField struct { + name string + help string + val interface{} // *int or *string +} + +type DebugFlag struct { + tab map[string]debugField + any *bool + + debugSSA DebugSSA +} + +// A DebugSSA function is called to set a -d ssa/... option. +// If nil, those options are reported as invalid options. +// If DebugSSA returns a non-empty string, that text is reported as a compiler error. +// If phase is "help", it should print usage information and terminate the process. +type DebugSSA func(phase, flag string, val int, valString string) string + +// NewDebugFlag constructs a DebugFlag for the fields of debug, which +// must be a pointer to a struct. +// +// Each field of *debug is a different value, named for the lower-case of the field name. +// Each field must be an int or string and must have a `help` struct tag. +// There may be an "Any bool" field, which will be set if any debug flags are set. +// +// The returned flag takes a comma-separated list of settings. +// Each setting is name=value; for ints, name is short for name=1. +// +// If debugSSA is non-nil, any debug flags of the form ssa/... will be +// passed to debugSSA for processing. +func NewDebugFlag(debug interface{}, debugSSA DebugSSA) *DebugFlag { + flag := &DebugFlag{ + tab: make(map[string]debugField), + debugSSA: debugSSA, + } + + v := reflect.ValueOf(debug).Elem() + t := v.Type() + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + ptr := v.Field(i).Addr().Interface() + if f.Name == "Any" { + switch ptr := ptr.(type) { + default: + panic("debug.Any must have type bool") + case *bool: + flag.any = ptr + } + continue + } + name := strings.ToLower(f.Name) + help := f.Tag.Get("help") + if help == "" { + panic(fmt.Sprintf("debug.%s is missing help text", f.Name)) + } + switch ptr.(type) { + default: + panic(fmt.Sprintf("debug.%s has invalid type %v (must be int or string)", f.Name, f.Type)) + case *int, *string: + // ok + } + flag.tab[name] = debugField{name, help, ptr} + } + + return flag +} + +func (f *DebugFlag) Set(debugstr string) error { + if debugstr == "" { + return nil + } + if f.any != nil { + *f.any = true + } + for _, name := range strings.Split(debugstr, ",") { + if name == "" { + continue + } + // display help about the debug option itself and quit + if name == "help" { + fmt.Print(debugHelpHeader) + maxLen, names := 0, []string{} + if f.debugSSA != nil { + maxLen = len("ssa/help") + } + for name := range f.tab { + if len(name) > maxLen { + maxLen = len(name) + } + names = append(names, name) + } + sort.Strings(names) + // Indent multi-line help messages. + nl := fmt.Sprintf("\n\t%-*s\t", maxLen, "") + for _, name := range names { + help := f.tab[name].help + fmt.Printf("\t%-*s\t%s\n", maxLen, name, strings.Replace(help, "\n", nl, -1)) + } + if f.debugSSA != nil { + // ssa options have their own help + fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging") + } + os.Exit(0) + } + + val, valstring, haveInt := 1, "", true + if i := strings.IndexAny(name, "=:"); i >= 0 { + var err error + name, valstring = name[:i], name[i+1:] + val, err = strconv.Atoi(valstring) + if err != nil { + val, haveInt = 1, false + } + } + + if t, ok := f.tab[name]; ok { + switch vp := t.val.(type) { + case nil: + // Ignore + case *string: + *vp = valstring + case *int: + if !haveInt { + log.Fatalf("invalid debug value %v", name) + } + *vp = val + default: + panic("bad debugtab type") + } + } else if f.debugSSA != nil && strings.HasPrefix(name, "ssa/") { + // expect form ssa/phase/flag + // e.g. -d=ssa/generic_cse/time + // _ in phase name also matches space + phase := name[4:] + flag := "debug" // default flag is debug + if i := strings.Index(phase, "/"); i >= 0 { + flag = phase[i+1:] + phase = phase[:i] + } + err := f.debugSSA(phase, flag, val, valstring) + if err != "" { + log.Fatalf(err) + } + } else { + return fmt.Errorf("unknown debug key %s\n", name) + } + } + + return nil +} + +const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>] + +<key> is one of: + +` + +func (f *DebugFlag) String() string { + return "" +} diff --git a/libgo/go/cmd/internal/objabi/funcdata.go b/libgo/go/cmd/internal/objabi/funcdata.go index 4ff0ebe13d8..4d49a8d548d 100644 --- a/libgo/go/cmd/internal/objabi/funcdata.go +++ b/libgo/go/cmd/internal/objabi/funcdata.go @@ -14,6 +14,7 @@ const ( PCDATA_UnsafePoint = 0 PCDATA_StackMapIndex = 1 PCDATA_InlTreeIndex = 2 + PCDATA_ArgLiveIndex = 3 FUNCDATA_ArgsPointerMaps = 0 FUNCDATA_LocalsPointerMaps = 1 @@ -21,6 +22,7 @@ const ( FUNCDATA_InlTree = 3 FUNCDATA_OpenCodedDeferInfo = 4 FUNCDATA_ArgInfo = 5 + FUNCDATA_ArgLiveInfo = 6 // ArgsSizeUnknown is set in Func.argsize to mark all functions // whose argument size is unknown (C vararg functions, and diff --git a/libgo/go/cmd/internal/objabi/funcid.go b/libgo/go/cmd/internal/objabi/funcid.go index 93ebd7be943..084fcdf7121 100644 --- a/libgo/go/cmd/internal/objabi/funcid.go +++ b/libgo/go/cmd/internal/objabi/funcid.go @@ -13,6 +13,7 @@ type FuncFlag uint8 const ( FuncFlag_TOPFRAME = 1 << iota FuncFlag_SPWRITE + FuncFlag_ASM ) // A FuncID identifies particular functions that need to be treated @@ -34,7 +35,6 @@ const ( FuncID_gogo FuncID_gopanic FuncID_handleAsyncEvent - FuncID_jmpdefer FuncID_mcall FuncID_morestack FuncID_mstart @@ -60,7 +60,6 @@ var funcIDs = map[string]FuncID{ "gogo": FuncID_gogo, "gopanic": FuncID_gopanic, "handleAsyncEvent": FuncID_handleAsyncEvent, - "jmpdefer": FuncID_jmpdefer, "main": FuncID_runtime_main, "mcall": FuncID_mcall, "morestack": FuncID_morestack, @@ -74,7 +73,6 @@ var funcIDs = map[string]FuncID{ // Don't show in call stack but otherwise not special. "deferreturn": FuncID_wrapper, "runOpenDeferFrame": FuncID_wrapper, - "reflectcallSave": FuncID_wrapper, "deferCallSave": FuncID_wrapper, } diff --git a/libgo/go/cmd/internal/objabi/reloctype.go b/libgo/go/cmd/internal/objabi/reloctype.go index 52827a6deee..a9c3030181a 100644 --- a/libgo/go/cmd/internal/objabi/reloctype.go +++ b/libgo/go/cmd/internal/objabi/reloctype.go @@ -59,8 +59,6 @@ const ( // R_CALLMIPS (only used on mips64) resolves to non-PC-relative target address // of a CALL (JAL) instruction, by encoding the address into the instruction. R_CALLMIPS - // R_CALLRISCV marks RISC-V CALLs for stack checking. - R_CALLRISCV R_CONST R_PCREL // R_TLS_LE, used on 386, amd64, and ARM, resolves to the offset of the @@ -95,6 +93,11 @@ const ( // This is a marker relocation (0-sized), for the linker's reachabililty // analysis. R_USEIFACEMETHOD + // Similar to R_USEIFACEMETHOD, except instead of indicating a type + + // method offset with Sym+Add, Sym points to a symbol containing the name + // of the method being called. See the description in + // cmd/compile/internal/reflectdata/reflect.go:MarkUsedIfaceMethod for details. + R_USEGENERICIFACEMETHOD // R_METHODOFF resolves to a 32-bit offset from the beginning of the section // holding the data being relocated to the referenced symbol. // It is a variant of R_ADDROFF used when linking from the uncommonType of a @@ -218,6 +221,15 @@ const ( // RISC-V. + // R_RISCV_CALL relocates a J-type instruction with a 21 bit PC-relative + // address. + R_RISCV_CALL + + // R_RISCV_CALL_TRAMP is the same as R_RISCV_CALL but denotes the use of a + // trampoline, which we may be able to avoid during relocation. These are + // only used by the linker and are not emitted by the compiler or assembler. + R_RISCV_CALL_TRAMP + // R_RISCV_PCREL_ITYPE resolves a 32-bit PC-relative address using an // AUIPC + I-type instruction pair. R_RISCV_PCREL_ITYPE @@ -274,7 +286,7 @@ const ( // the target address in register or memory. func (r RelocType) IsDirectCall() bool { switch r { - case R_CALL, R_CALLARM, R_CALLARM64, R_CALLMIPS, R_CALLPOWER, R_CALLRISCV: + case R_CALL, R_CALLARM, R_CALLARM64, R_CALLMIPS, R_CALLPOWER, R_RISCV_CALL, R_RISCV_CALL_TRAMP: return true } return false diff --git a/libgo/go/cmd/internal/objabi/reloctype_string.go b/libgo/go/cmd/internal/objabi/reloctype_string.go index 4638ef14d91..d1b15b5a194 100644 --- a/libgo/go/cmd/internal/objabi/reloctype_string.go +++ b/libgo/go/cmd/internal/objabi/reloctype_string.go @@ -20,19 +20,19 @@ func _() { _ = x[R_CALLIND-10] _ = x[R_CALLPOWER-11] _ = x[R_CALLMIPS-12] - _ = x[R_CALLRISCV-13] - _ = x[R_CONST-14] - _ = x[R_PCREL-15] - _ = x[R_TLS_LE-16] - _ = x[R_TLS_IE-17] - _ = x[R_GOTOFF-18] - _ = x[R_PLT0-19] - _ = x[R_PLT1-20] - _ = x[R_PLT2-21] - _ = x[R_USEFIELD-22] - _ = x[R_USETYPE-23] - _ = x[R_USEIFACE-24] - _ = x[R_USEIFACEMETHOD-25] + _ = x[R_CONST-13] + _ = x[R_PCREL-14] + _ = x[R_TLS_LE-15] + _ = x[R_TLS_IE-16] + _ = x[R_GOTOFF-17] + _ = x[R_PLT0-18] + _ = x[R_PLT1-19] + _ = x[R_PLT2-20] + _ = x[R_USEFIELD-21] + _ = x[R_USETYPE-22] + _ = x[R_USEIFACE-23] + _ = x[R_USEIFACEMETHOD-24] + _ = x[R_USEGENERICIFACEMETHOD-25] _ = x[R_METHODOFF-26] _ = x[R_KEEP-27] _ = x[R_POWER_TOC-28] @@ -58,21 +58,23 @@ func _() { _ = x[R_ADDRPOWER_PCREL-48] _ = x[R_ADDRPOWER_TOCREL-49] _ = x[R_ADDRPOWER_TOCREL_DS-50] - _ = x[R_RISCV_PCREL_ITYPE-51] - _ = x[R_RISCV_PCREL_STYPE-52] - _ = x[R_RISCV_TLS_IE_ITYPE-53] - _ = x[R_RISCV_TLS_IE_STYPE-54] - _ = x[R_PCRELDBL-55] - _ = x[R_ADDRMIPSU-56] - _ = x[R_ADDRMIPSTLS-57] - _ = x[R_ADDRCUOFF-58] - _ = x[R_WASMIMPORT-59] - _ = x[R_XCOFFREF-60] + _ = x[R_RISCV_CALL-51] + _ = x[R_RISCV_CALL_TRAMP-52] + _ = x[R_RISCV_PCREL_ITYPE-53] + _ = x[R_RISCV_PCREL_STYPE-54] + _ = x[R_RISCV_TLS_IE_ITYPE-55] + _ = x[R_RISCV_TLS_IE_STYPE-56] + _ = x[R_PCRELDBL-57] + _ = x[R_ADDRMIPSU-58] + _ = x[R_ADDRMIPSTLS-59] + _ = x[R_ADDRCUOFF-60] + _ = x[R_WASMIMPORT-61] + _ = x[R_XCOFFREF-62] } -const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CALLRISCVR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IE_ITYPER_RISCV_TLS_IE_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF" +const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_USEGENERICIFACEMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_CALLR_RISCV_CALL_TRAMPR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IE_ITYPER_RISCV_TLS_IE_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF" -var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 120, 127, 134, 142, 150, 158, 164, 170, 176, 186, 195, 205, 221, 232, 238, 249, 259, 268, 281, 295, 309, 323, 339, 350, 363, 376, 390, 404, 418, 433, 447, 461, 472, 486, 501, 518, 536, 557, 576, 595, 615, 635, 645, 656, 669, 680, 692, 702} +var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 233, 244, 250, 261, 271, 280, 293, 307, 321, 335, 351, 362, 375, 388, 402, 416, 430, 445, 459, 473, 484, 498, 513, 530, 548, 569, 581, 599, 618, 637, 657, 677, 687, 698, 711, 722, 734, 744} func (i RelocType) String() string { i -= 1 diff --git a/libgo/go/cmd/internal/objabi/symkind.go b/libgo/go/cmd/internal/objabi/symkind.go index 6c991121e72..28f430fc547 100644 --- a/libgo/go/cmd/internal/objabi/symkind.go +++ b/libgo/go/cmd/internal/objabi/symkind.go @@ -65,13 +65,6 @@ const ( SDWARFRANGE SDWARFLOC SDWARFLINES - // ABI alias. An ABI alias symbol is an empty symbol with a - // single relocation with 0 size that references the native - // function implementation symbol. - // - // TODO(austin): Remove this and all uses once the compiler - // generates real ABI wrappers rather than symbol aliases. - SABIALIAS // Coverage instrumentation counter for libfuzzer. SLIBFUZZER_EXTRA_COUNTER // Update cmd/link/internal/sym/AbiSymKindToSymKind for new SymKind values. diff --git a/libgo/go/cmd/internal/objabi/symkind_string.go b/libgo/go/cmd/internal/objabi/symkind_string.go index 1b1c394038a..c90cf43b502 100644 --- a/libgo/go/cmd/internal/objabi/symkind_string.go +++ b/libgo/go/cmd/internal/objabi/symkind_string.go @@ -25,13 +25,12 @@ func _() { _ = x[SDWARFRANGE-14] _ = x[SDWARFLOC-15] _ = x[SDWARFLINES-16] - _ = x[SABIALIAS-17] - _ = x[SLIBFUZZER_EXTRA_COUNTER-18] + _ = x[SLIBFUZZER_EXTRA_COUNTER-17] } -const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINESSABIALIASSLIBFUZZER_EXTRA_COUNTER" +const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINESSLIBFUZZER_EXTRA_COUNTER" -var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 63, 74, 83, 95, 105, 114, 125, 134, 145, 154, 178} +var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 63, 74, 83, 95, 105, 114, 125, 134, 145, 169} func (i SymKind) String() string { if i >= SymKind(len(_SymKind_index)-1) { diff --git a/libgo/go/cmd/internal/objabi/util.go b/libgo/go/cmd/internal/objabi/util.go index fcda97d515b..78fa0e97e4b 100644 --- a/libgo/go/cmd/internal/objabi/util.go +++ b/libgo/go/cmd/internal/objabi/util.go @@ -12,7 +12,8 @@ import ( const ( ElfRelocOffset = 256 - MachoRelocOffset = 2048 // reserve enough space for ELF relocations + MachoRelocOffset = 2048 // reserve enough space for ELF relocations + GlobalDictPrefix = ".dict" // prefix for names of global dictionaries ) // HeaderString returns the toolchain configuration string written in diff --git a/libgo/go/cmd/internal/quoted/quoted.go b/libgo/go/cmd/internal/quoted/quoted.go new file mode 100644 index 00000000000..e7575dfc669 --- /dev/null +++ b/libgo/go/cmd/internal/quoted/quoted.go @@ -0,0 +1,127 @@ +// 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. + +// Package quoted provides string manipulation utilities. +package quoted + +import ( + "flag" + "fmt" + "strings" + "unicode" +) + +func isSpaceByte(c byte) bool { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' +} + +// Split splits s into a list of fields, +// allowing single or double quotes around elements. +// There is no unescaping or other processing within +// quoted fields. +func Split(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 +} + +// Join joins a list of arguments into a string that can be parsed +// with Split. Arguments are quoted only if necessary; arguments +// without spaces or quotes are kept as-is. No argument may contain both +// single and double quotes. +func Join(args []string) (string, error) { + var buf []byte + for i, arg := range args { + if i > 0 { + buf = append(buf, ' ') + } + var sawSpace, sawSingleQuote, sawDoubleQuote bool + for _, c := range arg { + switch { + case c > unicode.MaxASCII: + continue + case isSpaceByte(byte(c)): + sawSpace = true + case c == '\'': + sawSingleQuote = true + case c == '"': + sawDoubleQuote = true + } + } + switch { + case !sawSpace && !sawSingleQuote && !sawDoubleQuote: + buf = append(buf, []byte(arg)...) + + case !sawSingleQuote: + buf = append(buf, '\'') + buf = append(buf, []byte(arg)...) + buf = append(buf, '\'') + + case !sawDoubleQuote: + buf = append(buf, '"') + buf = append(buf, []byte(arg)...) + buf = append(buf, '"') + + default: + return "", fmt.Errorf("argument %q contains both single and double quotes and cannot be quoted", arg) + } + } + return string(buf), nil +} + +// A Flag parses a list of string arguments encoded with Join. +// It is useful for flags like cmd/link's -extldflags. +type Flag []string + +var _ flag.Value = (*Flag)(nil) + +func (f *Flag) Set(v string) error { + fs, err := Split(v) + if err != nil { + return err + } + *f = fs[:len(fs):len(fs)] + return nil +} + +func (f *Flag) String() string { + if f == nil { + return "" + } + s, err := Join(*f) + if err != nil { + return strings.Join(*f, " ") + } + return s +} diff --git a/libgo/go/cmd/internal/quoted/quoted_test.go b/libgo/go/cmd/internal/quoted/quoted_test.go new file mode 100644 index 00000000000..d76270c87b4 --- /dev/null +++ b/libgo/go/cmd/internal/quoted/quoted_test.go @@ -0,0 +1,88 @@ +// Copyright 2020 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 quoted + +import ( + "reflect" + "strings" + "testing" +) + +func TestSplit(t *testing.T) { + for _, test := range []struct { + name string + value string + want []string + wantErr string + }{ + {name: "empty", value: "", want: nil}, + {name: "space", value: " ", want: nil}, + {name: "one", value: "a", want: []string{"a"}}, + {name: "leading_space", value: " a", want: []string{"a"}}, + {name: "trailing_space", value: "a ", want: []string{"a"}}, + {name: "two", value: "a b", want: []string{"a", "b"}}, + {name: "two_multi_space", value: "a b", want: []string{"a", "b"}}, + {name: "two_tab", value: "a\tb", want: []string{"a", "b"}}, + {name: "two_newline", value: "a\nb", want: []string{"a", "b"}}, + {name: "quote_single", value: `'a b'`, want: []string{"a b"}}, + {name: "quote_double", value: `"a b"`, want: []string{"a b"}}, + {name: "quote_both", value: `'a '"b "`, want: []string{"a ", "b "}}, + {name: "quote_contains", value: `'a "'"'b"`, want: []string{`a "`, `'b`}}, + {name: "escape", value: `\'`, want: []string{`\'`}}, + {name: "quote_unclosed", value: `'a`, wantErr: "unterminated ' string"}, + } { + t.Run(test.name, func(t *testing.T) { + got, err := Split(test.value) + if err != nil { + if test.wantErr == "" { + t.Fatalf("unexpected error: %v", err) + } else if errMsg := err.Error(); !strings.Contains(errMsg, test.wantErr) { + t.Fatalf("error %q does not contain %q", errMsg, test.wantErr) + } + return + } + if test.wantErr != "" { + t.Fatalf("unexpected success; wanted error containing %q", test.wantErr) + } + if !reflect.DeepEqual(got, test.want) { + t.Errorf("got %q; want %q", got, test.want) + } + }) + } +} + +func TestJoin(t *testing.T) { + for _, test := range []struct { + name string + args []string + want, wantErr string + }{ + {name: "empty", args: nil, want: ""}, + {name: "one", args: []string{"a"}, want: "a"}, + {name: "two", args: []string{"a", "b"}, want: "a b"}, + {name: "space", args: []string{"a ", "b"}, want: "'a ' b"}, + {name: "newline", args: []string{"a\n", "b"}, want: "'a\n' b"}, + {name: "quote", args: []string{`'a `, "b"}, want: `"'a " b`}, + {name: "unquoteable", args: []string{`'"`}, wantErr: "contains both single and double quotes and cannot be quoted"}, + } { + t.Run(test.name, func(t *testing.T) { + got, err := Join(test.args) + if err != nil { + if test.wantErr == "" { + t.Fatalf("unexpected error: %v", err) + } else if errMsg := err.Error(); !strings.Contains(errMsg, test.wantErr) { + t.Fatalf("error %q does not contain %q", errMsg, test.wantErr) + } + return + } + if test.wantErr != "" { + t.Fatalf("unexpected success; wanted error containing %q", test.wantErr) + } + if got != test.want { + t.Errorf("got %s; want %s", got, test.want) + } + }) + } +} diff --git a/libgo/go/cmd/internal/sys/arch.go b/libgo/go/cmd/internal/sys/arch.go index 2029b7dd9e1..97d0ac9bb3e 100644 --- a/libgo/go/cmd/internal/sys/arch.go +++ b/libgo/go/cmd/internal/sys/arch.go @@ -16,6 +16,7 @@ const ( ARM ARM64 I386 + Loong64 MIPS MIPS64 PPC64 @@ -47,6 +48,11 @@ type Arch struct { // Loads or stores smaller than Alignment must be naturally aligned. // Loads or stores larger than Alignment need only be Alignment-aligned. Alignment int8 + + // CanMergeLoads reports whether the backend optimization passes + // can combine adjacent loads into a single larger, possibly unaligned, load. + // Note that currently the optimizations must be able to handle little endian byte order. + CanMergeLoads bool } // InFamily reports whether a is a member of any of the specified @@ -61,103 +67,124 @@ func (a *Arch) InFamily(xs ...ArchFamily) bool { } var Arch386 = &Arch{ - Name: "386", - Family: I386, - ByteOrder: binary.LittleEndian, - PtrSize: 4, - RegSize: 4, - MinLC: 1, - Alignment: 1, + Name: "386", + Family: I386, + ByteOrder: binary.LittleEndian, + PtrSize: 4, + RegSize: 4, + MinLC: 1, + Alignment: 1, + CanMergeLoads: true, } var ArchAMD64 = &Arch{ - Name: "amd64", - Family: AMD64, - ByteOrder: binary.LittleEndian, - PtrSize: 8, - RegSize: 8, - MinLC: 1, - Alignment: 1, + Name: "amd64", + Family: AMD64, + ByteOrder: binary.LittleEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 1, + Alignment: 1, + CanMergeLoads: true, } var ArchARM = &Arch{ - Name: "arm", - Family: ARM, - ByteOrder: binary.LittleEndian, - PtrSize: 4, - RegSize: 4, - MinLC: 4, - Alignment: 4, // TODO: just for arm5? + Name: "arm", + Family: ARM, + ByteOrder: binary.LittleEndian, + PtrSize: 4, + RegSize: 4, + MinLC: 4, + Alignment: 4, // TODO: just for arm5? + CanMergeLoads: false, } var ArchARM64 = &Arch{ - Name: "arm64", - Family: ARM64, - ByteOrder: binary.LittleEndian, - PtrSize: 8, - RegSize: 8, - MinLC: 4, - Alignment: 1, + Name: "arm64", + Family: ARM64, + ByteOrder: binary.LittleEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 4, + Alignment: 1, + CanMergeLoads: true, +} + +var ArchLoong64 = &Arch{ + Name: "loong64", + Family: Loong64, + ByteOrder: binary.LittleEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 4, + Alignment: 8, // Unaligned accesses are not guaranteed to be fast + CanMergeLoads: false, } var ArchMIPS = &Arch{ - Name: "mips", - Family: MIPS, - ByteOrder: binary.BigEndian, - PtrSize: 4, - RegSize: 4, - MinLC: 4, - Alignment: 4, + Name: "mips", + Family: MIPS, + ByteOrder: binary.BigEndian, + PtrSize: 4, + RegSize: 4, + MinLC: 4, + Alignment: 4, + CanMergeLoads: false, } var ArchMIPSLE = &Arch{ - Name: "mipsle", - Family: MIPS, - ByteOrder: binary.LittleEndian, - PtrSize: 4, - RegSize: 4, - MinLC: 4, - Alignment: 4, + Name: "mipsle", + Family: MIPS, + ByteOrder: binary.LittleEndian, + PtrSize: 4, + RegSize: 4, + MinLC: 4, + Alignment: 4, + CanMergeLoads: false, } var ArchMIPS64 = &Arch{ - Name: "mips64", - Family: MIPS64, - ByteOrder: binary.BigEndian, - PtrSize: 8, - RegSize: 8, - MinLC: 4, - Alignment: 8, + Name: "mips64", + Family: MIPS64, + ByteOrder: binary.BigEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 4, + Alignment: 8, + CanMergeLoads: false, } var ArchMIPS64LE = &Arch{ - Name: "mips64le", - Family: MIPS64, - ByteOrder: binary.LittleEndian, - PtrSize: 8, - RegSize: 8, - MinLC: 4, - Alignment: 8, + Name: "mips64le", + Family: MIPS64, + ByteOrder: binary.LittleEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 4, + Alignment: 8, + CanMergeLoads: false, } var ArchPPC64 = &Arch{ - Name: "ppc64", - Family: PPC64, - ByteOrder: binary.BigEndian, - PtrSize: 8, - RegSize: 8, - MinLC: 4, - Alignment: 1, + Name: "ppc64", + Family: PPC64, + ByteOrder: binary.BigEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 4, + Alignment: 1, + CanMergeLoads: false, } var ArchPPC64LE = &Arch{ - Name: "ppc64le", - Family: PPC64, - ByteOrder: binary.LittleEndian, - PtrSize: 8, - RegSize: 8, - MinLC: 4, - Alignment: 1, + Name: "ppc64le", + Family: PPC64, + ByteOrder: binary.LittleEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 4, + Alignment: 1, + CanMergeLoads: true, } var ArchRISCV = &Arch{ @@ -170,33 +197,36 @@ var ArchRISCV = &Arch{ } var ArchRISCV64 = &Arch{ - Name: "riscv64", - Family: RISCV64, - ByteOrder: binary.LittleEndian, - PtrSize: 8, - RegSize: 8, - MinLC: 4, - Alignment: 8, // riscv unaligned loads work, but are really slow (trap + simulated by OS) + Name: "riscv64", + Family: RISCV64, + ByteOrder: binary.LittleEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 4, + Alignment: 8, // riscv unaligned loads work, but are really slow (trap + simulated by OS) + CanMergeLoads: false, } var ArchS390X = &Arch{ - Name: "s390x", - Family: S390X, - ByteOrder: binary.BigEndian, - PtrSize: 8, - RegSize: 8, - MinLC: 2, - Alignment: 1, + Name: "s390x", + Family: S390X, + ByteOrder: binary.BigEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 2, + Alignment: 1, + CanMergeLoads: true, } var ArchWasm = &Arch{ - Name: "wasm", - Family: Wasm, - ByteOrder: binary.LittleEndian, - PtrSize: 8, - RegSize: 8, - MinLC: 1, - Alignment: 1, + Name: "wasm", + Family: Wasm, + ByteOrder: binary.LittleEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 1, + Alignment: 1, + CanMergeLoads: false, } var Archs = [...]*Arch{ @@ -204,6 +234,7 @@ var Archs = [...]*Arch{ ArchAMD64, ArchARM, ArchARM64, + ArchLoong64, ArchMIPS, ArchMIPSLE, ArchMIPS64, diff --git a/libgo/go/cmd/internal/sys/args.go b/libgo/go/cmd/internal/sys/args.go new file mode 100644 index 00000000000..cc9fb64af2d --- /dev/null +++ b/libgo/go/cmd/internal/sys/args.go @@ -0,0 +1,13 @@ +// 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 sys + +// ExecArgLengthLimit is the number of bytes we can safely +// pass as arguments to an exec.Command. +// +// Windows has a limit of 32 KB. To be conservative and not worry about whether +// that includes spaces or not, just use 30 KB. Darwin's limit is less clear. +// The OS claims 256KB, but we've seen failures with arglen as small as 50KB. +const ExecArgLengthLimit = (30 << 10) diff --git a/libgo/go/cmd/internal/sys/supported.go b/libgo/go/cmd/internal/sys/supported.go index 0d2bad96127..82b65511de4 100644 --- a/libgo/go/cmd/internal/sys/supported.go +++ b/libgo/go/cmd/internal/sys/supported.go @@ -34,6 +34,41 @@ func MSanSupported(goos, goarch string) bool { } } +// ASanSupported reports whether goos/goarch supports the address +// sanitizer option. +// There is a copy of this function in misc/cgo/testsanitizers/cc_test.go. +func ASanSupported(goos, goarch string) bool { + switch goos { + case "linux": + return goarch == "arm64" || goarch == "amd64" + default: + return false + } +} + +// FuzzSupported reports whether goos/goarch supports fuzzing +// ('go test -fuzz=.'). +func FuzzSupported(goos, goarch string) bool { + switch goos { + case "darwin", "freebsd", "linux", "windows": + return true + default: + return false + } +} + +// FuzzInstrumented reports whether fuzzing on goos/goarch uses coverage +// instrumentation. (FuzzInstrumented implies FuzzSupported.) +func FuzzInstrumented(goos, goarch string) bool { + switch goarch { + case "amd64", "arm64": + // TODO(#14565): support more architectures. + return FuzzSupported(goos, goarch) + default: + return false + } +} + // MustLinkExternal reports whether goos/goarch requires external linking. // (This is the opposite of internal/testenv.CanInternalLink. Keep them in sync.) func MustLinkExternal(goos, goarch string) bool { @@ -70,7 +105,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "c-shared": switch platform { - case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/s390x", + case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/riscv64", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", "darwin/amd64", "darwin/arm64", @@ -123,7 +158,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { func InternalLinkPIESupported(goos, goarch string) bool { switch goos + "/" + goarch { case "darwin/amd64", "darwin/arm64", - "linux/amd64", "linux/arm64", + "linux/amd64", "linux/arm64", "linux/ppc64le", "android/arm64", "windows-amd64", "windows-386", "windows-arm": return true diff --git a/libgo/go/cmd/internal/test2json/test2json_test.go b/libgo/go/cmd/internal/test2json/test2json_test.go index 4683907888c..e69739d3fec 100644 --- a/libgo/go/cmd/internal/test2json/test2json_test.go +++ b/libgo/go/cmd/internal/test2json/test2json_test.go @@ -145,7 +145,7 @@ func writeAndKill(w io.Writer, b []byte) { // and fails the test with a useful message if they don't match. func diffJSON(t *testing.T, have, want []byte) { t.Helper() - type event map[string]interface{} + type event map[string]any // Parse into events, one per line. parseEvents := func(b []byte) ([]event, []string) { diff --git a/libgo/go/cmd/internal/traceviewer/format.go b/libgo/go/cmd/internal/traceviewer/format.go index 871477447f1..3636c1053de 100644 --- a/libgo/go/cmd/internal/traceviewer/format.go +++ b/libgo/go/cmd/internal/traceviewer/format.go @@ -16,20 +16,20 @@ type Data struct { } type Event struct { - Name string `json:"name,omitempty"` - Phase string `json:"ph"` - Scope string `json:"s,omitempty"` - Time float64 `json:"ts"` - Dur float64 `json:"dur,omitempty"` - PID uint64 `json:"pid"` - TID uint64 `json:"tid"` - ID uint64 `json:"id,omitempty"` - BindPoint string `json:"bp,omitempty"` - Stack int `json:"sf,omitempty"` - EndStack int `json:"esf,omitempty"` - Arg interface{} `json:"args,omitempty"` - Cname string `json:"cname,omitempty"` - Category string `json:"cat,omitempty"` + Name string `json:"name,omitempty"` + Phase string `json:"ph"` + Scope string `json:"s,omitempty"` + Time float64 `json:"ts"` + Dur float64 `json:"dur,omitempty"` + PID uint64 `json:"pid"` + TID uint64 `json:"tid"` + ID uint64 `json:"id,omitempty"` + BindPoint string `json:"bp,omitempty"` + Stack int `json:"sf,omitempty"` + EndStack int `json:"esf,omitempty"` + Arg any `json:"args,omitempty"` + Cname string `json:"cname,omitempty"` + Category string `json:"cat,omitempty"` } type Frame struct { diff --git a/libgo/go/cmd/vendor/modules.txt b/libgo/go/cmd/vendor/modules.txt index 34dbdaf5dd3..f2cd884b820 100644 --- a/libgo/go/cmd/vendor/modules.txt +++ b/libgo/go/cmd/vendor/modules.txt @@ -1,4 +1,4 @@ -# github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a +# github.com/google/pprof v0.0.0-20211104044539-f987b9c94b31 ## explicit; go 1.14 github.com/google/pprof/driver github.com/google/pprof/internal/binutils @@ -15,20 +15,20 @@ github.com/google/pprof/profile github.com/google/pprof/third_party/d3 github.com/google/pprof/third_party/d3flamegraph github.com/google/pprof/third_party/svgpan -# github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 -## explicit +# github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d +## explicit; go 1.12 github.com/ianlancetaylor/demangle -# golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e +# golang.org/x/arch v0.0.0-20210923205945-b76863e36670 ## explicit; go 1.17 golang.org/x/arch/arm/armasm golang.org/x/arch/arm64/arm64asm golang.org/x/arch/ppc64/ppc64asm golang.org/x/arch/x86/x86asm -# golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e +# golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 ## explicit; go 1.17 golang.org/x/crypto/ed25519 golang.org/x/crypto/ed25519/internal/edwards25519 -# golang.org/x/mod v0.4.3-0.20210608190319-0f08993efd8a +# golang.org/x/mod v0.6.0-dev.0.20211102181907-3a5865c02020 ## explicit; go 1.17 golang.org/x/mod/internal/lazyregexp golang.org/x/mod/modfile @@ -39,16 +39,19 @@ golang.org/x/mod/sumdb/dirhash golang.org/x/mod/sumdb/note golang.org/x/mod/sumdb/tlog golang.org/x/mod/zip -# golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 +# golang.org/x/sync v0.0.0-20210220032951-036812b2e83c +## explicit +golang.org/x/sync/semaphore +# golang.org/x/sys v0.0.0-20211205182925-97ca703d548d ## explicit; go 1.17 golang.org/x/sys/internal/unsafeheader golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.0.0-20210503060354-a79de5458b56 +# golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 ## explicit; go 1.17 golang.org/x/term -# golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9 +# golang.org/x/tools v0.1.9-0.20220124164225-97de9ec46646 ## explicit; go 1.17 golang.org/x/tools/cover golang.org/x/tools/go/analysis @@ -92,6 +95,8 @@ golang.org/x/tools/go/types/objectpath golang.org/x/tools/go/types/typeutil golang.org/x/tools/internal/analysisinternal golang.org/x/tools/internal/lsp/fuzzy +golang.org/x/tools/internal/typeparams +golang.org/x/tools/txtar # golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 ## explicit; go 1.11 golang.org/x/xerrors diff --git a/libgo/go/cmd/vet/testdata/print/print.go b/libgo/go/cmd/vet/testdata/print/print.go index fca594925f7..46240e87bfd 100644 --- a/libgo/go/cmd/vet/testdata/print/print.go +++ b/libgo/go/cmd/vet/testdata/print/print.go @@ -491,10 +491,10 @@ type recursiveStringer int func (s recursiveStringer) String() string { _ = fmt.Sprintf("%d", s) _ = fmt.Sprintf("%#v", s) - _ = fmt.Sprintf("%v", s) // ERROR "Sprintf format %v with arg s causes recursive String method call" - _ = fmt.Sprintf("%v", &s) // ERROR "Sprintf format %v with arg &s causes recursive String method call" + _ = fmt.Sprintf("%v", s) // ERROR "Sprintf format %v with arg s causes recursive .*String method call" + _ = fmt.Sprintf("%v", &s) // ERROR "Sprintf format %v with arg &s causes recursive .*String method call" _ = fmt.Sprintf("%T", s) // ok; does not recursively call String - return fmt.Sprintln(s) // ERROR "Sprintln arg s causes recursive call to String method" + return fmt.Sprintln(s) // ERROR "Sprintln arg s causes recursive call to .*String method" } type recursivePtrStringer int @@ -502,7 +502,7 @@ type recursivePtrStringer int func (p *recursivePtrStringer) String() string { _ = fmt.Sprintf("%v", *p) _ = fmt.Sprint(&p) // ok; prints address - return fmt.Sprintln(p) // ERROR "Sprintln arg p causes recursive call to String method" + return fmt.Sprintln(p) // ERROR "Sprintln arg p causes recursive call to .*String method" } type BoolFormatter bool diff --git a/libgo/go/cmd/vet/vet_test.go b/libgo/go/cmd/vet/vet_test.go index 4726da2ac95..ce54b68bf98 100644 --- a/libgo/go/cmd/vet/vet_test.go +++ b/libgo/go/cmd/vet/vet_test.go @@ -245,8 +245,8 @@ func errorCheck(outStr string, wantAuto bool, fullshort ...string) (err error) { // Assume errmsg says "file:line: foo". // Cut leading "file:line: " to avoid accidental matching of file name instead of message. text := errmsg - if i := strings.Index(text, " "); i >= 0 { - text = text[i+1:] + if _, suffix, ok := strings.Cut(text, " "); ok { + text = suffix } if we.re.MatchString(text) { matched = true |