Skip to content

Commit 951eee1

Browse files
authored
Merge pull request #10 from myitcv/vgo_loader
vgo: Loader initial commit
2 parents 517b5a6 + 715b30d commit 951eee1

File tree

7 files changed

+245
-6
lines changed

7 files changed

+245
-6
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,13 @@
11
### `myitcv.io/...` mono-repo
22

3+
<!-- __TEMPLATE: go list -f "{{${DOLLAR}ip := .ImportPath}}{{range .Deps}}{{if (eq \"myitcv.io/vgo\" .)}}{{${DOLLAR}ip}}{{end}}{{end}}" ./...
4+
{{ with . }}
5+
Please note the following packages current rely on `vgo` with https://go-review.googlesource.com/c/vgo/+/105855 applied:
6+
7+
```
8+
{{. -}}
9+
```
10+
{{end -}}
11+
-->
12+
<!-- END -->
13+

_scripts/run_tests.sh

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ if [ "${CI:-}" == "true" ]
1010
then
1111
go get -u golang.org/x/vgo
1212
pushd $(go list -f "{{.Dir}}" golang.org/x/vgo) > /dev/null
13-
git checkout -qf $VGO_COMMIT
13+
14+
# git checkout -qf $VGO_COMMIT
15+
git fetch -q https://go.googlesource.com/vgo refs/changes/55/105855/3 && git checkout -qf FETCH_HEAD
1416
go install
17+
1518
popd > /dev/null
1619

1720
# so we can access Github without hitting rate limits
@@ -63,17 +66,33 @@ do
6366
then
6467
./_scripts/run_tests.sh
6568
else
69+
if [ -f ./_scripts/pre_run_tests.sh ]
70+
then
71+
./_scripts/pre_run_tests.sh
72+
fi
73+
6674
$go generate ./...
6775
$go test ./...
6876

69-
# we can remove this once we resolve https://github.com/golang/go/issues/24661
70-
$go install ./...
77+
if [ -f ./_scripts/post_run_tests.sh ]
78+
then
79+
./_scripts/post_run_tests.sh
80+
fi
7181
fi
7282
popd > /dev/null
7383
echo "----"
7484
echo ""
7585
done
7686

87+
# we use regular go to list here because of https://github.com/golang/go/issues/24749;
88+
# this is also the reason why we need to change to the directory to do the vgo install
89+
for i in $(go list -f "{{if eq .Name \"main\"}}{{.Dir}}{{end}}" ./...)
90+
do
91+
pushd $i > /dev/null
92+
$go install
93+
popd > /dev/null
94+
done
95+
7796
echo Checking markdown files are current
7897
# by this point we will have mdreplace installed. Hence check that
7998
# committed .md files are "fresh"

