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  }