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