| package main |
| |
| import ( |
| "go/parser" |
| "go/token" |
| "log" |
| "os" |
| "path" |
| "path/filepath" |
| "strconv" |
| "strings" |
| ) |
| |
| var cmdUpdate = &Command{ |
| Name: "update", |
| Args: "[-goversion] [packages]", |
| Short: "update selected packages or the go version", |
| Long: ` |
| Update changes the named dependency packages to use the |
| revision of each currently installed in GOPATH. New code will |
| be copied into the Godeps workspace or vendor folder and the |
| new revision will be written to the manifest. |
| |
| If -goversion is specified, update the recorded go version. |
| |
| For more about specifying packages, see 'go help packages'. |
| `, |
| Run: runUpdate, |
| OnlyInGOPATH: true, |
| } |
| |
| var ( |
| updateGoVer bool |
| ) |
| |
| func init() { |
| cmdUpdate.Flag.BoolVar(&saveT, "t", false, "save test files during update") |
| cmdUpdate.Flag.BoolVar(&updateGoVer, "goversion", false, "update the recorded go version") |
| } |
| |
| func runUpdate(cmd *Command, args []string) { |
| if updateGoVer { |
| err := updateGoVersion() |
| if err != nil { |
| log.Fatalln(err) |
| } |
| } |
| if len(args) > 0 { |
| err := update(args) |
| if err != nil { |
| log.Fatalln(err) |
| } |
| } |
| } |
| |
| func updateGoVersion() error { |
| gold, err := loadDefaultGodepsFile() |
| if err != nil { |
| if !os.IsNotExist(err) { |
| return err |
| } |
| } |
| cv, err := goVersion() |
| if err != nil { |
| return err |
| } |
| |
| gv := gold.GoVersion |
| gold.GoVersion = cv |
| _, err = gold.save() |
| if err != nil { |
| return err |
| } |
| |
| if gv != cv { |
| log.Println("Updated major go version to", cv) |
| } |
| return nil |
| |
| } |
| |
| func update(args []string) error { |
| if len(args) == 0 { |
| args = []string{"."} |
| } |
| g, err := loadDefaultGodepsFile() |
| if err != nil { |
| return err |
| } |
| for _, arg := range args { |
| arg := path.Clean(arg) |
| any := markMatches(arg, g.Deps) |
| if !any { |
| log.Println("not in manifest:", arg) |
| } |
| } |
| deps, rdeps, err := LoadVCSAndUpdate(g.Deps) |
| if err != nil { |
| return err |
| } |
| if len(deps) == 0 { |
| return errorNoPackagesUpdatable |
| } |
| g.addOrUpdateDeps(deps) |
| g.removeDeps(rdeps) |
| if _, err = g.save(); err != nil { |
| return err |
| } |
| |
| srcdir := relativeVendorTarget(VendorExperiment) |
| if err := removeSrc(filepath.FromSlash(strings.Trim(sep, "/")), rdeps); err != nil { |
| return err |
| } |
| copySrc(srcdir, deps) |
| |
| ok, err := needRewrite(g.Packages) |
| if err != nil { |
| return err |
| } |
| var rewritePaths []string |
| if ok { |
| for _, dep := range g.Deps { |
| rewritePaths = append(rewritePaths, dep.ImportPath) |
| } |
| } |
| return rewrite(nil, g.ImportPath, rewritePaths) |
| } |
| |
| func needRewrite(importPaths []string) (bool, error) { |
| if len(importPaths) == 0 { |
| importPaths = []string{"."} |
| } |
| a, err := LoadPackages(importPaths...) |
| if err != nil { |
| return false, err |
| } |
| for _, p := range a { |
| for _, name := range p.allGoFiles() { |
| path := filepath.Join(p.Dir, name) |
| hasSep, err := hasRewrittenImportStatement(path) |
| if err != nil { |
| return false, err |
| } |
| if hasSep { |
| return true, nil |
| } |
| } |
| } |
| return false, nil |
| } |
| |
| func hasRewrittenImportStatement(path string) (bool, error) { |
| fset := token.NewFileSet() |
| f, err := parser.ParseFile(fset, path, nil, 0) |
| if err != nil { |
| return false, err |
| } |
| for _, s := range f.Imports { |
| name, _ := strconv.Unquote(s.Path.Value) |
| if strings.Contains(name, sep) { |
| return true, nil |
| } |
| } |
| return false, nil |
| } |
| |
| // markMatches marks each entry in deps with an import path that |
| // matches pat. It returns whether any matches occurred. |
| func markMatches(pat string, deps []Dependency) (matched bool) { |
| f := matchPattern(pat) |
| for i, dep := range deps { |
| if f(dep.ImportPath) { |
| deps[i].matched = true |
| matched = true |
| } |
| } |
| return matched |
| } |
| |
| func fillDeps(deps []Dependency) ([]Dependency, error) { |
| for i := range deps { |
| if deps[i].pkg != nil { |
| continue |
| } |
| ps, err := LoadPackages(deps[i].ImportPath) |
| if err != nil { |
| if _, ok := err.(errPackageNotFound); ok { |
| deps[i].missing = true |
| continue |
| } |
| return nil, err |
| } |
| if len(ps) > 1 { |
| panic("More than one package found for " + deps[i].ImportPath) |
| } |
| p := ps[0] |
| deps[i].pkg = p |
| deps[i].dir = p.Dir |
| deps[i].ws = p.Root |
| |
| vcs, reporoot, err := VCSFromDir(p.Dir, filepath.Join(p.Root, "src")) |
| if err != nil { |
| return nil, errorLoadingDeps |
| } |
| deps[i].root = filepath.ToSlash(reporoot) |
| deps[i].vcs = vcs |
| } |
| |
| return deps, nil |
| } |
| |
| // LoadVCSAndUpdate loads and updates a set of dependencies. |
| func LoadVCSAndUpdate(deps []Dependency) ([]Dependency, []Dependency, error) { |
| var err1 error |
| |
| deps, err := fillDeps(deps) |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| repoMask := make(map[string]bool) |
| for i := range deps { |
| if !deps[i].matched { |
| repoMask[deps[i].root] = true |
| } |
| } |
| |
| // Determine if we need any new packages because of new transitive imports |
| for _, dep := range deps { |
| if !dep.matched || dep.missing { |
| continue |
| } |
| for _, dp := range dep.pkg.Dependencies { |
| if dp.Goroot { |
| continue |
| } |
| var have bool |
| for _, d := range deps { |
| if d.ImportPath == dp.ImportPath { |
| have = true |
| break |
| } |
| } |
| if !have { |
| deps = append(deps, Dependency{ImportPath: dp.ImportPath, matched: true}) |
| } |
| } |
| } |
| |
| deps, err = fillDeps(deps) |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| var toUpdate, toRemove []Dependency |
| for _, d := range deps { |
| if !d.matched || repoMask[d.root] { |
| continue |
| } |
| if d.missing { |
| toRemove = append(toRemove, d) |
| continue |
| } |
| toUpdate = append(toUpdate, d) |
| } |
| |
| debugln("toUpdate") |
| ppln(toUpdate) |
| |
| var toCopy []Dependency |
| for _, d := range toUpdate { |
| id, err := d.vcs.identify(d.dir) |
| if err != nil { |
| log.Println(err) |
| err1 = errorLoadingDeps |
| continue |
| } |
| if d.vcs.isDirty(d.dir, id) { |
| log.Println("dirty working tree (please commit changes):", d.dir) |
| } |
| d.Rev = id |
| d.Comment = d.vcs.describe(d.dir, id) |
| toCopy = append(toCopy, d) |
| } |
| debugln("toCopy") |
| ppln(toCopy) |
| |
| if err1 != nil { |
| return nil, nil, err1 |
| } |
| return toCopy, toRemove, nil |
| } |