github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/cmd/go/internal/modload/import.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 "internal/goroot" 13 "os" 14 "path/filepath" 15 "sort" 16 "strings" 17 "time" 18 19 "cmd/go/internal/cfg" 20 "cmd/go/internal/modfetch" 21 "cmd/go/internal/modfetch/codehost" 22 "cmd/go/internal/module" 23 "cmd/go/internal/par" 24 "cmd/go/internal/search" 25 "cmd/go/internal/semver" 26 ) 27 28 type ImportMissingError struct { 29 ImportPath string 30 Module module.Version 31 } 32 33 func (e *ImportMissingError) Error() string { 34 if e.Module.Path == "" { 35 return "cannot find module providing package " + e.ImportPath 36 } 37 return "missing module for import: " + e.Module.Path + "@" + e.Module.Version + " provides " + e.ImportPath 38 } 39 40 // Import finds the module and directory in the build list 41 // containing the package with the given import path. 42 // The answer must be unique: Import returns an error 43 // if multiple modules attempt to provide the same package. 44 // Import can return a module with an empty m.Path, for packages in the standard library. 45 // Import can return an empty directory string, for fake packages like "C" and "unsafe". 46 // 47 // If the package cannot be found in the current build list, 48 // Import returns an ImportMissingError as the error. 49 // If Import can identify a module that could be added to supply the package, 50 // the ImportMissingError records that module. 51 func Import(path string) (m module.Version, dir string, err error) { 52 if strings.Contains(path, "@") { 53 return module.Version{}, "", fmt.Errorf("import path should not have @version") 54 } 55 if build.IsLocalImport(path) { 56 return module.Version{}, "", fmt.Errorf("relative import not supported") 57 } 58 if path == "C" || path == "unsafe" { 59 // There's no directory for import "C" or import "unsafe". 60 return module.Version{}, "", nil 61 } 62 63 // Is the package in the standard library? 64 if search.IsStandardImportPath(path) { 65 if goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { 66 dir := filepath.Join(cfg.GOROOT, "src", path) 67 return module.Version{}, dir, nil 68 } 69 } 70 71 // -mod=vendor is special. 72 // Everything must be in the main module or the main module's vendor directory. 73 if cfg.BuildMod == "vendor" { 74 mainDir, mainOK := dirInModule(path, Target.Path, ModRoot(), true) 75 vendorDir, vendorOK := dirInModule(path, "", filepath.Join(ModRoot(), "vendor"), false) 76 if mainOK && vendorOK { 77 return module.Version{}, "", fmt.Errorf("ambiguous import: found %s in multiple directories:\n\t%s\n\t%s", path, mainDir, vendorDir) 78 } 79 // Prefer to return main directory if there is one, 80 // Note that we're not checking that the package exists. 81 // We'll leave that for load. 82 if !vendorOK && mainDir != "" { 83 return Target, mainDir, nil 84 } 85 readVendorList() 86 return vendorMap[path], vendorDir, nil 87 } 88 89 // Check each module on the build list. 90 var dirs []string 91 var mods []module.Version 92 for _, m := range buildList { 93 if !maybeInModule(path, m.Path) { 94 // Avoid possibly downloading irrelevant modules. 95 continue 96 } 97 root, isLocal, err := fetch(m) 98 if err != nil { 99 // Report fetch error. 100 // Note that we don't know for sure this module is necessary, 101 // but it certainly _could_ provide the package, and even if we 102 // continue the loop and find the package in some other module, 103 // we need to look at this module to make sure the import is 104 // not ambiguous. 105 return module.Version{}, "", err 106 } 107 dir, ok := dirInModule(path, m.Path, root, isLocal) 108 if ok { 109 mods = append(mods, m) 110 dirs = append(dirs, dir) 111 } 112 } 113 if len(mods) == 1 { 114 return mods[0], dirs[0], nil 115 } 116 if len(mods) > 0 { 117 var buf bytes.Buffer 118 fmt.Fprintf(&buf, "ambiguous import: found %s in multiple modules:", path) 119 for i, m := range mods { 120 fmt.Fprintf(&buf, "\n\t%s", m.Path) 121 if m.Version != "" { 122 fmt.Fprintf(&buf, " %s", m.Version) 123 } 124 fmt.Fprintf(&buf, " (%s)", dirs[i]) 125 } 126 return module.Version{}, "", errors.New(buf.String()) 127 } 128 129 // Look up module containing the package, for addition to the build list. 130 // Goal is to determine the module, download it to dir, and return m, dir, ErrMissing. 131 if cfg.BuildMod == "readonly" { 132 return module.Version{}, "", fmt.Errorf("import lookup disabled by -mod=%s", cfg.BuildMod) 133 } 134 135 // Not on build list. 136 // To avoid spurious remote fetches, next try the latest replacement for each module. 137 // (golang.org/issue/26241) 138 if modFile != nil { 139 latest := map[string]string{} // path -> version 140 for _, r := range modFile.Replace { 141 if maybeInModule(path, r.Old.Path) { 142 latest[r.Old.Path] = semver.Max(r.Old.Version, latest[r.Old.Path]) 143 } 144 } 145 146 mods = make([]module.Version, 0, len(latest)) 147 for p, v := range latest { 148 // If the replacement didn't specify a version, synthesize a 149 // pseudo-version with an appropriate major version and a timestamp below 150 // any real timestamp. That way, if the main module is used from within 151 // some other module, the user will be able to upgrade the requirement to 152 // any real version they choose. 153 if v == "" { 154 if _, pathMajor, ok := module.SplitPathVersion(p); ok && len(pathMajor) > 0 { 155 v = modfetch.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000") 156 } else { 157 v = modfetch.PseudoVersion("v0", "", time.Time{}, "000000000000") 158 } 159 } 160 mods = append(mods, module.Version{Path: p, Version: v}) 161 } 162 163 // Every module path in mods is a prefix of the import path. 164 // As in QueryPackage, prefer the longest prefix that satisfies the import. 165 sort.Slice(mods, func(i, j int) bool { 166 return len(mods[i].Path) > len(mods[j].Path) 167 }) 168 for _, m := range mods { 169 root, isLocal, err := fetch(m) 170 if err != nil { 171 // Report fetch error as above. 172 return module.Version{}, "", err 173 } 174 _, ok := dirInModule(path, m.Path, root, isLocal) 175 if ok { 176 return m, "", &ImportMissingError{ImportPath: path, Module: m} 177 } 178 } 179 } 180 181 m, _, err = QueryPackage(path, "latest", Allowed) 182 if err != nil { 183 if _, ok := err.(*codehost.VCSError); ok { 184 return module.Version{}, "", err 185 } 186 return module.Version{}, "", &ImportMissingError{ImportPath: path} 187 } 188 return m, "", &ImportMissingError{ImportPath: path, Module: m} 189 } 190 191 // maybeInModule reports whether, syntactically, 192 // a package with the given import path could be supplied 193 // by a module with the given module path (mpath). 194 func maybeInModule(path, mpath string) bool { 195 return mpath == path || 196 len(path) > len(mpath) && path[len(mpath)] == '/' && path[:len(mpath)] == mpath 197 } 198 199 var haveGoModCache, haveGoFilesCache par.Cache 200 201 // dirInModule locates the directory that would hold the package named by the given path, 202 // if it were in the module with module path mpath and root mdir. 203 // If path is syntactically not within mpath, 204 // or if mdir is a local file tree (isLocal == true) and the directory 205 // that would hold path is in a sub-module (covered by a go.mod below mdir), 206 // dirInModule returns "", false. 207 // 208 // Otherwise, dirInModule returns the name of the directory where 209 // Go source files would be expected, along with a boolean indicating 210 // whether there are in fact Go source files in that directory. 211 func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFiles bool) { 212 // Determine where to expect the package. 213 if path == mpath { 214 dir = mdir 215 } else if mpath == "" { // vendor directory 216 dir = filepath.Join(mdir, path) 217 } else if len(path) > len(mpath) && path[len(mpath)] == '/' && path[:len(mpath)] == mpath { 218 dir = filepath.Join(mdir, path[len(mpath)+1:]) 219 } else { 220 return "", false 221 } 222 223 // Check that there aren't other modules in the way. 224 // This check is unnecessary inside the module cache 225 // and important to skip in the vendor directory, 226 // where all the module trees have been overlaid. 227 // So we only check local module trees 228 // (the main module, and any directory trees pointed at by replace directives). 229 if isLocal { 230 for d := dir; d != mdir && len(d) > len(mdir); { 231 haveGoMod := haveGoModCache.Do(d, func() interface{} { 232 _, err := os.Stat(filepath.Join(d, "go.mod")) 233 return err == nil 234 }).(bool) 235 236 if haveGoMod { 237 return "", false 238 } 239 parent := filepath.Dir(d) 240 if parent == d { 241 // Break the loop, as otherwise we'd loop 242 // forever if d=="." and mdir=="". 243 break 244 } 245 d = parent 246 } 247 } 248 249 // Now committed to returning dir (not ""). 250 251 // Are there Go source files in the directory? 252 // We don't care about build tags, not even "+build ignore". 253 // We're just looking for a plausible directory. 254 haveGoFiles = haveGoFilesCache.Do(dir, func() interface{} { 255 f, err := os.Open(dir) 256 if err != nil { 257 return false 258 } 259 defer f.Close() 260 names, _ := f.Readdirnames(-1) 261 for _, name := range names { 262 if strings.HasSuffix(name, ".go") { 263 info, err := os.Stat(filepath.Join(dir, name)) 264 if err == nil && info.Mode().IsRegular() { 265 return true 266 } 267 } 268 } 269 return false 270 }).(bool) 271 272 return dir, haveGoFiles 273 }