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