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 }