gopkg.in/tools/godep.v39@v39.0.0-20151216234527-2721f686f09e/rewrite.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"log"
     6  	"os"
     7  	"path/filepath"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"go/ast"
    12  	"go/parser"
    13  	"go/printer"
    14  	"go/token"
    15  
    16  	"github.com/tools/godep/Godeps/_workspace/src/github.com/kr/fs"
    17  )
    18  
    19  // rewrite visits the go files in pkgs, plus all go files
    20  // in the directory tree Godeps, rewriting import statments
    21  // according to the rules for func qualify.
    22  func rewrite(pkgs []*Package, qual string, paths []string) error {
    23  	for _, path := range pkgFiles(pkgs) {
    24  		err := rewriteTree(path, qual, paths)
    25  		if err != nil {
    26  			return err
    27  		}
    28  	}
    29  	return rewriteTree("Godeps", qual, paths)
    30  }
    31  
    32  // pkgFiles returns the full filesystem path to all go files in pkgs.
    33  func pkgFiles(pkgs []*Package) []string {
    34  	var a []string
    35  	for _, pkg := range pkgs {
    36  		for _, s := range pkg.allGoFiles() {
    37  			a = append(a, filepath.Join(pkg.Dir, s))
    38  		}
    39  	}
    40  	return a
    41  }
    42  
    43  // rewriteTree recursively visits the go files in path, rewriting
    44  // import statments according to the rules for func qualify.
    45  // This function ignores the 'testdata' directory.
    46  func rewriteTree(path, qual string, paths []string) error {
    47  	w := fs.Walk(path)
    48  	for w.Step() {
    49  		if w.Err() != nil {
    50  			log.Println("rewrite:", w.Err())
    51  			continue
    52  		}
    53  		s := w.Stat()
    54  		switch s.IsDir() {
    55  		case true:
    56  			if s.Name() == "testdata" {
    57  				w.SkipDir()
    58  			}
    59  		case false:
    60  			if strings.HasSuffix(w.Path(), ".go") {
    61  				err := rewriteGoFile(w.Path(), qual, paths)
    62  				if err != nil {
    63  					return err
    64  				}
    65  			}
    66  		}
    67  	}
    68  	return nil
    69  }
    70  
    71  // rewriteGoFile rewrites import statments in the named file
    72  // according to the rules for func qualify.
    73  func rewriteGoFile(name, qual string, paths []string) error {
    74  	printerConfig := &printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}
    75  	fset := token.NewFileSet()
    76  	f, err := parser.ParseFile(fset, name, nil, parser.ParseComments)
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	var changed bool
    82  	for _, s := range f.Imports {
    83  		name, err := strconv.Unquote(s.Path.Value)
    84  		if err != nil {
    85  			return err // can't happen
    86  		}
    87  		q := qualify(unqualify(name), qual, paths)
    88  		if q != name {
    89  			s.Path.Value = strconv.Quote(q)
    90  			changed = true
    91  		}
    92  	}
    93  	if !changed {
    94  		return nil
    95  	}
    96  	var buffer bytes.Buffer
    97  	if err = printerConfig.Fprint(&buffer, fset, f); err != nil {
    98  		return err
    99  	}
   100  	fset = token.NewFileSet()
   101  	f, err = parser.ParseFile(fset, name, &buffer, parser.ParseComments)
   102  	ast.SortImports(fset, f)
   103  	tpath := name + ".temp"
   104  	t, err := os.Create(tpath)
   105  	if err != nil {
   106  		return err
   107  	}
   108  	if err = printerConfig.Fprint(t, fset, f); err != nil {
   109  		return err
   110  	}
   111  	if err = t.Close(); err != nil {
   112  		return err
   113  	}
   114  	// This is required before the rename on windows.
   115  	if err = os.Remove(name); err != nil {
   116  		return err
   117  	}
   118  	return os.Rename(tpath, name)
   119  }
   120  
   121  // VendorExperiment is the Go 1.5 vendor directory experiment flag, see
   122  // https://github.com/golang/go/commit/183cc0cd41f06f83cb7a2490a499e3f9101befff
   123  var VendorExperiment = os.Getenv("GO15VENDOREXPERIMENT") == "1"
   124  
   125  // sep is the signature set of path elements that
   126  // precede the original path of an imported package.
   127  var sep = defaultSep(VendorExperiment)
   128  
   129  func defaultSep(experiment bool) string {
   130  	if experiment {
   131  		return "/vendor/"
   132  	}
   133  	return "/Godeps/_workspace/src/"
   134  }
   135  
   136  func relativeVendorTarget(experiment bool) string {
   137  	full := defaultSep(experiment)
   138  	if full[0] == '/' {
   139  		full = full[1:]
   140  	}
   141  	return filepath.FromSlash(full)
   142  }
   143  
   144  // unqualify returns the part of importPath after the last
   145  // occurrence of the signature path elements
   146  // (Godeps/_workspace/src) that always precede imported
   147  // packages in rewritten import paths.
   148  //
   149  // For example,
   150  //   unqualify(C)                         = C
   151  //   unqualify(D/Godeps/_workspace/src/C) = C
   152  func unqualify(importPath string) string {
   153  	if i := strings.LastIndex(importPath, sep); i != -1 {
   154  		importPath = importPath[i+len(sep):]
   155  	}
   156  	return importPath
   157  }
   158  
   159  // qualify qualifies importPath with its corresponding import
   160  // path in the Godeps src copy of package pkg. If importPath
   161  // is a directory lexically contained in a path in paths,
   162  // it will be qualified with package pkg; otherwise, it will
   163  // be returned unchanged.
   164  //
   165  // For example, given paths {D, T} and pkg C,
   166  //   importPath  returns
   167  //   C           C
   168  //   fmt         fmt
   169  //   D           C/Godeps/_workspace/src/D
   170  //   D/P         C/Godeps/_workspace/src/D/P
   171  //   T           C/Godeps/_workspace/src/T
   172  func qualify(importPath, pkg string, paths []string) string {
   173  	if containsPathPrefix(paths, importPath) {
   174  		return pkg + sep + importPath
   175  	}
   176  	return importPath
   177  }