forked from tools/godep
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rewrite.go
141 lines (129 loc) · 3.43 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
package main
import (
"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 statments
// according to the rules for func qualify.
func rewrite(pkgs []*Package, qual string, paths []string) error {
for _, path := range pkgFiles(pkgs) {
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 statments according to the rules for func qualify.
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
}
if !w.Stat().IsDir() && strings.HasSuffix(w.Path(), ".go") {
err := rewriteGoFile(w.Path(), qual, paths)
if err != nil {
return err
}
}
}
return nil
}
// rewriteGoFile rewrites import statments in the named file
// according to the rules for func qualify.
func rewriteGoFile(name, qual string, paths []string) error {
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
}
ast.SortImports(fset, f)
wpath := name + ".temp"
w, err := os.Create(wpath)
if err != nil {
return err
}
err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(w, fset, f)
if err != nil {
return err
}
err = w.Close()
if err != nil {
return err
}
return os.Rename(wpath, name)
}
// sep is the signature set of path elements that
// precede the original path of an imported package
// in a rewritten import path.
const sep = "/Godeps/_workspace/src/"
// 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
}