github.com/chjlangzi/glide@v0.0.0-20171121052037-a806f0aaeda0/repo/installer.go (about) 1 package repo 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "runtime" 9 "strings" 10 "sync" 11 "syscall" 12 "time" 13 14 "github.com/Masterminds/semver" 15 "github.com/Masterminds/vcs" 16 "github.com/codegangsta/cli" 17 "github.com/xkeyideal/glide/cache" 18 "github.com/xkeyideal/glide/cfg" 19 "github.com/xkeyideal/glide/dependency" 20 "github.com/xkeyideal/glide/importer" 21 "github.com/xkeyideal/glide/msg" 22 gpath "github.com/xkeyideal/glide/path" 23 "github.com/xkeyideal/glide/util" 24 ) 25 26 // Installer provides facilities for installing the repos in a config file. 27 type Installer struct { 28 29 // Force the install when certain normally stopping conditions occur. 30 Force bool 31 32 // Home is the location of cache 33 Home string 34 35 // Vendor contains the path to put the vendor packages 36 Vendor string 37 38 // ResolveAllFiles enables a resolver that will examine the dependencies 39 // of every file of every package, rather than only following imported 40 // packages. 41 ResolveAllFiles bool 42 43 // ResolveTest sets if test dependencies should be resolved. 44 ResolveTest bool 45 46 // Updated tracks the packages that have been remotely fetched. 47 Updated *UpdateTracker 48 } 49 50 // NewInstaller returns an Installer instance ready to use. This is the constructor. 51 func NewInstaller() *Installer { 52 i := &Installer{} 53 i.Updated = NewUpdateTracker() 54 return i 55 } 56 57 // VendorPath returns the path to the location to put vendor packages 58 func (i *Installer) VendorPath() string { 59 if i.Vendor != "" { 60 return i.Vendor 61 } 62 63 vp, err := gpath.Vendor() 64 if err != nil { 65 return filepath.FromSlash("./vendor") 66 } 67 68 return vp 69 } 70 71 // Install installs the dependencies from a Lockfile. 72 func (i *Installer) Install(lock *cfg.Lockfile, conf *cfg.Config) (*cfg.Config, error) { 73 74 // Create a config setup based on the Lockfile data to process with 75 // existing commands. 76 newConf := &cfg.Config{} 77 newConf.Name = conf.Name 78 79 newConf.Imports = make(cfg.Dependencies, len(lock.Imports)) 80 for k, v := range lock.Imports { 81 newConf.Imports[k] = cfg.DependencyFromLock(v) 82 } 83 84 newConf.DevImports = make(cfg.Dependencies, len(lock.DevImports)) 85 for k, v := range lock.DevImports { 86 newConf.DevImports[k] = cfg.DependencyFromLock(v) 87 } 88 89 newConf.DeDupe() 90 91 if len(newConf.Imports) == 0 && len(newConf.DevImports) == 0 { 92 msg.Info("No dependencies found. Nothing installed.") 93 return newConf, nil 94 } 95 96 msg.Info("Downloading dependencies. Please wait...") 97 98 err := LazyConcurrentUpdate(newConf.Imports, i, newConf) 99 if err != nil { 100 return newConf, err 101 } 102 err = LazyConcurrentUpdate(newConf.DevImports, i, newConf) 103 104 return newConf, err 105 } 106 107 // Checkout reads the config file and checks out all dependencies mentioned there. 108 // 109 // This is used when initializing an empty vendor directory, or when updating a 110 // vendor directory based on changed config. 111 func (i *Installer) Checkout(conf *cfg.Config) error { 112 113 msg.Info("Downloading dependencies. Please wait...") 114 115 if err := ConcurrentUpdate(conf.Imports, i, conf); err != nil { 116 return err 117 } 118 119 if i.ResolveTest { 120 return ConcurrentUpdate(conf.DevImports, i, conf) 121 } 122 123 return nil 124 } 125 126 // Update updates all dependencies. 127 // 128 // It begins with the dependencies in the config file, but also resolves 129 // transitive dependencies. The returned lockfile has all of the dependencies 130 // listed, but the version reconciliation has not been done. 131 // 132 // In other words, all versions in the Lockfile will be empty. 133 func (i *Installer) Update(conf *cfg.Config) error { 134 base := "." 135 136 ic := newImportCache() 137 138 m := &MissingPackageHandler{ 139 home: i.Home, 140 force: i.Force, 141 Config: conf, 142 Use: ic, 143 updated: i.Updated, 144 } 145 146 v := &VersionHandler{ 147 Use: ic, 148 Imported: make(map[string]bool), 149 Conflicts: make(map[string]bool), 150 Config: conf, 151 } 152 153 // Update imports 154 res, err := dependency.NewResolver(base) 155 res.ResolveTest = i.ResolveTest 156 if err != nil { 157 msg.Die("Failed to create a resolver: %s", err) 158 } 159 res.Config = conf 160 res.Handler = m 161 res.VersionHandler = v 162 res.ResolveAllFiles = i.ResolveAllFiles 163 msg.Info("Resolving imports") 164 165 imps, timps, err := res.ResolveLocal(false) 166 if err != nil { 167 msg.Die("Failed to resolve local packages: %s", err) 168 } 169 var deps cfg.Dependencies 170 var tdeps cfg.Dependencies 171 for _, v := range imps { 172 n := res.Stripv(v) 173 if conf.HasIgnore(n) { 174 continue 175 } 176 rt, sub := util.NormalizeName(n) 177 if sub == "" { 178 sub = "." 179 } 180 d := deps.Get(rt) 181 if d == nil { 182 nd := &cfg.Dependency{ 183 Name: rt, 184 Subpackages: []string{sub}, 185 } 186 deps = append(deps, nd) 187 } else if !d.HasSubpackage(sub) { 188 d.Subpackages = append(d.Subpackages, sub) 189 } 190 } 191 if i.ResolveTest { 192 for _, v := range timps { 193 n := res.Stripv(v) 194 if conf.HasIgnore(n) { 195 continue 196 } 197 rt, sub := util.NormalizeName(n) 198 if sub == "" { 199 sub = "." 200 } 201 d := deps.Get(rt) 202 if d == nil { 203 d = tdeps.Get(rt) 204 } 205 if d == nil { 206 nd := &cfg.Dependency{ 207 Name: rt, 208 Subpackages: []string{sub}, 209 } 210 tdeps = append(tdeps, nd) 211 } else if !d.HasSubpackage(sub) { 212 d.Subpackages = append(d.Subpackages, sub) 213 } 214 } 215 } 216 217 _, err = allPackages(deps, res, false) 218 if err != nil { 219 msg.Die("Failed to retrieve a list of dependencies: %s", err) 220 } 221 222 if i.ResolveTest { 223 msg.Debug("Resolving test dependencies") 224 _, err = allPackages(tdeps, res, true) 225 if err != nil { 226 msg.Die("Failed to retrieve a list of test dependencies: %s", err) 227 } 228 } 229 230 msg.Info("Downloading dependencies. Please wait...") 231 232 err = ConcurrentUpdate(conf.Imports, i, conf) 233 if err != nil { 234 return err 235 } 236 237 if i.ResolveTest { 238 err = ConcurrentUpdate(conf.DevImports, i, conf) 239 if err != nil { 240 return err 241 } 242 } 243 244 return nil 245 } 246 247 // Export from the cache to the vendor directory 248 func (i *Installer) Export(conf *cfg.Config) error { 249 tempDir, err := ioutil.TempDir(gpath.Tmp, "glide-vendor") 250 if err != nil { 251 return err 252 } 253 defer func() { 254 err = os.RemoveAll(tempDir) 255 if err != nil { 256 msg.Err(err.Error()) 257 } 258 }() 259 260 vp := filepath.Join(tempDir, "vendor") 261 err = os.MkdirAll(vp, 0755) 262 263 msg.Info("Exporting resolved dependencies...") 264 done := make(chan struct{}, concurrentWorkers) 265 in := make(chan *cfg.Dependency, concurrentWorkers) 266 var wg sync.WaitGroup 267 var lock sync.Mutex 268 var returnErr error 269 270 for ii := 0; ii < concurrentWorkers; ii++ { 271 go func(ch <-chan *cfg.Dependency) { 272 for { 273 select { 274 case dep := <-ch: 275 loc := dep.Remote() 276 key, err := cache.Key(loc) 277 if err != nil { 278 msg.Die(err.Error()) 279 } 280 cache.Lock(key) 281 282 cdir := filepath.Join(cache.Location(), "src", key) 283 repo, err := dep.GetRepo(cdir) 284 if err != nil { 285 msg.Die(err.Error()) 286 } 287 path := dep.Base() 288 if strings.Compare(path, "") == 0 { 289 path = dep.Name 290 } 291 msg.Info("--> Exporting %s", dep.Name) 292 if err := repo.ExportDir(filepath.Join(vp, filepath.ToSlash(path))); err != nil { 293 msg.Err("Export failed for %s: %s\n", dep.Name, err) 294 // Capture the error while making sure the concurrent 295 // operations don't step on each other. 296 lock.Lock() 297 if returnErr == nil { 298 returnErr = err 299 } else { 300 returnErr = cli.NewMultiError(returnErr, err) 301 } 302 lock.Unlock() 303 } 304 cache.Unlock(key) 305 wg.Done() 306 case <-done: 307 return 308 } 309 } 310 }(in) 311 } 312 313 for _, dep := range conf.Imports { 314 if !conf.HasIgnore(dep.Name) { 315 err = os.MkdirAll(filepath.Join(vp, filepath.ToSlash(dep.Name)), 0755) 316 if err != nil { 317 lock.Lock() 318 if returnErr == nil { 319 returnErr = err 320 } else { 321 returnErr = cli.NewMultiError(returnErr, err) 322 } 323 lock.Unlock() 324 } 325 wg.Add(1) 326 in <- dep 327 } 328 } 329 330 if i.ResolveTest { 331 for _, dep := range conf.DevImports { 332 if !conf.HasIgnore(dep.Name) { 333 err = os.MkdirAll(filepath.Join(vp, filepath.ToSlash(dep.Name)), 0755) 334 if err != nil { 335 lock.Lock() 336 if returnErr == nil { 337 returnErr = err 338 } else { 339 returnErr = cli.NewMultiError(returnErr, err) 340 } 341 lock.Unlock() 342 } 343 wg.Add(1) 344 in <- dep 345 } 346 } 347 } 348 349 wg.Wait() 350 351 // Close goroutines setting the version 352 for ii := 0; ii < concurrentWorkers; ii++ { 353 done <- struct{}{} 354 } 355 356 if returnErr != nil { 357 return returnErr 358 } 359 360 msg.Info("Replacing existing vendor dependencies") 361 362 // Check if a .git directory exists under the old vendor dir. If it does, 363 // move it over to the newly-generated vendor dir - the user is probably 364 // submoduling, and it's easy enough not to break their setup. 365 ivg := filepath.Join(i.VendorPath(), ".git") 366 gitInfo, err := os.Stat(ivg) 367 if err == nil { 368 msg.Info("Preserving existing vendor/.git") 369 vpg := filepath.Join(vp, ".git") 370 err = os.Rename(ivg, vpg) 371 372 if terr, ok := err.(*os.LinkError); ok { 373 if gitInfo.IsDir() { 374 err = fixcle(ivg, vpg, terr) 375 } else { 376 // When this is a submodule, .git is just a file. Don't try to copy 377 // it as a directory in this case (see #828). 378 err = gpath.CopyFile(ivg, vpg) 379 } 380 381 if err != nil { 382 msg.Warn("Failed to preserve existing vendor/.git") 383 } 384 } 385 } 386 387 err = gpath.CustomRemoveAll(i.VendorPath()) 388 if err != nil { 389 return err 390 } 391 392 err = gpath.CustomRename(vp, i.VendorPath()) 393 if terr, ok := err.(*os.LinkError); ok { 394 return fixcle(vp, i.VendorPath(), terr) 395 } 396 397 return err 398 399 } 400 401 // fixcle is a helper function that tries to recover from cross-device rename 402 // errors by falling back to copying. 403 func fixcle(from, to string, terr *os.LinkError) error { 404 // When there are different physical devices we cannot rename cross device. 405 // Instead we copy. 406 407 // syscall.EXDEV is the common name for the cross device link error 408 // which has varying output text across different operating systems. 409 if terr.Err == syscall.EXDEV { 410 msg.Debug("Cross link err, trying manual copy: %s", terr) 411 return gpath.CopyDir(from, to) 412 } else if runtime.GOOS == "windows" { 413 // In windows it can drop down to an operating system call that 414 // returns an operating system error with a different number and 415 // message. Checking for that as a fall back. 416 noerr, ok := terr.Err.(syscall.Errno) 417 // 0x11 (ERROR_NOT_SAME_DEVICE) is the windows error. 418 // See https://msdn.microsoft.com/en-us/library/cc231199.aspx 419 if ok && noerr == 0x11 { 420 msg.Debug("Cross link err on Windows, trying manual copy: %s", terr) 421 return gpath.CopyDir(from, to) 422 } 423 } 424 425 return terr 426 } 427 428 // List resolves the complete dependency tree and returns a list of dependencies. 429 func (i *Installer) List(conf *cfg.Config) []*cfg.Dependency { 430 base := "." 431 432 ic := newImportCache() 433 434 v := &VersionHandler{ 435 Use: ic, 436 Imported: make(map[string]bool), 437 Conflicts: make(map[string]bool), 438 Config: conf, 439 } 440 441 // Update imports 442 res, err := dependency.NewResolver(base) 443 if err != nil { 444 msg.Die("Failed to create a resolver: %s", err) 445 } 446 res.Config = conf 447 res.VersionHandler = v 448 res.ResolveAllFiles = i.ResolveAllFiles 449 450 msg.Info("Resolving imports") 451 _, _, err = res.ResolveLocal(false) 452 if err != nil { 453 msg.Die("Failed to resolve local packages: %s", err) 454 } 455 456 _, err = allPackages(conf.Imports, res, false) 457 if err != nil { 458 msg.Die("Failed to retrieve a list of dependencies: %s", err) 459 } 460 461 if len(conf.DevImports) > 0 { 462 msg.Warn("dev imports not resolved.") 463 } 464 465 return conf.Imports 466 } 467 468 // LazyConcurrentUpdate updates only deps that are not already checkout out at the right version. 469 // 470 // This is only safe when updating from a lock file. 471 func LazyConcurrentUpdate(deps []*cfg.Dependency, i *Installer, c *cfg.Config) error { 472 473 newDeps := []*cfg.Dependency{} 474 for _, dep := range deps { 475 476 key, err := cache.Key(dep.Remote()) 477 if err != nil { 478 newDeps = append(newDeps, dep) 479 continue 480 } 481 destPath := filepath.Join(cache.Location(), "src", key) 482 483 // Get a VCS object for this directory 484 repo, err := dep.GetRepo(destPath) 485 if err != nil { 486 newDeps = append(newDeps, dep) 487 continue 488 } 489 490 ver, err := repo.Version() 491 if err != nil { 492 newDeps = append(newDeps, dep) 493 continue 494 } 495 if dep.Reference != "" { 496 ci, err := repo.CommitInfo(dep.Reference) 497 if err == nil && ci.Commit == dep.Reference { 498 msg.Info("--> Found desired version locally %s %s!", dep.Name, dep.Reference) 499 continue 500 } 501 } 502 503 msg.Debug("--> Queue %s for update (%s != %s).", dep.Name, ver, dep.Reference) 504 newDeps = append(newDeps, dep) 505 } 506 if len(newDeps) > 0 { 507 return ConcurrentUpdate(newDeps, i, c) 508 } 509 510 return nil 511 } 512 513 // ConcurrentUpdate takes a list of dependencies and updates in parallel. 514 func ConcurrentUpdate(deps []*cfg.Dependency, i *Installer, c *cfg.Config) error { 515 done := make(chan struct{}, concurrentWorkers) 516 in := make(chan *cfg.Dependency, concurrentWorkers) 517 var wg sync.WaitGroup 518 var lock sync.Mutex 519 var returnErr error 520 521 for ii := 0; ii < concurrentWorkers; ii++ { 522 go func(ch <-chan *cfg.Dependency) { 523 for { 524 select { 525 case dep := <-ch: 526 loc := dep.Remote() 527 key, err := cache.Key(loc) 528 if err != nil { 529 msg.Die(err.Error()) 530 } 531 cache.Lock(key) 532 if err := VcsUpdate(dep, i.Force, i.Updated); err != nil { 533 msg.Err("Update failed for %s: %s\n", dep.Name, err) 534 // Capture the error while making sure the concurrent 535 // operations don't step on each other. 536 lock.Lock() 537 if returnErr == nil { 538 returnErr = err 539 } else { 540 returnErr = cli.NewMultiError(returnErr, err) 541 } 542 lock.Unlock() 543 } 544 cache.Unlock(key) 545 wg.Done() 546 case <-done: 547 return 548 } 549 } 550 }(in) 551 } 552 553 for _, dep := range deps { 554 if !c.HasIgnore(dep.Name) { 555 wg.Add(1) 556 in <- dep 557 } 558 } 559 560 wg.Wait() 561 562 // Close goroutines setting the version 563 for ii := 0; ii < concurrentWorkers; ii++ { 564 done <- struct{}{} 565 } 566 567 return returnErr 568 } 569 570 // allPackages gets a list of all packages required to satisfy the given deps. 571 func allPackages(deps []*cfg.Dependency, res *dependency.Resolver, addTest bool) ([]string, error) { 572 if len(deps) == 0 { 573 return []string{}, nil 574 } 575 576 vdir, err := gpath.Vendor() 577 if err != nil { 578 return []string{}, err 579 } 580 vdir += string(os.PathSeparator) 581 ll, err := res.ResolveAll(deps, addTest) 582 if err != nil { 583 return []string{}, err 584 } 585 586 for i := 0; i < len(ll); i++ { 587 ll[i] = strings.TrimPrefix(ll[i], vdir) 588 } 589 return ll, nil 590 } 591 592 // MissingPackageHandler is a dependency.MissingPackageHandler. 593 // 594 // When a package is not found, this attempts to resolve and fetch. 595 // 596 // When a package is found on the GOPATH, this notifies the user. 597 type MissingPackageHandler struct { 598 home string 599 force bool 600 Config *cfg.Config 601 Use *importCache 602 updated *UpdateTracker 603 } 604 605 // NotFound attempts to retrieve a package when not found in the local cache 606 // folder. It will attempt to get it from the remote location info. 607 func (m *MissingPackageHandler) NotFound(pkg string, addTest bool) (bool, error) { 608 err := m.fetchToCache(pkg, addTest) 609 if err != nil { 610 return false, err 611 } 612 613 return true, err 614 } 615 616 // OnGopath will either copy a package, already found in the GOPATH, to the 617 // vendor/ directory or download it from the internet. This is dependent if 618 // useGopath on the installer is set to true to copy from the GOPATH. 619 func (m *MissingPackageHandler) OnGopath(pkg string, addTest bool) (bool, error) { 620 621 err := m.fetchToCache(pkg, addTest) 622 if err != nil { 623 return false, err 624 } 625 626 return true, err 627 } 628 629 // InVendor updates a package in the vendor/ directory to make sure the latest 630 // is available. 631 func (m *MissingPackageHandler) InVendor(pkg string, addTest bool) error { 632 return m.fetchToCache(pkg, addTest) 633 } 634 635 // PkgPath resolves the location on the filesystem where the package should be. 636 // This handles making sure to use the cache location. 637 func (m *MissingPackageHandler) PkgPath(pkg string) string { 638 root, sub := util.NormalizeName(pkg) 639 640 // For the parent applications source skip the cache. 641 if root == m.Config.Name { 642 pth := gpath.Basepath() 643 return filepath.Join(pth, filepath.FromSlash(sub)) 644 } 645 646 d := m.Config.Imports.Get(root) 647 if d == nil { 648 d = m.Config.DevImports.Get(root) 649 } 650 651 if d == nil { 652 d, _ = m.Use.Get(root) 653 654 if d == nil { 655 d = &cfg.Dependency{Name: root} 656 } 657 } 658 659 key, err := cache.Key(d.Remote()) 660 if err != nil { 661 msg.Die("Error generating cache key for %s", d.Name) 662 } 663 664 return filepath.Join(cache.Location(), "src", key, filepath.FromSlash(sub)) 665 } 666 667 func (m *MissingPackageHandler) fetchToCache(pkg string, addTest bool) error { 668 root := util.GetRootFromPackage(pkg) 669 // Skip any references to the root package. 670 if root == m.Config.Name { 671 return nil 672 } 673 674 d := m.Config.Imports.Get(root) 675 if d == nil && addTest { 676 d = m.Config.DevImports.Get(root) 677 } 678 679 // If the dependency is nil it means the Config doesn't yet know about it. 680 if d == nil { 681 d, _ = m.Use.Get(root) 682 // We don't know about this dependency so we create a basic instance. 683 if d == nil { 684 d = &cfg.Dependency{Name: root} 685 } 686 687 if addTest { 688 m.Config.DevImports = append(m.Config.DevImports, d) 689 } else { 690 m.Config.Imports = append(m.Config.Imports, d) 691 } 692 } 693 694 return VcsUpdate(d, m.force, m.updated) 695 } 696 697 // VersionHandler handles setting the proper version in the VCS. 698 type VersionHandler struct { 699 700 // If Try to use the version here if we have one. This is a cache and will 701 // change over the course of setting versions. 702 Use *importCache 703 704 // Cache if importing scan has already occurred here. 705 Imported map[string]bool 706 707 Config *cfg.Config 708 709 // There's a problem where many sub-packages have been asked to set a version 710 // and you can end up with numerous conflict messages that are exactly the 711 // same. We are keeping track to only display them once. 712 // the parent pac 713 Conflicts map[string]bool 714 } 715 716 // Process imports dependencies for a package 717 func (d *VersionHandler) Process(pkg string) (e error) { 718 root := util.GetRootFromPackage(pkg) 719 720 // Skip any references to the root package. 721 if root == d.Config.Name { 722 return nil 723 } 724 725 // We have not tried to import, yet. 726 // Should we look in places other than the root of the project? 727 if d.Imported[root] == false { 728 d.Imported[root] = true 729 p := d.pkgPath(root) 730 f, deps, err := importer.Import(p) 731 if f && err == nil { 732 for _, dep := range deps { 733 734 // The fist one wins. Would something smater than this be better? 735 exists, _ := d.Use.Get(dep.Name) 736 if exists == nil && (dep.Reference != "" || dep.Repository != "") { 737 d.Use.Add(dep.Name, dep, root) 738 } 739 } 740 } else if err != nil { 741 msg.Err("Unable to import from %s. Err: %s", root, err) 742 e = err 743 } 744 } 745 746 return 747 } 748 749 // SetVersion sets the version for a package. If that package version is already 750 // set it handles the case by: 751 // - keeping the already set version 752 // - proviting messaging about the version conflict 753 // TODO(mattfarina): The way version setting happens can be improved. Currently not optimal. 754 func (d *VersionHandler) SetVersion(pkg string, addTest bool) (e error) { 755 root := util.GetRootFromPackage(pkg) 756 757 // Skip any references to the root package. 758 if root == d.Config.Name { 759 return nil 760 } 761 762 v := d.Config.Imports.Get(root) 763 if addTest { 764 if v == nil { 765 v = d.Config.DevImports.Get(root) 766 } else if d.Config.DevImports.Has(root) { 767 // Both imports and test imports lists the same dependency. 768 // There are import chains (because the import tree is resolved 769 // before the test tree) that can cause this. 770 tempD := d.Config.DevImports.Get(root) 771 if tempD.Reference != v.Reference { 772 msg.Warn("Using import %s (version %s) for test instead of testImport (version %s).", v.Name, v.Reference, tempD.Reference) 773 } 774 // TODO(mattfarina): Note repo difference in a warning. 775 } 776 } 777 778 dep, req := d.Use.Get(root) 779 if dep != nil && v != nil { 780 if v.Reference == "" && dep.Reference != "" { 781 v.Reference = dep.Reference 782 // Clear the pin, if set, so the new version can be used. 783 v.Pin = "" 784 dep = v 785 } else if v.Reference != "" && dep.Reference != "" && v.Reference != dep.Reference { 786 dest := d.pkgPath(pkg) 787 dep = determineDependency(v, dep, dest, req) 788 } else { 789 dep = v 790 } 791 792 } else if v != nil { 793 dep = v 794 } else if dep != nil { 795 // We've got an imported dependency to use and don't already have a 796 // record of it. Append it to the Imports. 797 if addTest { 798 d.Config.DevImports = append(d.Config.DevImports, dep) 799 } else { 800 d.Config.Imports = append(d.Config.Imports, dep) 801 } 802 } else { 803 // If we've gotten here we don't have any depenency objects. 804 r, sp := util.NormalizeName(pkg) 805 dep = &cfg.Dependency{ 806 Name: r, 807 } 808 if sp != "" { 809 dep.Subpackages = []string{sp} 810 } 811 if addTest { 812 d.Config.DevImports = append(d.Config.DevImports, dep) 813 } else { 814 d.Config.Imports = append(d.Config.Imports, dep) 815 } 816 } 817 818 err := VcsVersion(dep) 819 if err != nil { 820 msg.Warn("Unable to set version on %s to %s. Err: %s", root, dep.Reference, err) 821 e = err 822 } 823 824 return 825 } 826 827 func (d *VersionHandler) pkgPath(pkg string) string { 828 root, sub := util.NormalizeName(pkg) 829 830 // For the parent applications source skip the cache. 831 if root == d.Config.Name { 832 pth := gpath.Basepath() 833 return filepath.Join(pth, filepath.FromSlash(sub)) 834 } 835 836 dep := d.Config.Imports.Get(root) 837 if dep == nil { 838 dep = d.Config.DevImports.Get(root) 839 } 840 841 if dep == nil { 842 dep, _ = d.Use.Get(root) 843 844 if dep == nil { 845 dep = &cfg.Dependency{Name: root} 846 } 847 } 848 849 key, err := cache.Key(dep.Remote()) 850 if err != nil { 851 msg.Die("Error generating cache key for %s", dep.Name) 852 } 853 854 return filepath.Join(cache.Location(), "src", key, filepath.FromSlash(sub)) 855 } 856 857 func determineDependency(v, dep *cfg.Dependency, dest, req string) *cfg.Dependency { 858 repo, err := v.GetRepo(dest) 859 if err != nil { 860 singleWarn("Unable to access repo for %s\n", v.Name) 861 singleInfo("Keeping %s %s", v.Name, v.Reference) 862 return v 863 } 864 865 vIsRef := repo.IsReference(v.Reference) 866 depIsRef := repo.IsReference(dep.Reference) 867 868 // Both are references and they are different ones. 869 if vIsRef && depIsRef { 870 singleWarn("Conflict: %s rev is currently %s, but %s wants %s\n", v.Name, v.Reference, req, dep.Reference) 871 872 displayCommitInfo(repo, v) 873 displayCommitInfo(repo, dep) 874 875 singleInfo("Keeping %s %s", v.Name, v.Reference) 876 return v 877 } else if vIsRef { 878 // The current one is a reference and the suggestion is a SemVer constraint. 879 con, err := semver.NewConstraint(dep.Reference) 880 if err != nil { 881 singleWarn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", dep.Name, dep.Reference) 882 singleInfo("Keeping %s %s", v.Name, v.Reference) 883 return v 884 } 885 886 ver, err := semver.NewVersion(v.Reference) 887 if err != nil { 888 // The existing version is not a semantic version. 889 singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) 890 displayCommitInfo(repo, v) 891 singleInfo("Keeping %s %s", v.Name, v.Reference) 892 return v 893 } 894 895 if con.Check(ver) { 896 singleInfo("Keeping %s %s because it fits constraint '%s'", v.Name, v.Reference, dep.Reference) 897 return v 898 } 899 singleWarn("Conflict: %s version is %s but does not meet constraint '%s'\n", v.Name, v.Reference, dep.Reference) 900 singleInfo("Keeping %s %s", v.Name, v.Reference) 901 return v 902 } else if depIsRef { 903 904 con, err := semver.NewConstraint(v.Reference) 905 if err != nil { 906 singleWarn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", v.Name, v.Reference) 907 singleInfo("Keeping %s %s", v.Name, v.Reference) 908 return v 909 } 910 911 ver, err := semver.NewVersion(dep.Reference) 912 if err != nil { 913 singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) 914 displayCommitInfo(repo, dep) 915 singleInfo("Keeping %s %s", v.Name, v.Reference) 916 return v 917 } 918 919 if con.Check(ver) { 920 v.Reference = dep.Reference 921 singleInfo("Using %s %s because it fits constraint '%s'", v.Name, v.Reference, v.Reference) 922 return v 923 } 924 singleWarn("Conflict: %s semantic version constraint is %s but '%s' does not meet the constraint\n", v.Name, v.Reference, v.Reference) 925 singleInfo("Keeping %s %s", v.Name, v.Reference) 926 return v 927 } 928 // Neither is a vcs reference and both could be semantic version 929 // constraints that are different. 930 931 _, err = semver.NewConstraint(dep.Reference) 932 if err != nil { 933 // dd.Reference is not a reference or a valid constraint. 934 singleWarn("Version %s %s is not a reference or valid semantic version constraint\n", dep.Name, dep.Reference) 935 singleInfo("Keeping %s %s", v.Name, v.Reference) 936 return v 937 } 938 939 _, err = semver.NewConstraint(v.Reference) 940 if err != nil { 941 // existing.Reference is not a reference or a valid constraint. 942 // We really should never end up here. 943 singleWarn("Version %s %s is not a reference or valid semantic version constraint\n", v.Name, v.Reference) 944 945 v.Reference = dep.Reference 946 v.Pin = "" 947 singleInfo("Using %s %s because it is a valid version", v.Name, v.Reference) 948 return v 949 } 950 951 // Both versions are constraints. Try to merge them. 952 // If either comparison has an || skip merging. That's complicated. 953 ddor := strings.Index(dep.Reference, "||") 954 eor := strings.Index(v.Reference, "||") 955 if ddor == -1 && eor == -1 { 956 // Add the comparisons together. 957 newRef := v.Reference + ", " + dep.Reference 958 v.Reference = newRef 959 v.Pin = "" 960 singleInfo("Combining %s semantic version constraints %s and %s", v.Name, v.Reference, dep.Reference) 961 return v 962 } 963 singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) 964 singleInfo("Keeping %s %s", v.Name, v.Reference) 965 return v 966 } 967 968 var warningMessage = make(map[string]bool) 969 var infoMessage = make(map[string]bool) 970 971 func singleWarn(ft string, v ...interface{}) { 972 m := fmt.Sprintf(ft, v...) 973 _, f := warningMessage[m] 974 if !f { 975 msg.Warn(m) 976 warningMessage[m] = true 977 } 978 } 979 980 func singleInfo(ft string, v ...interface{}) { 981 m := fmt.Sprintf(ft, v...) 982 _, f := infoMessage[m] 983 if !f { 984 msg.Info(m) 985 infoMessage[m] = true 986 } 987 } 988 989 type importCache struct { 990 cache map[string]*cfg.Dependency 991 from map[string]string 992 } 993 994 func newImportCache() *importCache { 995 return &importCache{ 996 cache: make(map[string]*cfg.Dependency), 997 from: make(map[string]string), 998 } 999 } 1000 1001 func (i *importCache) Get(name string) (*cfg.Dependency, string) { 1002 d, f := i.cache[name] 1003 if f { 1004 return d, i.from[name] 1005 } 1006 1007 return nil, "" 1008 } 1009 1010 func (i *importCache) Add(name string, dep *cfg.Dependency, root string) { 1011 i.cache[name] = dep 1012 i.from[name] = root 1013 } 1014 1015 var displayCommitInfoPrefix = msg.Default.Color(msg.Green, "[INFO] ") 1016 var displayCommitInfoTemplate = "%s reference %s:\n" + 1017 displayCommitInfoPrefix + "- author: %s\n" + 1018 displayCommitInfoPrefix + "- commit date: %s\n" + 1019 displayCommitInfoPrefix + "- subject (first line): %s\n" 1020 1021 func displayCommitInfo(repo vcs.Repo, dep *cfg.Dependency) { 1022 c, err := repo.CommitInfo(dep.Reference) 1023 ref := dep.Reference 1024 1025 if err == nil { 1026 tgs, err2 := repo.TagsFromCommit(c.Commit) 1027 if err2 == nil && len(tgs) > 0 { 1028 if tgs[0] != dep.Reference { 1029 ref = ref + " (" + tgs[0] + ")" 1030 } 1031 } 1032 singleInfo(displayCommitInfoTemplate, dep.Name, ref, c.Author, c.Date.Format(time.RFC1123Z), commitSubjectFirstLine(c.Message)) 1033 } 1034 } 1035 1036 func commitSubjectFirstLine(sub string) string { 1037 lines := strings.Split(sub, "\n") 1038 if len(lines) <= 1 { 1039 return sub 1040 } 1041 1042 return lines[0] 1043 }