2017-05-02 19:26:49 +03:00
|
|
|
// Copyright 2017 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2017-01-12 08:17:37 +03:00
|
|
|
package gps
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sort"
|
|
|
|
|
|
|
|
"github.com/armon/go-radix"
|
2017-05-10 07:13:22 +03:00
|
|
|
"github.com/golang/dep/internal/gps/pkgtree"
|
2017-01-12 08:17:37 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// rootdata holds static data and constraining rules from the root project for
|
|
|
|
// use in solving.
|
|
|
|
type rootdata struct {
|
|
|
|
// Path to the root of the project on which gps is operating.
|
|
|
|
dir string
|
|
|
|
|
|
|
|
// Map of packages to ignore.
|
|
|
|
ig map[string]bool
|
|
|
|
|
|
|
|
// Map of packages to require.
|
|
|
|
req map[string]bool
|
|
|
|
|
|
|
|
// A ProjectConstraints map containing the validated (guaranteed non-empty)
|
|
|
|
// overrides declared by the root manifest.
|
|
|
|
ovr ProjectConstraints
|
|
|
|
|
|
|
|
// A map of the ProjectRoot (local names) that should be allowed to change
|
|
|
|
chng map[ProjectRoot]struct{}
|
|
|
|
|
|
|
|
// Flag indicating all projects should be allowed to change, without regard
|
|
|
|
// for lock.
|
|
|
|
chngall bool
|
|
|
|
|
|
|
|
// A map of the project names listed in the root's lock.
|
|
|
|
rlm map[ProjectRoot]LockedProject
|
|
|
|
|
2017-01-16 02:52:42 +03:00
|
|
|
// A defensively copied instance of the root manifest.
|
|
|
|
rm SimpleManifest
|
2017-01-12 08:17:37 +03:00
|
|
|
|
2017-01-16 02:52:42 +03:00
|
|
|
// A defensively copied instance of the root lock.
|
|
|
|
rl safeLock
|
2017-01-12 08:17:37 +03:00
|
|
|
|
2017-01-16 02:52:42 +03:00
|
|
|
// A defensively copied instance of params.RootPackageTree
|
2017-03-11 01:43:16 +03:00
|
|
|
rpt pkgtree.PackageTree
|
2017-04-01 08:14:58 +03:00
|
|
|
|
|
|
|
// The ProjectAnalyzer to use for all GetManifestAndLock calls.
|
|
|
|
an ProjectAnalyzer
|
2017-01-12 08:17:37 +03:00
|
|
|
}
|
|
|
|
|
2017-03-11 01:43:16 +03:00
|
|
|
// externalImportList returns a list of the unique imports from the root data.
|
2017-02-22 09:44:13 +03:00
|
|
|
// Ignores and requires are taken into consideration, stdlib is excluded, and
|
|
|
|
// errors within the local set of package are not backpropagated.
|
2017-05-18 04:13:59 +03:00
|
|
|
func (rd rootdata) externalImportList(stdLibFn func(string) bool) []string {
|
2017-02-22 09:44:13 +03:00
|
|
|
rm, _ := rd.rpt.ToReachMap(true, true, false, rd.ig)
|
2017-05-18 04:13:59 +03:00
|
|
|
reach := rm.FlattenFn(stdLibFn)
|
2017-01-12 08:17:37 +03:00
|
|
|
|
|
|
|
// If there are any requires, slide them into the reach list, as well.
|
|
|
|
if len(rd.req) > 0 {
|
2017-01-16 02:52:42 +03:00
|
|
|
// Make a map of imports that are both in the import path list and the
|
|
|
|
// required list to avoid duplication.
|
2017-01-12 08:17:37 +03:00
|
|
|
skip := make(map[string]bool, len(rd.req))
|
|
|
|
for _, r := range reach {
|
|
|
|
if rd.req[r] {
|
|
|
|
skip[r] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for r := range rd.req {
|
|
|
|
if !skip[r] {
|
2017-01-15 22:11:21 +03:00
|
|
|
reach = append(reach, r)
|
2017-01-12 08:17:37 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-15 22:11:21 +03:00
|
|
|
sort.Strings(reach)
|
2017-01-12 08:17:37 +03:00
|
|
|
return reach
|
|
|
|
}
|
|
|
|
|
2017-05-18 04:13:59 +03:00
|
|
|
func (rd rootdata) getApplicableConstraints(stdLibFn func(string) bool) []workingConstraint {
|
2017-01-14 08:41:06 +03:00
|
|
|
// Merge the normal and test constraints together
|
|
|
|
pc := rd.rm.DependencyConstraints().merge(rd.rm.TestDependencyConstraints())
|
|
|
|
|
|
|
|
// Ensure that overrides which aren't in the combined pc map already make it
|
2017-02-14 00:05:03 +03:00
|
|
|
// in. Doing so makes input hashes equal in more useful cases.
|
2017-01-14 08:41:06 +03:00
|
|
|
for pr, pp := range rd.ovr {
|
|
|
|
if _, has := pc[pr]; !has {
|
|
|
|
cpp := ProjectProperties{
|
|
|
|
Constraint: pp.Constraint,
|
|
|
|
Source: pp.Source,
|
|
|
|
}
|
|
|
|
if cpp.Constraint == nil {
|
|
|
|
cpp.Constraint = anyConstraint{}
|
|
|
|
}
|
|
|
|
|
|
|
|
pc[pr] = cpp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now override them all to produce a consolidated workingConstraint slice
|
|
|
|
combined := rd.ovr.overrideAll(pc)
|
2017-01-12 08:17:37 +03:00
|
|
|
|
|
|
|
type wccount struct {
|
|
|
|
count int
|
|
|
|
wc workingConstraint
|
|
|
|
}
|
2017-01-14 08:41:06 +03:00
|
|
|
xt := radix.New()
|
2017-01-12 08:17:37 +03:00
|
|
|
for _, wc := range combined {
|
|
|
|
xt.Insert(string(wc.Ident.ProjectRoot), wccount{wc: wc})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Walk all dep import paths we have to consider and mark the corresponding
|
|
|
|
// wc entry in the trie, if any
|
2017-05-18 04:13:59 +03:00
|
|
|
for _, im := range rd.externalImportList(stdLibFn) {
|
|
|
|
if stdLibFn(im) {
|
2017-01-12 08:17:37 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if pre, v, match := xt.LongestPrefix(im); match && isPathPrefixOrEqual(pre, im) {
|
|
|
|
wcc := v.(wccount)
|
|
|
|
wcc.count++
|
|
|
|
xt.Insert(pre, wcc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var ret []workingConstraint
|
|
|
|
|
|
|
|
xt.Walk(func(s string, v interface{}) bool {
|
|
|
|
wcc := v.(wccount)
|
2017-01-15 22:11:21 +03:00
|
|
|
if wcc.count > 0 {
|
2017-01-12 08:17:37 +03:00
|
|
|
ret = append(ret, wcc.wc)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rd rootdata) combineConstraints() []workingConstraint {
|
|
|
|
return rd.ovr.overrideAll(rd.rm.DependencyConstraints().merge(rd.rm.TestDependencyConstraints()))
|
|
|
|
}
|
|
|
|
|
|
|
|
// needVersionListFor indicates whether we need a version list for a given
|
|
|
|
// project root, based solely on general solver inputs (no constraint checking
|
2017-01-24 16:21:08 +03:00
|
|
|
// required). Assuming the argument is not the root project itself, this will be
|
|
|
|
// true if any of the following conditions hold:
|
2017-01-12 08:17:37 +03:00
|
|
|
//
|
|
|
|
// - ChangeAll is on
|
2017-01-16 02:52:42 +03:00
|
|
|
// - The project is not in the lock
|
2017-01-12 08:17:37 +03:00
|
|
|
// - The project is in the lock, but is also in the list of projects to change
|
|
|
|
func (rd rootdata) needVersionsFor(pr ProjectRoot) bool {
|
2017-01-24 16:21:08 +03:00
|
|
|
if rd.isRoot(pr) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-01-12 08:17:37 +03:00
|
|
|
if rd.chngall {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, has := rd.rlm[pr]; !has {
|
|
|
|
// not in the lock
|
|
|
|
return true
|
2017-01-24 16:21:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if _, has := rd.chng[pr]; has {
|
2017-01-12 08:17:37 +03:00
|
|
|
// in the lock, but marked for change
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
// in the lock, not marked for change
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rd rootdata) isRoot(pr ProjectRoot) bool {
|
|
|
|
return pr == ProjectRoot(rd.rpt.ImportRoot)
|
|
|
|
}
|
|
|
|
|
|
|
|
// rootAtom creates an atomWithPackages that represents the root project.
|
|
|
|
func (rd rootdata) rootAtom() atomWithPackages {
|
|
|
|
a := atom{
|
|
|
|
id: ProjectIdentifier{
|
|
|
|
ProjectRoot: ProjectRoot(rd.rpt.ImportRoot),
|
|
|
|
},
|
|
|
|
// This is a hack so that the root project doesn't have a nil version.
|
|
|
|
// It's sort of OK because the root never makes it out into the results.
|
|
|
|
// We may need a more elegant solution if we discover other side
|
|
|
|
// effects, though.
|
|
|
|
v: rootRev,
|
|
|
|
}
|
|
|
|
|
|
|
|
list := make([]string, 0, len(rd.rpt.Packages))
|
|
|
|
for path, pkg := range rd.rpt.Packages {
|
|
|
|
if pkg.Err != nil && !rd.ig[path] {
|
|
|
|
list = append(list, path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sort.Strings(list)
|
|
|
|
|
|
|
|
return atomWithPackages{
|
|
|
|
a: a,
|
|
|
|
pl: list,
|
|
|
|
}
|
|
|
|
}
|