github.com/opsmatic/godep@v0.1.5/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  }