forked from tools/godep
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rewrite.go
168 lines (156 loc) · 4.08 KB
/
rewrite.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package main
import (
"bytes"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"github.com/kr/fs"
)
// rewrite visits the go files in pkgs, plus all go files
// in the directory tree Godeps, rewriting import statements
// according to the rules for func qualify.
func rewrite(pkgs []*Package, qual string, paths []string) error {
for _, path := range pkgFiles(pkgs) {
debugln("rewrite", path)
err := rewriteTree(path, qual, paths)
if err != nil {
return err
}
}
return rewriteTree("Godeps", qual, paths)
}
// pkgFiles returns the full filesystem path to all go files in pkgs.
func pkgFiles(pkgs []*Package) []string {
var a []string
for _, pkg := range pkgs {
for _, s := range pkg.allGoFiles() {
a = append(a, filepath.Join(pkg.Dir, s))
}
}
return a
}
// rewriteTree recursively visits the go files in path, rewriting
// import statements according to the rules for func qualify.
// This function ignores the 'testdata' directory.
func rewriteTree(path, qual string, paths []string) error {
w := fs.Walk(path)
for w.Step() {
if w.Err() != nil {
log.Println("rewrite:", w.Err())
continue
}
s := w.Stat()
if s.IsDir() && s.Name() == "testdata" {
w.SkipDir()
continue
}
if strings.HasSuffix(w.Path(), ".go") {
err := rewriteGoFile(w.Path(), qual, paths)
if err != nil {
return err
}
}
}
return nil
}
// rewriteGoFile rewrites import statements in the named file
// according to the rules for func qualify.
func rewriteGoFile(name, qual string, paths []string) error {
debugln("rewriteGoFile", name, ",", qual, ",", paths)
printerConfig := &printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, name, nil, parser.ParseComments)
if err != nil {
return err
}
var changed bool
for _, s := range f.Imports {
name, err := strconv.Unquote(s.Path.Value)
if err != nil {
return err // can't happen
}
q := qualify(unqualify(name), qual, paths)
if q != name {
s.Path.Value = strconv.Quote(q)
changed = true
}
}
if !changed {
return nil
}
var buffer bytes.Buffer
if err = printerConfig.Fprint(&buffer, fset, f); err != nil {
return err
}
fset = token.NewFileSet()
f, err = parser.ParseFile(fset, name, &buffer, parser.ParseComments)
ast.SortImports(fset, f)
tpath := name + ".temp"
t, err := os.Create(tpath)
if err != nil {
return err
}
if err = printerConfig.Fprint(t, fset, f); err != nil {
return err
}
if err = t.Close(); err != nil {
return err
}
// This is required before the rename on windows.
if err = os.Remove(name); err != nil {
return err
}
return os.Rename(tpath, name)
}
func defaultSep(experiment bool) string {
if experiment {
return "/vendor/"
}
return "/Godeps/_workspace/src/"
}
func relativeVendorTarget(experiment bool) string {
full := defaultSep(experiment)
if full[0] == '/' {
full = full[1:]
}
return filepath.FromSlash(full)
}
// unqualify returns the part of importPath after the last
// occurrence of the signature path elements
// (Godeps/_workspace/src) that always precede imported
// packages in rewritten import paths.
//
// For example,
// unqualify(C) = C
// unqualify(D/Godeps/_workspace/src/C) = C
func unqualify(importPath string) string {
if i := strings.LastIndex(importPath, sep); i != -1 {
importPath = importPath[i+len(sep):]
}
return importPath
}
// qualify qualifies importPath with its corresponding import
// path in the Godeps src copy of package pkg. If importPath
// is a directory lexically contained in a path in paths,
// it will be qualified with package pkg; otherwise, it will
// be returned unchanged.
//
// For example, given paths {D, T} and pkg C,
// importPath returns
// C C
// fmt fmt
// D C/Godeps/_workspace/src/D
// D/P C/Godeps/_workspace/src/D/P
// T C/Godeps/_workspace/src/T
func qualify(importPath, pkg string, paths []string) string {
if containsPathPrefix(paths, importPath) {
return pkg + sep + importPath
}
return importPath
}