| package main |
| |
| import ( |
| "bytes" |
| "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 statements |
| // according to the rules for func qualify. |
| func rewrite(pkgs []*Package, qual string, paths []string) error { |
| for _, path := range pkgFiles(pkgs) { |
| debugln("rewrite", path) |
| 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 statements according to the rules for func qualify. |
| // This function ignores the 'testdata' directory. |
| 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 |
| } |
| s := w.Stat() |
| if s.IsDir() && s.Name() == "testdata" { |
| w.SkipDir() |
| continue |
| } |
| if strings.HasSuffix(w.Path(), ".go") { |
| err := rewriteGoFile(w.Path(), qual, paths) |
| if err != nil { |
| return err |
| } |
| } |
| } |
| return nil |
| } |
| |
| // rewriteGoFile rewrites import statements in the named file |
| // according to the rules for func qualify. |
| func rewriteGoFile(name, qual string, paths []string) error { |
| debugln("rewriteGoFile", name, ",", qual, ",", paths) |
| printerConfig := &printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8} |
| 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 |
| } |
| var buffer bytes.Buffer |
| if err = printerConfig.Fprint(&buffer, fset, f); err != nil { |
| return err |
| } |
| fset = token.NewFileSet() |
| f, err = parser.ParseFile(fset, name, &buffer, parser.ParseComments) |
| if err != nil { |
| return err |
| } |
| ast.SortImports(fset, f) |
| tpath := name + ".temp" |
| t, err := os.Create(tpath) |
| if err != nil { |
| return err |
| } |
| if err = printerConfig.Fprint(t, fset, f); err != nil { |
| return err |
| } |
| if err = t.Close(); err != nil { |
| return err |
| } |
| // This is required before the rename on windows. |
| if err = os.Remove(name); err != nil { |
| return err |
| } |
| return os.Rename(tpath, name) |
| } |
| |
| func defaultSep(experiment bool) string { |
| if experiment { |
| return "/vendor/" |
| } |
| return "/Godeps/_workspace/src/" |
| } |
| |
| func relativeVendorTarget(experiment bool) string { |
| full := defaultSep(experiment) |
| if full[0] == '/' { |
| full = full[1:] |
| } |
| return filepath.FromSlash(full) |
| } |
| |
| // 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 |
| } |