doc.go

  1// Copyright 2015 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
  5// Package loader loads a complete Go program from source code, parsing
  6// and type-checking the initial packages plus their transitive closure
  7// of dependencies.  The ASTs and the derived facts are retained for
  8// later use.
  9//
 10// THIS INTERFACE IS EXPERIMENTAL AND IS LIKELY TO CHANGE.
 11//
 12// The package defines two primary types: Config, which specifies a
 13// set of initial packages to load and various other options; and
 14// Program, which is the result of successfully loading the packages
 15// specified by a configuration.
 16//
 17// The configuration can be set directly, but *Config provides various
 18// convenience methods to simplify the common cases, each of which can
 19// be called any number of times.  Finally, these are followed by a
 20// call to Load() to actually load and type-check the program.
 21//
 22//      var conf loader.Config
 23//
 24//      // Use the command-line arguments to specify
 25//      // a set of initial packages to load from source.
 26//      // See FromArgsUsage for help.
 27//      rest, err := conf.FromArgs(os.Args[1:], wantTests)
 28//
 29//      // Parse the specified files and create an ad hoc package with path "foo".
 30//      // All files must have the same 'package' declaration.
 31//      conf.CreateFromFilenames("foo", "foo.go", "bar.go")
 32//
 33//      // Create an ad hoc package with path "foo" from
 34//      // the specified already-parsed files.
 35//      // All ASTs must have the same 'package' declaration.
 36//      conf.CreateFromFiles("foo", parsedFiles)
 37//
 38//      // Add "runtime" to the set of packages to be loaded.
 39//      conf.Import("runtime")
 40//
 41//      // Adds "fmt" and "fmt_test" to the set of packages
 42//      // to be loaded.  "fmt" will include *_test.go files.
 43//      conf.ImportWithTests("fmt")
 44//
 45//      // Finally, load all the packages specified by the configuration.
 46//      prog, err := conf.Load()
 47//
 48// See examples_test.go for examples of API usage.
 49//
 50//
 51// CONCEPTS AND TERMINOLOGY
 52//
 53// The WORKSPACE is the set of packages accessible to the loader.  The
 54// workspace is defined by Config.Build, a *build.Context.  The
 55// default context treats subdirectories of $GOROOT and $GOPATH as
 56// packages, but this behavior may be overridden.
 57//
 58// An AD HOC package is one specified as a set of source files on the
 59// command line.  In the simplest case, it may consist of a single file
 60// such as $GOROOT/src/net/http/triv.go.
 61//
 62// EXTERNAL TEST packages are those comprised of a set of *_test.go
 63// files all with the same 'package foo_test' declaration, all in the
 64// same directory.  (go/build.Package calls these files XTestFiles.)
 65//
 66// An IMPORTABLE package is one that can be referred to by some import
 67// spec.  Every importable package is uniquely identified by its
 68// PACKAGE PATH or just PATH, a string such as "fmt", "encoding/json",
 69// or "cmd/vendor/golang.org/x/arch/x86/x86asm".  A package path
 70// typically denotes a subdirectory of the workspace.
 71//
 72// An import declaration uses an IMPORT PATH to refer to a package.
 73// Most import declarations use the package path as the import path.
 74//
 75// Due to VENDORING (https://golang.org/s/go15vendor), the
 76// interpretation of an import path may depend on the directory in which
 77// it appears.  To resolve an import path to a package path, go/build
 78// must search the enclosing directories for a subdirectory named
 79// "vendor".
 80//
 81// ad hoc packages and external test packages are NON-IMPORTABLE.  The
 82// path of an ad hoc package is inferred from the package
 83// declarations of its files and is therefore not a unique package key.
 84// For example, Config.CreatePkgs may specify two initial ad hoc
 85// packages, both with path "main".
 86//
 87// An AUGMENTED package is an importable package P plus all the
 88// *_test.go files with same 'package foo' declaration as P.
 89// (go/build.Package calls these files TestFiles.)
 90//
 91// The INITIAL packages are those specified in the configuration.  A
 92// DEPENDENCY is a package loaded to satisfy an import in an initial
 93// package or another dependency.
 94//
 95package loader
 96
 97// IMPLEMENTATION NOTES
 98//
 99// 'go test', in-package test files, and import cycles
