Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 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 | |
| 5 | package modload |
| 6 | |
| 7 | import ( |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 8 | "context" |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 9 | "errors" |
| 10 | "fmt" |
| 11 | "go/build" |
Colin Cross | d9c6b80 | 2019-03-19 21:10:31 -0700 | [diff] [blame] | 12 | "internal/goroot" |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 13 | "io/fs" |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 14 | "os" |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 15 | pathpkg "path" |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 16 | "path/filepath" |
Colin Cross | d9c6b80 | 2019-03-19 21:10:31 -0700 | [diff] [blame] | 17 | "sort" |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 18 | "strings" |
| 19 | |
| 20 | "cmd/go/internal/cfg" |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 21 | "cmd/go/internal/fsys" |
Colin Cross | d9c6b80 | 2019-03-19 21:10:31 -0700 | [diff] [blame] | 22 | "cmd/go/internal/modfetch" |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 23 | "cmd/go/internal/par" |
| 24 | "cmd/go/internal/search" |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 25 | |
| 26 | "golang.org/x/mod/module" |
| 27 | "golang.org/x/mod/semver" |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 28 | ) |
| 29 | |
| 30 | type ImportMissingError struct { |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 31 | Path string |
| 32 | Module module.Version |
| 33 | QueryErr error |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 34 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 35 | ImportingMainModule module.Version |
| 36 | |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 37 | // isStd indicates whether we would expect to find the package in the standard |
| 38 | // library. This is normally true for all dotless import paths, but replace |
| 39 | // directives can cause us to treat the replaced paths as also being in |
| 40 | // modules. |
| 41 | isStd bool |
| 42 | |
| 43 | // replaced the highest replaced version of the module where the replacement |
| 44 | // contains the package. replaced is only set if the replacement is unused. |
| 45 | replaced module.Version |
| 46 | |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 47 | // newMissingVersion is set to a newer version of Module if one is present |
| 48 | // in the build list. When set, we can't automatically upgrade. |
| 49 | newMissingVersion string |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 50 | } |
| 51 | |
| 52 | func (e *ImportMissingError) Error() string { |
| 53 | if e.Module.Path == "" { |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 54 | if e.isStd { |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 55 | return fmt.Sprintf("package %s is not in GOROOT (%s)", e.Path, filepath.Join(cfg.GOROOT, "src", e.Path)) |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 56 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 57 | if e.QueryErr != nil && e.QueryErr != ErrNoModRoot { |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 58 | return fmt.Sprintf("cannot find module providing package %s: %v", e.Path, e.QueryErr) |
| 59 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 60 | if cfg.BuildMod == "mod" || (cfg.BuildMod == "readonly" && allowMissingModuleImports) { |
| 61 | return "cannot find module providing package " + e.Path |
| 62 | } |
| 63 | |
| 64 | if e.replaced.Path != "" { |
| 65 | suggestArg := e.replaced.Path |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 66 | if !module.IsZeroPseudoVersion(e.replaced.Version) { |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 67 | suggestArg = e.replaced.String() |
| 68 | } |
| 69 | return fmt.Sprintf("module %s provides package %s and is replaced but not required; to add it:\n\tgo get %s", e.replaced.Path, e.Path, suggestArg) |
| 70 | } |
| 71 | |
| 72 | message := fmt.Sprintf("no required module provides package %s", e.Path) |
| 73 | if e.QueryErr != nil { |
| 74 | return fmt.Sprintf("%s: %v", message, e.QueryErr) |
| 75 | } |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 76 | if e.ImportingMainModule.Path != "" && e.ImportingMainModule != MainModules.ModContainingCWD() { |
| 77 | return fmt.Sprintf("%s; to add it:\n\tcd %s\n\tgo get %s", message, MainModules.ModRoot(e.ImportingMainModule), e.Path) |
| 78 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 79 | return fmt.Sprintf("%s; to add it:\n\tgo get %s", message, e.Path) |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 80 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 81 | |
| 82 | if e.newMissingVersion != "" { |
| 83 | return fmt.Sprintf("package %s provided by %s at latest version %s but not at required version %s", e.Path, e.Module.Path, e.Module.Version, e.newMissingVersion) |
| 84 | } |
| 85 | |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 86 | return fmt.Sprintf("missing module for import: %s@%s provides %s", e.Module.Path, e.Module.Version, e.Path) |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 87 | } |
| 88 | |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 89 | func (e *ImportMissingError) Unwrap() error { |
| 90 | return e.QueryErr |
| 91 | } |
| 92 | |
| 93 | func (e *ImportMissingError) ImportPath() string { |
| 94 | return e.Path |
| 95 | } |
| 96 | |
| 97 | // An AmbiguousImportError indicates an import of a package found in multiple |
| 98 | // modules in the build list, or found in both the main module and its vendor |
| 99 | // directory. |
| 100 | type AmbiguousImportError struct { |
| 101 | importPath string |
| 102 | Dirs []string |
| 103 | Modules []module.Version // Either empty or 1:1 with Dirs. |
| 104 | } |
| 105 | |
| 106 | func (e *AmbiguousImportError) ImportPath() string { |
| 107 | return e.importPath |
| 108 | } |
| 109 | |
| 110 | func (e *AmbiguousImportError) Error() string { |
| 111 | locType := "modules" |
| 112 | if len(e.Modules) == 0 { |
| 113 | locType = "directories" |
| 114 | } |
| 115 | |
| 116 | var buf strings.Builder |
| 117 | fmt.Fprintf(&buf, "ambiguous import: found package %s in multiple %s:", e.importPath, locType) |
| 118 | |
| 119 | for i, dir := range e.Dirs { |
| 120 | buf.WriteString("\n\t") |
| 121 | if i < len(e.Modules) { |
| 122 | m := e.Modules[i] |
| 123 | buf.WriteString(m.Path) |
| 124 | if m.Version != "" { |
| 125 | fmt.Fprintf(&buf, " %s", m.Version) |
| 126 | } |
| 127 | fmt.Fprintf(&buf, " (%s)", dir) |
| 128 | } else { |
| 129 | buf.WriteString(dir) |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | return buf.String() |
| 134 | } |
| 135 | |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 136 | // A DirectImportFromImplicitDependencyError indicates a package directly |
| 137 | // imported by a package or test in the main module that is satisfied by a |
| 138 | // dependency that is not explicit in the main module's go.mod file. |
| 139 | type DirectImportFromImplicitDependencyError struct { |
| 140 | ImporterPath string |
| 141 | ImportedPath string |
| 142 | Module module.Version |
| 143 | } |
| 144 | |
| 145 | func (e *DirectImportFromImplicitDependencyError) Error() string { |
| 146 | return fmt.Sprintf("package %s imports %s from implicitly required module; to add missing requirements, run:\n\tgo get %s@%s", e.ImporterPath, e.ImportedPath, e.Module.Path, e.Module.Version) |
| 147 | } |
| 148 | |
| 149 | func (e *DirectImportFromImplicitDependencyError) ImportPath() string { |
| 150 | return e.ImporterPath |
| 151 | } |
| 152 | |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 153 | // ImportMissingSumError is reported in readonly mode when we need to check |
| 154 | // if a module contains a package, but we don't have a sum for its .zip file. |
| 155 | // We might need sums for multiple modules to verify the package is unique. |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 156 | // |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 157 | // TODO(#43653): consolidate multiple errors of this type into a single error |
| 158 | // that suggests a 'go get' command for root packages that transtively import |
| 159 | // packages from modules with missing sums. load.CheckPackageErrors would be |
| 160 | // a good place to consolidate errors, but we'll need to attach the import |
| 161 | // stack here. |
| 162 | type ImportMissingSumError struct { |
| 163 | importPath string |
| 164 | found bool |
| 165 | mods []module.Version |
| 166 | importer, importerVersion string // optional, but used for additional context |
| 167 | importerIsTest bool |
| 168 | } |
| 169 | |
| 170 | func (e *ImportMissingSumError) Error() string { |
| 171 | var importParen string |
| 172 | if e.importer != "" { |
| 173 | importParen = fmt.Sprintf(" (imported by %s)", e.importer) |
| 174 | } |
| 175 | var message string |
| 176 | if e.found { |
| 177 | message = fmt.Sprintf("missing go.sum entry needed to verify package %s%s is provided by exactly one module", e.importPath, importParen) |
| 178 | } else { |
| 179 | message = fmt.Sprintf("missing go.sum entry for module providing package %s%s", e.importPath, importParen) |
| 180 | } |
| 181 | var hint string |
| 182 | if e.importer == "" { |
| 183 | // Importing package is unknown, or the missing package was named on the |
| 184 | // command line. Recommend 'go mod download' for the modules that could |
| 185 | // provide the package, since that shouldn't change go.mod. |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 186 | if len(e.mods) > 0 { |
| 187 | args := make([]string, len(e.mods)) |
| 188 | for i, mod := range e.mods { |
| 189 | args[i] = mod.Path |
| 190 | } |
| 191 | hint = fmt.Sprintf("; to add:\n\tgo mod download %s", strings.Join(args, " ")) |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 192 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 193 | } else { |
| 194 | // Importing package is known (common case). Recommend 'go get' on the |
| 195 | // current version of the importing package. |
| 196 | tFlag := "" |
| 197 | if e.importerIsTest { |
| 198 | tFlag = " -t" |
| 199 | } |
| 200 | version := "" |
| 201 | if e.importerVersion != "" { |
| 202 | version = "@" + e.importerVersion |
| 203 | } |
| 204 | hint = fmt.Sprintf("; to add:\n\tgo get%s %s%s", tFlag, e.importer, version) |
| 205 | } |
| 206 | return message + hint |
| 207 | } |
| 208 | |
| 209 | func (e *ImportMissingSumError) ImportPath() string { |
| 210 | return e.importPath |
| 211 | } |
| 212 | |
| 213 | type invalidImportError struct { |
| 214 | importPath string |
| 215 | err error |
| 216 | } |
| 217 | |
| 218 | func (e *invalidImportError) ImportPath() string { |
| 219 | return e.importPath |
| 220 | } |
| 221 | |
| 222 | func (e *invalidImportError) Error() string { |
| 223 | return e.err.Error() |
| 224 | } |
| 225 | |
| 226 | func (e *invalidImportError) Unwrap() error { |
| 227 | return e.err |
| 228 | } |
| 229 | |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 230 | // importFromModules finds the module and directory in the dependency graph of |
| 231 | // rs containing the package with the given import path. If mg is nil, |
| 232 | // importFromModules attempts to locate the module using only the main module |
| 233 | // and the roots of rs before it loads the full graph. |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 234 | // |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 235 | // The answer must be unique: importFromModules returns an error if multiple |
| 236 | // modules are observed to provide the same package. |
| 237 | // |
| 238 | // importFromModules can return a module with an empty m.Path, for packages in |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 239 | // the standard library. |
| 240 | // |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 241 | // importFromModules can return an empty directory string, for fake packages |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 242 | // like "C" and "unsafe". |
| 243 | // |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 244 | // If the package is not present in any module selected from the requirement |
| 245 | // graph, importFromModules returns an *ImportMissingError. |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 246 | // |
| 247 | // If the package is present in exactly one module, importFromModules will |
| 248 | // return the module, its root directory, and a list of other modules that |
| 249 | // lexically could have provided the package but did not. |
| 250 | func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, dir string, altMods []module.Version, err error) { |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 251 | if strings.Contains(path, "@") { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 252 | return module.Version{}, "", nil, fmt.Errorf("import path should not have @version") |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 253 | } |
| 254 | if build.IsLocalImport(path) { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 255 | return module.Version{}, "", nil, fmt.Errorf("relative import not supported") |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 256 | } |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 257 | if path == "C" { |
| 258 | // There's no directory for import "C". |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 259 | return module.Version{}, "", nil, nil |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 260 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 261 | // Before any further lookup, check that the path is valid. |
| 262 | if err := module.CheckImportPath(path); err != nil { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 263 | return module.Version{}, "", nil, &invalidImportError{importPath: path, err: err} |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 264 | } |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 265 | |
| 266 | // Is the package in the standard library? |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 267 | pathIsStd := search.IsStandardImportPath(path) |
| 268 | if pathIsStd && goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 269 | for _, mainModule := range MainModules.Versions() { |
| 270 | if MainModules.InGorootSrc(mainModule) { |
| 271 | if dir, ok, err := dirInModule(path, MainModules.PathPrefix(mainModule), MainModules.ModRoot(mainModule), true); err != nil { |
| 272 | return module.Version{}, dir, nil, err |
| 273 | } else if ok { |
| 274 | return mainModule, dir, nil, nil |
| 275 | } |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 276 | } |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 277 | } |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 278 | dir := filepath.Join(cfg.GOROOT, "src", path) |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 279 | return module.Version{}, dir, nil, nil |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 280 | } |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 281 | |
| 282 | // -mod=vendor is special. |
| 283 | // Everything must be in the main module or the main module's vendor directory. |
| 284 | if cfg.BuildMod == "vendor" { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 285 | mainModule := MainModules.mustGetSingleMainModule() |
| 286 | modRoot := MainModules.ModRoot(mainModule) |
| 287 | mainDir, mainOK, mainErr := dirInModule(path, MainModules.PathPrefix(mainModule), modRoot, true) |
| 288 | vendorDir, vendorOK, _ := dirInModule(path, "", filepath.Join(modRoot, "vendor"), false) |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 289 | if mainOK && vendorOK { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 290 | return module.Version{}, "", nil, &AmbiguousImportError{importPath: path, Dirs: []string{mainDir, vendorDir}} |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 291 | } |
| 292 | // Prefer to return main directory if there is one, |
| 293 | // Note that we're not checking that the package exists. |
| 294 | // We'll leave that for load. |
| 295 | if !vendorOK && mainDir != "" { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 296 | return mainModule, mainDir, nil, nil |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 297 | } |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 298 | if mainErr != nil { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 299 | return module.Version{}, "", nil, mainErr |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 300 | } |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 301 | readVendorList(mainModule) |
| 302 | return vendorPkgModule[path], vendorDir, nil, nil |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 303 | } |
| 304 | |
| 305 | // Check each module on the build list. |
| 306 | var dirs []string |
| 307 | var mods []module.Version |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 308 | |
| 309 | // Iterate over possible modules for the path, not all selected modules. |
| 310 | // Iterating over selected modules would make the overall loading time |
| 311 | // O(M × P) for M modules providing P imported packages, whereas iterating |
| 312 | // over path prefixes is only O(P × k) with maximum path depth k. For |
| 313 | // large projects both M and P may be very large (note that M ≤ P), but k |
| 314 | // will tend to remain smallish (if for no other reason than filesystem |
| 315 | // path limitations). |
| 316 | // |
| 317 | // We perform this iteration either one or two times. If mg is initially nil, |
| 318 | // then we first attempt to load the package using only the main module and |
| 319 | // its root requirements. If that does not identify the package, or if mg is |
| 320 | // already non-nil, then we attempt to load the package using the full |
| 321 | // requirements in mg. |
| 322 | for { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 323 | var sumErrMods, altMods []module.Version |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 324 | for prefix := path; prefix != "."; prefix = pathpkg.Dir(prefix) { |
| 325 | var ( |
| 326 | v string |
| 327 | ok bool |
| 328 | ) |
| 329 | if mg == nil { |
| 330 | v, ok = rs.rootSelected(prefix) |
| 331 | } else { |
| 332 | v, ok = mg.Selected(prefix), true |
| 333 | } |
| 334 | if !ok || v == "none" { |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 335 | continue |
| 336 | } |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 337 | m := module.Version{Path: prefix, Version: v} |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 338 | |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 339 | needSum := true |
| 340 | root, isLocal, err := fetch(ctx, m, needSum) |
| 341 | if err != nil { |
| 342 | if sumErr := (*sumMissingError)(nil); errors.As(err, &sumErr) { |
| 343 | // We are missing a sum needed to fetch a module in the build list. |
| 344 | // We can't verify that the package is unique, and we may not find |
| 345 | // the package at all. Keep checking other modules to decide which |
| 346 | // error to report. Multiple sums may be missing if we need to look in |
| 347 | // multiple nested modules to resolve the import; we'll report them all. |
| 348 | sumErrMods = append(sumErrMods, m) |
| 349 | continue |
| 350 | } |
| 351 | // Report fetch error. |
| 352 | // Note that we don't know for sure this module is necessary, |
| 353 | // but it certainly _could_ provide the package, and even if we |
| 354 | // continue the loop and find the package in some other module, |
| 355 | // we need to look at this module to make sure the import is |
| 356 | // not ambiguous. |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 357 | return module.Version{}, "", nil, err |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 358 | } |
| 359 | if dir, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 360 | return module.Version{}, "", nil, err |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 361 | } else if ok { |
| 362 | mods = append(mods, m) |
| 363 | dirs = append(dirs, dir) |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 364 | } else { |
| 365 | altMods = append(altMods, m) |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 366 | } |
| 367 | } |
| 368 | |
| 369 | if len(mods) > 1 { |
| 370 | // We produce the list of directories from longest to shortest candidate |
| 371 | // module path, but the AmbiguousImportError should report them from |
| 372 | // shortest to longest. Reverse them now. |
| 373 | for i := 0; i < len(mods)/2; i++ { |
| 374 | j := len(mods) - 1 - i |
| 375 | mods[i], mods[j] = mods[j], mods[i] |
| 376 | dirs[i], dirs[j] = dirs[j], dirs[i] |
| 377 | } |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 378 | return module.Version{}, "", nil, &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods} |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 379 | } |
| 380 | |
| 381 | if len(sumErrMods) > 0 { |
| 382 | for i := 0; i < len(sumErrMods)/2; i++ { |
| 383 | j := len(sumErrMods) - 1 - i |
| 384 | sumErrMods[i], sumErrMods[j] = sumErrMods[j], sumErrMods[i] |
| 385 | } |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 386 | return module.Version{}, "", nil, &ImportMissingSumError{ |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 387 | importPath: path, |
| 388 | mods: sumErrMods, |
| 389 | found: len(mods) > 0, |
| 390 | } |
| 391 | } |
| 392 | |
| 393 | if len(mods) == 1 { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 394 | return mods[0], dirs[0], altMods, nil |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 395 | } |
| 396 | |
| 397 | if mg != nil { |
| 398 | // We checked the full module graph and still didn't find the |
| 399 | // requested package. |
| 400 | var queryErr error |
| 401 | if !HasModRoot() { |
| 402 | queryErr = ErrNoModRoot |
| 403 | } |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 404 | return module.Version{}, "", nil, &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd} |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 405 | } |
| 406 | |
| 407 | // So far we've checked the root dependencies. |
| 408 | // Load the full module graph and try again. |
| 409 | mg, err = rs.Graph(ctx) |
| 410 | if err != nil { |
| 411 | // We might be missing one or more transitive (implicit) dependencies from |
| 412 | // the module graph, so we can't return an ImportMissingError here — one |
| 413 | // of the missing modules might actually contain the package in question, |
| 414 | // in which case we shouldn't go looking for it in some new dependency. |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 415 | return module.Version{}, "", nil, err |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 416 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 417 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 418 | } |
| 419 | |
| 420 | // queryImport attempts to locate a module that can be added to the current |
| 421 | // build list to provide the package with the given import path. |
| 422 | // |
| 423 | // Unlike QueryPattern, queryImport prefers to add a replaced version of a |
| 424 | // module *before* checking the proxies for a version to add. |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 425 | func queryImport(ctx context.Context, path string, rs *Requirements) (module.Version, error) { |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 426 | // To avoid spurious remote fetches, try the latest replacement for each |
| 427 | // module (golang.org/issue/26241). |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 428 | var mods []module.Version |
| 429 | if MainModules != nil { // TODO(#48912): Ensure MainModules exists at this point, and remove the check. |
| 430 | for mp, mv := range MainModules.HighestReplaced() { |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 431 | if !maybeInModule(path, mp) { |
| 432 | continue |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 433 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 434 | if mv == "" { |
| 435 | // The only replacement is a wildcard that doesn't specify a version, so |
| 436 | // synthesize a pseudo-version with an appropriate major version and a |
| 437 | // timestamp below any real timestamp. That way, if the main module is |
| 438 | // used from within some other module, the user will be able to upgrade |
| 439 | // the requirement to any real version they choose. |
| 440 | if _, pathMajor, ok := module.SplitPathVersion(mp); ok && len(pathMajor) > 0 { |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 441 | mv = module.ZeroPseudoVersion(pathMajor[1:]) |
Colin Cross | d9c6b80 | 2019-03-19 21:10:31 -0700 | [diff] [blame] | 442 | } else { |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 443 | mv = module.ZeroPseudoVersion("v0") |
Colin Cross | d9c6b80 | 2019-03-19 21:10:31 -0700 | [diff] [blame] | 444 | } |
| 445 | } |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 446 | mg, err := rs.Graph(ctx) |
| 447 | if err != nil { |
| 448 | return module.Version{}, err |
| 449 | } |
| 450 | if cmpVersion(mg.Selected(mp), mv) >= 0 { |
| 451 | // We can't resolve the import by adding mp@mv to the module graph, |
| 452 | // because the selected version of mp is already at least mv. |
| 453 | continue |
| 454 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 455 | mods = append(mods, module.Version{Path: mp, Version: mv}) |
Colin Cross | d9c6b80 | 2019-03-19 21:10:31 -0700 | [diff] [blame] | 456 | } |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 457 | } |
Colin Cross | d9c6b80 | 2019-03-19 21:10:31 -0700 | [diff] [blame] | 458 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 459 | // Every module path in mods is a prefix of the import path. |
| 460 | // As in QueryPattern, prefer the longest prefix that satisfies the import. |
| 461 | sort.Slice(mods, func(i, j int) bool { |
| 462 | return len(mods[i].Path) > len(mods[j].Path) |
| 463 | }) |
| 464 | for _, m := range mods { |
| 465 | needSum := true |
| 466 | root, isLocal, err := fetch(ctx, m, needSum) |
| 467 | if err != nil { |
| 468 | if sumErr := (*sumMissingError)(nil); errors.As(err, &sumErr) { |
| 469 | return module.Version{}, &ImportMissingSumError{importPath: path} |
Colin Cross | d9c6b80 | 2019-03-19 21:10:31 -0700 | [diff] [blame] | 470 | } |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 471 | return module.Version{}, err |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 472 | } |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 473 | if _, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil { |
| 474 | return m, err |
| 475 | } else if ok { |
| 476 | if cfg.BuildMod == "readonly" { |
| 477 | return module.Version{}, &ImportMissingError{Path: path, replaced: m} |
Colin Cross | d9c6b80 | 2019-03-19 21:10:31 -0700 | [diff] [blame] | 478 | } |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 479 | return m, nil |
| 480 | } |
| 481 | } |
| 482 | if len(mods) > 0 && module.CheckPath(path) != nil { |
| 483 | // The package path is not valid to fetch remotely, |
| 484 | // so it can only exist in a replaced module, |
| 485 | // and we know from the above loop that it is not. |
| 486 | replacement := Replacement(mods[0]) |
| 487 | return module.Version{}, &PackageNotInModuleError{ |
| 488 | Mod: mods[0], |
| 489 | Query: "latest", |
| 490 | Pattern: path, |
| 491 | Replacement: replacement, |
Colin Cross | d9c6b80 | 2019-03-19 21:10:31 -0700 | [diff] [blame] | 492 | } |
| 493 | } |
| 494 | |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 495 | if search.IsStandardImportPath(path) { |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 496 | // This package isn't in the standard library, isn't in any module already |
| 497 | // in the build list, and isn't in any other module that the user has |
| 498 | // shimmed in via a "replace" directive. |
| 499 | // Moreover, the import path is reserved for the standard library, so |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 500 | // QueryPattern cannot possibly find a module containing this package. |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 501 | // |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 502 | // Instead of trying QueryPattern, report an ImportMissingError immediately. |
| 503 | return module.Version{}, &ImportMissingError{Path: path, isStd: true} |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 504 | } |
| 505 | |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 506 | if cfg.BuildMod == "readonly" && !allowMissingModuleImports { |
| 507 | // In readonly mode, we can't write go.mod, so we shouldn't try to look up |
| 508 | // the module. If readonly mode was enabled explicitly, include that in |
| 509 | // the error message. |
| 510 | var queryErr error |
| 511 | if cfg.BuildModExplicit { |
| 512 | queryErr = fmt.Errorf("import lookup disabled by -mod=%s", cfg.BuildMod) |
| 513 | } else if cfg.BuildModReason != "" { |
| 514 | queryErr = fmt.Errorf("import lookup disabled by -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason) |
| 515 | } |
| 516 | return module.Version{}, &ImportMissingError{Path: path, QueryErr: queryErr} |
| 517 | } |
| 518 | |
| 519 | // Look up module containing the package, for addition to the build list. |
| 520 | // Goal is to determine the module, download it to dir, |
| 521 | // and return m, dir, ImpportMissingError. |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 522 | fmt.Fprintf(os.Stderr, "go: finding module for package %s\n", path) |
| 523 | |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 524 | mg, err := rs.Graph(ctx) |
| 525 | if err != nil { |
| 526 | return module.Version{}, err |
| 527 | } |
| 528 | |
| 529 | candidates, err := QueryPackages(ctx, path, "latest", mg.Selected, CheckAllowed) |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 530 | if err != nil { |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 531 | if errors.Is(err, fs.ErrNotExist) { |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 532 | // Return "cannot find module providing package […]" instead of whatever |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 533 | // low-level error QueryPattern produced. |
| 534 | return module.Version{}, &ImportMissingError{Path: path, QueryErr: err} |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 535 | } else { |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 536 | return module.Version{}, err |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 537 | } |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 538 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 539 | |
| 540 | candidate0MissingVersion := "" |
| 541 | for i, c := range candidates { |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 542 | if v := mg.Selected(c.Mod.Path); semver.Compare(v, c.Mod.Version) > 0 { |
| 543 | // QueryPattern proposed that we add module c.Mod to provide the package, |
| 544 | // but we already depend on a newer version of that module (and that |
| 545 | // version doesn't have the package). |
| 546 | // |
| 547 | // This typically happens when a package is present at the "@latest" |
| 548 | // version (e.g., v1.0.0) of a module, but we have a newer version |
| 549 | // of the same module in the build list (e.g., v1.0.1-beta), and |
| 550 | // the package is not present there. |
| 551 | if i == 0 { |
| 552 | candidate0MissingVersion = v |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 553 | } |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 554 | continue |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 555 | } |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 556 | return c.Mod, nil |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 557 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 558 | return module.Version{}, &ImportMissingError{ |
| 559 | Path: path, |
| 560 | Module: candidates[0].Mod, |
| 561 | newMissingVersion: candidate0MissingVersion, |
| 562 | } |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 563 | } |
| 564 | |
| 565 | // maybeInModule reports whether, syntactically, |
| 566 | // a package with the given import path could be supplied |
| 567 | // by a module with the given module path (mpath). |
| 568 | func maybeInModule(path, mpath string) bool { |
| 569 | return mpath == path || |
| 570 | len(path) > len(mpath) && path[len(mpath)] == '/' && path[:len(mpath)] == mpath |
| 571 | } |
| 572 | |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 573 | var ( |
| 574 | haveGoModCache par.Cache // dir → bool |
| 575 | haveGoFilesCache par.Cache // dir → goFilesEntry |
| 576 | ) |
| 577 | |
| 578 | type goFilesEntry struct { |
| 579 | haveGoFiles bool |
| 580 | err error |
| 581 | } |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 582 | |
| 583 | // dirInModule locates the directory that would hold the package named by the given path, |
| 584 | // if it were in the module with module path mpath and root mdir. |
| 585 | // If path is syntactically not within mpath, |
| 586 | // or if mdir is a local file tree (isLocal == true) and the directory |
| 587 | // that would hold path is in a sub-module (covered by a go.mod below mdir), |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 588 | // dirInModule returns "", false, nil. |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 589 | // |
| 590 | // Otherwise, dirInModule returns the name of the directory where |
| 591 | // Go source files would be expected, along with a boolean indicating |
| 592 | // whether there are in fact Go source files in that directory. |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 593 | // A non-nil error indicates that the existence of the directory and/or |
| 594 | // source files could not be determined, for example due to a permission error. |
| 595 | func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFiles bool, err error) { |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 596 | // Determine where to expect the package. |
| 597 | if path == mpath { |
| 598 | dir = mdir |
| 599 | } else if mpath == "" { // vendor directory |
| 600 | dir = filepath.Join(mdir, path) |
| 601 | } else if len(path) > len(mpath) && path[len(mpath)] == '/' && path[:len(mpath)] == mpath { |
| 602 | dir = filepath.Join(mdir, path[len(mpath)+1:]) |
| 603 | } else { |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 604 | return "", false, nil |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 605 | } |
| 606 | |
| 607 | // Check that there aren't other modules in the way. |
| 608 | // This check is unnecessary inside the module cache |
| 609 | // and important to skip in the vendor directory, |
| 610 | // where all the module trees have been overlaid. |
| 611 | // So we only check local module trees |
| 612 | // (the main module, and any directory trees pointed at by replace directives). |
| 613 | if isLocal { |
| 614 | for d := dir; d != mdir && len(d) > len(mdir); { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 615 | haveGoMod := haveGoModCache.Do(d, func() any { |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 616 | fi, err := fsys.Stat(filepath.Join(d, "go.mod")) |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 617 | return err == nil && !fi.IsDir() |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 618 | }).(bool) |
| 619 | |
| 620 | if haveGoMod { |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 621 | return "", false, nil |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 622 | } |
| 623 | parent := filepath.Dir(d) |
| 624 | if parent == d { |
| 625 | // Break the loop, as otherwise we'd loop |
| 626 | // forever if d=="." and mdir=="". |
| 627 | break |
| 628 | } |
| 629 | d = parent |
| 630 | } |
| 631 | } |
| 632 | |
| 633 | // Now committed to returning dir (not ""). |
| 634 | |
| 635 | // Are there Go source files in the directory? |
| 636 | // We don't care about build tags, not even "+build ignore". |
| 637 | // We're just looking for a plausible directory. |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 638 | res := haveGoFilesCache.Do(dir, func() any { |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 639 | ok, err := fsys.IsDirWithGoFiles(dir) |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 640 | return goFilesEntry{haveGoFiles: ok, err: err} |
| 641 | }).(goFilesEntry) |
| 642 | |
| 643 | return dir, res.haveGoFiles, res.err |
| 644 | } |
| 645 | |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 646 | // fetch downloads the given module (or its replacement) |
| 647 | // and returns its location. |
| 648 | // |
| 649 | // needSum indicates whether the module may be downloaded in readonly mode |
| 650 | // without a go.sum entry. It should only be false for modules fetched |
| 651 | // speculatively (for example, for incompatible version filtering). The sum |
| 652 | // will still be verified normally. |
| 653 | // |
| 654 | // The isLocal return value reports whether the replacement, |
| 655 | // if any, is local to the filesystem. |
| 656 | func fetch(ctx context.Context, mod module.Version, needSum bool) (dir string, isLocal bool, err error) { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 657 | if modRoot := MainModules.ModRoot(mod); modRoot != "" { |
| 658 | return modRoot, true, nil |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 659 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 660 | if r := Replacement(mod); r.Path != "" { |
| 661 | if r.Version == "" { |
| 662 | dir = r.Path |
| 663 | if !filepath.IsAbs(dir) { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 664 | dir = filepath.Join(replaceRelativeTo(), dir) |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 665 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 666 | // Ensure that the replacement directory actually exists: |
| 667 | // dirInModule does not report errors for missing modules, |
| 668 | // so if we don't report the error now, later failures will be |
| 669 | // very mysterious. |
| 670 | if _, err := fsys.Stat(dir); err != nil { |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 671 | if os.IsNotExist(err) { |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 672 | // Semantically the module version itself “exists” — we just don't |
| 673 | // have its source code. Remove the equivalence to os.ErrNotExist, |
| 674 | // and make the message more concise while we're at it. |
| 675 | err = fmt.Errorf("replacement directory %s does not exist", r.Path) |
| 676 | } else { |
| 677 | err = fmt.Errorf("replacement directory %s: %w", r.Path, err) |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 678 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 679 | return dir, true, module.VersionError(mod, err) |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 680 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 681 | return dir, true, nil |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 682 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 683 | mod = r |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 684 | } |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 685 | |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 686 | if HasModRoot() && cfg.BuildMod == "readonly" && !inWorkspaceMode() && needSum && !modfetch.HaveSum(mod) { |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 687 | return "", false, module.VersionError(mod, &sumMissingError{}) |
| 688 | } |
| 689 | |
| 690 | dir, err = modfetch.Download(ctx, mod) |
| 691 | return dir, false, err |
| 692 | } |
| 693 | |
| 694 | type sumMissingError struct { |
| 695 | suggestion string |
| 696 | } |
| 697 | |
| 698 | func (e *sumMissingError) Error() string { |
| 699 | return "missing go.sum entry" + e.suggestion |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 700 | } |