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