100// ---------------------------------------------------
101//
102// An external test package may depend upon members of the augmented
103// package that are not in the unaugmented package, such as functions
104// that expose internals.  (See bufio/export_test.go for an example.)
105// So, the loader must ensure that for each external test package
106// it loads, it also augments the corresponding non-test package.
107//
108// The import graph over n unaugmented packages must be acyclic; the
109// import graph over n-1 unaugmented packages plus one augmented
110// package must also be acyclic.  ('go test' relies on this.)  But the
111// import graph over n augmented packages may contain cycles.
112//
113// First, all the (unaugmented) non-test packages and their
114// dependencies are imported in the usual way; the loader reports an
115// error if it detects an import cycle.
116//
117// Then, each package P for which testing is desired is augmented by
118// the list P' of its in-package test files, by calling
119// (*types.Checker).Files.  This arrangement ensures that P' may
120// reference definitions within P, but P may not reference definitions
121// within P'.  Furthermore, P' may import any other package, including
122// ones that depend upon P, without an import cycle error.
123//
124// Consider two packages A and B, both of which have lists of
125// in-package test files we'll call A' and B', and which have the
126// following import graph edges:
127//    B  imports A
128//    B' imports A
129//    A' imports B
130// This last edge would be expected to create an error were it not
131// for the special type-checking discipline above.
132// Cycles of size greater than two are possible.  For example:
133//   compress/bzip2/bzip2_test.go (package bzip2)  imports "io/ioutil"
134//   io/ioutil/tempfile_test.go   (package ioutil) imports "regexp"
135//   regexp/exec_test.go          (package regexp) imports "compress/bzip2"
136//
137//
138// Concurrency
139// -----------
140//
141// Let us define the import dependency graph as follows.  Each node is a
142// list of files passed to (Checker).Files at once.  Many of these lists
143// are the production code of an importable Go package, so those nodes
144// are labelled by the package's path.  The remaining nodes are
145// ad hoc packages and lists of in-package *_test.go files that augment
146// an importable package; those nodes have no label.
147//
148// The edges of the graph represent import statements appearing within a
149// file.  An edge connects a node (a list of files) to the node it
150// imports, which is importable and thus always labelled.
151//
152// Loading is controlled by this dependency graph.
153//
154// To reduce I/O latency, we start loading a package's dependencies
155// asynchronously as soon as we've parsed its files and enumerated its
156// imports (scanImports).  This performs a preorder traversal of the
157// import dependency graph.
158//
159// To exploit hardware parallelism, we type-check unrelated packages in
160// parallel, where "unrelated" means not ordered by the partial order of
161// the import dependency graph.
162//
163// We use a concurrency-safe non-blocking cache (importer.imported) to
164// record the results of type-checking, whether success or failure.  An
165// entry is created in this cache by startLoad the first time the
166// package is imported.  The first goroutine to request an entry becomes
167// responsible for completing the task and broadcasting completion to
168// subsequent requestors, which block until then.
169//
170// Type checking occurs in (parallel) postorder: we cannot type-check a
171// set of files until we have loaded and type-checked all of their
172// immediate dependencies (and thus all of their transitive
173// dependencies). If the input were guaranteed free of import cycles,
174// this would be trivial: we could simply wait for completion of the
175// dependencies and then invoke the typechecker.
176//
177// But as we saw in the 'go test' section above, some cycles in the
178// import graph over packages are actually legal, so long as the
179// cycle-forming edge originates in the in-package test files that
180// augment the package.  This explains why the nodes of the import
181// dependency graph are not packages, but lists of files: the unlabelled
182// nodes avoid the cycles.  Consider packages A and B where B imports A
183// and A's in-package tests AT import B.  The naively constructed import
184// graph over packages would contain a cycle (A+AT) --> B --> (A+AT) but
185// the graph over lists of files is AT --> B --> A, where AT is an
186// unlabelled node.
187//
188// Awaiting completion of the dependencies in a cyclic graph would
189// deadlock, so we must materialize the import dependency graph (as
190// importer.graph) and check whether each import edge forms a cycle.  If
191// x imports y, and the graph already contains a path from y to x, then
192// there is an import cycle, in which case the processing of x must not
193// wait for the completion of processing of y.
194//
195// When the type-checker makes a callback (doImport) to the loader for a
196// given import edge, there are two possible cases.  In the normal case,
197// the dependency has already been completely type-checked; doImport
198// does a cache lookup and returns it.  In the cyclic case, the entry in
199// the cache is still necessarily incomplete, indicating a cycle.  We
200// perform the cycle check again to obtain the error message, and return
201// the error.
202//
203// The result of using concurrency is about a 2.5x speedup for stdlib_test.
204
205// TODO(adonovan): overhaul the package documentation.