gopkg.in/tools/godep.v25@v25.0.0-20151106015334-218402f1be30/update.go (about)

     1  package main
     2  
     3  import (
     4  	"go/parser"
     5  	"go/token"
     6  	"log"
     7  	"path/filepath"
     8  	"regexp"
     9  	"strconv"
    10  	"strings"
    11  )
    12  
    13  var cmdUpdate = &Command{
    14  	Usage: "update [packages]",
    15  	Short: "use different revision of selected packages",
    16  	Long: `
    17  Update changes the named dependency packages to use the
    18  revision of each currently installed in GOPATH. New code will
    19  be copied into Godeps and the new revision will be written to
    20  the manifest.
    21  
    22  For more about specifying packages, see 'go help packages'.
    23  `,
    24  	Run: runUpdate,
    25  }
    26  
    27  func init() {
    28  	cmdUpdate.Flag.BoolVar(&saveT, "t", false, "save test files during update")
    29  }
    30  
    31  func runUpdate(cmd *Command, args []string) {
    32  	err := update(args)
    33  	if err != nil {
    34  		log.Fatalln(err)
    35  	}
    36  }
    37  
    38  func update(args []string) error {
    39  	if len(args) == 0 {
    40  		args = []string{"."}
    41  	}
    42  	g, err := loadDefaultGodepsFile()
    43  	if err != nil {
    44  		return err
    45  	}
    46  	for _, arg := range args {
    47  		any := markMatches(arg, g.Deps)
    48  		if !any {
    49  			log.Println("not in manifest:", arg)
    50  		}
    51  	}
    52  	deps, err := LoadVCSAndUpdate(g.Deps)
    53  	if err != nil {
    54  		return err
    55  	}
    56  	if len(deps) == 0 {
    57  		return errorNoPackagesUpdatable
    58  	}
    59  	if _, err = g.save(); err != nil {
    60  		return err
    61  	}
    62  
    63  	srcdir := relativeVendorTarget(VendorExperiment)
    64  	copySrc(srcdir, deps)
    65  
    66  	ok, err := needRewrite(g.Packages)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	var rewritePaths []string
    71  	if ok {
    72  		for _, dep := range g.Deps {
    73  			rewritePaths = append(rewritePaths, dep.ImportPath)
    74  		}
    75  	}
    76  	return rewrite(nil, g.ImportPath, rewritePaths)
    77  }
    78  
    79  func needRewrite(importPaths []string) (bool, error) {
    80  	if len(importPaths) == 0 {
    81  		importPaths = []string{"."}
    82  	}
    83  	a, err := LoadPackages(importPaths...)
    84  	if err != nil {
    85  		return false, err
    86  	}
    87  	for _, p := range a {
    88  		for _, name := range p.allGoFiles() {
    89  			path := filepath.Join(p.Dir, name)
    90  			hasSep, err := hasRewrittenImportStatement(path)
    91  			if err != nil {
    92  				return false, err
    93  			}
    94  			if hasSep {
    95  				return true, nil
    96  			}
    97  		}
    98  	}
    99  	return false, nil
   100  }
   101  
   102  func hasRewrittenImportStatement(path string) (bool, error) {
   103  	fset := token.NewFileSet()
   104  	f, err := parser.ParseFile(fset, path, nil, 0)
   105  	if err != nil {
   106  		return false, err
   107  	}
   108  	for _, s := range f.Imports {
   109  		name, _ := strconv.Unquote(s.Path.Value)
   110  		if strings.Contains(name, sep) {
   111  			return true, nil
   112  		}
   113  	}
   114  	return false, nil
   115  }
   116  
   117  // markMatches marks each entry in deps with an import path that
   118  // matches pat. It returns whether any matches occurred.
   119  func markMatches(pat string, deps []Dependency) (matched bool) {
   120  	f := matchPattern(pat)
   121  	for i, dep := range deps {
   122  		if f(dep.ImportPath) {
   123  			deps[i].matched = true
   124  			matched = true
   125  		}
   126  	}
   127  	return matched
   128  }
   129  
   130  // matchPattern(pattern)(name) reports whether
   131  // name matches pattern.  Pattern is a limited glob
   132  // pattern in which '...' means 'any string' and there
   133  // is no other special syntax.
   134  // Taken from $GOROOT/src/cmd/go/main.go.
   135  func matchPattern(pattern string) func(name string) bool {
   136  	re := regexp.QuoteMeta(pattern)
   137  	re = strings.Replace(re, `\.\.\.`, `.*`, -1)
   138  	// Special case: foo/... matches foo too.
   139  	switch {
   140  	case strings.HasSuffix(re, `/`):
   141  		re = re[:len(re)-len(`/`)] + `(/)?`
   142  	case strings.HasSuffix(re, `/.*`):
   143  		re = re[:len(re)-len(`/.*`)] + `(/.*)?`
   144  	}
   145  	reg := regexp.MustCompile(`^` + re + `$`)
   146  	return func(name string) bool {
   147  		return reg.MatchString(name)
   148  	}
   149  }
   150  
   151  // LoadVCSAndUpdate loads and updates a set of dependencies.
   152  func LoadVCSAndUpdate(deps []Dependency) ([]Dependency, error) {
   153  	var err1 error
   154  	var paths []string
   155  	for _, dep := range deps {
   156  		paths = append(paths, dep.ImportPath)
   157  	}
   158  	ps, err := LoadPackages(paths...)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  	noupdate := make(map[string]bool) // repo roots
   163  	var candidates []*Dependency
   164  	var tocopy []Dependency
   165  	for i := range deps {
   166  		dep := &deps[i]
   167  		for _, pkg := range ps {
   168  			if dep.ImportPath == pkg.ImportPath {
   169  				dep.pkg = pkg
   170  				break
   171  			}
   172  		}
   173  		if dep.pkg == nil {
   174  			log.Println(dep.ImportPath + ": error listing package")
   175  			err1 = errorLoadingDeps
   176  			continue
   177  		}
   178  		if dep.pkg.Error.Err != "" {
   179  			log.Println(dep.pkg.Error.Err)
   180  			err1 = errorLoadingDeps
   181  			continue
   182  		}
   183  		vcs, reporoot, err := VCSFromDir(dep.pkg.Dir, filepath.Join(dep.pkg.Root, "src"))
   184  		if err != nil {
   185  			log.Println(err)
   186  			err1 = errorLoadingDeps
   187  			continue
   188  		}
   189  		dep.dir = dep.pkg.Dir
   190  		dep.ws = dep.pkg.Root
   191  		dep.root = filepath.ToSlash(reporoot)
   192  		dep.vcs = vcs
   193  		if dep.matched {
   194  			candidates = append(candidates, dep)
   195  		} else {
   196  			noupdate[dep.root] = true
   197  		}
   198  	}
   199  	if err1 != nil {
   200  		return nil, err1
   201  	}
   202  
   203  	for _, dep := range candidates {
   204  		dep.dir = dep.pkg.Dir
   205  		dep.ws = dep.pkg.Root
   206  		if noupdate[dep.root] {
   207  			continue
   208  		}
   209  		id, err := dep.vcs.identify(dep.pkg.Dir)
   210  		if err != nil {
   211  			log.Println(err)
   212  			err1 = errorLoadingDeps
   213  			continue
   214  		}
   215  		if dep.vcs.isDirty(dep.pkg.Dir, id) {
   216  			log.Println("dirty working tree (please commit changes):", dep.pkg.Dir)
   217  			err1 = errorLoadingDeps
   218  			break
   219  		}
   220  		dep.Rev = id
   221  		dep.Comment = dep.vcs.describe(dep.pkg.Dir, id)
   222  		tocopy = append(tocopy, *dep)
   223  	}
   224  	if err1 != nil {
   225  		return nil, err1
   226  	}
   227  	return tocopy, nil
   228  }