• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1package main
2
3import (
4	"bufio"
5	"fmt"
6	"go/build/constraint"
7	"log"
8	"os"
9	"path/filepath"
10	"regexp"
11	"sort"
12	"strings"
13)
14
15var goVersionRegex = regexp.MustCompile(`^go1.(\d+)$`)
16
17// Used to update the list of tags affecting the standard library kept in
18// transitions.bzl.
19func main() {
20	if len(os.Args) < 2 {
21		log.Fatal("usage: stdlib_tags <go SDK src directory>...")
22	}
23
24	filteredTags, err := extractBuildTags(os.Args[1:]...)
25	if err != nil {
26		log.Fatal(err.Error())
27	}
28
29	fmt.Printf("_TAG_AFFECTS_STDLIB = {\n")
30	for _, tag := range filteredTags {
31		fmt.Printf("    %q: None,\n", tag)
32	}
33	fmt.Printf("}\n")
34}
35
36func extractBuildTags(sdkPaths ...string) ([]string, error) {
37	tags := make(map[string]struct{})
38	for _, dir := range sdkPaths {
39		err := filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
40			if d.IsDir() {
41				if d.Name() == "testdata" {
42					return filepath.SkipDir
43				}
44				return nil
45			}
46			if filepath.Ext(path) != ".go" {
47				return nil
48			}
49			if strings.HasSuffix(filepath.Base(path), "_test.go") {
50				return nil
51			}
52			return walkFile(path, tags)
53		})
54		if err != nil {
55			return nil, fmt.Errorf("%s: %w", dir, err)
56		}
57	}
58
59	filteredTags := make([]string, 0, len(tags))
60	for tag := range tags {
61		if !shouldExclude(tag) {
62			filteredTags = append(filteredTags, tag)
63		}
64	}
65	sort.Strings(filteredTags)
66
67	return filteredTags, nil
68}
69
70func shouldExclude(tag string) bool {
71	// Set via CGO_ENABLED
72	return tag == "cgo" ||
73		// Set via GOARCH and GOOS
74		knownOS[tag] || knownArch[tag] || tag == "unix" ||
75		// Set via GOEXPERIMENT and GOAMD64
76		strings.HasPrefix(tag, "goexperiment.") || strings.HasPrefix(tag, "amd64.") ||
77		// Set implicitly
78		goVersionRegex.MatchString(tag)
79}
80
81func walkFile(path string, tags map[string]struct{}) error {
82	file, err := os.Open(path)
83	if err != nil {
84		return err
85	}
86
87	scanner := bufio.NewScanner(file)
88	// The Go SDK contains some very long lines in vendored files (minified JS).
89	scanner.Buffer(make([]byte, 0, 128*1024), 1024*1024)
90	for scanner.Scan() {
91		line := scanner.Text()
92		if !isConstraint(line) {
93			continue
94		}
95		c, err := constraint.Parse(line)
96		if err != nil {
97			continue
98		}
99		walkConstraint(c, tags)
100	}
101
102	if err = scanner.Err(); err != nil {
103		return fmt.Errorf("%s: %w", path, err)
104	}
105	return nil
106}
107
108func walkConstraint(c constraint.Expr, tags map[string]struct{}) {
109	switch c.(type) {
110	case *constraint.AndExpr:
111		walkConstraint(c.(*constraint.AndExpr).X, tags)
112		walkConstraint(c.(*constraint.AndExpr).Y, tags)
113	case *constraint.OrExpr:
114		walkConstraint(c.(*constraint.OrExpr).X, tags)
115		walkConstraint(c.(*constraint.OrExpr).Y, tags)
116	case *constraint.NotExpr:
117		walkConstraint(c.(*constraint.NotExpr).X, tags)
118	case *constraint.TagExpr:
119		tags[c.(*constraint.TagExpr).Tag] = struct{}{}
120	}
121}
122
123func isConstraint(line string) bool {
124	return constraint.IsPlusBuild(line) || constraint.IsGoBuild(line)
125}
126
127// Taken from
128// https://github.com/golang/go/blob/3d5391ed87d813110e10b954c62bf7ed578b591f/src/go/build/syslist.go
129var knownOS = map[string]bool{
130	"aix":       true,
131	"android":   true,
132	"darwin":    true,
133	"dragonfly": true,
134	"freebsd":   true,
135	"hurd":      true,
136	"illumos":   true,
137	"ios":       true,
138	"js":        true,
139	"linux":     true,
140	"nacl":      true,
141	"netbsd":    true,
142	"openbsd":   true,
143	"plan9":     true,
144	"solaris":   true,
145	"windows":   true,
146	"zos":       true,
147}
148
149var knownArch = map[string]bool{
150	"386":         true,
151	"amd64":       true,
152	"amd64p32":    true,
153	"arm":         true,
154	"armbe":       true,
155	"arm64":       true,
156	"arm64be":     true,
157	"loong64":     true,
158	"mips":        true,
159	"mipsle":      true,
160	"mips64":      true,
161	"mips64le":    true,
162	"mips64p32":   true,
163	"mips64p32le": true,
164	"ppc":         true,
165	"ppc64":       true,
166	"ppc64le":     true,
167	"riscv":       true,
168	"riscv64":     true,
169	"s390":        true,
170	"s390x":       true,
171	"sparc":       true,
172	"sparc64":     true,
173	"wasm":        true,
174}
175