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.
17 "cmd/go/internal/base"
19 "cmd/go/internal/modfetch"
20 "cmd/go/internal/modinfo"
21 "cmd/go/internal/search"
23 "golang.org/x/mod/module"
24 "golang.org/x/mod/semver"
28 infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6")
29 infoEnd, _ = hex.DecodeString("f932433186182072008242104116d8f2")
32 func isStandardImportPath(path string) bool {
33 return findStandardImportPath(path) != ""
36 func findStandardImportPath(path string) string {
38 panic("findStandardImportPath called with empty path")
40 if search.IsStandardImportPath(path) {
41 if goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) {
42 return filepath.Join(cfg.GOROOT, "src", path)
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() {
56 m, ok := findModule(pkgpath)
60 return moduleInfo(m, true)
63 func ModuleInfo(path string) *modinfo.ModulePublic {
68 if i := strings.Index(path, "@"); i >= 0 {
69 return moduleInfo(module.Version{Path: path[:i], Version: path[i+1:]}, false)
72 for _, m := range BuildList() {
74 return moduleInfo(m, true)
78 return &modinfo.ModulePublic{
80 Error: &modinfo.ModuleError{
81 Err: "module not in current build",
86 // addUpdate fills in m.Update if an updated version is available.
87 func addUpdate(m *modinfo.ModulePublic) {
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{
95 Version: info.Version,
101 // addVersions fills in m.Versions with the list of known versions.
102 func addVersions(m *modinfo.ModulePublic) {
103 m.Versions, _ = versions(m.Path)
106 func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
108 info := &modinfo.ModulePublic{
115 info.GoMod = ModFilePath()
116 if modFile.Go != nil {
117 info.GoVersion = modFile.Go.Version
123 info := &modinfo.ModulePublic{
126 Indirect: fromBuildList && loaded != nil && !loaded.direct[m.Path],
129 info.GoVersion = loaded.goVersion[m.Path]
132 // completeFromModCache fills in the extra fields in m using the module cache.
133 completeFromModCache := func(m *modinfo.ModulePublic) {
135 if q, err := Query(m.Path, m.Version, "", nil); err != nil {
136 m.Error = &modinfo.ModuleError{Err: err.Error()}
138 m.Version = q.Version
142 mod := module.Version{Path: m.Path, Version: m.Version}
143 gomod, err := modfetch.CachePath(mod, "mod")
145 if info, err := os.Stat(gomod); err == nil && info.Mode().IsRegular() {
149 dir, err := modfetch.DownloadDir(mod)
157 completeFromModCache(info) // Will set m.Error in vendor mode.
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.
170 completeFromModCache(info)
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{
182 GoVersion: info.GoVersion,
185 if filepath.IsAbs(r.Path) {
186 info.Replace.Dir = r.Path
188 info.Replace.Dir = filepath.Join(ModRoot(), r.Path)
190 info.Replace.GoMod = filepath.Join(info.Replace.Dir, "go.mod")
192 if cfg.BuildMod != "vendor" {
193 completeFromModCache(info.Replace)
194 info.Dir = info.Replace.Dir
195 info.GoMod = info.Replace.GoMod
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() {
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
215 var mods []module.Version
216 delete(mdeps, target)
217 for mod := range mdeps {
218 mods = append(mods, mod)
223 fmt.Fprintf(&buf, "path\t%s\n", path)
228 fmt.Fprintf(&buf, "mod\t%s\t%s\t%s\n", target.Path, tv, modfetch.Sum(target))
229 for _, mod := range mods {
234 r := Replacement(mod)
237 h = "\t" + modfetch.Sum(mod)
239 fmt.Fprintf(&buf, "dep\t%s\t%s%s\n", mod.Path, mv, h)
241 fmt.Fprintf(&buf, "=>\t%s\t%s\t%s\n", r.Path, r.Version, modfetch.Sum(r))
247 // mustFindModule is like findModule, but it calls base.Fatalf if the
248 // module can't be found.
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)
256 base.Fatalf("build %v: cannot load %v: %v", target, path, pkg.err)
261 if path == "command-line-arguments" {
268 base.Fatalf("build %v: cannot find module for path %v", target, path)
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{}
280 if path == "command-line-arguments" {
283 return module.Version{}, false
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.
292 // The runtime startup code refers to the variable, which keeps it live
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.
303 return []byte(fmt.Sprintf(`package main
305 //go:linkname __set_modinfo__ runtime.setmodinfo
306 func __set_modinfo__(string)
307 func init() { __set_modinfo__(%q) }
308 `, string(infoStart)+info+string(infoEnd)))
310 return []byte(fmt.Sprintf(`package main
312 //go:linkname __set_debug_modinfo__ runtime.setmodinfo
313 func __set_debug_modinfo__(string)
314 func init() { __set_debug_modinfo__(%q) }
315 `, string(infoStart)+info+string(infoEnd)))