cmd/mdreplace/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ _To see this in action, look at the [source of the
5555
### Usage
5656
5757
```
58-
{{.}}
58+
{{. -}}
5959
```
6060
-->
6161
### Usage
@@ -69,12 +69,13 @@ Usage:
6969
When called with no file arguments, mdreplace works with stdin
7070
7171
Flags:
72+
-debug
73+
whether to print debug information of not
7274
-strip
7375
whether to strip special comments from the file
7476
-w whether to write back to input files (cannot be used when reading from
7577
stdin)
7678
77-
7879
```
7980
<!-- END -->
8081

cmd/mdreplace/mdreplace.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import (
5050
var (
5151
fWrite = flag.Bool("w", false, "whether to write back to input files (cannot be used when reading from stdin)")
5252
fStrip = flag.Bool("strip", false, "whether to strip special comments from the file")
53+
fDebug = flag.Bool("debug", false, "whether to print debug information of not")
5354
)
5455

5556
//go:generate pkgconcat -out gen_cliflag.go myitcv.io/_tmpls/cliflag
@@ -150,7 +151,7 @@ func infof(format string, args ...interface{}) {
150151
}
151152

152153
func debugf(format string, args ...interface{}) {
153-
if debug {
154+
if debug || *fDebug {
154155
fmt.Fprintf(os.Stderr, format, args...)
155156
}
156157
}

vgo/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module "myitcv.io/vgo"
2+
3+
require "golang.org/x/net" v0.0.0-20180406214816-61147c48b25b

vgo/loader.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// package vgo provides some utility types, functions etc to support vgo
2+
package vgo // import "myitcv.io/vgo"
3+
4+
import (
5+
"bytes"
6+
"encoding/json"
7+
"fmt"
8+
"go/build"
9+
"go/importer"
10+
"go/types"
11+
"io"
12+
"os"
13+
"os/exec"
14+
"strings"
15+
"sync"
16+
)
17+
18+
// Loader supports loading of vgo-build cached packages. NewLoader returns a
19+
// correctly initialised *Loader. A Loader must not be copied once created.
20+
type Loader struct {
21+
mu sync.Mutex
22+
23+
dir string
24+
compiler string
25+
resCache map[string]map[string]*types.Package
26+
importers map[string]types.ImporterFrom
27+
test bool
28+
}
29+
30+
func NewLoader(dir string) *Loader {
31+
res := &Loader{
32+
dir: dir,
33+
compiler: "gc",
34+
resCache: make(map[string]map[string]*types.Package),
35+
importers: make(map[string]types.ImporterFrom),
36+
}
37+
38+
return res
39+
}
40+
41+
func NewTestLoader(dir string) *Loader {
42+
res := NewLoader(dir)
43+
res.test = true
44+
return res
45+
}
46+
47+
var _ types.ImporterFrom = new(Loader)
48+
49+
func (l *Loader) Import(path string) (*types.Package, error) {
50+
return nil, fmt.Errorf("did not expect this method to be used; we implement types.ImporterFrom")
51+
}
52+
53+
func (l *Loader) ImportFrom(path, dir string, mode types.ImportMode) (*types.Package, error) {
54+
if mode != 0 {
55+
panic(fmt.Errorf("unknown types.ImportMode %v", mode))
56+
}
57+
58+
l.mu.Lock()
59+
defer l.mu.Unlock()
60+
61+
// TODO optimise mutex usage later... keep it simple for now
62+
dirCache, ok := l.resCache[dir]
63+
if ok {
64+
if p, ok := dirCache[path]; ok {
65+
return p, nil
66+
}
67+
} else {
68+
// ensures dirCache is now set
69+
dirCache = make(map[string]*types.Package)
70+
l.resCache[dir] = dirCache
71+
}
72+
73+
// res cache miss
74+
imp, ok := l.importers[dir]
75+
if !ok {
76+
// we need to load the results for this dir and build an importer
77+
78+
// resolve the package found in dir
79+
bpkg, err := build.ImportDir(dir, 0)
80+
if err != nil {
81+
return nil, fmt.Errorf("unable to resolve %v to a package: %v", dir, err)
82+
}
83+
84+
// now run vgo depbuildlist with the import path
85+
args := []string{"vgo", "deplist", "-build"}
86+
87+
if l.test {
88+
args = append(args, "-test")
89+
}
90+
91+
args = append(args, bpkg.ImportPath)
92+
93+
cmd := exec.Command(args[0], args[1:]...)
94+
cmd.Dir = l.dir
95+
96+
out, err := cmd.CombinedOutput()
97+
if err != nil {
98+
return nil, fmt.Errorf("unable to run %v: %v [%q]", strings.Join(cmd.Args, " "), err, string(out))
99+
}
100+
101+
// parse the JSON
102+
103+
lookup := make(map[string]string)
104+
105+
dec := json.NewDecoder(bytes.NewBuffer(out))
106+
107+
for {
108+
var d struct {
109+
ImportPath string
110+
PackageFile string
111+
}
112+
113+
if err := dec.Decode(&d); err != nil {
114+
if err == io.EOF {
115+
break
116+
}
117+
118+
return nil, fmt.Errorf("failed to parse vgo output: %v\noutput was:\n%v", err, string(out))
119+
}
120+
121+
lookup[d.ImportPath] = d.PackageFile
122+
}
123+
124+
i := importer.For(l.compiler, func(path string) (io.ReadCloser, error) {
125+
file, ok := lookup[path]
126+
if !ok {
127+
return nil, fmt.Errorf("failed to resolve import path %q", path)
128+
}
129+
130+
f, err := os.Open(file)
131+
if err != nil {
132+
return nil, fmt.Errorf("failed to open file %v: %v", file, err)
133+
}
134+
135+
return f, nil
136+
})
137+
138+
from, ok := i.(types.ImporterFrom)
139+
if !ok {
140+
return nil, fmt.Errorf("failed to get an importer that implements go/types.ImporterFrom; got %T", i)
141+
}
142+
143+
imp = from
144+
l.importers[dir] = imp
145+
}
146+
147+
p, err := imp.ImportFrom(path, dir, mode)
148+
if err != nil {
149+
return nil, fmt.Errorf("failed to import: %v", err)
150+
}
151+
152+
dirCache[path] = p
153+
154+
return p, nil
155+
}

vgo/loader_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package vgo_test
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
// import a non-standard library package for its side effects.
8+
// vgo will then detect this
9+
_ "golang.org/x/net/html"
10+
"myitcv.io/vgo"
11+
)
12+
13+
func TestLoader(t *testing.T) {
14+
// given the side-effect import above, we can now create a Loader
15+
// to load "golang.org/x/net/html" in the context of the current
16+
// directory
17+
18+
l := vgo.NewTestLoader(".")
19+
20+
cwd, err := os.Getwd()
21+
if err != nil {
22+
t.Fatalf("failed to get cwd: %v", cwd)
23+
}
24+
25+
cases := []string{
26+
"golang.org/x/net/html",
27+
28+
// this is a dependency of x/net/html; hence an indirect
29+
// test dependency of vgo_test
30+
"golang.org/x/net/html/atom",
31+
}
32+
33+
for _, c := range cases {
34+
t.Run(c, func(t *testing.T) {
35+
p, err := l.ImportFrom(c, cwd, 0)
36+
if err != nil {
37+
t.Fatalf("unexpected error: %v", err)
38+
}
39+
40+
if p == nil {
41+
t.Fatal("expected response; got nil")
42+
}
43+
44+
if v := p.Path(); v != c {
45+
t.Fatalf("expected ImportPath %q; got %q", c, v)
46+
}
47+
})
48+
}
49+
}

0 commit comments

Comments
 (0)