|
9 | 9 | "errors"
|
10 | 10 | "fmt"
|
11 | 11 | "go/ast"
|
| 12 | + "go/build/constraint" |
12 | 13 | "go/doc"
|
13 | 14 | "go/token"
|
14 | 15 | exec "internal/execabs"
|
@@ -1423,7 +1424,7 @@ func (ctxt *Context) matchFile(dir, name string, allTags map[string]bool, binary
|
1423 | 1424 | // Look for +build comments to accept or reject the file.
|
1424 | 1425 | ok, sawBinaryOnly, err := ctxt.shouldBuild(info.header, allTags)
|
1425 | 1426 | if err != nil {
|
1426 |
| - return nil, err |
| 1427 | + return nil, fmt.Errorf("%s: %v", name, err) |
1427 | 1428 | }
|
1428 | 1429 | if !ok && !ctxt.UseAllFiles {
|
1429 | 1430 | return nil, nil
|
@@ -1459,11 +1460,12 @@ var (
|
1459 | 1460 | bSlashSlash = []byte(slashSlash)
|
1460 | 1461 | bStarSlash = []byte(starSlash)
|
1461 | 1462 | bSlashStar = []byte(slashStar)
|
| 1463 | + bPlusBuild = []byte("+build") |
1462 | 1464 |
|
1463 | 1465 | goBuildComment = []byte("//go:build")
|
1464 | 1466 |
|
1465 | 1467 | errGoBuildWithoutBuild = errors.New("//go:build comment without // +build comment")
|
1466 |
| - errMultipleGoBuild = errors.New("multiple //go:build comments") // unused in Go 1.(N-1) |
| 1468 | + errMultipleGoBuild = errors.New("multiple //go:build comments") |
1467 | 1469 | )
|
1468 | 1470 |
|
1469 | 1471 | func isGoBuildComment(line []byte) bool {
|
@@ -1498,53 +1500,50 @@ var binaryOnlyComment = []byte("//go:binary-only-package")
|
1498 | 1500 | // shouldBuild reports whether the file should be built
|
1499 | 1501 | // and whether a //go:binary-only-package comment was found.
|
1500 | 1502 | func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool) (shouldBuild, binaryOnly bool, err error) {
|
1501 |
| - |
1502 |
| - // Pass 1. Identify leading run of // comments and blank lines, |
| 1503 | + // Identify leading run of // comments and blank lines, |
1503 | 1504 | // which must be followed by a blank line.
|
1504 | 1505 | // Also identify any //go:build comments.
|
1505 | 1506 | content, goBuild, sawBinaryOnly, err := parseFileHeader(content)
|
1506 | 1507 | if err != nil {
|
1507 | 1508 | return false, false, err
|
1508 | 1509 | }
|
1509 | 1510 |
|
1510 |
| - // Pass 2. Process each +build line in the run. |
1511 |
| - p := content |
1512 |
| - shouldBuild = true |
1513 |
| - sawBuild := false |
1514 |
| - for len(p) > 0 { |
1515 |
| - line := p |
1516 |
| - if i := bytes.IndexByte(line, '\n'); i >= 0 { |
1517 |
| - line, p = line[:i], p[i+1:] |
1518 |
| - } else { |
1519 |
| - p = p[len(p):] |
1520 |
| - } |
1521 |
| - line = bytes.TrimSpace(line) |
1522 |
| - if !bytes.HasPrefix(line, bSlashSlash) { |
1523 |
| - continue |
| 1511 | + // If //go:build line is present, it controls. |
| 1512 | + // Otherwise fall back to +build processing. |
| 1513 | + switch { |
| 1514 | + case goBuild != nil: |
| 1515 | + x, err := constraint.Parse(string(goBuild)) |
| 1516 | + if err != nil { |
| 1517 | + return false, false, fmt.Errorf("parsing //go:build line: %v", err) |
1524 | 1518 | }
|
1525 |
| - line = bytes.TrimSpace(line[len(bSlashSlash):]) |
1526 |
| - if len(line) > 0 && line[0] == '+' { |
1527 |
| - // Looks like a comment +line. |
1528 |
| - f := strings.Fields(string(line)) |
1529 |
| - if f[0] == "+build" { |
1530 |
| - sawBuild = true |
1531 |
| - ok := false |
1532 |
| - for _, tok := range f[1:] { |
1533 |
| - if ctxt.match(tok, allTags) { |
1534 |
| - ok = true |
1535 |
| - } |
1536 |
| - } |
1537 |
| - if !ok { |
| 1519 | + shouldBuild = ctxt.eval(x, allTags) |
| 1520 | + |
| 1521 | + default: |
| 1522 | + shouldBuild = true |
| 1523 | + p := content |
| 1524 | + for len(p) > 0 { |
| 1525 | + line := p |
| 1526 | + if i := bytes.IndexByte(line, '\n'); i >= 0 { |
| 1527 | + line, p = line[:i], p[i+1:] |
| 1528 | + } else { |
| 1529 | + p = p[len(p):] |
| 1530 | + } |
| 1531 | + line = bytes.TrimSpace(line) |
| 1532 | + if !bytes.HasPrefix(line, bSlashSlash) || !bytes.Contains(line, bPlusBuild) { |
| 1533 | + continue |
| 1534 | + } |
| 1535 | + text := string(line) |
| 1536 | + if !constraint.IsPlusBuild(text) { |
| 1537 | + continue |
| 1538 | + } |
| 1539 | + if x, err := constraint.Parse(text); err == nil { |
| 1540 | + if !ctxt.eval(x, allTags) { |
1538 | 1541 | shouldBuild = false
|
1539 | 1542 | }
|
1540 | 1543 | }
|
1541 | 1544 | }
|
1542 | 1545 | }
|
1543 | 1546 |
|
1544 |
| - if goBuild != nil && !sawBuild { |
1545 |
| - return false, false, errGoBuildWithoutBuild |
1546 |
| - } |
1547 |
| - |
1548 | 1547 | return shouldBuild, sawBinaryOnly, nil
|
1549 | 1548 | }
|
1550 | 1549 |
|
@@ -1580,7 +1579,7 @@ Lines:
|
1580 | 1579 | }
|
1581 | 1580 |
|
1582 | 1581 | if !inSlashStar && isGoBuildComment(line) {
|
1583 |
| - if false && goBuild != nil { // enabled in Go 1.N |
| 1582 | + if goBuild != nil { |
1584 | 1583 | return nil, nil, false, errMultipleGoBuild
|
1585 | 1584 | }
|
1586 | 1585 | goBuild = line
|
@@ -1649,7 +1648,7 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
|
1649 | 1648 | if len(cond) > 0 {
|
1650 | 1649 | ok := false
|
1651 | 1650 | for _, c := range cond {
|
1652 |
| - if ctxt.match(c, nil) { |
| 1651 | + if ctxt.matchAuto(c, nil) { |
1653 | 1652 | ok = true
|
1654 | 1653 | break
|
1655 | 1654 | }
|
@@ -1831,50 +1830,44 @@ func splitQuoted(s string) (r []string, err error) {
|
1831 | 1830 | return args, err
|
1832 | 1831 | }
|
1833 | 1832 |
|
1834 |
| -// match reports whether the name is one of: |
| 1833 | +// matchAuto interprets text as either a +build or //go:build expression (whichever works), |
| 1834 | +// reporting whether the expression matches the build context. |
1835 | 1835 | //
|
| 1836 | +// matchAuto is only used for testing of tag evaluation |
| 1837 | +// and in #cgo lines, which accept either syntax. |
| 1838 | +func (ctxt *Context) matchAuto(text string, allTags map[string]bool) bool { |
| 1839 | + if strings.ContainsAny(text, "&|()") { |
| 1840 | + text = "//go:build " + text |
| 1841 | + } else { |
| 1842 | + text = "// +build " + text |
| 1843 | + } |
| 1844 | + x, err := constraint.Parse(text) |
| 1845 | + if err != nil { |
| 1846 | + return false |
| 1847 | + } |
| 1848 | + return ctxt.eval(x, allTags) |
| 1849 | +} |
| 1850 | + |
| 1851 | +func (ctxt *Context) eval(x constraint.Expr, allTags map[string]bool) bool { |
| 1852 | + return x.Eval(func(tag string) bool { return ctxt.matchTag(tag, allTags) }) |
| 1853 | +} |
| 1854 | + |
| 1855 | +// matchTag reports whether the name is one of: |
| 1856 | +// |
| 1857 | +// cgo (if cgo is enabled) |
1836 | 1858 | // $GOOS
|
1837 | 1859 | // $GOARCH
|
1838 |
| -// cgo (if cgo is enabled) |
1839 |
| -// !cgo (if cgo is disabled) |
1840 | 1860 | // ctxt.Compiler
|
1841 |
| -// !ctxt.Compiler |
| 1861 | +// linux (if GOOS = android) |
| 1862 | +// solaris (if GOOS = illumos) |
1842 | 1863 | // tag (if tag is listed in ctxt.BuildTags or ctxt.ReleaseTags)
|
1843 |
| -// !tag (if tag is not listed in ctxt.BuildTags or ctxt.ReleaseTags) |
1844 |
| -// a comma-separated list of any of these |
1845 | 1864 | //
|
1846 |
| -func (ctxt *Context) match(name string, allTags map[string]bool) bool { |
1847 |
| - if name == "" { |
1848 |
| - if allTags != nil { |
1849 |
| - allTags[name] = true |
1850 |
| - } |
1851 |
| - return false |
1852 |
| - } |
1853 |
| - if i := strings.Index(name, ","); i >= 0 { |
1854 |
| - // comma-separated list |
1855 |
| - ok1 := ctxt.match(name[:i], allTags) |
1856 |
| - ok2 := ctxt.match(name[i+1:], allTags) |
1857 |
| - return ok1 && ok2 |
1858 |
| - } |
1859 |
| - if strings.HasPrefix(name, "!!") { // bad syntax, reject always |
1860 |
| - return false |
1861 |
| - } |
1862 |
| - if strings.HasPrefix(name, "!") { // negation |
1863 |
| - return len(name) > 1 && !ctxt.match(name[1:], allTags) |
1864 |
| - } |
1865 |
| - |
| 1865 | +// It records all consulted tags in allTags. |
| 1866 | +func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool { |
1866 | 1867 | if allTags != nil {
|
1867 | 1868 | allTags[name] = true
|
1868 | 1869 | }
|
1869 | 1870 |
|
1870 |
| - // Tags must be letters, digits, underscores or dots. |
1871 |
| - // Unlike in Go identifiers, all digits are fine (e.g., "386"). |
1872 |
| - for _, c := range name { |
1873 |
| - if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { |
1874 |
| - return false |
1875 |
| - } |
1876 |
| - } |
1877 |
| - |
1878 | 1871 | // special tags
|
1879 | 1872 | if ctxt.CgoEnabled && name == "cgo" {
|
1880 | 1873 | return true
|
@@ -1946,10 +1939,10 @@ func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
|
1946 | 1939 | }
|
1947 | 1940 | n := len(l)
|
1948 | 1941 | if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
|
1949 |
| - return ctxt.match(l[n-1], allTags) && ctxt.match(l[n-2], allTags) |
| 1942 | + return ctxt.matchTag(l[n-1], allTags) && ctxt.matchTag(l[n-2], allTags) |
1950 | 1943 | }
|
1951 | 1944 | if n >= 1 && (knownOS[l[n-1]] || knownArch[l[n-1]]) {
|
1952 |
| - return ctxt.match(l[n-1], allTags) |
| 1945 | + return ctxt.matchTag(l[n-1], allTags) |
1953 | 1946 | }
|
1954 | 1947 | return true
|
1955 | 1948 | }
|
|
0 commit comments