github.com/dajulia3/godep@v0.0.0-20150225191720-7436fa4e6fdc/rewrite.go (about) 1 package main 2 3 import ( 4 "log" 5 "os" 6 "path/filepath" 7 "strconv" 8 "strings" 9 10 "go/ast" 11 "go/parser" 12 "go/printer" 13 "go/token" 14 15 "github.com/kr/fs" 16 ) 17 18 // rewrite visits the go files in pkgs, plus all go files 19 // in the directory tree Godeps, rewriting import statments 20 // according to the rules for func qualify. 21 func rewrite(pkgs []*Package, qual string, paths []string) error { 22 for _, path := range pkgFiles(pkgs) { 23 err := rewriteTree(path, qual, paths) 24 if err != nil { 25 return err 26 } 27 } 28 return rewriteTree("Godeps", qual, paths) 29 } 30 31 // pkgFiles returns the full filesystem path to all go files in pkgs. 32 func pkgFiles(pkgs []*Package) []string { 33 var a []string 34 for _, pkg := range pkgs { 35 for _, s := range pkg.allGoFiles() { 36 a = append(a, filepath.Join(pkg.Dir, s)) 37 } 38 } 39 return a 40 } 41 42 // rewriteTree recursively visits the go files in path, rewriting 43 // import statments according to the rules for func qualify. 44 func rewriteTree(path, qual string, paths []string) error { 45 w := fs.Walk(path) 46 for w.Step() { 47 if w.Err() != nil { 48 log.Println("rewrite:", w.Err()) 49 continue 50 } 51 if !w.Stat().IsDir() && strings.HasSuffix(w.Path(), ".go") { 52 err := rewriteGoFile(w.Path(), qual, paths) 53 if err != nil { 54 return err 55 } 56 } 57 } 58 return nil 59 } 60 61 // rewriteGoFile rewrites import statments in the named file 62 // according to the rules for func qualify. 63 func rewriteGoFile(name, qual string, paths []string) error { 64 fset := token.NewFileSet() 65 f, err := parser.ParseFile(fset, name, nil, parser.ParseComments) 66 if err != nil { 67 return err 68 } 69 70 var changed bool 71 for _, s := range f.Imports { 72 name, err := strconv.Unquote(s.Path.Value) 73 if err != nil { 74 return err // can't happen 75 } 76 q := qualify(unqualify(name), qual, paths) 77 if q != name { 78 s.Path.Value = strconv.Quote(q) 79 changed = true 80 } 81 } 82 if !changed { 83 return nil 84 } 85 86 ast.SortImports(fset, f) 87 wpath := name + ".temp" 88 w, err := os.Create(wpath) 89 if err != nil { 90 return err 91 } 92 err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(w, fset, f) 93 if err != nil { 94 return err 95 } 96 err = w.Close() 97 if err != nil { 98 return err 99 } 100 return os.Rename(wpath, name) 101 } 102 103 // sep is the signature set of path elements that 104 // precede the original path of an imported package 105 // in a rewritten import path. 106 const sep = "/Godeps/_workspace/src/" 107 108 // unqualify returns the part of importPath after the last 109 // occurrence of the signature path elements 110 // (Godeps/_workspace/src) that always precede imported 111 // packages in rewritten import paths. 112 // 113 // For example, 114 // unqualify(C) = C 115 // unqualify(D/Godeps/_workspace/src/C) = C 116 func unqualify(importPath string) string { 117 if i := strings.LastIndex(importPath, sep); i != -1 { 118 importPath = importPath[i+len(sep):] 119 } 120 return importPath 121 } 122 123 // qualify qualifies importPath with its corresponding import 124 // path in the Godeps src copy of package pkg. If importPath 125 // is a directory lexically contained in a path in paths, 126 // it will be qualified with package pkg; otherwise, it will 127 // be returned unchanged. 128 // 129 // For example, given paths {D, T} and pkg C, 130 // importPath returns 131 // C C 132 // fmt fmt 133 // D C/Godeps/_workspace/src/D 134 // D/P C/Godeps/_workspace/src/D/P 135 // T C/Godeps/_workspace/src/T 136 func qualify(importPath, pkg string, paths []string) string { 137 if containsPathPrefix(paths, importPath) { 138 return pkg + sep + importPath 139 } 140 return importPath 141 }