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