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