github.com/gernest/nezuko@v0.1.2/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 "os" 12 "path/filepath" 13 "runtime/debug" 14 "strings" 15 16 "github.com/gernest/nezuko/internal/base" 17 "github.com/gernest/nezuko/internal/cfg" 18 "github.com/gernest/nezuko/internal/modfetch" 19 "github.com/gernest/nezuko/internal/modinfo" 20 "github.com/gernest/nezuko/internal/module" 21 ) 22 23 var ( 24 infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6") 25 infoEnd, _ = hex.DecodeString("f932433186182072008242104116d8f2") 26 ) 27 28 func isStandardImportPath(path string) bool { 29 return strings.HasPrefix(path, "std") 30 } 31 32 func findStandardImportPath(path string) string { 33 if path == "" { 34 panic("findStandardImportPath called with empty path") 35 } 36 //TODO(gernest) find std import 37 return "" 38 } 39 40 func PackageModuleInfo(pkgpath string) *modinfo.ModulePublic { 41 if isStandardImportPath(pkgpath) { 42 return nil 43 } 44 return moduleInfo(findModule(pkgpath, pkgpath), true) 45 } 46 47 func ModuleInfo(path string) *modinfo.ModulePublic { 48 49 if i := strings.Index(path, "@"); i >= 0 { 50 return moduleInfo(module.Version{Path: path[:i], Version: path[i+1:]}, false) 51 } 52 53 for _, m := range BuildList() { 54 if m.Path == path { 55 return moduleInfo(m, true) 56 } 57 } 58 59 return &modinfo.ModulePublic{ 60 Path: path, 61 Error: &modinfo.ModuleError{ 62 Err: "module not in current build", 63 }, 64 } 65 } 66 67 // addUpdate fills in m.Update if an updated version is available. 68 func addUpdate(m *modinfo.ModulePublic) { 69 if m.Version != "" { 70 if info, err := Query(m.Path, "latest", Allowed); err == nil && info.Version != m.Version { 71 m.Update = &modinfo.ModulePublic{ 72 Path: m.Path, 73 Version: info.Version, 74 Time: &info.Time, 75 } 76 } 77 } 78 } 79 80 // addVersions fills in m.Versions with the list of known versions. 81 func addVersions(m *modinfo.ModulePublic) { 82 m.Versions, _ = versions(m.Path) 83 } 84 85 func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic { 86 if m == Target { 87 info := &modinfo.ModulePublic{ 88 Path: m.Path, 89 Version: m.Version, 90 Main: true, 91 } 92 if HasModRoot() { 93 info.Dir = ModRoot() 94 info.GoMod = filepath.Join(info.Dir, "z.mod") 95 if modFile.Exports != nil { 96 info.Exports = modFile.Exports.Name 97 } 98 } 99 return info 100 } 101 102 info := &modinfo.ModulePublic{ 103 Path: m.Path, 104 Version: m.Version, 105 Indirect: fromBuildList && loaded != nil && !loaded.direct[m.Path], 106 } 107 if loaded != nil { 108 info.Exports = loaded.exports[m.Path] 109 } 110 111 if cfg.BuildMod == "vendor" { 112 info.Dir = filepath.Join(ModRoot(), "vendor", m.Path) 113 return info 114 } 115 116 // complete fills in the extra fields in m. 117 complete := func(m *modinfo.ModulePublic) { 118 if m.Version != "" { 119 if q, err := Query(m.Path, m.Version, nil); err != nil { 120 m.Error = &modinfo.ModuleError{Err: err.Error()} 121 } else { 122 m.Version = q.Version 123 m.Time = &q.Time 124 } 125 126 mod := module.Version{Path: m.Path, Version: m.Version} 127 gomod, err := modfetch.CachePath(mod, "mod") 128 if err == nil { 129 if info, err := os.Stat(gomod); err == nil && info.Mode().IsRegular() { 130 m.GoMod = gomod 131 } 132 } 133 dir, err := modfetch.DownloadDir(mod) 134 if err == nil { 135 if info, err := os.Stat(dir); err == nil && info.IsDir() { 136 m.Dir = dir 137 } 138 } 139 } 140 } 141 142 if !fromBuildList { 143 complete(info) 144 return info 145 } 146 147 r := Replacement(m) 148 if r.Path == "" { 149 complete(info) 150 return info 151 } 152 153 // Don't hit the network to fill in extra data for replaced modules. 154 // The original resolved Version and Time don't matter enough to be 155 // worth the cost, and we're going to overwrite the GoMod and Dir from the 156 // replacement anyway. See https://golang.org/issue/27859. 157 info.Replace = &modinfo.ModulePublic{ 158 Path: r.Path, 159 Version: r.Version, 160 Exports: info.Exports, 161 } 162 if r.Version == "" { 163 if filepath.IsAbs(r.Path) { 164 info.Replace.Dir = r.Path 165 } else { 166 info.Replace.Dir = filepath.Join(ModRoot(), r.Path) 167 } 168 } 169 complete(info.Replace) 170 info.Dir = info.Replace.Dir 171 info.GoMod = filepath.Join(info.Dir, "z.mod") 172 return info 173 } 174 175 func PackageBuildInfo(path string, deps []string) string { 176 if isStandardImportPath(path) { 177 return "" 178 } 179 180 target := findModule(path, path) 181 mdeps := make(map[module.Version]bool) 182 for _, dep := range deps { 183 if !isStandardImportPath(dep) { 184 mdeps[findModule(path, dep)] = true 185 } 186 } 187 var mods []module.Version 188 delete(mdeps, target) 189 for mod := range mdeps { 190 mods = append(mods, mod) 191 } 192 module.Sort(mods) 193 194 var buf bytes.Buffer 195 fmt.Fprintf(&buf, "path\t%s\n", path) 196 tv := target.Version 197 if tv == "" { 198 tv = "(devel)" 199 } 200 fmt.Fprintf(&buf, "mod\t%s\t%s\t%s\n", target.Path, tv, modfetch.Sum(target)) 201 for _, mod := range mods { 202 mv := mod.Version 203 if mv == "" { 204 mv = "(devel)" 205 } 206 r := Replacement(mod) 207 h := "" 208 if r.Path == "" { 209 h = "\t" + modfetch.Sum(mod) 210 } 211 fmt.Fprintf(&buf, "dep\t%s\t%s%s\n", mod.Path, mod.Version, h) 212 if r.Path != "" { 213 fmt.Fprintf(&buf, "=>\t%s\t%s\t%s\n", r.Path, r.Version, modfetch.Sum(r)) 214 } 215 } 216 return buf.String() 217 } 218 219 // findModule returns the module containing the package at path, 220 // needed to build the package at target. 221 func findModule(target, path string) module.Version { 222 pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) 223 if ok { 224 if pkg.err != nil { 225 base.Fatalf("build %v: cannot load %v: %v", target, path, pkg.err) 226 } 227 return pkg.mod 228 } 229 230 if path == "command-line-arguments" { 231 return Target 232 } 233 234 if printStackInDie { 235 debug.PrintStack() 236 } 237 base.Fatalf("build %v: cannot find module for path %v", target, path) 238 panic("unreachable") 239 } 240 241 func ModInfoProg(info string) []byte { 242 // Inject a variable with the debug information as runtime/debug.modinfo, 243 // but compile it in package main so that it is specific to the binary. 244 // 245 // The variable must be a literal so that it will have the correct value 246 // before the initializer for package main runs. 247 // 248 // We also want the value to be present even if runtime/debug.modinfo is 249 // otherwise unused in the rest of the program. Reading it in an init function 250 // suffices for now. 251 252 return []byte(fmt.Sprintf(`package main 253 import _ "unsafe" 254 //go:linkname __debug_modinfo__ runtime/debug.modinfo 255 var __debug_modinfo__ = %q 256 var keepalive_modinfo = __debug_modinfo__ 257 func init() { keepalive_modinfo = __debug_modinfo__ } 258 `, string(infoStart)+info+string(infoEnd))) 259 }