Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 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 | |
Alan Donovan | 9c57c19 | 2015-04-14 16:56:02 -0400 | [diff] [blame] | 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. |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 9 | // |
Dmitri Shuralyov | 7f22187 | 2019-06-21 21:59:57 -0400 | [diff] [blame] | 10 | // Deprecated: This is an older API and does not have support |
| 11 | // for modules. Use golang.org/x/tools/go/packages instead. |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 12 | // |
| 13 | // The package defines two primary types: Config, which specifies a |
| 14 | // set of initial packages to load and various other options; and |
| 15 | // Program, which is the result of successfully loading the packages |
| 16 | // specified by a configuration. |
| 17 | // |
| 18 | // The configuration can be set directly, but *Config provides various |
| 19 | // convenience methods to simplify the common cases, each of which can |
| 20 | // be called any number of times. Finally, these are followed by a |
| 21 | // call to Load() to actually load and type-check the program. |
| 22 | // |
Russ Cox | d5f48fc | 2022-04-11 23:03:04 -0400 | [diff] [blame] | 23 | // var conf loader.Config |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 24 | // |
Russ Cox | d5f48fc | 2022-04-11 23:03:04 -0400 | [diff] [blame] | 25 | // // Use the command-line arguments to specify |
| 26 | // // a set of initial packages to load from source. |
| 27 | // // See FromArgsUsage for help. |
| 28 | // rest, err := conf.FromArgs(os.Args[1:], wantTests) |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 29 | // |
Russ Cox | d5f48fc | 2022-04-11 23:03:04 -0400 | [diff] [blame] | 30 | // // Parse the specified files and create an ad hoc package with path "foo". |
| 31 | // // All files must have the same 'package' declaration. |
| 32 | // conf.CreateFromFilenames("foo", "foo.go", "bar.go") |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 33 | // |
Russ Cox | d5f48fc | 2022-04-11 23:03:04 -0400 | [diff] [blame] | 34 | // // Create an ad hoc package with path "foo" from |
| 35 | // // the specified already-parsed files. |
| 36 | // // All ASTs must have the same 'package' declaration. |
| 37 | // conf.CreateFromFiles("foo", parsedFiles) |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 38 | // |
Russ Cox | d5f48fc | 2022-04-11 23:03:04 -0400 | [diff] [blame] | 39 | // // Add "runtime" to the set of packages to be loaded. |
| 40 | // conf.Import("runtime") |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 41 | // |
Russ Cox | d5f48fc | 2022-04-11 23:03:04 -0400 | [diff] [blame] | 42 | // // Adds "fmt" and "fmt_test" to the set of packages |
| 43 | // // to be loaded. "fmt" will include *_test.go files. |
| 44 | // conf.ImportWithTests("fmt") |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 45 | // |
Russ Cox | d5f48fc | 2022-04-11 23:03:04 -0400 | [diff] [blame] | 46 | // // Finally, load all the packages specified by the configuration. |
| 47 | // prog, err := conf.Load() |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 48 | // |
| 49 | // See examples_test.go for examples of API usage. |
| 50 | // |
Russ Cox | d5f48fc | 2022-04-11 23:03:04 -0400 | [diff] [blame] | 51 | // # CONCEPTS AND TERMINOLOGY |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 52 | // |
Alan Donovan | d6e83e5 | 2015-12-18 21:02:28 -0500 | [diff] [blame] | 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 | // |
Alan Donovan | 9c57c19 | 2015-04-14 16:56:02 -0400 | [diff] [blame] | 58 | // An AD HOC package is one specified as a set of source files on the |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 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 |
Alan Donovan | d6e83e5 | 2015-12-18 21:02:28 -0500 | [diff] [blame] | 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". |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 80 | // |
Alan Donovan | 9c57c19 | 2015-04-14 16:56:02 -0400 | [diff] [blame] | 81 | // ad hoc packages and external test packages are NON-IMPORTABLE. The |
Alan Donovan | d6e83e5 | 2015-12-18 21:02:28 -0500 | [diff] [blame] | 82 | // path of an ad hoc package is inferred from the package |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 83 | // declarations of its files and is therefore not a unique package key. |
Alan Donovan | 9c57c19 | 2015-04-14 16:56:02 -0400 | [diff] [blame] | 84 | // For example, Config.CreatePkgs may specify two initial ad hoc |
Alan Donovan | d6e83e5 | 2015-12-18 21:02:28 -0500 | [diff] [blame] | 85 | // packages, both with path "main". |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 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. |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 94 | package loader |
| 95 | |
| 96 | // IMPLEMENTATION NOTES |
| 97 | // |
| 98 | // 'go test', in-package test files, and import cycles |
| 99 | // --------------------------------------------------- |
| 100 | // |
| 101 | // An external test package may depend upon members of the augmented |
| 102 | // package that are not in the unaugmented package, such as functions |
| 103 | // that expose internals. (See bufio/export_test.go for an example.) |
| 104 | // So, the loader must ensure that for each external test package |
| 105 | // it loads, it also augments the corresponding non-test package. |
| 106 | // |
| 107 | // The import graph over n unaugmented packages must be acyclic; the |
| 108 | // import graph over n-1 unaugmented packages plus one augmented |
| 109 | // package must also be acyclic. ('go test' relies on this.) But the |
| 110 | // import graph over n augmented packages may contain cycles. |
| 111 | // |
| 112 | // First, all the (unaugmented) non-test packages and their |
| 113 | // dependencies are imported in the usual way; the loader reports an |
| 114 | // error if it detects an import cycle. |
| 115 | // |
| 116 | // Then, each package P for which testing is desired is augmented by |
| 117 | // the list P' of its in-package test files, by calling |
| 118 | // (*types.Checker).Files. This arrangement ensures that P' may |
| 119 | // reference definitions within P, but P may not reference definitions |
| 120 | // within P'. Furthermore, P' may import any other package, including |
| 121 | // ones that depend upon P, without an import cycle error. |
| 122 | // |
| 123 | // Consider two packages A and B, both of which have lists of |
| 124 | // in-package test files we'll call A' and B', and which have the |
| 125 | // following import graph edges: |
| 126 | // B imports A |
| 127 | // B' imports A |
| 128 | // A' imports B |
| 129 | // This last edge would be expected to create an error were it not |
| 130 | // for the special type-checking discipline above. |
| 131 | // Cycles of size greater than two are possible. For example: |
| 132 | // compress/bzip2/bzip2_test.go (package bzip2) imports "io/ioutil" |
| 133 | // io/ioutil/tempfile_test.go (package ioutil) imports "regexp" |
| 134 | // regexp/exec_test.go (package regexp) imports "compress/bzip2" |
| 135 | // |
| 136 | // |
| 137 | // Concurrency |
| 138 | // ----------- |
| 139 | // |
| 140 | // Let us define the import dependency graph as follows. Each node is a |
| 141 | // list of files passed to (Checker).Files at once. Many of these lists |
| 142 | // are the production code of an importable Go package, so those nodes |
Alan Donovan | d6e83e5 | 2015-12-18 21:02:28 -0500 | [diff] [blame] | 143 | // are labelled by the package's path. The remaining nodes are |
Alan Donovan | 9c57c19 | 2015-04-14 16:56:02 -0400 | [diff] [blame] | 144 | // ad hoc packages and lists of in-package *_test.go files that augment |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 145 | // an importable package; those nodes have no label. |
| 146 | // |
| 147 | // The edges of the graph represent import statements appearing within a |
| 148 | // file. An edge connects a node (a list of files) to the node it |
| 149 | // imports, which is importable and thus always labelled. |
| 150 | // |
| 151 | // Loading is controlled by this dependency graph. |
| 152 | // |
| 153 | // To reduce I/O latency, we start loading a package's dependencies |
| 154 | // asynchronously as soon as we've parsed its files and enumerated its |
| 155 | // imports (scanImports). This performs a preorder traversal of the |
| 156 | // import dependency graph. |
| 157 | // |
| 158 | // To exploit hardware parallelism, we type-check unrelated packages in |
| 159 | // parallel, where "unrelated" means not ordered by the partial order of |
| 160 | // the import dependency graph. |
| 161 | // |
Alan Donovan | 9aae8f0 | 2016-02-22 15:21:13 -0500 | [diff] [blame] | 162 | // We use a concurrency-safe non-blocking cache (importer.imported) to |
Alan Donovan | 1b6275a | 2015-04-16 15:42:28 -0400 | [diff] [blame] | 163 | // record the results of type-checking, whether success or failure. An |
| 164 | // entry is created in this cache by startLoad the first time the |
| 165 | // package is imported. The first goroutine to request an entry becomes |
| 166 | // responsible for completing the task and broadcasting completion to |
| 167 | // subsequent requestors, which block until then. |
| 168 | // |
| 169 | // Type checking occurs in (parallel) postorder: we cannot type-check a |
| 170 | // set of files until we have loaded and type-checked all of their |
| 171 | // immediate dependencies (and thus all of their transitive |
| 172 | // dependencies). If the input were guaranteed free of import cycles, |
| 173 | // this would be trivial: we could simply wait for completion of the |
| 174 | // dependencies and then invoke the typechecker. |
| 175 | // |
| 176 | // But as we saw in the 'go test' section above, some cycles in the |
| 177 | // import graph over packages are actually legal, so long as the |
| 178 | // cycle-forming edge originates in the in-package test files that |
| 179 | // augment the package. This explains why the nodes of the import |
| 180 | // dependency graph are not packages, but lists of files: the unlabelled |
| 181 | // nodes avoid the cycles. Consider packages A and B where B imports A |
| 182 | // and A's in-package tests AT import B. The naively constructed import |
| 183 | // graph over packages would contain a cycle (A+AT) --> B --> (A+AT) but |
| 184 | // the graph over lists of files is AT --> B --> A, where AT is an |
| 185 | // unlabelled node. |
| 186 | // |
| 187 | // Awaiting completion of the dependencies in a cyclic graph would |
| 188 | // deadlock, so we must materialize the import dependency graph (as |
| 189 | // importer.graph) and check whether each import edge forms a cycle. If |
| 190 | // x imports y, and the graph already contains a path from y to x, then |
| 191 | // there is an import cycle, in which case the processing of x must not |
| 192 | // wait for the completion of processing of y. |
| 193 | // |
| 194 | // When the type-checker makes a callback (doImport) to the loader for a |
| 195 | // given import edge, there are two possible cases. In the normal case, |
| 196 | // the dependency has already been completely type-checked; doImport |
| 197 | // does a cache lookup and returns it. In the cyclic case, the entry in |
| 198 | // the cache is still necessarily incomplete, indicating a cycle. We |
| 199 | // perform the cycle check again to obtain the error message, and return |
| 200 | // the error. |
| 201 | // |
| 202 | // The result of using concurrency is about a 2.5x speedup for stdlib_test. |