github.com/alexanderthaller/godep@v0.0.0-20141231210904-0baa7ea46402/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  }