-
-
Notifications
You must be signed in to change notification settings - Fork 678
/
Copy pathstdliblist.go
312 lines (273 loc) · 10.3 KB
/
stdliblist.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
// Copyright 2021 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"go/build"
"os"
"path/filepath"
"strings"
)
// Copy and pasted from golang.org/x/tools/go/packages
type flatPackagesError struct {
Pos string // "file:line:col" or "file:line" or "" or "-"
Msg string
Kind flatPackagesErrorKind
}
type flatPackagesErrorKind int
const (
UnknownError flatPackagesErrorKind = iota
ListError
ParseError
TypeError
)
func (err flatPackagesError) Error() string {
pos := err.Pos
if pos == "" {
pos = "-" // like token.Position{}.String()
}
return pos + ": " + err.Msg
}
// flatPackage is the JSON form of Package
// It drops all the type and syntax fields, and transforms the Imports
type flatPackage struct {
ID string
Name string `json:",omitempty"`
PkgPath string `json:",omitempty"`
Standard bool `json:",omitempty"`
Errors []flatPackagesError `json:",omitempty"`
GoFiles []string `json:",omitempty"`
CompiledGoFiles []string `json:",omitempty"`
OtherFiles []string `json:",omitempty"`
ExportFile string `json:",omitempty"`
Imports map[string]string `json:",omitempty"`
}
type goListPackage struct {
Dir string // directory containing package sources
ImportPath string // import path of package in dir
Name string // package name
Target string // install path
Goroot bool // is this package in the Go root?
Standard bool // is this package part of the standard Go library?
Root string // Go root or Go path dir containing this package
Export string // file containing export data (when using -export)
// Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string // .go source files that import "C"
CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
IgnoredGoFiles []string // .go source files ignored due to build constraints
IgnoredOtherFiles []string // non-.go source files ignored due to build constraints
CFiles []string // .c source files
CXXFiles []string // .cc, .cxx and .cpp source files
MFiles []string // .m source files
HFiles []string // .h, .hh, .hpp and .hxx source files
FFiles []string // .f, .F, .for and .f90 Fortran source files
SFiles []string // .s source files
SwigFiles []string // .swig files
SwigCXXFiles []string // .swigcxx files
SysoFiles []string // .syso object files to add to archive
TestGoFiles []string // _test.go files in package
XTestGoFiles []string // _test.go files outside package
// Embedded files
EmbedPatterns []string // //go:embed patterns
EmbedFiles []string // files matched by EmbedPatterns
TestEmbedPatterns []string // //go:embed patterns in TestGoFiles
TestEmbedFiles []string // files matched by TestEmbedPatterns
XTestEmbedPatterns []string // //go:embed patterns in XTestGoFiles
XTestEmbedFiles []string // files matched by XTestEmbedPatterns
// Dependency information
Imports []string // import paths used by this package
ImportMap map[string]string // map from source import to ImportPath (identity entries omitted)
// Error information
Incomplete bool // this package or a dependency has an error
Error *flatPackagesError // error loading package
DepsErrors []*flatPackagesError // errors loading dependencies
}
var rulesGoStdlibPrefix string
func init() {
if rulesGoStdlibPrefix == "" {
panic("rulesGoStdlibPrefix should have been set via -X")
}
}
func stdlibPackageID(importPath string) string {
return rulesGoStdlibPrefix + importPath
}
// outputBasePath replace the cloneBase with output base label
func outputBasePath(cloneBase, p string) string {
dir, _ := filepath.Rel(cloneBase, p)
return filepath.Join("__BAZEL_OUTPUT_BASE__", dir)
}
// absoluteSourcesPaths replace cloneBase of the absolution
// paths with the label for all source files in a package
func absoluteSourcesPaths(cloneBase, pkgDir string, srcs []string) []string {
ret := make([]string, 0, len(srcs))
pkgDir = outputBasePath(cloneBase, pkgDir)
for _, src := range srcs {
absPath := src
// Generated files will already have an absolute path. These come from
// the compiler's cache.
if !filepath.IsAbs(src) {
absPath = filepath.Join(pkgDir, src)
}
ret = append(ret, absPath)
}
return ret
}
// filterGoFiles keeps only files either ending in .go or those without an
// extension (which are from the cache). This is a work around for
// https://golang.org/issue/28749: cmd/go puts assembly, C, and C++ files in
// CompiledGoFiles.
func filterGoFiles(srcs []string, pathReplaceFn func(p string) string) []string {
ret := make([]string, 0, len(srcs))
for _, f := range srcs {
if ext := filepath.Ext(f); ext == ".go" || ext == "" {
ret = append(ret, pathReplaceFn(f))
}
}
return ret
}
func flatPackageForStd(cloneBase string, pkg *goListPackage, pathReplaceFn func(p string) string) *flatPackage {
goFiles := absoluteSourcesPaths(cloneBase, pkg.Dir, pkg.GoFiles)
compiledGoFiles := absoluteSourcesPaths(cloneBase, pkg.Dir, pkg.CompiledGoFiles)
newPkg := &flatPackage{
ID: stdlibPackageID(pkg.ImportPath),
Name: pkg.Name,
PkgPath: pkg.ImportPath,
ExportFile: outputBasePath(cloneBase, pkg.Target),
Imports: map[string]string{},
Standard: pkg.Standard,
GoFiles: goFiles,
CompiledGoFiles: filterGoFiles(compiledGoFiles, pathReplaceFn),
}
// imports
//
// Imports contains the IDs of all imported packages.
// ImportsMap records (path, ID) only where they differ.
ids := make(map[string]struct{})
for _, id := range pkg.Imports {
ids[id] = struct{}{}
}
for path, id := range pkg.ImportMap {
newPkg.Imports[path] = stdlibPackageID(id)
delete(ids, id)
}
for id := range ids {
if id != "C" {
newPkg.Imports[id] = stdlibPackageID(id)
}
}
return newPkg
}
// stdliblist runs `go list -json` on the standard library and saves it to a file.
func stdliblist(args []string) error {
// process the args
flags := flag.NewFlagSet("stdliblist", flag.ExitOnError)
goenv := envFlags(flags)
out := flags.String("out", "", "Path to output go list json")
cachePath := flags.String("cache", "", "Path to use for GOCACHE")
if err := flags.Parse(args); err != nil {
return err
}
if err := goenv.checkFlagsAndSetGoroot(); err != nil {
return err
}
if filepath.IsAbs(goenv.sdk) {
return fmt.Errorf("-sdk needs to be a relative path, but got %s", goenv.sdk)
}
// In Go 1.18, the standard library started using go:embed directives.
// When Bazel runs this action, it does so inside a sandbox where GOROOT points
// to an external/go_sdk directory that contains a symlink farm of all files in
// the Go SDK.
// If we run "go list" with that GOROOT, this action will fail because those
// go:embed directives will refuse to include the symlinks in the sandbox.
//
// To work around this, cloneGoRoot creates a copy of a subset of external/go_sdk
// that is sufficient to call "go list" into a new cloneBase directory, e.g.
// "go list" needs to call "compile", which needs "pkg/tool".
// We also need to retain the same relative path to the root directory, e.g.
// "$OUTPUT_BASE/external/go_sdk" becomes
// {cloneBase}/external/go_sdk", which will be set at GOROOT later. This ensures
// that file paths in the generated JSON are still valid.
//
// Here we replicate goRoot(absolute path of goenv.sdk) to newGoRoot.
cloneBase, cleanup, err := goenv.workDir()
if err != nil {
return err
}
defer func() { cleanup() }()
newGoRoot := filepath.Join(cloneBase, goenv.sdk)
if err := replicate(abs(goenv.sdk), abs(newGoRoot), replicatePaths("src", "pkg/tool", "pkg/include")); err != nil {
return err
}
// Ensure paths are absolute.
absPaths := []string{}
for _, path := range filepath.SplitList(os.Getenv("PATH")) {
absPaths = append(absPaths, abs(path))
}
os.Setenv("PATH", strings.Join(absPaths, string(os.PathListSeparator)))
os.Setenv("GOROOT", newGoRoot)
cgoEnabled := os.Getenv("CGO_ENABLED") == "1"
// Make sure we have an absolute path to the C compiler.
ccEnv, ok := os.LookupEnv("CC")
if cgoEnabled && !ok {
return fmt.Errorf("CC must be set")
}
os.Setenv("CC", quotePathIfNeeded(abs(ccEnv)))
if err := absCCCompiler(cgoEnvVars, cgoAbsEnvFlags); err != nil {
return fmt.Errorf("error modifying cgo environment to absolute path: %v", err)
}
// We want to keep the cache around so that the processed files can be used by other tools.
absCachePath := abs(*cachePath)
os.Setenv("GOCACHE", absCachePath)
os.Setenv("GOMODCACHE", absCachePath)
os.Setenv("GOPATH", absCachePath)
listArgs := goenv.goCmd("list")
if len(build.Default.BuildTags) > 0 {
listArgs = append(listArgs, "-tags", strings.Join(build.Default.BuildTags, ","))
}
if cgoEnabled {
listArgs = append(listArgs, "-compiled=true")
}
listArgs = append(listArgs, "-json", "builtin", "std", "runtime/cgo")
jsonFile, err := os.Create(*out)
if err != nil {
return err
}
defer jsonFile.Close()
jsonData := &bytes.Buffer{}
if err := goenv.runCommandToFile(jsonData, os.Stderr, listArgs); err != nil {
return err
}
encoder := json.NewEncoder(jsonFile)
decoder := json.NewDecoder(jsonData)
pathReplaceFn := func(s string) string {
if strings.HasPrefix(s, absCachePath) {
return strings.Replace(s, absCachePath, filepath.Join("__BAZEL_EXECROOT__", *cachePath), 1)
}
return s
}
for decoder.More() {
var pkg *goListPackage
if err := decoder.Decode(&pkg); err != nil {
return err
}
if err := encoder.Encode(flatPackageForStd(cloneBase, pkg, pathReplaceFn)); err != nil {
return err
}
}
return nil
}