1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package packages
6
7// See doc.go for package documentation and implementation notes.
8
9import (
10 "context"
11 "encoding/json"
12 "fmt"
13 "go/ast"
14 "go/parser"
15 "go/token"
16 "go/types"
17 "log"
18 "os"
19 "sync"
20
21 "golang.org/x/tools/go/gcexportdata"
22)
23
24// A LoadMode specifies the amount of detail to return when loading packages.
25// The modes are all strictly additive, as you go through the list it increases
26// the amount of information available to you, but may also increase the cost
27// of collecting the information.
28// Load is always allowed to return more information than requested.
29type LoadMode int
30
31const (
32 // LoadFiles finds the packages and computes their source file lists.
33 // Package fields: ID, Name, Errors, GoFiles, OtherFiles.
34 LoadFiles LoadMode = iota
35
36 // LoadImports adds import information for each package
37 // and its dependencies.
38 // Package fields added: Imports.
39 LoadImports
40
41 // LoadTypes adds type information for the package's exported symbols.
42 // Package fields added: Types, Fset, IllTyped.
43 // This will use the type information provided by the build system if
44 // possible, and the ExportFile field may be filled in.
45 LoadTypes
46
47 // LoadSyntax adds typed syntax trees for the packages matching the patterns.
48 // Package fields added: Syntax, TypesInfo, for direct pattern matches only.
49 LoadSyntax
50
51 // LoadAllSyntax adds typed syntax trees for the packages matching the patterns
52 // and all dependencies.
53 // Package fields added: Syntax, TypesInfo, for all packages in import graph.
54 LoadAllSyntax
55)
56
57// An Config specifies details about how packages should be loaded.
58// Calls to Load do not modify this struct.
59type Config struct {
60 // Mode controls the level of information returned for each package.
61 Mode LoadMode
62
63 // Context specifies the context for the load operation.
64 // If the context is cancelled, the loader may stop early
65 // and return an ErrCancelled error.
66 // If Context is nil, the load cannot be cancelled.
67 Context context.Context
68
69 // Dir is the directory in which to run the build system tool
70 // that provides information about the packages.
71 // If Dir is empty, the tool is run in the current directory.
72 Dir string
73
74 // Env is the environment to use when invoking the build system tool.
75 // If Env is nil, the current environment is used.
76 // Like in os/exec's Cmd, only the last value in the slice for
77 // each environment key is used. To specify the setting of only
78 // a few variables, append to the current environment, as in:
79 //
80 // opt.Env = append(os.Environ(), "GOOS=plan9", "GOARCH=386")
81 //
82 Env []string
83
84 // Flags is a list of command-line flags to be passed through to
85 // the underlying query tool.
86 Flags []string
87
88 // Error is called for each error encountered during package loading.
89 // It must be safe to call Error simultaneously from multiple goroutines.
90 // In addition to calling Error, the loader will record each error
91 // in the corresponding Package's Errors list.
92 // If Error is nil, the loader will print errors to os.Stderr.
93 // To disable printing of errors, set opt.Error = func(error){}.
94 // TODO(rsc): What happens in the Metadata loader? Currently nothing.
95 Error func(error)
96
97 // Fset is the token.FileSet to use when parsing source files or
98 // type information provided by the build system.
99 // If Fset is nil, the loader will create one.
100 Fset *token.FileSet
101
102 // ParseFile is called to read and parse each file
103 // when preparing a package's type-checked syntax tree.
104 // It must be safe to call ParseFile simultaneously from multiple goroutines.
105 // If ParseFile is nil, the loader will uses parser.ParseFile.
106 //
107 // Setting ParseFile to a custom implementation can allow
108 // providing alternate file content in order to type-check
109 // unsaved text editor buffers, or to selectively eliminate
110 // unwanted function bodies to reduce the amount of work
111 // done by the type checker.
112 ParseFile func(fset *token.FileSet, filename string) (*ast.File, error)
113
114 // If Tests is set, the loader includes not just the packages
115 // matching a particular pattern but also any related test packages,
116 // including test-only variants of the package and the test executable.
117 //
118 // For example, when using the go command, loading "fmt" with Tests=true
119 // returns four packages, with IDs "fmt" (the standard package),
120 // "fmt [fmt.test]" (the package as compiled for the test),
121 // "fmt_test" (the test functions from source files in package fmt_test),
122 // and "fmt.test" (the test binary).
123 //
124 // In build systems with explicit names for tests,
125 // setting Tests may have no effect.
126 Tests bool
127
128 // TypeChecker provides additional configuration for type-checking syntax trees.
129 //
130 // It is used for all packages in LoadAllSyntax mode,
131 // and for the packages matching the patterns, but not their dependencies,
132 // in LoadSyntax mode.
133 //
134 // The TypeChecker.Error function is ignored:
135 // errors are reported using the Error function defined above.
136 //
137 // The TypeChecker.Importer function is ignored:
138 // the loader defines an appropriate importer.
139 //
140 // TODO(rsc): TypeChecker.Sizes should use the same sizes as the main build.
141 // Derive them from the runtime?
142 TypeChecker types.Config
143}
144
145// driver is the type for functions that query the build system for the
146// packages named by the patterns.
147type driver func(cfg *Config, patterns ...string) (*driverResponse, error)
148
149// driverResponse contains the results for a driver query.
150type driverResponse struct {
151 // Roots is the set of package IDs that make up the root packages.
152 // We have to encode this separately because when we encode a single package
153 // we cannot know if it is one of the roots as that requires knowledge of the
154 // graph it is part of.
155 Roots []string `json:",omitempty"`
156
157 // Packages is the full set of packages in the graph.
158 // The packages are not connected into a graph.
159 // The Imports if populated will be stubs that only have their ID set.
160 // Imports will be connected and then type and syntax information added in a
161 // later pass (see refine).
162 Packages []*Package
163}
164
165// Load loads and returns the Go packages named by the given patterns.
166//
167// Config specifies loading options;
168// nil behaves the same as an empty Config.
169//
170// Load returns an error if any of the patterns was invalid
171// as defined by the underlying build system.
172// It may return an empty list of packages without an error,
173// for instance for an empty expansion of a valid wildcard.
174func Load(cfg *Config, patterns ...string) ([]*Package, error) {
175 l := newLoader(cfg)
176 response, err := defaultDriver(&l.Config, patterns...)
177 if err != nil {
178 return nil, err
179 }
180 return l.refine(response.Roots, response.Packages...)
181}
182
183// defaultDriver is a driver that looks for an external driver binary, and if
184// it does not find it falls back to the built in go list driver.
185func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
186 driver := findExternalDriver(cfg)
187 if driver == nil {
188 driver = goListDriver
189 }
190 return driver(cfg, patterns...)
191}
192
193// A Package describes a single loaded Go package.
194type Package struct {
195 // ID is a unique identifier for a package,
196 // in a syntax provided by the underlying build system.
197 //
198 // Because the syntax varies based on the build system,
199 // clients should treat IDs as opaque and not attempt to
200 // interpret them.
201 ID string
202
203 // Name is the package name as it appears in the package source code.
204 Name string
205
206 // This is the package path as used by the types package.
207 // This is used to map entries in the type information back to the package
208 // they come from.
209 PkgPath string
210
211 // Errors lists any errors encountered while loading the package.
212 // TODO(rsc): Say something about the errors or at least their Strings,
213 // as far as file:line being at the beginning and so on.
214 Errors []error
215
216 // GoFiles lists the absolute file paths of the package's Go source files.
217 GoFiles []string
218
219 // CompiledGoFiles lists the absolute file paths of the package's source
220 // files that were presented to the compiler.
221 // This may differ from GoFiles if files are processed before compilation.
222 CompiledGoFiles []string
223
224 // OtherFiles lists the absolute file paths of the package's non-Go source files,
225 // including assembly, C, C++, Fortran, Objective-C, SWIG, and so on.
226 OtherFiles []string
227
228 // ExportFile is the absolute path to a file containing the type information
229 // provided by the build system.
230 ExportFile string
231
232 // Imports maps import paths appearing in the package's Go source files
233 // to corresponding loaded Packages.
234 Imports map[string]*Package
235
236 // Types is the type information for the package.
237 // Modes LoadTypes and above set this field for all packages.
238 //
239 // TODO(adonovan): all packages? In Types mode this entails
240 // asymptotically more export data processing than is required
241 // to load the requested packages. Is that what we want?
242 Types *types.Package
243
244 // Fset provides position information for Types, TypesInfo, and Syntax.
245 // Modes LoadTypes and above set this field for all packages.
246 Fset *token.FileSet
247
248 // IllTyped indicates whether the package has any type errors.
249 // Modes LoadTypes and above set this field for all packages.
250 IllTyped bool
251
252 // Syntax is the package's syntax trees, for the files listed in GoFiles.
253 //
254 // Mode LoadSyntax set this field for packages matching the patterns.
255 // Mode LoadSyntaxAll sets this field for all packages, including dependencies.
256 Syntax []*ast.File
257
258 // TypesInfo is the type-checking results for the package's syntax trees.
259 // It is set only when Syntax is set.
260 TypesInfo *types.Info
261}
262
263// packageError is used to serialize structured errors as much as possible.
264// This has members compatible with the golist error type, and possibly some
265// more if we need other error information to survive.
266type packageError struct {
267 Pos string // position of error
268 Err string // the error itself
269}
270
271func (e *packageError) Error() string {
272 return e.Pos + ": " + e.Err
273}
274
275// flatPackage is the JSON form of Package
276// It drops all the type and syntax fields, and transforms the Imports and Errors
277type flatPackage struct {
278 ID string
279 Name string `json:",omitempty"`
280 PkgPath string `json:",omitempty"`
281 Errors []*packageError `json:",omitempty"`
282 GoFiles []string `json:",omitempty"`
283 CompiledGoFiles []string `json:",omitempty"`
284 OtherFiles []string `json:",omitempty"`
285 ExportFile string `json:",omitempty"`
286 Imports map[string]string `json:",omitempty"`
287}
288
289// MarshalJSON returns the Package in its JSON form.
290// For the most part, the structure fields are written out unmodified, and
291// the type and syntax fields are skipped.
292// The imports are written out as just a map of path to package id.
293// The errors are written using a custom type that tries to preserve the
294// structure of error types we know about.
295// This method exists to enable support for additional build systems. It is
296// not intended for use by clients of the API and we may change the format.
297func (p *Package) MarshalJSON() ([]byte, error) {
298 flat := &flatPackage{
299 ID: p.ID,
300 Name: p.Name,
301 PkgPath: p.PkgPath,
302 GoFiles: p.GoFiles,
303 CompiledGoFiles: p.CompiledGoFiles,
304 OtherFiles: p.OtherFiles,
305 ExportFile: p.ExportFile,
306 }
307 if len(p.Errors) > 0 {
308 flat.Errors = make([]*packageError, len(p.Errors))
309 for i, err := range p.Errors {
310 //TODO: best effort mapping of errors to the serialized form
311 switch err := err.(type) {
312 case *packageError:
313 flat.Errors[i] = err
314 default:
315 flat.Errors[i] = &packageError{Err: err.Error()}
316 }
317 }
318 }
319 if len(p.Imports) > 0 {
320 flat.Imports = make(map[string]string, len(p.Imports))
321 for path, ipkg := range p.Imports {
322 flat.Imports[path] = ipkg.ID
323 }
324 }
325 return json.Marshal(flat)
326}
327
328// UnmarshalJSON reads in a Package from its JSON format.
329// See MarshalJSON for details about the format accepted.
330func (p *Package) UnmarshalJSON(b []byte) error {
331 flat := &flatPackage{}
332 if err := json.Unmarshal(b, &flat); err != nil {
333 return err
334 }
335 *p = Package{
336 ID: flat.ID,
337 Name: flat.Name,
338 PkgPath: flat.PkgPath,
339 GoFiles: flat.GoFiles,
340 CompiledGoFiles: flat.CompiledGoFiles,
341 OtherFiles: flat.OtherFiles,
342 ExportFile: flat.ExportFile,
343 }
344 if len(flat.Errors) > 0 {
345 p.Errors = make([]error, len(flat.Errors))
346 for i, err := range flat.Errors {
347 p.Errors[i] = err
348 }
349 }
350 if len(flat.Imports) > 0 {
351 p.Imports = make(map[string]*Package, len(flat.Imports))
352 for path, id := range flat.Imports {
353 p.Imports[path] = &Package{ID: id}
354 }
355 }
356 return nil
357}
358
359func (p *Package) String() string { return p.ID }
360
361// loaderPackage augments Package with state used during the loading phase
362type loaderPackage struct {
363 *Package
364 importErrors map[string]error // maps each bad import to its error
365 loadOnce sync.Once
366 color uint8 // for cycle detection
367 mark, needsrc bool // used when Mode >= LoadTypes
368}
369
370// loader holds the working state of a single call to load.
371type loader struct {
372 pkgs map[string]*loaderPackage
373 Config
374 exportMu sync.Mutex // enforces mutual exclusion of exportdata operations
375}
376
377func newLoader(cfg *Config) *loader {
378 ld := &loader{}
379 if cfg != nil {
380 ld.Config = *cfg
381 }
382 if ld.Context == nil {
383 ld.Context = context.Background()
384 }
385 if ld.Dir == "" {
386 if dir, err := os.Getwd(); err == nil {
387 ld.Dir = dir
388 }
389 }
390
391 if ld.Mode >= LoadTypes {
392 if ld.Fset == nil {
393 ld.Fset = token.NewFileSet()
394 }
395
396 // Error and ParseFile are required even in LoadTypes mode
397 // because we load source if export data is missing.
398
399 if ld.Error == nil {
400 ld.Error = func(e error) {
401 fmt.Fprintln(os.Stderr, e)
402 }
403 }
404
405 if ld.ParseFile == nil {
406 ld.ParseFile = func(fset *token.FileSet, filename string) (*ast.File, error) {
407 const mode = parser.AllErrors | parser.ParseComments
408 return parser.ParseFile(fset, filename, nil, mode)
409 }
410 }
411 }
412 return ld
413}
414
415// refine connects the supplied packages into a graph and then adds type and
416// and syntax information as requested by the LoadMode.
417func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
418 if len(list) == 0 {
419 return nil, fmt.Errorf("packages not found")
420 }
421 isRoot := make(map[string]bool, len(roots))
422 for _, root := range roots {
423 isRoot[root] = true
424 }
425 ld.pkgs = make(map[string]*loaderPackage)
426 // first pass, fixup and build the map and roots
427 var initial []*loaderPackage
428 for _, pkg := range list {
429 lpkg := &loaderPackage{
430 Package: pkg,
431 needsrc: ld.Mode >= LoadAllSyntax ||
432 (ld.Mode >= LoadSyntax && isRoot[pkg.ID]) ||
433 (pkg.ExportFile == "" && pkg.PkgPath != "unsafe"),
434 }
435 ld.pkgs[lpkg.ID] = lpkg
436 if isRoot[lpkg.ID] {
437 initial = append(initial, lpkg)
438 }
439 }
440
441 // Materialize the import graph.
442
443 const (
444 white = 0 // new
445 grey = 1 // in progress
446 black = 2 // complete
447 )
448
449 // visit traverses the import graph, depth-first,
450 // and materializes the graph as Packages.Imports.
451 //
452 // Valid imports are saved in the Packages.Import map.
453 // Invalid imports (cycles and missing nodes) are saved in the importErrors map.
454 // Thus, even in the presence of both kinds of errors, the Import graph remains a DAG.
455 //
456 // visit returns whether the package needs src or has a transitive
457 // dependency on a package that does. These are the only packages
458 // for which we load source code.
459 var stack []*loaderPackage
460 var visit func(lpkg *loaderPackage) bool
461 visit = func(lpkg *loaderPackage) bool {
462 switch lpkg.color {
463 case black:
464 return lpkg.needsrc
465 case grey:
466 panic("internal error: grey node")
467 }
468 lpkg.color = grey
469 stack = append(stack, lpkg) // push
470 stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports
471 lpkg.Imports = make(map[string]*Package, len(stubs))
472 for importPath, ipkg := range stubs {
473 var importErr error
474 imp := ld.pkgs[ipkg.ID]
475 if imp == nil {
476 // (includes package "C" when DisableCgo)
477 importErr = fmt.Errorf("missing package: %q", ipkg.ID)
478 } else if imp.color == grey {
479 importErr = fmt.Errorf("import cycle: %s", stack)
480 }
481 if importErr != nil {
482 if lpkg.importErrors == nil {
483 lpkg.importErrors = make(map[string]error)
484 }
485 lpkg.importErrors[importPath] = importErr
486 continue
487 }
488
489 if visit(imp) {
490 lpkg.needsrc = true
491 }
492 lpkg.Imports[importPath] = imp.Package
493 }
494
495 stack = stack[:len(stack)-1] // pop
496 lpkg.color = black
497
498 return lpkg.needsrc
499 }
500
501 if ld.Mode < LoadImports {
502 //we do this to drop the stub import packages that we are not even going to try to resolve
503 for _, lpkg := range initial {
504 lpkg.Imports = nil
505 }
506 } else {
507 // For each initial package, create its import DAG.
508 for _, lpkg := range initial {
509 visit(lpkg)
510 }
511 }
512 // Load type data if needed, starting at
513 // the initial packages (roots of the import DAG).
514 if ld.Mode >= LoadTypes {
515 var wg sync.WaitGroup
516 for _, lpkg := range initial {
517 wg.Add(1)
518 go func(lpkg *loaderPackage) {
519 ld.loadRecursive(lpkg)
520 wg.Done()
521 }(lpkg)
522 }
523 wg.Wait()
524 }
525
526 result := make([]*Package, len(initial))
527 for i, lpkg := range initial {
528 result[i] = lpkg.Package
529 }
530 return result, nil
531}
532
533// loadRecursive loads the specified package and its dependencies,
534// recursively, in parallel, in topological order.
535// It is atomic and idempotent.
536// Precondition: ld.Mode >= LoadTypes.
537func (ld *loader) loadRecursive(lpkg *loaderPackage) {
538 lpkg.loadOnce.Do(func() {
539 // Load the direct dependencies, in parallel.
540 var wg sync.WaitGroup
541 for _, ipkg := range lpkg.Imports {
542 imp := ld.pkgs[ipkg.ID]
543 wg.Add(1)
544 go func(imp *loaderPackage) {
545 ld.loadRecursive(imp)
546 wg.Done()
547 }(imp)
548 }
549 wg.Wait()
550
551 ld.loadPackage(lpkg)
552 })
553}
554
555// loadPackage loads the specified package.
556// It must be called only once per Package,
557// after immediate dependencies are loaded.
558// Precondition: ld.Mode >= LoadTypes.
559func (ld *loader) loadPackage(lpkg *loaderPackage) {
560 if lpkg.PkgPath == "unsafe" {
561 // Fill in the blanks to avoid surprises.
562 lpkg.Types = types.Unsafe
563 lpkg.Fset = ld.Fset
564 lpkg.Syntax = []*ast.File{}
565 lpkg.TypesInfo = new(types.Info)
566 return
567 }
568
569 // Call NewPackage directly with explicit name.
570 // This avoids skew between golist and go/types when the files'
571 // package declarations are inconsistent.
572 lpkg.Types = types.NewPackage(lpkg.PkgPath, lpkg.Name)
573
574 if !lpkg.needsrc {
575 ld.loadFromExportData(lpkg)
576 return // not a source package, don't get syntax trees
577 }
578
579 hardErrors := false
580 appendError := func(err error) {
581 if terr, ok := err.(types.Error); ok && terr.Soft {
582 // Don't mark the package as bad.
583 } else {
584 hardErrors = true
585 }
586 ld.Error(err)
587 lpkg.Errors = append(lpkg.Errors, err)
588 }
589
590 files, errs := ld.parseFiles(lpkg.CompiledGoFiles)
591 for _, err := range errs {
592 appendError(err)
593 }
594
595 lpkg.Fset = ld.Fset
596 lpkg.Syntax = files
597
598 lpkg.TypesInfo = &types.Info{
599 Types: make(map[ast.Expr]types.TypeAndValue),
600 Defs: make(map[*ast.Ident]types.Object),
601 Uses: make(map[*ast.Ident]types.Object),
602 Implicits: make(map[ast.Node]types.Object),
603 Scopes: make(map[ast.Node]*types.Scope),
604 Selections: make(map[*ast.SelectorExpr]*types.Selection),
605 }
606
607 // Copy the prototype types.Config as it must vary across Packages.
608 tc := ld.TypeChecker // copy
609 tc.Importer = importerFunc(func(path string) (*types.Package, error) {
610 if path == "unsafe" {
611 return types.Unsafe, nil
612 }
613
614 // The imports map is keyed by import path.
615 ipkg := lpkg.Imports[path]
616 if ipkg == nil {
617 if err := lpkg.importErrors[path]; err != nil {
618 return nil, err
619 }
620 // There was skew between the metadata and the
621 // import declarations, likely due to an edit
622 // race, or because the ParseFile feature was
623 // used to supply alternative file contents.
624 return nil, fmt.Errorf("no metadata for %s", path)
625 }
626
627 if ipkg.Types != nil && ipkg.Types.Complete() {
628 return ipkg.Types, nil
629 }
630 log.Fatalf("internal error: nil Pkg importing %q from %q", path, lpkg)
631 panic("unreachable")
632 })
633 tc.Error = appendError
634
635 // type-check
636 types.NewChecker(&tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
637
638 lpkg.importErrors = nil // no longer needed
639
640 // If !Cgo, the type-checker uses FakeImportC mode, so
641 // it doesn't invoke the importer for import "C",
642 // nor report an error for the import,
643 // or for any undefined C.f reference.
644 // We must detect this explicitly and correctly
645 // mark the package as IllTyped (by reporting an error).
646 // TODO(adonovan): if these errors are annoying,
647 // we could just set IllTyped quietly.
648 if tc.FakeImportC {
649 outer:
650 for _, f := range lpkg.Syntax {
651 for _, imp := range f.Imports {
652 if imp.Path.Value == `"C"` {
653 appendError(fmt.Errorf(`%s: import "C" ignored`,
654 lpkg.Fset.Position(imp.Pos())))
655 break outer
656 }
657 }
658 }
659 }
660
661 // Record accumulated errors.
662 for _, imp := range lpkg.Imports {
663 if imp.IllTyped {
664 hardErrors = true
665 break
666 }
667 }
668
669 lpkg.IllTyped = hardErrors
670}
671
672// An importFunc is an implementation of the single-method
673// types.Importer interface based on a function value.
674type importerFunc func(path string) (*types.Package, error)
675
676func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
677
678// We use a counting semaphore to limit
679// the number of parallel I/O calls per process.
680var ioLimit = make(chan bool, 20)
681
682// parseFiles reads and parses the Go source files and returns the ASTs
683// of the ones that could be at least partially parsed, along with a
684// list of I/O and parse errors encountered.
685//
686// Because files are scanned in parallel, the token.Pos
687// positions of the resulting ast.Files are not ordered.
688//
689func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) {
690 var wg sync.WaitGroup
691 n := len(filenames)
692 parsed := make([]*ast.File, n)
693 errors := make([]error, n)
694 for i, file := range filenames {
695 wg.Add(1)
696 go func(i int, filename string) {
697 ioLimit <- true // wait
698 // ParseFile may return both an AST and an error.
699 parsed[i], errors[i] = ld.ParseFile(ld.Fset, filename)
700 <-ioLimit // signal
701 wg.Done()
702 }(i, file)
703 }
704 wg.Wait()
705
706 // Eliminate nils, preserving order.
707 var o int
708 for _, f := range parsed {
709 if f != nil {
710 parsed[o] = f
711 o++
712 }
713 }
714 parsed = parsed[:o]
715
716 o = 0
717 for _, err := range errors {
718 if err != nil {
719 errors[o] = err
720 o++
721 }
722 }
723 errors = errors[:o]
724
725 return parsed, errors
726}
727
728// loadFromExportData returns type information for the specified
729// package, loading it from an export data file on the first request.
730func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error) {
731 if lpkg.PkgPath == "" {
732 log.Fatalf("internal error: Package %s has no PkgPath", lpkg)
733 }
734
735 // Because gcexportdata.Read has the potential to create or
736 // modify the types.Package for each node in the transitive
737 // closure of dependencies of lpkg, all exportdata operations
738 // must be sequential. (Finer-grained locking would require
739 // changes to the gcexportdata API.)
740 //
741 // The exportMu lock guards the Package.Pkg field and the
742 // types.Package it points to, for each Package in the graph.
743 //
744 // Not all accesses to Package.Pkg need to be protected by exportMu:
745 // graph ordering ensures that direct dependencies of source
746 // packages are fully loaded before the importer reads their Pkg field.
747 ld.exportMu.Lock()
748 defer ld.exportMu.Unlock()
749
750 if tpkg := lpkg.Types; tpkg != nil && tpkg.Complete() {
751 return tpkg, nil // cache hit
752 }
753
754 lpkg.IllTyped = true // fail safe
755
756 if lpkg.ExportFile == "" {
757 // Errors while building export data will have been printed to stderr.
758 return nil, fmt.Errorf("no export data file")
759 }
760 f, err := os.Open(lpkg.ExportFile)
761 if err != nil {
762 return nil, err
763 }
764 defer f.Close()
765
766 // Read gc export data.
767 //
768 // We don't currently support gccgo export data because all
769 // underlying workspaces use the gc toolchain. (Even build
770 // systems that support gccgo don't use it for workspace
771 // queries.)
772 r, err := gcexportdata.NewReader(f)
773 if err != nil {
774 return nil, fmt.Errorf("reading %s: %v", lpkg.ExportFile, err)
775 }
776
777 // Build the view.
778 //
779 // The gcexportdata machinery has no concept of package ID.
780 // It identifies packages by their PkgPath, which although not
781 // globally unique is unique within the scope of one invocation
782 // of the linker, type-checker, or gcexportdata.
783 //
784 // So, we must build a PkgPath-keyed view of the global
785 // (conceptually ID-keyed) cache of packages and pass it to
786 // gcexportdata. The view must contain every existing
787 // package that might possibly be mentioned by the
788 // current package---its transitive closure.
789 //
790 // TODO(adonovan): it would be more simpler and more efficient
791 // if the export data machinery invoked a callback to
792 // get-or-create a package instead of a map.
793 //
794 view := make(map[string]*types.Package) // view seen by gcexportdata
795 seen := make(map[*loaderPackage]bool) // all visited packages
796 var visit func(pkgs map[string]*Package)
797 visit = func(pkgs map[string]*Package) {
798 for _, p := range pkgs {
799 lpkg := ld.pkgs[p.ID]
800 if !seen[lpkg] {
801 seen[lpkg] = true
802 view[lpkg.PkgPath] = lpkg.Types
803 visit(lpkg.Imports)
804 }
805 }
806 }
807 visit(lpkg.Imports)
808
809 // Parse the export data.
810 // (May create/modify packages in view.)
811 tpkg, err := gcexportdata.Read(r, ld.Fset, view, lpkg.PkgPath)
812 if err != nil {
813 return nil, fmt.Errorf("reading %s: %v", lpkg.ExportFile, err)
814 }
815
816 lpkg.Types = tpkg
817 lpkg.IllTyped = false
818
819 return tpkg, nil
820}
821
822func usesExportData(cfg *Config) bool {
823 return LoadTypes <= cfg.Mode && cfg.Mode < LoadAllSyntax
824}