github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/modload/build.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 "encoding/hex" 10 "fmt" 11 "github.com/gagliardetto/golang-go/not-internal/goroot" 12 "os" 13 "path/filepath" 14 "runtime/debug" 15 "strings" 16 17 "github.com/gagliardetto/golang-go/cmd/go/not-internal/base" 18 "github.com/gagliardetto/golang-go/cmd/go/not-internal/cfg" 19 "github.com/gagliardetto/golang-go/cmd/go/not-internal/modfetch" 20 "github.com/gagliardetto/golang-go/cmd/go/not-internal/modinfo" 21 "github.com/gagliardetto/golang-go/cmd/go/not-internal/search" 22 23 "golang.org/x/mod/module" 24 "golang.org/x/mod/semver" 25 ) 26 27 var ( 28 infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6") 29 infoEnd, _ = hex.DecodeString("f932433186182072008242104116d8f2") 30 ) 31 32 func isStandardImportPath(path string) bool { 33 return findStandardImportPath(path) != "" 34 } 35 36 func findStandardImportPath(path string) string { 37 if path == "" { 38 panic("findStandardImportPath called with empty path") 39 } 40 if search.IsStandardImportPath(path) { 41 if goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { 42 return filepath.Join(cfg.GOROOT, "src", path) 43 } 44 } 45 return "" 46 } 47 48 // PackageModuleInfo returns information about the module that provides 49 // a given package. If modules are not enabled or if the package is in the 50 // standard library or if the package was not successfully loaded with 51 // ImportPaths or a similar loading function, nil is returned. 52 func PackageModuleInfo(pkgpath string) *modinfo.ModulePublic { 53 if isStandardImportPath(pkgpath) || !Enabled() { 54 return nil 55 } 56 m, ok := findModule(pkgpath) 57 if !ok { 58 return nil 59 } 60 return moduleInfo(m, true) 61 } 62 63 func ModuleInfo(path string) *modinfo.ModulePublic { 64 if !Enabled() { 65 return nil 66 } 67 68 if i := strings.Index(path, "@"); i >= 0 { 69 return moduleInfo(module.Version{Path: path[:i], Version: path[i+1:]}, false) 70 } 71 72 for _, m := range BuildList() { 73 if m.Path == path { 74 return moduleInfo(m, true) 75 } 76 } 77 78 return &modinfo.ModulePublic{ 79 Path: path, 80 Error: &modinfo.ModuleError{ 81 Err: "module not in current build", 82 }, 83 } 84 } 85 86 // addUpdate fills in m.Update if an updated version is available. 87 func addUpdate(m *modinfo.ModulePublic) { 88 if m.Version == "" { 89 return 90 } 91 92 if info, err := Query(m.Path, "upgrade", m.Version, Allowed); err == nil && semver.Compare(info.Version, m.Version) > 0 { 93 m.Update = &modinfo.ModulePublic{ 94 Path: m.Path, 95 Version: info.Version, 96 Time: &info.Time, 97 } 98 } 99 } 100 101 // addVersions fills in m.Versions with the list of known versions. 102 func addVersions(m *modinfo.ModulePublic) { 103 m.Versions, _ = versions(m.Path) 104 } 105 106 func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic { 107 if m == Target { 108 info := &modinfo.ModulePublic{ 109 Path: m.Path, 110 Version: m.Version, 111 Main: true, 112 } 113 if HasModRoot() { 114 info.Dir = ModRoot() 115 info.GoMod = ModFilePath() 116 if modFile.Go != nil { 117 info.GoVersion = modFile.Go.Version 118 } 119 } 120 return info 121 } 122 123 info := &modinfo.ModulePublic{ 124 Path: m.Path, 125 Version: m.Version, 126 Indirect: fromBuildList && loaded != nil && !loaded.direct[m.Path], 127 } 128 if loaded != nil { 129 info.GoVersion = loaded.goVersion[m.Path] 130 } 131 132 // completeFromModCache fills in the extra fields in m using the module cache. 133 completeFromModCache := func(m *modinfo.ModulePublic) { 134 if m.Version != "" { 135 if q, err := Query(m.Path, m.Version, "", nil); err != nil { 136 m.Error = &modinfo.ModuleError{Err: err.Error()} 137 } else { 138 m.Version = q.Version 139 m.Time = &q.Time 140 } 141 142 mod := module.Version{Path: m.Path, Version: m.Version} 143 gomod, err := modfetch.CachePath(mod, "mod") 144 if err == nil { 145 if info, err := os.Stat(gomod); err == nil && info.Mode().IsRegular() { 146 m.GoMod = gomod 147 } 148 } 149 dir, err := modfetch.DownloadDir(mod) 150 if err == nil { 151 m.Dir = dir 152 } 153 } 154 } 155 156 if !fromBuildList { 157 completeFromModCache(info) // Will set m.Error in vendor mode. 158 return info 159 } 160 161 r := Replacement(m) 162 if r.Path == "" { 163 if cfg.BuildMod == "vendor" { 164 // It's tempting to fill in the "Dir" field to point within the vendor 165 // directory, but that would be misleading: the vendor directory contains 166 // a flattened package tree, not complete modules, and it can even 167 // interleave packages from different modules if one module path is a 168 // prefix of the other. 169 } else { 170 completeFromModCache(info) 171 } 172 return info 173 } 174 175 // Don't hit the network to fill in extra data for replaced modules. 176 // The original resolved Version and Time don't matter enough to be 177 // worth the cost, and we're going to overwrite the GoMod and Dir from the 178 // replacement anyway. See https://golang.org/issue/27859. 179 info.Replace = &modinfo.ModulePublic{ 180 Path: r.Path, 181 Version: r.Version, 182 GoVersion: info.GoVersion, 183 } 184 if r.Version == "" { 185 if filepath.IsAbs(r.Path) { 186 info.Replace.Dir = r.Path 187 } else { 188 info.Replace.Dir = filepath.Join(ModRoot(), r.Path) 189 } 190 info.Replace.GoMod = filepath.Join(info.Replace.Dir, "go.mod") 191 } 192 if cfg.BuildMod != "vendor" { 193 completeFromModCache(info.Replace) 194 info.Dir = info.Replace.Dir 195 info.GoMod = info.Replace.GoMod 196 } 197 return info 198 } 199 200 // PackageBuildInfo returns a string containing module version information 201 // for modules providing packages named by path and deps. path and deps must 202 // name packages that were resolved successfully with ImportPaths or one of 203 // the Load functions. 204 func PackageBuildInfo(path string, deps []string) string { 205 if isStandardImportPath(path) || !Enabled() { 206 return "" 207 } 208 target := mustFindModule(path, path) 209 mdeps := make(map[module.Version]bool) 210 for _, dep := range deps { 211 if !isStandardImportPath(dep) { 212 mdeps[mustFindModule(path, dep)] = true 213 } 214 } 215 var mods []module.Version 216 delete(mdeps, target) 217 for mod := range mdeps { 218 mods = append(mods, mod) 219 } 220 module.Sort(mods) 221 222 var buf bytes.Buffer 223 fmt.Fprintf(&buf, "path\t%s\n", path) 224 tv := target.Version 225 if tv == "" { 226 tv = "(devel)" 227 } 228 fmt.Fprintf(&buf, "mod\t%s\t%s\t%s\n", target.Path, tv, modfetch.Sum(target)) 229 for _, mod := range mods { 230 mv := mod.Version 231 if mv == "" { 232 mv = "(devel)" 233 } 234 r := Replacement(mod) 235 h := "" 236 if r.Path == "" { 237 h = "\t" + modfetch.Sum(mod) 238 } 239 fmt.Fprintf(&buf, "dep\t%s\t%s%s\n", mod.Path, mv, h) 240 if r.Path != "" { 241 fmt.Fprintf(&buf, "=>\t%s\t%s\t%s\n", r.Path, r.Version, modfetch.Sum(r)) 242 } 243 } 244 return buf.String() 245 } 246 247 // mustFindModule is like findModule, but it calls base.Fatalf if the 248 // module can't be found. 249 // 250 // TODO(jayconrod): remove this. Callers should use findModule and return 251 // errors instead of relying on base.Fatalf. 252 func mustFindModule(target, path string) module.Version { 253 pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) 254 if ok { 255 if pkg.err != nil { 256 base.Fatalf("build %v: cannot load %v: %v", target, path, pkg.err) 257 } 258 return pkg.mod 259 } 260 261 if path == "command-line-arguments" { 262 return Target 263 } 264 265 if printStackInDie { 266 debug.PrintStack() 267 } 268 base.Fatalf("build %v: cannot find module for path %v", target, path) 269 panic("unreachable") 270 } 271 272 // findModule searches for the module that contains the package at path. 273 // If the package was loaded with ImportPaths or one of the other loading 274 // functions, its containing module and true are returned. Otherwise, 275 // module.Version{} and false are returend. 276 func findModule(path string) (module.Version, bool) { 277 if pkg, ok := loaded.pkgCache.Get(path).(*loadPkg); ok { 278 return pkg.mod, pkg.mod != module.Version{} 279 } 280 if path == "command-line-arguments" { 281 return Target, true 282 } 283 return module.Version{}, false 284 } 285 286 func ModInfoProg(info string, isgccgo bool) []byte { 287 // Inject a variable with the debug information as runtime.modinfo, 288 // but compile it in package main so that it is specific to the binary. 289 // The variable must be a literal so that it will have the correct value 290 // before the initializer for package main runs. 291 // 292 // The runtime startup code refers to the variable, which keeps it live 293 // in all binaries. 294 // 295 // Note: we use an alternate recipe below for gccgo (based on an 296 // init function) due to the fact that gccgo does not support 297 // applying a "//go:linkname" directive to a variable. This has 298 // drawbacks in that other packages may want to look at the module 299 // info in their init functions (see issue 29628), which won't 300 // work for gccgo. See also issue 30344. 301 302 if !isgccgo { 303 return []byte(fmt.Sprintf(`package main 304 import _ "unsafe" 305 //go:linkname __debug_modinfo__ runtime.modinfo 306 var __debug_modinfo__ = %q 307 `, string(infoStart)+info+string(infoEnd))) 308 } else { 309 return []byte(fmt.Sprintf(`package main 310 import _ "unsafe" 311 //go:linkname __set_debug_modinfo__ runtime.setmodinfo 312 func __set_debug_modinfo__(string) 313 func init() { __set_debug_modinfo__(%q) } 314 `, string(infoStart)+info+string(infoEnd))) 315 } 316 }