aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/golang.org/x/mod
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2022-02-11 14:53:56 -0800
committerIan Lance Taylor <iant@golang.org>2022-02-11 15:01:19 -0800
commit8dc2499aa62f768c6395c9754b8cabc1ce25c494 (patch)
tree43d7fd2bbfd7ad8c9625a718a5e8718889351994 /libgo/go/golang.org/x/mod
parent9a56779dbc4e2d9c15be8d31e36f2f59be7331a8 (diff)
downloadgcc-8dc2499aa62f768c6395c9754b8cabc1ce25c494.zip
gcc-8dc2499aa62f768c6395c9754b8cabc1ce25c494.tar.gz
gcc-8dc2499aa62f768c6395c9754b8cabc1ce25c494.tar.bz2
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/golang.org/x/mod')
-rw-r--r--libgo/go/golang.org/x/mod/modfile/rule.go545
-rw-r--r--libgo/go/golang.org/x/mod/modfile/work.go234
-rw-r--r--libgo/go/golang.org/x/mod/module/module.go9
-rw-r--r--libgo/go/golang.org/x/mod/semver/semver.go10
-rw-r--r--libgo/go/golang.org/x/mod/zip/zip.go143
5 files changed, 717 insertions, 224 deletions
diff --git a/libgo/go/golang.org/x/mod/modfile/rule.go b/libgo/go/golang.org/x/mod/modfile/rule.go
index 78f83fa..ed2f31a 100644
--- a/libgo/go/golang.org/x/mod/modfile/rule.go
+++ b/libgo/go/golang.org/x/mod/modfile/rule.go
@@ -423,68 +423,12 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a
}
case "replace":
- arrow := 2
- if len(args) >= 2 && args[1] == "=>" {
- arrow = 1
- }
- if len(args) < arrow+2 || len(args) > arrow+3 || args[arrow] != "=>" {
- errorf("usage: %s module/path [v1.2.3] => other/module v1.4\n\t or %s module/path [v1.2.3] => ../local/directory", verb, verb)
- return
- }
- s, err := parseString(&args[0])
- if err != nil {
- errorf("invalid quoted string: %v", err)
- return
- }
- pathMajor, err := modulePathMajor(s)
- if err != nil {
- wrapModPathError(s, err)
- return
- }
- var v string
- if arrow == 2 {
- v, err = parseVersion(verb, s, &args[1], fix)
- if err != nil {
- wrapError(err)
- return
- }
- if err := module.CheckPathMajor(v, pathMajor); err != nil {
- wrapModPathError(s, err)
- return
- }
- }
- ns, err := parseString(&args[arrow+1])
- if err != nil {
- errorf("invalid quoted string: %v", err)
+ replace, wrappederr := parseReplace(f.Syntax.Name, line, verb, args, fix)
+ if wrappederr != nil {
+ *errs = append(*errs, *wrappederr)
return
}
- nv := ""
- if len(args) == arrow+2 {
- if !IsDirectoryPath(ns) {
- errorf("replacement module without version must be directory path (rooted or starting with ./ or ../)")
- return
- }
- if filepath.Separator == '/' && strings.Contains(ns, `\`) {
- errorf("replacement directory appears to be Windows path (on a non-windows system)")
- return
- }
- }
- if len(args) == arrow+3 {
- nv, err = parseVersion(verb, ns, &args[arrow+2], fix)
- if err != nil {
- wrapError(err)
- return
- }
- if IsDirectoryPath(ns) {
- errorf("replacement module directory path %q cannot have version", ns)
- return
- }
- }
- f.Replace = append(f.Replace, &Replace{
- Old: module.Version{Path: s, Version: v},
- New: module.Version{Path: ns, Version: nv},
- Syntax: line,
- })
+ f.Replace = append(f.Replace, replace)
case "retract":
rationale := parseDirectiveComment(block, line)
@@ -515,6 +459,83 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a
}
}
+func parseReplace(filename string, line *Line, verb string, args []string, fix VersionFixer) (*Replace, *Error) {
+ wrapModPathError := func(modPath string, err error) *Error {
+ return &Error{
+ Filename: filename,
+ Pos: line.Start,
+ ModPath: modPath,
+ Verb: verb,
+ Err: err,
+ }
+ }
+ wrapError := func(err error) *Error {
+ return &Error{
+ Filename: filename,
+ Pos: line.Start,
+ Err: err,
+ }
+ }
+ errorf := func(format string, args ...interface{}) *Error {
+ return wrapError(fmt.Errorf(format, args...))
+ }
+
+ arrow := 2
+ if len(args) >= 2 && args[1] == "=>" {
+ arrow = 1
+ }
+ if len(args) < arrow+2 || len(args) > arrow+3 || args[arrow] != "=>" {
+ return nil, errorf("usage: %s module/path [v1.2.3] => other/module v1.4\n\t or %s module/path [v1.2.3] => ../local/directory", verb, verb)
+ }
+ s, err := parseString(&args[0])
+ if err != nil {
+ return nil, errorf("invalid quoted string: %v", err)
+ }
+ pathMajor, err := modulePathMajor(s)
+ if err != nil {
+ return nil, wrapModPathError(s, err)
+
+ }
+ var v string
+ if arrow == 2 {
+ v, err = parseVersion(verb, s, &args[1], fix)
+ if err != nil {
+ return nil, wrapError(err)
+ }
+ if err := module.CheckPathMajor(v, pathMajor); err != nil {
+ return nil, wrapModPathError(s, err)
+ }
+ }
+ ns, err := parseString(&args[arrow+1])
+ if err != nil {
+ return nil, errorf("invalid quoted string: %v", err)
+ }
+ nv := ""
+ if len(args) == arrow+2 {
+ if !IsDirectoryPath(ns) {
+ return nil, errorf("replacement module without version must be directory path (rooted or starting with ./ or ../)")
+ }
+ if filepath.Separator == '/' && strings.Contains(ns, `\`) {
+ return nil, errorf("replacement directory appears to be Windows path (on a non-windows system)")
+ }
+ }
+ if len(args) == arrow+3 {
+ nv, err = parseVersion(verb, ns, &args[arrow+2], fix)
+ if err != nil {
+ return nil, wrapError(err)
+ }
+ if IsDirectoryPath(ns) {
+ return nil, errorf("replacement module directory path %q cannot have version", ns)
+
+ }
+ }
+ return &Replace{
+ Old: module.Version{Path: s, Version: v},
+ New: module.Version{Path: ns, Version: nv},
+ Syntax: line,
+ }, nil
+}
+
// fixRetract applies fix to each retract directive in f, appending any errors
// to errs.
//
@@ -556,6 +577,63 @@ func (f *File) fixRetract(fix VersionFixer, errs *ErrorList) {
}
}
+func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string, fix VersionFixer) {
+ wrapError := func(err error) {
+ *errs = append(*errs, Error{
+ Filename: f.Syntax.Name,
+ Pos: line.Start,
+ Err: err,
+ })
+ }
+ errorf := func(format string, args ...interface{}) {
+ wrapError(fmt.Errorf(format, args...))
+ }
+
+ switch verb {
+ default:
+ errorf("unknown directive: %s", verb)
+
+ case "go":
+ if f.Go != nil {
+ errorf("repeated go statement")
+ return
+ }
+ if len(args) != 1 {
+ errorf("go directive expects exactly one argument")
+ return
+ } else if !GoVersionRE.MatchString(args[0]) {
+ errorf("invalid go version '%s': must match format 1.23", args[0])
+ return
+ }
+
+ f.Go = &Go{Syntax: line}
+ f.Go.Version = args[0]
+
+ case "use":
+ if len(args) != 1 {
+ errorf("usage: %s local/dir", verb)
+ return
+ }
+ s, err := parseString(&args[0])
+ if err != nil {
+ errorf("invalid quoted string: %v", err)
+ return
+ }
+ f.Use = append(f.Use, &Use{
+ Path: s,
+ Syntax: line,
+ })
+
+ case "replace":
+ replace, wrappederr := parseReplace(f.Syntax.Name, line, verb, args, fix)
+ if wrappederr != nil {
+ *errs = append(*errs, *wrappederr)
+ return
+ }
+ f.Replace = append(f.Replace, replace)
+ }
+}
+
// IsDirectoryPath reports whether the given path should be interpreted
// as a directory path. Just like on the go command line, relative paths
// and rooted paths are directory paths; the rest are module paths.
@@ -956,170 +1034,217 @@ func (f *File) SetRequire(req []*Require) {
// SetRequireSeparateIndirect updates the requirements of f to contain the given
// requirements. Comment contents (except for 'indirect' markings) are retained
-// from the first existing requirement for each module path, and block structure
-// is maintained as long as the indirect markings match.
+// from the first existing requirement for each module path. Like SetRequire,
+// SetRequireSeparateIndirect adds requirements for new paths in req,
+// updates the version and "// indirect" comment on existing requirements,
+// and deletes requirements on paths not in req. Existing duplicate requirements
+// are deleted.
//
-// Any requirements on paths not already present in the file are added. Direct
-// requirements are added to the last block containing *any* other direct
-// requirement. Indirect requirements are added to the last block containing
-// *only* other indirect requirements. If no suitable block exists, a new one is
-// added, with the last block containing a direct dependency (if any)
-// immediately before the first block containing only indirect dependencies.
+// As its name suggests, SetRequireSeparateIndirect puts direct and indirect
+// requirements into two separate blocks, one containing only direct
+// requirements, and the other containing only indirect requirements.
+// SetRequireSeparateIndirect may move requirements between these two blocks
+// when their indirect markings change. However, SetRequireSeparateIndirect
+// won't move requirements from other blocks, especially blocks with comments.
//
-// The Syntax field is ignored for requirements in the given blocks.
+// If the file initially has one uncommented block of requirements,
+// SetRequireSeparateIndirect will split it into a direct-only and indirect-only
+// block. This aids in the transition to separate blocks.
func (f *File) SetRequireSeparateIndirect(req []*Require) {
- type modKey struct {
- path string
- indirect bool
- }
- need := make(map[modKey]string)
- for _, r := range req {
- need[modKey{r.Mod.Path, r.Indirect}] = r.Mod.Version
+ // hasComments returns whether a line or block has comments
+ // other than "indirect".
+ hasComments := func(c Comments) bool {
+ return len(c.Before) > 0 || len(c.After) > 0 || len(c.Suffix) > 1 ||
+ (len(c.Suffix) == 1 &&
+ strings.TrimSpace(strings.TrimPrefix(c.Suffix[0].Token, string(slashSlash))) != "indirect")
}
- comments := make(map[string]Comments)
- for _, r := range f.Require {
- v, ok := need[modKey{r.Mod.Path, r.Indirect}]
- if !ok {
- if _, ok := need[modKey{r.Mod.Path, !r.Indirect}]; ok {
- if _, dup := comments[r.Mod.Path]; !dup {
- comments[r.Mod.Path] = r.Syntax.Comments
- }
+ // moveReq adds r to block. If r was in another block, moveReq deletes
+ // it from that block and transfers its comments.
+ moveReq := func(r *Require, block *LineBlock) {
+ var line *Line
+ if r.Syntax == nil {
+ line = &Line{Token: []string{AutoQuote(r.Mod.Path), r.Mod.Version}}
+ r.Syntax = line
+ if r.Indirect {
+ r.setIndirect(true)
}
- r.markRemoved()
- continue
+ } else {
+ line = new(Line)
+ *line = *r.Syntax
+ if !line.InBlock && len(line.Token) > 0 && line.Token[0] == "require" {
+ line.Token = line.Token[1:]
+ }
+ r.Syntax.Token = nil // Cleanup will delete the old line.
+ r.Syntax = line
}
- r.setVersion(v)
- delete(need, modKey{r.Mod.Path, r.Indirect})
+ line.InBlock = true
+ block.Line = append(block.Line, line)
}
+ // Examine existing require lines and blocks.
var (
- lastDirectOrMixedBlock Expr
- firstIndirectOnlyBlock Expr
- lastIndirectOnlyBlock Expr
+ // We may insert new requirements into the last uncommented
+ // direct-only and indirect-only blocks. We may also move requirements
+ // to the opposite block if their indirect markings change.
+ lastDirectIndex = -1
+ lastIndirectIndex = -1
+
+ // If there are no direct-only or indirect-only blocks, a new block may
+ // be inserted after the last require line or block.
+ lastRequireIndex = -1
+
+ // If there's only one require line or block, and it's uncommented,
+ // we'll move its requirements to the direct-only or indirect-only blocks.
+ requireLineOrBlockCount = 0
+
+ // Track the block each requirement belongs to (if any) so we can
+ // move them later.
+ lineToBlock = make(map[*Line]*LineBlock)
)
- for _, stmt := range f.Syntax.Stmt {
+ for i, stmt := range f.Syntax.Stmt {
switch stmt := stmt.(type) {
case *Line:
if len(stmt.Token) == 0 || stmt.Token[0] != "require" {
continue
}
- if isIndirect(stmt) {
- lastIndirectOnlyBlock = stmt
- } else {
- lastDirectOrMixedBlock = stmt
+ lastRequireIndex = i
+ requireLineOrBlockCount++
+ if !hasComments(stmt.Comments) {
+ if isIndirect(stmt) {
+ lastIndirectIndex = i
+ } else {
+ lastDirectIndex = i
+ }
}
+
case *LineBlock:
if len(stmt.Token) == 0 || stmt.Token[0] != "require" {
continue
}
- indirectOnly := true
+ lastRequireIndex = i
+ requireLineOrBlockCount++
+ allDirect := len(stmt.Line) > 0 && !hasComments(stmt.Comments)
+ allIndirect := len(stmt.Line) > 0 && !hasComments(stmt.Comments)
for _, line := range stmt.Line {
- if len(line.Token) == 0 {
- continue
- }
- if !isIndirect(line) {
- indirectOnly = false
- break
+ lineToBlock[line] = stmt
+ if hasComments(line.Comments) {
+ allDirect = false
+ allIndirect = false
+ } else if isIndirect(line) {
+ allDirect = false
+ } else {
+ allIndirect = false
}
}
- if indirectOnly {
- lastIndirectOnlyBlock = stmt
- if firstIndirectOnlyBlock == nil {
- firstIndirectOnlyBlock = stmt
- }
- } else {
- lastDirectOrMixedBlock = stmt
+ if allDirect {
+ lastDirectIndex = i
+ }
+ if allIndirect {
+ lastIndirectIndex = i
}
}
}
- isOrContainsStmt := func(stmt Expr, target Expr) bool {
- if stmt == target {
- return true
- }
- if stmt, ok := stmt.(*LineBlock); ok {
- if target, ok := target.(*Line); ok {
- for _, line := range stmt.Line {
- if line == target {
- return true
- }
- }
+ oneFlatUncommentedBlock := requireLineOrBlockCount == 1 &&
+ !hasComments(*f.Syntax.Stmt[lastRequireIndex].Comment())
+
+ // Create direct and indirect blocks if needed. Convert lines into blocks
+ // if needed. If we end up with an empty block or a one-line block,
+ // Cleanup will delete it or convert it to a line later.
+ insertBlock := func(i int) *LineBlock {
+ block := &LineBlock{Token: []string{"require"}}
+ f.Syntax.Stmt = append(f.Syntax.Stmt, nil)
+ copy(f.Syntax.Stmt[i+1:], f.Syntax.Stmt[i:])
+ f.Syntax.Stmt[i] = block
+ return block
+ }
+
+ ensureBlock := func(i int) *LineBlock {
+ switch stmt := f.Syntax.Stmt[i].(type) {
+ case *LineBlock:
+ return stmt
+ case *Line:
+ block := &LineBlock{
+ Token: []string{"require"},
+ Line: []*Line{stmt},
}
+ stmt.Token = stmt.Token[1:] // remove "require"
+ stmt.InBlock = true
+ f.Syntax.Stmt[i] = block
+ return block
+ default:
+ panic(fmt.Sprintf("unexpected statement: %v", stmt))
}
- return false
}
- addRequire := func(path, vers string, indirect bool, comments Comments) {
- var line *Line
- if indirect {
- if lastIndirectOnlyBlock != nil {
- line = f.Syntax.addLine(lastIndirectOnlyBlock, "require", path, vers)
- } else {
- // Add a new require block after the last direct-only or mixed "require"
- // block (if any).
- //
- // (f.Syntax.addLine would add the line to an existing "require" block if
- // present, but here the existing "require" blocks are all direct-only, so
- // we know we need to add a new block instead.)
- line = &Line{Token: []string{"require", path, vers}}
- lastIndirectOnlyBlock = line
- firstIndirectOnlyBlock = line // only block implies first block
- if lastDirectOrMixedBlock == nil {
- f.Syntax.Stmt = append(f.Syntax.Stmt, line)
- } else {
- for i, stmt := range f.Syntax.Stmt {
- if isOrContainsStmt(stmt, lastDirectOrMixedBlock) {
- f.Syntax.Stmt = append(f.Syntax.Stmt, nil) // increase size
- copy(f.Syntax.Stmt[i+2:], f.Syntax.Stmt[i+1:]) // shuffle elements up
- f.Syntax.Stmt[i+1] = line
- break
- }
- }
- }
- }
+ var lastDirectBlock *LineBlock
+ if lastDirectIndex < 0 {
+ if lastIndirectIndex >= 0 {
+ lastDirectIndex = lastIndirectIndex
+ lastIndirectIndex++
+ } else if lastRequireIndex >= 0 {
+ lastDirectIndex = lastRequireIndex + 1
} else {
- if lastDirectOrMixedBlock != nil {
- line = f.Syntax.addLine(lastDirectOrMixedBlock, "require", path, vers)
- } else {
- // Add a new require block before the first indirect block (if any).
- //
- // That way if the file initially contains only indirect lines,
- // the direct lines still appear before it: we preserve existing
- // structure, but only to the extent that that structure already
- // reflects the direct/indirect split.
- line = &Line{Token: []string{"require", path, vers}}
- lastDirectOrMixedBlock = line
- if firstIndirectOnlyBlock == nil {
- f.Syntax.Stmt = append(f.Syntax.Stmt, line)
- } else {
- for i, stmt := range f.Syntax.Stmt {
- if isOrContainsStmt(stmt, firstIndirectOnlyBlock) {
- f.Syntax.Stmt = append(f.Syntax.Stmt, nil) // increase size
- copy(f.Syntax.Stmt[i+1:], f.Syntax.Stmt[i:]) // shuffle elements up
- f.Syntax.Stmt[i] = line
- break
- }
- }
- }
- }
+ lastDirectIndex = len(f.Syntax.Stmt)
}
+ lastDirectBlock = insertBlock(lastDirectIndex)
+ } else {
+ lastDirectBlock = ensureBlock(lastDirectIndex)
+ }
- line.Comments.Before = commentsAdd(line.Comments.Before, comments.Before)
- line.Comments.Suffix = commentsAdd(line.Comments.Suffix, comments.Suffix)
+ var lastIndirectBlock *LineBlock
+ if lastIndirectIndex < 0 {
+ lastIndirectIndex = lastDirectIndex + 1
+ lastIndirectBlock = insertBlock(lastIndirectIndex)
+ } else {
+ lastIndirectBlock = ensureBlock(lastIndirectIndex)
+ }
- r := &Require{
- Mod: module.Version{Path: path, Version: vers},
- Indirect: indirect,
- Syntax: line,
+ // Delete requirements we don't want anymore.
+ // Update versions and indirect comments on requirements we want to keep.
+ // If a requirement is in last{Direct,Indirect}Block with the wrong
+ // indirect marking after this, or if the requirement is in an single
+ // uncommented mixed block (oneFlatUncommentedBlock), move it to the
+ // correct block.
+ //
+ // Some blocks may be empty after this. Cleanup will remove them.
+ need := make(map[string]*Require)
+ for _, r := range req {
+ need[r.Mod.Path] = r
+ }
+ have := make(map[string]*Require)
+ for _, r := range f.Require {
+ path := r.Mod.Path
+ if need[path] == nil || have[path] != nil {
+ // Requirement not needed, or duplicate requirement. Delete.
+ r.markRemoved()
+ continue
+ }
+ have[r.Mod.Path] = r
+ r.setVersion(need[path].Mod.Version)
+ r.setIndirect(need[path].Indirect)
+ if need[path].Indirect &&
+ (oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastDirectBlock) {
+ moveReq(r, lastIndirectBlock)
+ } else if !need[path].Indirect &&
+ (oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastIndirectBlock) {
+ moveReq(r, lastDirectBlock)
}
- r.setIndirect(indirect)
- f.Require = append(f.Require, r)
}
- for k, vers := range need {
- addRequire(k.path, vers, k.indirect, comments[k.path])
+ // Add new requirements.
+ for path, r := range need {
+ if have[path] == nil {
+ if r.Indirect {
+ moveReq(r, lastIndirectBlock)
+ } else {
+ moveReq(r, lastDirectBlock)
+ }
+ f.Require = append(f.Require, r)
+ }
}
+
f.SortBlocks()
}
@@ -1165,6 +1290,10 @@ func (f *File) DropExclude(path, vers string) error {
}
func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error {
+ return addReplace(f.Syntax, &f.Replace, oldPath, oldVers, newPath, newVers)
+}
+
+func addReplace(syntax *FileSyntax, replace *[]*Replace, oldPath, oldVers, newPath, newVers string) error {
need := true
old := module.Version{Path: oldPath, Version: oldVers}
new := module.Version{Path: newPath, Version: newVers}
@@ -1178,12 +1307,12 @@ func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error {
}
var hint *Line
- for _, r := range f.Replace {
+ for _, r := range *replace {
if r.Old.Path == oldPath && (oldVers == "" || r.Old.Version == oldVers) {
if need {
// Found replacement for old; update to use new.
r.New = new
- f.Syntax.updateLine(r.Syntax, tokens...)
+ syntax.updateLine(r.Syntax, tokens...)
need = false
continue
}
@@ -1196,7 +1325,7 @@ func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error {
}
}
if need {
- f.Replace = append(f.Replace, &Replace{Old: old, New: new, Syntax: f.Syntax.addLine(hint, tokens...)})
+ *replace = append(*replace, &Replace{Old: old, New: new, Syntax: syntax.addLine(hint, tokens...)})
}
return nil
}
@@ -1282,30 +1411,36 @@ func (f *File) SortBlocks() {
// retract directives are not de-duplicated since comments are
// meaningful, and versions may be retracted multiple times.
func (f *File) removeDups() {
+ removeDups(f.Syntax, &f.Exclude, &f.Replace)
+}
+
+func removeDups(syntax *FileSyntax, exclude *[]*Exclude, replace *[]*Replace) {
kill := make(map[*Line]bool)
// Remove duplicate excludes.
- haveExclude := make(map[module.Version]bool)
- for _, x := range f.Exclude {
- if haveExclude[x.Mod] {
- kill[x.Syntax] = true
- continue
+ if exclude != nil {
+ haveExclude := make(map[module.Version]bool)
+ for _, x := range *exclude {
+ if haveExclude[x.Mod] {
+ kill[x.Syntax] = true
+ continue
+ }
+ haveExclude[x.Mod] = true
}
- haveExclude[x.Mod] = true
- }
- var excl []*Exclude
- for _, x := range f.Exclude {
- if !kill[x.Syntax] {
- excl = append(excl, x)
+ var excl []*Exclude
+ for _, x := range *exclude {
+ if !kill[x.Syntax] {
+ excl = append(excl, x)
+ }
}
+ *exclude = excl
}
- f.Exclude = excl
// Remove duplicate replacements.
// Later replacements take priority over earlier ones.
haveReplace := make(map[module.Version]bool)
- for i := len(f.Replace) - 1; i >= 0; i-- {
- x := f.Replace[i]
+ for i := len(*replace) - 1; i >= 0; i-- {
+ x := (*replace)[i]
if haveReplace[x.Old] {
kill[x.Syntax] = true
continue
@@ -1313,18 +1448,18 @@ func (f *File) removeDups() {
haveReplace[x.Old] = true
}
var repl []*Replace
- for _, x := range f.Replace {
+ for _, x := range *replace {
if !kill[x.Syntax] {
repl = append(repl, x)
}
}
- f.Replace = repl
+ *replace = repl
// Duplicate require and retract directives are not removed.
// Drop killed statements from the syntax tree.
var stmts []Expr
- for _, stmt := range f.Syntax.Stmt {
+ for _, stmt := range syntax.Stmt {
switch stmt := stmt.(type) {
case *Line:
if kill[stmt] {
@@ -1344,7 +1479,7 @@ func (f *File) removeDups() {
}
stmts = append(stmts, stmt)
}
- f.Syntax.Stmt = stmts
+ syntax.Stmt = stmts
}
// lineLess returns whether li should be sorted before lj. It sorts
diff --git a/libgo/go/golang.org/x/mod/modfile/work.go b/libgo/go/golang.org/x/mod/modfile/work.go
new file mode 100644
index 0000000..0c0e521
--- /dev/null
+++ b/libgo/go/golang.org/x/mod/modfile/work.go
@@ -0,0 +1,234 @@
+// 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 modfile
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+)
+
+// A WorkFile is the parsed, interpreted form of a go.work file.
+type WorkFile struct {
+ Go *Go
+ Use []*Use
+ Replace []*Replace
+
+ Syntax *FileSyntax
+}
+
+// A Use is a single directory statement.
+type Use struct {
+ Path string // Use path of module.
+ ModulePath string // Module path in the comment.
+ Syntax *Line
+}
+
+// ParseWork parses and returns a go.work file.
+//
+// file is the name of the file, used in positions and errors.
+//
+// data is the content of the file.
+//
+// fix is an optional function that canonicalizes module versions.
+// If fix is nil, all module versions must be canonical (module.CanonicalVersion
+// must return the same string).
+func ParseWork(file string, data []byte, fix VersionFixer) (*WorkFile, error) {
+ fs, err := parse(file, data)
+ if err != nil {
+ return nil, err
+ }
+ f := &WorkFile{
+ Syntax: fs,
+ }
+ var errs ErrorList
+
+ for _, x := range fs.Stmt {
+ switch x := x.(type) {
+ case *Line:
+ f.add(&errs, x, x.Token[0], x.Token[1:], fix)
+
+ case *LineBlock:
+ if len(x.Token) > 1 {
+ errs = append(errs, Error{
+ Filename: file,
+ Pos: x.Start,
+ Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")),
+ })
+ continue
+ }
+ switch x.Token[0] {
+ default:
+ errs = append(errs, Error{
+ Filename: file,
+ Pos: x.Start,
+ Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")),
+ })
+ continue
+ case "use", "replace":
+ for _, l := range x.Line {
+ f.add(&errs, l, x.Token[0], l.Token, fix)
+ }
+ }
+ }
+ }
+
+ if len(errs) > 0 {
+ return nil, errs
+ }
+ return f, nil
+}
+
+// Cleanup cleans up the file f after any edit operations.
+// To avoid quadratic behavior, modifications like DropRequire
+// clear the entry but do not remove it from the slice.
+// Cleanup cleans out all the cleared entries.
+func (f *WorkFile) Cleanup() {
+ w := 0
+ for _, r := range f.Use {
+ if r.Path != "" {
+ f.Use[w] = r
+ w++
+ }
+ }
+ f.Use = f.Use[:w]
+
+ w = 0
+ for _, r := range f.Replace {
+ if r.Old.Path != "" {
+ f.Replace[w] = r
+ w++
+ }
+ }
+ f.Replace = f.Replace[:w]
+
+ f.Syntax.Cleanup()
+}
+
+func (f *WorkFile) AddGoStmt(version string) error {
+ if !GoVersionRE.MatchString(version) {
+ return fmt.Errorf("invalid language version string %q", version)
+ }
+ if f.Go == nil {
+ stmt := &Line{Token: []string{"go", version}}
+ f.Go = &Go{
+ Version: version,
+ Syntax: stmt,
+ }
+ // Find the first non-comment-only block that's and add
+ // the go statement before it. That will keep file comments at the top.
+ i := 0
+ for i = 0; i < len(f.Syntax.Stmt); i++ {
+ if _, ok := f.Syntax.Stmt[i].(*CommentBlock); !ok {
+ break
+ }
+ }
+ f.Syntax.Stmt = append(append(f.Syntax.Stmt[:i:i], stmt), f.Syntax.Stmt[i:]...)
+ } else {
+ f.Go.Version = version
+ f.Syntax.updateLine(f.Go.Syntax, "go", version)
+ }
+ return nil
+}
+
+func (f *WorkFile) AddUse(diskPath, modulePath string) error {
+ need := true
+ for _, d := range f.Use {
+ if d.Path == diskPath {
+ if need {
+ d.ModulePath = modulePath
+ f.Syntax.updateLine(d.Syntax, "use", AutoQuote(diskPath))
+ need = false
+ } else {
+ d.Syntax.markRemoved()
+ *d = Use{}
+ }
+ }
+ }
+
+ if need {
+ f.AddNewUse(diskPath, modulePath)
+ }
+ return nil
+}
+
+func (f *WorkFile) AddNewUse(diskPath, modulePath string) {
+ line := f.Syntax.addLine(nil, "use", AutoQuote(diskPath))
+ f.Use = append(f.Use, &Use{Path: diskPath, ModulePath: modulePath, Syntax: line})
+}
+
+func (f *WorkFile) SetUse(dirs []*Use) {
+ need := make(map[string]string)
+ for _, d := range dirs {
+ need[d.Path] = d.ModulePath
+ }
+
+ for _, d := range f.Use {
+ if modulePath, ok := need[d.Path]; ok {
+ d.ModulePath = modulePath
+ } else {
+ d.Syntax.markRemoved()
+ *d = Use{}
+ }
+ }
+
+ // TODO(#45713): Add module path to comment.
+
+ for diskPath, modulePath := range need {
+ f.AddNewUse(diskPath, modulePath)
+ }
+ f.SortBlocks()
+}
+
+func (f *WorkFile) DropUse(path string) error {
+ for _, d := range f.Use {
+ if d.Path == path {
+ d.Syntax.markRemoved()
+ *d = Use{}
+ }
+ }
+ return nil
+}
+
+func (f *WorkFile) AddReplace(oldPath, oldVers, newPath, newVers string) error {
+ return addReplace(f.Syntax, &f.Replace, oldPath, oldVers, newPath, newVers)
+}
+
+func (f *WorkFile) DropReplace(oldPath, oldVers string) error {
+ for _, r := range f.Replace {
+ if r.Old.Path == oldPath && r.Old.Version == oldVers {
+ r.Syntax.markRemoved()
+ *r = Replace{}
+ }
+ }
+ return nil
+}
+
+func (f *WorkFile) SortBlocks() {
+ f.removeDups() // otherwise sorting is unsafe
+
+ for _, stmt := range f.Syntax.Stmt {
+ block, ok := stmt.(*LineBlock)
+ if !ok {
+ continue
+ }
+ sort.SliceStable(block.Line, func(i, j int) bool {
+ return lineLess(block.Line[i], block.Line[j])
+ })
+ }
+}
+
+// removeDups removes duplicate replace directives.
+//
+// Later replace directives take priority.
+//
+// require directives are not de-duplicated. That's left up to higher-level
+// logic (MVS).
+//
+// retract directives are not de-duplicated since comments are
+// meaningful, and versions may be retracted multiple times.
+func (f *WorkFile) removeDups() {
+ removeDups(f.Syntax, nil, &f.Replace)
+}
diff --git a/libgo/go/golang.org/x/mod/module/module.go b/libgo/go/golang.org/x/mod/module/module.go
index ba97ac3..355b5a4 100644
--- a/libgo/go/golang.org/x/mod/module/module.go
+++ b/libgo/go/golang.org/x/mod/module/module.go
@@ -286,12 +286,7 @@ func fileNameOK(r rune) bool {
if '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' {
return true
}
- for i := 0; i < len(allowed); i++ {
- if rune(allowed[i]) == r {
- return true
- }
- }
- return false
+ return strings.ContainsRune(allowed, r)
}
// It may be OK to add more ASCII punctuation here, but only carefully.
// For example Windows disallows < > \, and macOS disallows :, so we must not allow those.
@@ -803,6 +798,7 @@ func unescapeString(escaped string) (string, bool) {
// GOPRIVATE environment variable, as described by 'go help module-private'.
//
// It ignores any empty or malformed patterns in the list.
+// Trailing slashes on patterns are ignored.
func MatchPrefixPatterns(globs, target string) bool {
for globs != "" {
// Extract next non-empty glob in comma-separated list.
@@ -812,6 +808,7 @@ func MatchPrefixPatterns(globs, target string) bool {
} else {
glob, globs = globs, ""
}
+ glob = strings.TrimSuffix(glob, "/")
if glob == "" {
continue
}
diff --git a/libgo/go/golang.org/x/mod/semver/semver.go b/libgo/go/golang.org/x/mod/semver/semver.go
index 7be398f..a30a22b 100644
--- a/libgo/go/golang.org/x/mod/semver/semver.go
+++ b/libgo/go/golang.org/x/mod/semver/semver.go
@@ -32,7 +32,6 @@ type parsed struct {
short string
prerelease string
build string
- err string
}
// IsValid reports whether v is a valid semantic version string.
@@ -172,12 +171,10 @@ func Sort(list []string) {
func parse(v string) (p parsed, ok bool) {
if v == "" || v[0] != 'v' {
- p.err = "missing v prefix"
return
}
p.major, v, ok = parseInt(v[1:])
if !ok {
- p.err = "bad major version"
return
}
if v == "" {
@@ -187,13 +184,11 @@ func parse(v string) (p parsed, ok bool) {
return
}
if v[0] != '.' {
- p.err = "bad minor prefix"
ok = false
return
}
p.minor, v, ok = parseInt(v[1:])
if !ok {
- p.err = "bad minor version"
return
}
if v == "" {
@@ -202,31 +197,26 @@ func parse(v string) (p parsed, ok bool) {
return
}
if v[0] != '.' {
- p.err = "bad patch prefix"
ok = false
return
}
p.patch, v, ok = parseInt(v[1:])
if !ok {
- p.err = "bad patch version"
return
}
if len(v) > 0 && v[0] == '-' {
p.prerelease, v, ok = parsePrerelease(v)
if !ok {
- p.err = "bad prerelease"
return
}
}
if len(v) > 0 && v[0] == '+' {
p.build, v, ok = parseBuild(v)
if !ok {
- p.err = "bad build"
return
}
}
if v != "" {
- p.err = "junk on end"
ok = false
return
}
diff --git a/libgo/go/golang.org/x/mod/zip/zip.go b/libgo/go/golang.org/x/mod/zip/zip.go
index 5b401ad..ca0f7ad 100644
--- a/libgo/go/golang.org/x/mod/zip/zip.go
+++ b/libgo/go/golang.org/x/mod/zip/zip.go
@@ -53,6 +53,7 @@ import (
"io"
"io/ioutil"
"os"
+ "os/exec"
"path"
"path/filepath"
"strings"
@@ -192,8 +193,10 @@ func CheckFiles(files []File) (CheckedFiles, error) {
}
// checkFiles implements CheckFiles and also returns lists of valid files and
-// their sizes, corresponding to cf.Valid. These lists are used in Crewate to
-// avoid repeated calls to File.Lstat.
+// their sizes, corresponding to cf.Valid. It omits files in submodules, files
+// in vendored packages, symlinked files, and various other unwanted files.
+//
+// The lists returned are used in Create to avoid repeated calls to File.Lstat.
func checkFiles(files []File) (cf CheckedFiles, validFiles []File, validSizes []int64) {
errPaths := make(map[string]struct{})
addError := func(path string, omitted bool, err error) {
@@ -254,10 +257,12 @@ func checkFiles(files []File) (cf CheckedFiles, validFiles []File, validSizes []
continue
}
if isVendoredPackage(p) {
+ // Skip files in vendored packages.
addError(p, true, errVendored)
continue
}
if inSubmodule(p) {
+ // Skip submodule files.
addError(p, true, errSubmoduleFile)
continue
}
@@ -551,7 +556,7 @@ func CreateFromDir(w io.Writer, m module.Version, dir string) (err error) {
if zerr, ok := err.(*zipError); ok {
zerr.path = dir
} else if err != nil {
- err = &zipError{verb: "create zip", path: dir, err: err}
+ err = &zipError{verb: "create zip from directory", path: dir, err: err}
}
}()
@@ -563,6 +568,129 @@ func CreateFromDir(w io.Writer, m module.Version, dir string) (err error) {
return Create(w, m, files)
}
+// CreateFromVCS creates a module zip file for module m from the contents of a
+// VCS repository stored locally. The zip content is written to w.
+//
+// repoRoot must be an absolute path to the base of the repository, such as
+// "/Users/some-user/some-repo".
+//
+// revision is the revision of the repository to create the zip from. Examples
+// include HEAD or SHA sums for git repositories.
+//
+// subdir must be the relative path from the base of the repository, such as
+// "sub/dir". To create a zip from the base of the repository, pass an empty
+// string.
+//
+// If CreateFromVCS returns ErrUnrecognizedVCS, consider falling back to
+// CreateFromDir.
+func CreateFromVCS(w io.Writer, m module.Version, repoRoot, revision, subdir string) (err error) {
+ defer func() {
+ if zerr, ok := err.(*zipError); ok {
+ zerr.path = repoRoot
+ } else if err != nil {
+ err = &zipError{verb: "create zip from version control system", path: repoRoot, err: err}
+ }
+ }()
+
+ var filesToCreate []File
+
+ switch {
+ case isGitRepo(repoRoot):
+ files, err := filesInGitRepo(repoRoot, revision, subdir)
+ if err != nil {
+ return err
+ }
+
+ filesToCreate = files
+ default:
+ return &UnrecognizedVCSError{RepoRoot: repoRoot}
+ }
+
+ return Create(w, m, filesToCreate)
+}
+
+// UnrecognizedVCSError indicates that no recognized version control system was
+// found in the given directory.
+type UnrecognizedVCSError struct {
+ RepoRoot string
+}
+
+func (e *UnrecognizedVCSError) Error() string {
+ return fmt.Sprintf("could not find a recognized version control system at %q", e.RepoRoot)
+}
+
+// filterGitIgnored filters out any files that are git ignored in the directory.
+func filesInGitRepo(dir, rev, subdir string) ([]File, error) {
+ stderr := bytes.Buffer{}
+ stdout := bytes.Buffer{}
+
+ // Incredibly, git produces different archives depending on whether
+ // it is running on a Windows system or not, in an attempt to normalize
+ // text file line endings. Setting -c core.autocrlf=input means only
+ // translate files on the way into the repo, not on the way out (archive).
+ // The -c core.eol=lf should be unnecessary but set it anyway.
+ //
+ // Note: We use git archive to understand which files are actually included,
+ // ignoring things like .gitignore'd files. We could also use other
+ // techniques like git ls-files, but this approach most closely matches what
+ // the Go command does, which is beneficial.
+ //
+ // Note: some of this code copied from https://go.googlesource.com/go/+/refs/tags/go1.16.5/src/cmd/go/internal/modfetch/codehost/git.go#826.
+ cmd := exec.Command("git", "-c", "core.autocrlf=input", "-c", "core.eol=lf", "archive", "--format=zip", rev)
+ if subdir != "" {
+ cmd.Args = append(cmd.Args, subdir)
+ }
+ cmd.Dir = dir
+ cmd.Stdout = &stdout
+ cmd.Stderr = &stderr
+ if err := cmd.Run(); err != nil {
+ return nil, fmt.Errorf("error running `git archive`: %w, %s", err, stderr.String())
+ }
+
+ rawReader := bytes.NewReader(stdout.Bytes())
+ zipReader, err := zip.NewReader(rawReader, int64(stdout.Len()))
+ if err != nil {
+ return nil, err
+ }
+
+ var fs []File
+ for _, zf := range zipReader.File {
+ if !strings.HasPrefix(zf.Name, subdir) || strings.HasSuffix(zf.Name, "/") {
+ continue
+ }
+
+ n := strings.TrimPrefix(zf.Name, subdir)
+ if n == "" {
+ continue
+ }
+ n = strings.TrimPrefix(n, string(filepath.Separator))
+
+ fs = append(fs, zipFile{
+ name: n,
+ f: zf,
+ })
+ }
+
+ return fs, nil
+}
+
+// isGitRepo reports whether the given directory is a git repo.
+func isGitRepo(dir string) bool {
+ stdout := &bytes.Buffer{}
+ cmd := exec.Command("git", "rev-parse", "--git-dir")
+ cmd.Dir = dir
+ cmd.Stdout = stdout
+ if err := cmd.Run(); err != nil {
+ return false
+ }
+ gitDir := strings.TrimSpace(string(stdout.Bytes()))
+ if !filepath.IsAbs(gitDir) {
+ gitDir = filepath.Join(dir, gitDir)
+ }
+ wantDir := filepath.Join(dir, ".git")
+ return wantDir == gitDir
+}
+
type dirFile struct {
filePath, slashPath string
info os.FileInfo
@@ -572,6 +700,15 @@ func (f dirFile) Path() string { return f.slashPath }
func (f dirFile) Lstat() (os.FileInfo, error) { return f.info, nil }
func (f dirFile) Open() (io.ReadCloser, error) { return os.Open(f.filePath) }
+type zipFile struct {
+ name string
+ f *zip.File
+}
+
+func (f zipFile) Path() string { return f.name }
+func (f zipFile) Lstat() (os.FileInfo, error) { return f.f.FileInfo(), nil }
+func (f zipFile) Open() (io.ReadCloser, error) { return f.f.Open() }
+
// isVendoredPackage attempts to report whether the given filename is contained
// in a package whose import path contains (but does not end with) the component
// "vendor".