-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cmd/go/internal/vgo: main vgo drivers
- Loading branch information
Showing
9 changed files
with
1,891 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// Copyright 2018 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package vgo | ||
|
||
import ( | ||
"bytes" | ||
"cmd/go/internal/base" | ||
"cmd/go/internal/module" | ||
"cmd/go/internal/search" | ||
"encoding/hex" | ||
"fmt" | ||
) | ||
|
||
var ( | ||
infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6") | ||
infoEnd, _ = hex.DecodeString("f932433186182072008242104116d8f2") | ||
) | ||
|
||
func PackageModuleInfo(path string, deps []string) string { | ||
if search.IsStandardImportPath(path) { | ||
return "" | ||
} | ||
target := findModule(path, path) | ||
mdeps := make(map[module.Version]bool) | ||
for _, dep := range deps { | ||
if !search.IsStandardImportPath(dep) { | ||
mdeps[findModule(path, dep)] = true | ||
} | ||
} | ||
var mods []module.Version | ||
delete(mdeps, target) | ||
for mod := range mdeps { | ||
mods = append(mods, mod) | ||
} | ||
sortModules(mods) | ||
|
||
var buf bytes.Buffer | ||
fmt.Fprintf(&buf, "path\t%s\n", path) | ||
tv := target.Version | ||
if tv == "" { | ||
tv = "(devel)" | ||
} | ||
fmt.Fprintf(&buf, "mod\t%s\t%s\t%s\n", target.Path, tv, findModHash(target)) | ||
for _, mod := range mods { | ||
mv := mod.Version | ||
if mv == "" { | ||
mv = "(devel)" | ||
} | ||
fmt.Fprintf(&buf, "dep\t%s\t%s\t%s\n", mod.Path, mod.Version, findModHash(mod)) | ||
} | ||
return buf.String() | ||
} | ||
|
||
func findModule(target, path string) module.Version { | ||
if path == "." { | ||
return buildList[0] | ||
} | ||
for _, mod := range buildList { | ||
if importPathInModule(path, mod.Path) { | ||
return mod | ||
} | ||
} | ||
base.Fatalf("build %v: cannot find module for path %v", target, path) | ||
panic("unreachable") | ||
} | ||
|
||
func ModInfoProg(info string) []byte { | ||
return []byte(fmt.Sprintf(` | ||
package main | ||
import _ "unsafe" | ||
//go:linkname __debug_modinfo__ runtime/debug.modinfo | ||
var __debug_modinfo__ string | ||
func init() { | ||
__debug_modinfo__ = %q | ||
} | ||
`, string(infoStart)+info+string(infoEnd))) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
// Copyright 2018 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package vgo | ||
|
||
import ( | ||
"archive/zip" | ||
"bytes" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"sort" | ||
"strings" | ||
|
||
"cmd/go/internal/base" | ||
"cmd/go/internal/dirhash" | ||
"cmd/go/internal/modfetch" | ||
"cmd/go/internal/module" | ||
"cmd/go/internal/semver" | ||
) | ||
|
||
// fetch returns the directory in the local download cache | ||
// holding the root of mod's source tree. | ||
// It downloads the module if needed. | ||
func fetch(mod module.Version) (dir string, err error) { | ||
if r := replaced(mod); r != nil { | ||
if r.New.Version == "" { | ||
dir = r.New.Path | ||
if !filepath.IsAbs(dir) { | ||
dir = filepath.Join(ModRoot, dir) | ||
} | ||
return dir, nil | ||
} | ||
mod = r.New | ||
} | ||
|
||
modpath := mod.Path + "@" + mod.Version | ||
dir = filepath.Join(srcV, modpath) | ||
if files, _ := ioutil.ReadDir(dir); len(files) == 0 { | ||
zipfile := filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".zip") | ||
if _, err := os.Stat(zipfile); err == nil { | ||
// Use it. | ||
// This should only happen if the v/cache directory is preinitialized | ||
// or if src/v/modpath was removed but not src/v/cache. | ||
fmt.Fprintf(os.Stderr, "vgo: extracting %s %s\n", mod.Path, mod.Version) | ||
} else { | ||
if err := os.MkdirAll(filepath.Join(srcV, "cache", mod.Path, "@v"), 0777); err != nil { | ||
return "", err | ||
} | ||
fmt.Fprintf(os.Stderr, "vgo: downloading %s %s\n", mod.Path, mod.Version) | ||
if err := downloadZip(mod, zipfile); err != nil { | ||
return "", err | ||
} | ||
} | ||
if err := modfetch.Unzip(dir, zipfile, modpath, 0); err != nil { | ||
fmt.Fprintf(os.Stderr, "-> %s\n", err) | ||
return "", err | ||
} | ||
} | ||
checkModHash(mod) | ||
return dir, nil | ||
} | ||
|
||
func downloadZip(mod module.Version, target string) error { | ||
repo, err := modfetch.Lookup(mod.Path) | ||
if err != nil { | ||
return err | ||
} | ||
tmpfile, err := repo.Zip(mod.Version, os.TempDir()) | ||
if err != nil { | ||
return err | ||
} | ||
defer os.Remove(tmpfile) | ||
|
||
// Double-check zip file looks OK. | ||
z, err := zip.OpenReader(tmpfile) | ||
if err != nil { | ||
z.Close() | ||
return err | ||
} | ||
prefix := mod.Path + "@" + mod.Version | ||
for _, f := range z.File { | ||
if !strings.HasPrefix(f.Name, prefix) { | ||
z.Close() | ||
return fmt.Errorf("zip for %s has unexpected file %s", prefix[:len(prefix)-1], f.Name) | ||
} | ||
} | ||
z.Close() | ||
|
||
hash, err := dirhash.HashZip(tmpfile, dirhash.DefaultHash) | ||
if err != nil { | ||
return err | ||
} | ||
r, err := os.Open(tmpfile) | ||
if err != nil { | ||
return err | ||
} | ||
defer r.Close() | ||
w, err := os.Create(target) | ||
if err != nil { | ||
return err | ||
} | ||
if _, err := io.Copy(w, r); err != nil { | ||
w.Close() | ||
return fmt.Errorf("copying: %v", err) | ||
} | ||
if err := w.Close(); err != nil { | ||
return err | ||
} | ||
if err := ioutil.WriteFile(target+"hash", []byte(hash), 0666); err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
var useModHash = false | ||
var modHash map[module.Version][]string | ||
|
||
func initModHash() { | ||
if modHash != nil { | ||
return | ||
} | ||
modHash = make(map[module.Version][]string) | ||
file := filepath.Join(ModRoot, "go.modverify") | ||
data, err := ioutil.ReadFile(file) | ||
if err != nil && os.IsNotExist(err) { | ||
return | ||
} | ||
if err != nil { | ||
base.Fatalf("vgo: %v", err) | ||
} | ||
useModHash = true | ||
lineno := 0 | ||
for len(data) > 0 { | ||
var line []byte | ||
lineno++ | ||
i := bytes.IndexByte(data, '\n') | ||
if i < 0 { | ||
line, data = data, nil | ||
} else { | ||
line, data = data[:i], data[i+1:] | ||
} | ||
f := strings.Fields(string(line)) | ||
if len(f) != 3 { | ||
base.Fatalf("vgo: malformed go.modverify:\n%s:%d: wrong number of fields", file, lineno) | ||
} | ||
mod := module.Version{Path: f[0], Version: f[1]} | ||
modHash[mod] = append(modHash[mod], f[2]) | ||
} | ||
} | ||
|
||
func checkModHash(mod module.Version) { | ||
initModHash() | ||
if !useModHash { | ||
return | ||
} | ||
|
||
data, err := ioutil.ReadFile(filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".ziphash")) | ||
if err != nil { | ||
base.Fatalf("vgo: verifying %s %s: %v", mod.Path, mod.Version, err) | ||
} | ||
h := strings.TrimSpace(string(data)) | ||
if !strings.HasPrefix(h, "h1:") { | ||
base.Fatalf("vgo: verifying %s %s: unexpected ziphash: %q", mod.Path, mod.Version, h) | ||
} | ||
|
||
for _, vh := range modHash[mod] { | ||
if h == vh { | ||
return | ||
} | ||
if strings.HasPrefix(vh, "h1:") { | ||
base.Fatalf("vgo: verifying %s %s: module hash mismatch\n\tdownloaded: %v\n\tgo.modverify: %v", mod.Path, mod.Version, h, vh) | ||
} | ||
} | ||
if len(modHash[mod]) > 0 { | ||
fmt.Fprintf(os.Stderr, "warning: verifying %s %s: unknown hashes in go.modverify: %v; adding %v", mod.Path, mod.Version, strings.Join(modHash[mod], ", "), h) | ||
} | ||
modHash[mod] = append(modHash[mod], h) | ||
} | ||
|
||
func findModHash(mod module.Version) string { | ||
data, err := ioutil.ReadFile(filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".ziphash")) | ||
if err != nil { | ||
return "" | ||
} | ||
return strings.TrimSpace(string(data)) | ||
} | ||
|
||
func writeModHash() { | ||
if !useModHash { | ||
return | ||
} | ||
|
||
var mods []module.Version | ||
for m := range modHash { | ||
mods = append(mods, m) | ||
} | ||
sortModules(mods) | ||
var buf bytes.Buffer | ||
for _, m := range mods { | ||
list := modHash[m] | ||
sort.Strings(list) | ||
for _, h := range list { | ||
fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h) | ||
} | ||
} | ||
|
||
file := filepath.Join(ModRoot, "go.modverify") | ||
data, _ := ioutil.ReadFile(filepath.Join(ModRoot, "go.modverify")) | ||
if bytes.Equal(data, buf.Bytes()) { | ||
return | ||
} | ||
|
||
if err := ioutil.WriteFile(file, buf.Bytes(), 0666); err != nil { | ||
base.Fatalf("vgo: writing go.modverify: %v", err) | ||
} | ||
} | ||
|
||
func sortModules(mods []module.Version) { | ||
sort.Slice(mods, func(i, j int) bool { | ||
mi := mods[i] | ||
mj := mods[j] | ||
if mi.Path != mj.Path { | ||
return mi.Path < mj.Path | ||
} | ||
return semver.Compare(mi.Version, mj.Version) < 0 | ||
}) | ||
} |
Oops, something went wrong.