github.com/penguinn/godep@v0.0.0-20170205210856-a9cd0561f946/update.go (about) 1 package main 2 3 import ( 4 "go/parser" 5 "go/token" 6 "log" 7 "os" 8 "path" 9 "path/filepath" 10 "strconv" 11 "strings" 12 ) 13 14 var cmdUpdate = &Command{ 15 Name: "update", 16 Args: "[-goversion] [packages]", 17 Short: "update selected packages or the go version", 18 Long: ` 19 Update changes the named dependency packages to use the 20 revision of each currently installed in GOPATH. New code will 21 be copied into the Godeps workspace or vendor folder and the 22 new revision will be written to the manifest. 23 24 If -goversion is specified, update the recorded go version. 25 26 For more about specifying packages, see 'go help packages'. 27 `, 28 Run: runUpdate, 29 OnlyInGOPATH: true, 30 } 31 32 var ( 33 updateGoVer bool 34 ) 35 36 func init() { 37 cmdUpdate.Flag.BoolVar(&saveT, "t", false, "save test files during update") 38 cmdUpdate.Flag.BoolVar(&updateGoVer, "goversion", false, "update the recorded go version") 39 } 40 41 func runUpdate(cmd *Command, args []string) { 42 if updateGoVer { 43 err := updateGoVersion() 44 if err != nil { 45 log.Fatalln(err) 46 } 47 } 48 if len(args) > 0 { 49 err := update(args) 50 if err != nil { 51 log.Fatalln(err) 52 } 53 } 54 } 55 56 func updateGoVersion() error { 57 gold, err := loadDefaultGodepsFile() 58 if err != nil { 59 if !os.IsNotExist(err) { 60 return err 61 } 62 } 63 cv, err := goVersion() 64 if err != nil { 65 return err 66 } 67 68 gv := gold.GoVersion 69 gold.GoVersion = cv 70 _, err = gold.save() 71 if err != nil { 72 return err 73 } 74 75 if gv != cv { 76 log.Println("Updated major go version to", cv) 77 } 78 return nil 79 80 } 81 82 func update(args []string) error { 83 if len(args) == 0 { 84 args = []string{"."} 85 } 86 g, err := loadDefaultGodepsFile() 87 if err != nil { 88 return err 89 } 90 for _, arg := range args { 91 arg := path.Clean(arg) 92 any := markMatches(arg, g.Deps) 93 if !any { 94 log.Println("not in manifest:", arg) 95 } 96 } 97 deps, rdeps, err := LoadVCSAndUpdate(g.Deps) 98 if err != nil { 99 return err 100 } 101 if len(deps) == 0 { 102 return errorNoPackagesUpdatable 103 } 104 g.addOrUpdateDeps(deps) 105 g.removeDeps(rdeps) 106 if _, err = g.save(); err != nil { 107 return err 108 } 109 110 srcdir := relativeVendorTarget(VendorExperiment) 111 if err := removeSrc(filepath.FromSlash(strings.Trim(sep, "/")), rdeps); err != nil { 112 return err 113 } 114 copySrc(srcdir, deps) 115 116 ok, err := needRewrite(g.Packages) 117 if err != nil { 118 return err 119 } 120 var rewritePaths []string 121 if ok { 122 for _, dep := range g.Deps { 123 rewritePaths = append(rewritePaths, dep.ImportPath) 124 } 125 } 126 return rewrite(nil, g.ImportPath, rewritePaths) 127 } 128 129 func needRewrite(importPaths []string) (bool, error) { 130 if len(importPaths) == 0 { 131 importPaths = []string{"."} 132 } 133 a, err := LoadPackages(importPaths...) 134 if err != nil { 135 return false, err 136 } 137 for _, p := range a { 138 for _, name := range p.allGoFiles() { 139 path := filepath.Join(p.Dir, name) 140 hasSep, err := hasRewrittenImportStatement(path) 141 if err != nil { 142 return false, err 143 } 144 if hasSep { 145 return true, nil 146 } 147 } 148 } 149 return false, nil 150 } 151 152 func hasRewrittenImportStatement(path string) (bool, error) { 153 fset := token.NewFileSet() 154 f, err := parser.ParseFile(fset, path, nil, 0) 155 if err != nil { 156 return false, err 157 } 158 for _, s := range f.Imports { 159 name, _ := strconv.Unquote(s.Path.Value) 160 if strings.Contains(name, sep) { 161 return true, nil 162 } 163 } 164 return false, nil 165 } 166 167 // markMatches marks each entry in deps with an import path that 168 // matches pat. It returns whether any matches occurred. 169 func markMatches(pat string, deps []Dependency) (matched bool) { 170 f := matchPattern(pat) 171 for i, dep := range deps { 172 if f(dep.ImportPath) { 173 deps[i].matched = true 174 matched = true 175 } 176 } 177 return matched 178 } 179 180 func fillDeps(deps []Dependency) ([]Dependency, error) { 181 for i := range deps { 182 if deps[i].pkg != nil { 183 continue 184 } 185 ps, err := LoadPackages(deps[i].ImportPath) 186 if err != nil { 187 if _, ok := err.(errPackageNotFound); ok { 188 deps[i].missing = true 189 continue 190 } 191 return nil, err 192 } 193 if len(ps) > 1 { 194 panic("More than one package found for " + deps[i].ImportPath) 195 } 196 p := ps[0] 197 deps[i].pkg = p 198 deps[i].dir = p.Dir 199 deps[i].ws = p.Root 200 201 vcs, reporoot, err := VCSFromDir(p.Dir, filepath.Join(p.Root, "src")) 202 if err != nil { 203 return nil, errorLoadingDeps 204 } 205 deps[i].root = filepath.ToSlash(reporoot) 206 deps[i].vcs = vcs 207 } 208 209 return deps, nil 210 } 211 212 // LoadVCSAndUpdate loads and updates a set of dependencies. 213 func LoadVCSAndUpdate(deps []Dependency) ([]Dependency, []Dependency, error) { 214 var err1 error 215 216 deps, err := fillDeps(deps) 217 if err != nil { 218 return nil, nil, err 219 } 220 221 repoMask := make(map[string]bool) 222 for i := range deps { 223 if !deps[i].matched { 224 repoMask[deps[i].root] = true 225 } 226 } 227 228 // Determine if we need any new packages because of new transitive imports 229 for _, dep := range deps { 230 if !dep.matched || dep.missing { 231 continue 232 } 233 for _, dp := range dep.pkg.Dependencies { 234 if dp.Goroot { 235 continue 236 } 237 var have bool 238 for _, d := range deps { 239 if d.ImportPath == dp.ImportPath { 240 have = true 241 break 242 } 243 } 244 if !have { 245 deps = append(deps, Dependency{ImportPath: dp.ImportPath, matched: true}) 246 } 247 } 248 } 249 250 deps, err = fillDeps(deps) 251 if err != nil { 252 return nil, nil, err 253 } 254 255 var toUpdate, toRemove []Dependency 256 for _, d := range deps { 257 if !d.matched || repoMask[d.root] { 258 continue 259 } 260 if d.missing { 261 toRemove = append(toRemove, d) 262 continue 263 } 264 toUpdate = append(toUpdate, d) 265 } 266 267 debugln("toUpdate") 268 ppln(toUpdate) 269 270 var toCopy []Dependency 271 for _, d := range toUpdate { 272 id, err := d.vcs.identify(d.dir) 273 if err != nil { 274 log.Println(err) 275 err1 = errorLoadingDeps 276 continue 277 } 278 if d.vcs.isDirty(d.dir, id) { 279 log.Println("dirty working tree (please commit changes):", d.dir) 280 } 281 d.Rev = id 282 d.Comment = d.vcs.describe(d.dir, id) 283 toCopy = append(toCopy, d) 284 } 285 debugln("toCopy") 286 ppln(toCopy) 287 288 if err1 != nil { 289 return nil, nil, err1 290 } 291 return toCopy, toRemove, nil 292 }