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