github.com/gernest/nezuko@v0.1.2/internal/modload/load.go (about) 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 ( 8 "bytes" 9 "errors" 10 "fmt" 11 "go/build" 12 "io/ioutil" 13 "os" 14 "path" 15 "path/filepath" 16 "sort" 17 "strings" 18 "sync" 19 20 "github.com/gernest/nezuko/internal/base" 21 "github.com/gernest/nezuko/internal/cfg" 22 "github.com/gernest/nezuko/internal/imports" 23 "github.com/gernest/nezuko/internal/modfetch" 24 "github.com/gernest/nezuko/internal/modfile" 25 "github.com/gernest/nezuko/internal/module" 26 "github.com/gernest/nezuko/internal/mvs" 27 "github.com/gernest/nezuko/internal/par" 28 "github.com/gernest/nezuko/internal/search" 29 "github.com/gernest/nezuko/internal/semver" 30 "github.com/gernest/nezuko/internal/str" 31 ) 32 33 // buildList is the list of modules to use for building packages. 34 // It is initialized by calling ImportPaths, ImportFromFiles, 35 // LoadALL, or LoadBuildList, each of which uses loaded.load. 36 // 37 // Ideally, exactly ONE of those functions would be called, 38 // and exactly once. Most of the time, that's true. 39 // During "go get" it may not be. TODO(rsc): Figure out if 40 // that restriction can be established, or else document why not. 41 // 42 var buildList []module.Version 43 44 // loaded is the most recently-used package loader. 45 // It holds details about individual packages. 46 // 47 // Note that loaded.buildList is only valid during a load operation; 48 // afterward, it is copied back into the global buildList, 49 // which should be used instead. 50 var loaded *loader 51 52 // ImportPaths returns the set of packages matching the args (patterns), 53 // adding modules to the build list as needed to satisfy new imports. 54 func ImportPaths(patterns []string) []*search.Match { 55 InitMod() 56 57 var matches []*search.Match 58 for _, pattern := range search.CleanPatterns(patterns) { 59 m := &search.Match{ 60 Pattern: pattern, 61 Literal: !strings.Contains(pattern, "...") && !search.IsMetaPackage(pattern), 62 } 63 if m.Literal { 64 m.Pkgs = []string{pattern} 65 } 66 matches = append(matches, m) 67 } 68 69 fsDirs := make([][]string, len(matches)) 70 loaded = newLoader() 71 updateMatches := func(iterating bool) { 72 for i, m := range matches { 73 switch { 74 case build.IsLocalImport(m.Pattern) || filepath.IsAbs(m.Pattern): 75 // Evaluate list of file system directories on first iteration. 76 if fsDirs[i] == nil { 77 var dirs []string 78 if m.Literal { 79 dirs = []string{m.Pattern} 80 } else { 81 dirs = search.MatchPackagesInFS(m.Pattern).Pkgs 82 } 83 fsDirs[i] = dirs 84 } 85 86 // Make a copy of the directory list and translate to import paths. 87 // Note that whether a directory corresponds to an import path 88 // changes as the build list is updated, and a directory can change 89 // from not being in the build list to being in it and back as 90 // the exact version of a particular module increases during 91 // the loader iterations. 92 m.Pkgs = str.StringList(fsDirs[i]) 93 for j, pkg := range m.Pkgs { 94 dir := pkg 95 if !filepath.IsAbs(dir) { 96 dir = filepath.Join(cwd, pkg) 97 } else { 98 dir = filepath.Clean(dir) 99 } 100 101 // Note: The checks for @ here are just to avoid misinterpreting 102 // the module cache directories (formerly GOPATH/src/mod/foo@v1.5.2/bar). 103 // It's not strictly necessary but helpful to keep the checks. 104 if modRoot != "" && dir == modRoot { 105 pkg = Target.Path 106 } else if modRoot != "" && strings.HasPrefix(dir, modRoot+string(filepath.Separator)) && !strings.Contains(dir[len(modRoot):], "@") { 107 suffix := filepath.ToSlash(dir[len(modRoot):]) 108 if strings.HasPrefix(suffix, "/vendor/") { 109 // TODO getmode vendor check 110 pkg = strings.TrimPrefix(suffix, "/vendor/") 111 } else { 112 pkg = Target.Path + suffix 113 } 114 } else if path := pathInModuleCache(dir); path != "" { 115 pkg = path 116 } else { 117 pkg = "" 118 if !iterating { 119 ModRoot() 120 base.Errorf("z: directory %s outside available modules", base.ShortPath(dir)) 121 } 122 } 123 info, err := os.Stat(dir) 124 if err != nil || !info.IsDir() { 125 // If the directory is local but does not exist, don't return it 126 // while loader is iterating, since this would trigger a fetch. 127 // After loader is done iterating, we still need to return the 128 // path, so that "go list -e" produces valid output. 129 if iterating { 130 pkg = "" 131 } 132 } 133 m.Pkgs[j] = pkg 134 } 135 136 case strings.Contains(m.Pattern, "..."): 137 m.Pkgs = matchPackages(m.Pattern, loaded.tags, true, buildList) 138 139 case m.Pattern == "all": 140 loaded.testAll = true 141 if iterating { 142 // Enumerate the packages in the main module. 143 // We'll load the dependencies as we find them. 144 m.Pkgs = matchPackages("...", loaded.tags, false, []module.Version{Target}) 145 } else { 146 // Starting with the packages in the main module, 147 // enumerate the full list of "all". 148 m.Pkgs = loaded.computePatternAll(m.Pkgs) 149 } 150 151 case search.IsMetaPackage(m.Pattern): // std, cmd 152 if len(m.Pkgs) == 0 { 153 m.Pkgs = search.MatchPackages(m.Pattern).Pkgs 154 } 155 } 156 } 157 } 158 159 loaded.load(func() []string { 160 var roots []string 161 updateMatches(true) 162 for _, m := range matches { 163 for _, pkg := range m.Pkgs { 164 if pkg != "" { 165 roots = append(roots, pkg) 166 } 167 } 168 } 169 return roots 170 }) 171 172 // One last pass to finalize wildcards. 173 updateMatches(false) 174 175 // A given module path may be used as itself or as a replacement for another 176 // module, but not both at the same time. Otherwise, the aliasing behavior is 177 // too subtle (see https://golang.org/issue/26607), and we don't want to 178 // commit to a specific behavior at this point. 179 firstPath := make(map[module.Version]string, len(buildList)) 180 for _, mod := range buildList { 181 src := mod 182 if rep := Replacement(mod); rep.Path != "" { 183 src = rep 184 } 185 if prev, ok := firstPath[src]; !ok { 186 firstPath[src] = mod.Path 187 } else if prev != mod.Path { 188 base.Errorf("z: %s@%s used for two different module paths (%s and %s)", src.Path, src.Version, prev, mod.Path) 189 } 190 } 191 base.ExitIfErrors() 192 WriteGoMod() 193 194 search.WarnUnmatched(matches) 195 return matches 196 } 197 198 // pathInModuleCache returns the import path of the directory dir, 199 // if dir is in the module cache copy of a module in our build list. 200 func pathInModuleCache(dir string) string { 201 for _, m := range buildList[1:] { 202 root, err := modfetch.DownloadDir(m) 203 if err != nil { 204 continue 205 } 206 if sub := search.InDir(dir, root); sub != "" { 207 sub = filepath.ToSlash(sub) 208 if !strings.Contains(sub, "/vendor/") && !strings.HasPrefix(sub, "vendor/") && !strings.Contains(sub, "@") { 209 return path.Join(m.Path, filepath.ToSlash(sub)) 210 } 211 } 212 } 213 return "" 214 } 215 216 // warnPattern returns list, the result of matching pattern, 217 // but if list is empty then first it prints a warning about 218 // the pattern not matching any packages. 219 func warnPattern(pattern string, list []string) []string { 220 if len(list) == 0 { 221 fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) 222 } 223 return list 224 } 225 226 // ImportFromFiles adds modules to the build list as needed 227 // to satisfy the imports in the named Go source files. 228 func ImportFromFiles(gofiles []string) { 229 InitMod() 230 imports, testImports, err := imports.ScanFiles(gofiles, imports.Tags()) 231 if err != nil { 232 base.Fatalf("z: %v", err) 233 } 234 235 loaded = newLoader() 236 loaded.load(func() []string { 237 var roots []string 238 roots = append(roots, imports...) 239 roots = append(roots, testImports...) 240 return roots 241 }) 242 WriteGoMod() 243 } 244 245 // DirImportPath returns the effective import path for dir, 246 // provided it is within the main module, or else returns ".". 247 func DirImportPath(dir string) string { 248 if modRoot == "" { 249 return "." 250 } 251 252 if !filepath.IsAbs(dir) { 253 dir = filepath.Join(cwd, dir) 254 } else { 255 dir = filepath.Clean(dir) 256 } 257 258 if dir == modRoot { 259 return Target.Path 260 } 261 if strings.HasPrefix(dir, modRoot+string(filepath.Separator)) { 262 suffix := filepath.ToSlash(dir[len(modRoot):]) 263 if strings.HasPrefix(suffix, "/vendor/") { 264 return strings.TrimPrefix(suffix, "/vendor/") 265 } 266 return Target.Path + suffix 267 } 268 return "." 269 } 270 271 // LoadBuildList loads and returns the build list from z.mod. 272 // The loading of the build list happens automatically in ImportPaths: 273 // LoadBuildList need only be called if ImportPaths is not 274 // (typically in commands that care about the module but 275 // no particular package). 276 func LoadBuildList() []module.Version { 277 InitMod() 278 ReloadBuildList() 279 WriteGoMod() 280 return buildList 281 } 282 283 func ReloadBuildList() []module.Version { 284 loaded = newLoader() 285 loaded.load(func() []string { return nil }) 286 return buildList 287 } 288 289 // LoadALL returns the set of all packages in the current module 290 // and their dependencies in any other modules, without filtering 291 // due to build tags, except "+build ignore". 292 // It adds modules to the build list as needed to satisfy new imports. 293 // This set is useful for deciding whether a particular import is needed 294 // anywhere in a module. 295 func LoadALL() []string { 296 return loadAll(true) 297 } 298 299 // LoadVendor is like LoadALL but only follows test dependencies 300 // for tests in the main module. Tests in dependency modules are 301 // ignored completely. 302 // This set is useful for identifying the which packages to include in a vendor directory. 303 func LoadVendor() []string { 304 return loadAll(false) 305 } 306 307 func loadAll(testAll bool) []string { 308 InitMod() 309 310 loaded = newLoader() 311 loaded.isALL = true 312 loaded.tags = anyTags 313 loaded.testAll = testAll 314 if !testAll { 315 loaded.testRoots = true 316 } 317 all := []string{Target.Path} 318 loaded.load(func() []string { return all }) 319 WriteGoMod() 320 321 var paths []string 322 for _, pkg := range loaded.pkgs { 323 if e, ok := pkg.err.(*ImportMissingError); ok && e.Module.Path == "" { 324 continue // Package doesn't actually exist. 325 } 326 paths = append(paths, pkg.path) 327 } 328 return paths 329 } 330 331 // anyTags is a special tags map that satisfies nearly all build tag expressions. 332 // Only "ignore" and malformed build tag requirements are considered false. 333 var anyTags = map[string]bool{"*": true} 334 335 // TargetPackages returns the list of packages in the target (top-level) module, 336 // under all build tag settings. 337 func TargetPackages() []string { 338 return matchPackages("...", anyTags, false, []module.Version{Target}) 339 } 340 341 // BuildList returns the module build list, 342 // typically constructed by a previous call to 343 // LoadBuildList or ImportPaths. 344 // The caller must not modify the returned list. 345 func BuildList() []module.Version { 346 return buildList 347 } 348 349 // SetBuildList sets the module build list. 350 // The caller is responsible for ensuring that the list is valid. 351 // SetBuildList does not retain a reference to the original list. 352 func SetBuildList(list []module.Version) { 353 buildList = append([]module.Version{}, list...) 354 } 355 356 // ImportMap returns the actual package import path 357 // for an import path found in source code. 358 // If the given import path does not appear in the source code 359 // for the packages that have been loaded, ImportMap returns the empty string. 360 func ImportMap(path string) string { 361 pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) 362 if !ok { 363 return "" 364 } 365 return pkg.path 366 } 367 368 // PackageDir returns the directory containing the source code 369 // for the package named by the import path. 370 func PackageDir(path string) string { 371 pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) 372 if !ok { 373 return "" 374 } 375 return pkg.dir 376 } 377 378 // PackageModule returns the module providing the package named by the import path. 379 func PackageModule(path string) module.Version { 380 pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) 381 if !ok { 382 return module.Version{} 383 } 384 return pkg.mod 385 } 386 387 // ModuleUsedDirectly reports whether the main module directly imports 388 // some package in the module with the given path. 389 func ModuleUsedDirectly(path string) bool { 390 return loaded.direct[path] 391 } 392 393 // Lookup returns the source directory, import path, and any loading error for 394 // the package at path. 395 // Lookup requires that one of the Load functions in this package has already 396 // been called. 397 func Lookup(path string) (dir, realPath string, err error) { 398 if path == "" { 399 panic("Lookup called with empty package path") 400 } 401 pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) 402 if !ok { 403 // The loader should have found all the relevant paths. 404 // There are a few exceptions, though: 405 // - during go list without -test, the p.Resolve calls to process p.TestImports and p.XTestImports 406 // end up here to canonicalize the import paths. 407 // - during any load, non-loaded packages like "unsafe" end up here. 408 // - during any load, build-injected dependencies like "runtime/cgo" end up here. 409 // - because we ignore appengine/* in the module loader, 410 // the dependencies of any actual appengine/* library end up here. 411 dir := findStandardImportPath(path) 412 if dir != "" { 413 return dir, path, nil 414 } 415 return "", "", errMissing 416 } 417 return pkg.dir, pkg.path, pkg.err 418 } 419 420 // A loader manages the process of loading information about 421 // the required packages for a particular build, 422 // checking that the packages are available in the module set, 423 // and updating the module set if needed. 424 // Loading is an iterative process: try to load all the needed packages, 425 // but if imports are missing, try to resolve those imports, and repeat. 426 // 427 // Although most of the loading state is maintained in the loader struct, 428 // one key piece - the build list - is a global, so that it can be modified 429 // separate from the loading operation, such as during "go get" 430 // upgrades/downgrades or in "z mod" operations. 431 // TODO(rsc): It might be nice to make the loader take and return 432 // a buildList rather than hard-coding use of the global. 433 type loader struct { 434 tags map[string]bool // tags for scanDir 435 testRoots bool // include tests for roots 436 isALL bool // created with LoadALL 437 testAll bool // include tests for all packages 438 439 // reset on each iteration 440 roots []*loadPkg 441 pkgs []*loadPkg 442 work *par.Work // current work queue 443 pkgCache *par.Cache // map from string to *loadPkg 444 445 // computed at end of iterations 446 direct map[string]bool // imported directly by main module 447 exports map[string]string 448 } 449 450 // LoadTests controls whether the loaders load tests of the root packages. 451 var LoadTests bool 452 453 func newLoader() *loader { 454 ld := new(loader) 455 ld.tags = imports.Tags() 456 ld.testRoots = LoadTests 457 return ld 458 } 459 460 func (ld *loader) reset() { 461 ld.roots = nil 462 ld.pkgs = nil 463 ld.work = new(par.Work) 464 ld.pkgCache = new(par.Cache) 465 } 466 467 // A loadPkg records information about a single loaded package. 468 type loadPkg struct { 469 path string // import path 470 mod module.Version // module providing package 471 modFile *modfile.File 472 entryFile string 473 exports string 474 dir string // directory containing source code 475 imports []*loadPkg // packages imported by this one 476 err error // error loading package 477 stack *loadPkg // package importing this one in minimal import stack for this pkg 478 test *loadPkg // package with test imports, if we need test 479 testOf *loadPkg 480 testImports []string // test-only imports, saved for use by pkg.test. 481 } 482 483 var errMissing = errors.New("cannot find package") 484 485 // load attempts to load the build graph needed to process a set of root packages. 486 // The set of root packages is defined by the addRoots function, 487 // which must call add(path) with the import path of each root package. 488 func (ld *loader) load(roots func() []string) { 489 var err error 490 reqs := Reqs() 491 buildList, err = mvs.BuildList(Target, reqs) 492 if err != nil { 493 base.Fatalf("z: %v", err) 494 } 495 496 added := make(map[string]bool) 497 for { 498 ld.reset() 499 if roots != nil { 500 // Note: the returned roots can change on each iteration, 501 // since the expansion of package patterns depends on the 502 // build list we're using. 503 for _, path := range roots() { 504 ld.work.Add(ld.pkg(path, true)) 505 } 506 } 507 ld.work.Do(10, ld.doPkg) 508 ld.buildStacks() 509 numAdded := 0 510 haveMod := make(map[module.Version]bool) 511 for _, m := range buildList { 512 haveMod[m] = true 513 } 514 for _, pkg := range ld.pkgs { 515 if err, ok := pkg.err.(*ImportMissingError); ok && err.Module.Path != "" { 516 if added[pkg.path] { 517 base.Fatalf("z: %s: looping trying to add package", pkg.stackText()) 518 } 519 added[pkg.path] = true 520 numAdded++ 521 if !haveMod[err.Module] { 522 haveMod[err.Module] = true 523 buildList = append(buildList, err.Module) 524 } 525 continue 526 } 527 // Leave other errors for Import or load.Packages to report. 528 } 529 base.ExitIfErrors() 530 if numAdded == 0 { 531 break 532 } 533 534 // Recompute buildList with all our additions. 535 reqs = Reqs() 536 buildList, err = mvs.BuildList(Target, reqs) 537 if err != nil { 538 base.Fatalf("z: %v", err) 539 } 540 } 541 base.ExitIfErrors() 542 543 // Compute directly referenced dependency modules. 544 ld.direct = make(map[string]bool) 545 for _, pkg := range ld.pkgs { 546 if pkg.mod == Target { 547 for _, dep := range pkg.imports { 548 if dep.mod.Path != "" { 549 ld.direct[dep.mod.Path] = true 550 } 551 } 552 } 553 } 554 555 // Add Go versions, computed during walk. 556 ld.exports = make(map[string]string) 557 for _, m := range buildList { 558 v, _ := reqs.(*mvsReqs).exports.Load(m) 559 ld.exports[m.Path], _ = v.(string) 560 } 561 562 // Mix in direct markings (really, lack of indirect markings) 563 // from z.mod, unless we scanned the whole module 564 // and can therefore be sure we know better than z.mod. 565 if !ld.isALL && modFile != nil { 566 for _, r := range modFile.Require { 567 if !r.Indirect { 568 ld.direct[r.Mod.Path] = true 569 } 570 } 571 } 572 } 573 574 // pkg returns the *loadPkg for path, creating and queuing it if needed. 575 // If the package should be tested, its test is created but not queued 576 // (the test is queued after processing pkg). 577 // If isRoot is true, the pkg is being queued as one of the roots of the work graph. 578 func (ld *loader) pkg(path string, isRoot bool) *loadPkg { 579 return ld.pkgCache.Do(path, func() interface{} { 580 pkg := &loadPkg{ 581 path: path, 582 } 583 if ld.testRoots && isRoot || ld.testAll { 584 test := &loadPkg{ 585 path: path, 586 testOf: pkg, 587 } 588 pkg.test = test 589 } 590 if isRoot { 591 ld.roots = append(ld.roots, pkg) 592 } 593 ld.work.Add(pkg) 594 return pkg 595 }).(*loadPkg) 596 } 597 598 // doPkg processes a package on the work queue. 599 func (ld *loader) doPkg(item interface{}) { 600 // TODO: what about replacements? 601 pkg := item.(*loadPkg) 602 if strings.Contains(pkg.path, "@") { 603 // Leave for error during load. 604 return 605 } 606 if build.IsLocalImport(pkg.path) { 607 // Leave for error during load. 608 // (Module mode does not allow local imports.) 609 return 610 } 611 612 pkg.mod, pkg.dir, pkg.err = Import(pkg.path) 613 if pkg.dir == "" { 614 return 615 } 616 var err error 617 gomod := filepath.Join(pkg.dir, "z.mod") 618 data, err := ioutil.ReadFile(gomod) 619 if err != nil { 620 pkg.err = err 621 return 622 } 623 624 f, err := modfile.Parse(gomod, data, fixVersion) 625 if err != nil { 626 pkg.err = err 627 return 628 } 629 pkg.modFile = f 630 var entries []string 631 if f.Exports == nil { 632 pkg.err = errors.New("missing exports on package z.mod file") 633 return 634 } 635 if f.Exports != nil { 636 entries = append(entries, f.Exports.Name+".zig") 637 pkg.exports = f.Exports.Name 638 } 639 entries = append(entries, "lib.zig", "main.zig") 640 for _, entry := range entries { 641 n := cfg.BuildContext.JoinPath(pkg.dir, "src", entry) 642 if cfg.BuildContext.IsFile(n) { 643 pkg.entryFile = n 644 break 645 } 646 } 647 for _, r := range f.Require { 648 pkg.imports = append(pkg.imports, ld.pkg(r.Mod.Path, false)) 649 } 650 } 651 652 // computePatternAll returns the list of packages matching pattern "all", 653 // starting with a list of the import paths for the packages in the main module. 654 func (ld *loader) computePatternAll(paths []string) []string { 655 seen := make(map[*loadPkg]bool) 656 var all []string 657 var walk func(*loadPkg) 658 walk = func(pkg *loadPkg) { 659 if seen[pkg] { 660 return 661 } 662 seen[pkg] = true 663 if pkg.testOf == nil { 664 all = append(all, pkg.path) 665 } 666 for _, p := range pkg.imports { 667 walk(p) 668 } 669 if p := pkg.test; p != nil { 670 walk(p) 671 } 672 } 673 for _, path := range paths { 674 walk(ld.pkg(path, false)) 675 } 676 sort.Strings(all) 677 678 return all 679 } 680 681 // scanDir is like imports.ScanDir but elides known magic imports from the list, 682 // so that we do not go looking for packages that don't really exist. 683 // 684 // The standard magic import is "C", for cgo. 685 // 686 // The only other known magic imports are appengine and appengine/*. 687 // These are so old that they predate "go get" and did not use URL-like paths. 688 // Most code today now uses google.golang.org/appengine instead, 689 // but not all code has been so updated. When we mostly ignore build tags 690 // during "go vendor", we look into "// +build appengine" files and 691 // may see these legacy imports. We drop them so that the module 692 // search does not look for modules to try to satisfy them. 693 func scanDir(dir string, tags map[string]bool) (imports_, testImports []string, err error) { 694 imports_, testImports, err = imports.ScanDir(dir, tags) 695 696 filter := func(x []string) []string { 697 w := 0 698 for _, pkg := range x { 699 if pkg != "C" && pkg != "appengine" && !strings.HasPrefix(pkg, "appengine/") && 700 pkg != "appengine_internal" && !strings.HasPrefix(pkg, "appengine_internal/") { 701 x[w] = pkg 702 w++ 703 } 704 } 705 return x[:w] 706 } 707 708 return filter(imports_), filter(testImports), err 709 } 710 711 // buildStacks computes minimal import stacks for each package, 712 // for use in error messages. When it completes, packages that 713 // are part of the original root set have pkg.stack == nil, 714 // and other packages have pkg.stack pointing at the next 715 // package up the import stack in their minimal chain. 716 // As a side effect, buildStacks also constructs ld.pkgs, 717 // the list of all packages loaded. 718 func (ld *loader) buildStacks() { 719 if len(ld.pkgs) > 0 { 720 panic("buildStacks") 721 } 722 for _, pkg := range ld.roots { 723 pkg.stack = pkg // sentinel to avoid processing in next loop 724 ld.pkgs = append(ld.pkgs, pkg) 725 } 726 for i := 0; i < len(ld.pkgs); i++ { // not range: appending to ld.pkgs in loop 727 pkg := ld.pkgs[i] 728 for _, next := range pkg.imports { 729 if next.stack == nil { 730 next.stack = pkg 731 ld.pkgs = append(ld.pkgs, next) 732 } 733 } 734 if next := pkg.test; next != nil && next.stack == nil { 735 next.stack = pkg 736 ld.pkgs = append(ld.pkgs, next) 737 } 738 } 739 for _, pkg := range ld.roots { 740 pkg.stack = nil 741 } 742 } 743 744 // stackText builds the import stack text to use when 745 // reporting an error in pkg. It has the general form 746 // 747 // import root -> 748 // import other -> 749 // import other2 -> 750 // import pkg 751 // 752 func (pkg *loadPkg) stackText() string { 753 var stack []*loadPkg 754 for p := pkg.stack; p != nil; p = p.stack { 755 stack = append(stack, p) 756 } 757 758 var buf bytes.Buffer 759 for i := len(stack) - 1; i >= 0; i-- { 760 p := stack[i] 761 if p.testOf != nil { 762 fmt.Fprintf(&buf, "test ->\n\t") 763 } else { 764 fmt.Fprintf(&buf, "import %q ->\n\t", p.path) 765 } 766 } 767 fmt.Fprintf(&buf, "import %q", pkg.path) 768 return buf.String() 769 } 770 771 // why returns the text to use in "z mod why" output about the given package. 772 // It is less ornate than the stackText but contains the same information. 773 func (pkg *loadPkg) why() string { 774 var buf strings.Builder 775 var stack []*loadPkg 776 for p := pkg; p != nil; p = p.stack { 777 stack = append(stack, p) 778 } 779 780 for i := len(stack) - 1; i >= 0; i-- { 781 p := stack[i] 782 if p.testOf != nil { 783 fmt.Fprintf(&buf, "%s.test\n", p.testOf.path) 784 } else { 785 fmt.Fprintf(&buf, "%s\n", p.path) 786 } 787 } 788 return buf.String() 789 } 790 791 // Why returns the "z mod why" output stanza for the given package, 792 // without the leading # comment. 793 // The package graph must have been loaded already, usually by LoadALL. 794 // If there is no reason for the package to be in the current build, 795 // Why returns an empty string. 796 func Why(path string) string { 797 pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) 798 if !ok { 799 return "" 800 } 801 return pkg.why() 802 } 803 804 // WhyDepth returns the number of steps in the Why listing. 805 // If there is no reason for the package to be in the current build, 806 // WhyDepth returns 0. 807 func WhyDepth(path string) int { 808 n := 0 809 pkg, _ := loaded.pkgCache.Get(path).(*loadPkg) 810 for p := pkg; p != nil; p = p.stack { 811 n++ 812 } 813 return n 814 } 815 816 // Replacement returns the replacement for mod, if any, from z.mod. 817 // If there is no replacement for mod, Replacement returns 818 // a module.Version with Path == "". 819 func Replacement(mod module.Version) module.Version { 820 if modFile == nil { 821 // Happens during testing and if invoking 'go get' or 'go list' outside a module. 822 return module.Version{} 823 } 824 825 var found *modfile.Replace 826 for _, r := range modFile.Replace { 827 if r.Old.Path == mod.Path && (r.Old.Version == "" || r.Old.Version == mod.Version) { 828 found = r // keep going 829 } 830 } 831 if found == nil { 832 return module.Version{} 833 } 834 return found.New 835 } 836 837 // mvsReqs implements mvs.Reqs for module semantic versions, 838 // with any exclusions or replacements applied internally. 839 type mvsReqs struct { 840 buildList []module.Version 841 cache par.Cache 842 exports sync.Map 843 } 844 845 // Reqs returns the current module requirement graph. 846 // Future calls to SetBuildList do not affect the operation 847 // of the returned Reqs. 848 func Reqs() mvs.Reqs { 849 r := &mvsReqs{ 850 buildList: buildList, 851 } 852 return r 853 } 854 855 func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) { 856 type cached struct { 857 list []module.Version 858 err error 859 } 860 861 c := r.cache.Do(mod, func() interface{} { 862 list, err := r.required(mod) 863 if err != nil { 864 return cached{nil, err} 865 } 866 for i, mv := range list { 867 for excluded[mv] { 868 mv1, err := r.next(mv) 869 if err != nil { 870 return cached{nil, err} 871 } 872 if mv1.Version == "none" { 873 return cached{nil, fmt.Errorf("%s(%s) depends on excluded %s(%s) with no newer version available", mod.Path, mod.Version, mv.Path, mv.Version)} 874 } 875 mv = mv1 876 } 877 list[i] = mv 878 } 879 880 return cached{list, nil} 881 }).(cached) 882 883 return c.list, c.err 884 } 885 886 var vendorOnce sync.Once 887 888 var ( 889 vendorList []module.Version 890 vendorMap map[string]module.Version 891 ) 892 893 // readVendorList reads the list of vendored modules from vendor/modules.txt. 894 func readVendorList() { 895 vendorOnce.Do(func() { 896 vendorList = nil 897 vendorMap = make(map[string]module.Version) 898 data, _ := ioutil.ReadFile(filepath.Join(ModRoot(), "vendor/modules.txt")) 899 var m module.Version 900 for _, line := range strings.Split(string(data), "\n") { 901 if strings.HasPrefix(line, "# ") { 902 f := strings.Fields(line) 903 m = module.Version{} 904 if len(f) == 3 && semver.IsValid(f[2]) { 905 m = module.Version{Path: f[1], Version: f[2]} 906 vendorList = append(vendorList, m) 907 } 908 } else if m.Path != "" { 909 f := strings.Fields(line) 910 if len(f) == 1 { 911 vendorMap[f[0]] = m 912 } 913 } 914 } 915 }) 916 } 917 918 func (r *mvsReqs) modFileToList(f *modfile.File) []module.Version { 919 var list []module.Version 920 for _, r := range f.Require { 921 list = append(list, r.Mod) 922 } 923 return list 924 } 925 926 func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) { 927 if mod == Target { 928 if modFile != nil && modFile.Exports != nil { 929 r.exports.LoadOrStore(mod, modFile.Exports.Name) 930 } 931 var list []module.Version 932 return append(list, r.buildList[1:]...), nil 933 } 934 935 if cfg.BuildMod == "vendor" { 936 // For every module other than the target, 937 // return the full list of modules from modules.txt. 938 readVendorList() 939 return vendorList, nil 940 } 941 942 origPath := mod.Path 943 if repl := Replacement(mod); repl.Path != "" { 944 if repl.Version == "" { 945 // TODO: need to slip the new version into the tags list etc. 946 dir := repl.Path 947 if !filepath.IsAbs(dir) { 948 dir = filepath.Join(ModRoot(), dir) 949 } 950 gomod := filepath.Join(dir, "z.mod") 951 data, err := ioutil.ReadFile(gomod) 952 if err != nil { 953 base.Errorf("z: parsing %s: %v", base.ShortPath(gomod), err) 954 return nil, ErrRequire 955 } 956 f, err := modfile.ParseLax(gomod, data, nil) 957 if err != nil { 958 base.Errorf("z: parsing %s: %v", base.ShortPath(gomod), err) 959 return nil, ErrRequire 960 } 961 if f.Exports != nil { 962 r.exports.LoadOrStore(mod, f.Exports.Name) 963 } 964 return r.modFileToList(f), nil 965 } 966 mod = repl 967 } 968 969 if mod.Version == "none" { 970 return nil, nil 971 } 972 973 if !semver.IsValid(mod.Version) { 974 // Disallow the broader queries supported by fetch.Lookup. 975 base.Fatalf("z: internal error: %s@%s: unexpected invalid semantic version", mod.Path, mod.Version) 976 } 977 978 data, err := modfetch.GoMod(mod.Path, mod.Version) 979 if err != nil { 980 base.Errorf("z: %s@%s: %v\n", mod.Path, mod.Version, err) 981 return nil, ErrRequire 982 } 983 f, err := modfile.ParseLax("z.mod", data, nil) 984 if err != nil { 985 base.Errorf("z: %s@%s: parsing z.mod: %v", mod.Path, mod.Version, err) 986 return nil, ErrRequire 987 } 988 989 if f.Module == nil { 990 base.Errorf("z: %s@%s: parsing z.mod: missing module line", mod.Path, mod.Version) 991 return nil, ErrRequire 992 } 993 if mpath := f.Module.Mod.Path; mpath != origPath && mpath != mod.Path { 994 base.Errorf("z: %s@%s: parsing z.mod: unexpected module path %q", mod.Path, mod.Version, mpath) 995 return nil, ErrRequire 996 } 997 if f.Exports != nil { 998 r.exports.LoadOrStore(mod, f.Exports.Name) 999 } 1000 1001 return r.modFileToList(f), nil 1002 } 1003 1004 // ErrRequire is the sentinel error returned when Require encounters problems. 1005 // It prints the problems directly to standard error, so that multiple errors 1006 // can be displayed easily. 1007 var ErrRequire = errors.New("error loading module requirements") 1008 1009 func (*mvsReqs) Max(v1, v2 string) string { 1010 if v1 != "" && semver.Compare(v1, v2) == -1 { 1011 return v2 1012 } 1013 return v1 1014 } 1015 1016 // Upgrade is a no-op, here to implement mvs.Reqs. 1017 // The upgrade logic for go get -u is in ../modget/get.go. 1018 func (*mvsReqs) Upgrade(m module.Version) (module.Version, error) { 1019 return m, nil 1020 } 1021 1022 func versions(path string) ([]string, error) { 1023 // Note: modfetch.Lookup and repo.Versions are cached, 1024 // so there's no need for us to add extra caching here. 1025 repo, err := modfetch.Lookup(path) 1026 if err != nil { 1027 return nil, err 1028 } 1029 return repo.Versions("") 1030 } 1031 1032 // Previous returns the tagged version of m.Path immediately prior to 1033 // m.Version, or version "none" if no prior version is tagged. 1034 func (*mvsReqs) Previous(m module.Version) (module.Version, error) { 1035 list, err := versions(m.Path) 1036 if err != nil { 1037 return module.Version{}, err 1038 } 1039 i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) >= 0 }) 1040 if i > 0 { 1041 return module.Version{Path: m.Path, Version: list[i-1]}, nil 1042 } 1043 return module.Version{Path: m.Path, Version: "none"}, nil 1044 } 1045 1046 // next returns the next version of m.Path after m.Version. 1047 // It is only used by the exclusion processing in the Required method, 1048 // not called directly by MVS. 1049 func (*mvsReqs) next(m module.Version) (module.Version, error) { 1050 list, err := versions(m.Path) 1051 if err != nil { 1052 return module.Version{}, err 1053 } 1054 i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) > 0 }) 1055 if i < len(list) { 1056 return module.Version{Path: m.Path, Version: list[i]}, nil 1057 } 1058 return module.Version{Path: m.Path, Version: "none"}, nil 1059 } 1060 1061 func fetch(mod module.Version) (dir string, isLocal bool, err error) { 1062 if mod == Target { 1063 return ModRoot(), true, nil 1064 } 1065 if r := Replacement(mod); r.Path != "" { 1066 if r.Version == "" { 1067 dir = r.Path 1068 if !filepath.IsAbs(dir) { 1069 dir = filepath.Join(ModRoot(), dir) 1070 } 1071 return dir, true, nil 1072 } 1073 mod = r 1074 } 1075 1076 dir, err = modfetch.Download(mod) 1077 return dir, false, err 1078 }