github.com/ryanfowler/glide@v0.12.1-0.20160826135910-1071a8c18883/repo/installer.go (about)

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