github.com/golang/dep@v0.5.4/gps/rootdata.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gps
     6  
     7  import (
     8  	"sort"
     9  
    10  	"github.com/armon/go-radix"
    11  	"github.com/golang/dep/gps/pkgtree"
    12  )
    13  
    14  // rootdata holds static data and constraining rules from the root project for
    15  // use in solving.
    16  type rootdata struct {
    17  	// Path to the root of the project on which gps is operating.
    18  	dir string
    19  
    20  	// Ruleset for ignored import paths.
    21  	ir *pkgtree.IgnoredRuleset
    22  
    23  	// Map of packages to require.
    24  	req map[string]bool
    25  
    26  	// A ProjectConstraints map containing the validated (guaranteed non-empty)
    27  	// overrides declared by the root manifest.
    28  	ovr ProjectConstraints
    29  
    30  	// A map of the ProjectRoot (local names) that should be allowed to change
    31  	chng map[ProjectRoot]struct{}
    32  
    33  	// Flag indicating all projects should be allowed to change, without regard
    34  	// for lock.
    35  	chngall bool
    36  
    37  	// A map of the project names listed in the root's lock.
    38  	rlm map[ProjectRoot]LockedProject
    39  
    40  	// A defensively copied instance of the root manifest.
    41  	rm SimpleManifest
    42  
    43  	// A defensively copied instance of the root lock.
    44  	rl safeLock
    45  
    46  	// A defensively copied instance of params.RootPackageTree
    47  	rpt pkgtree.PackageTree
    48  
    49  	// The ProjectAnalyzer to use for all GetManifestAndLock calls.
    50  	an ProjectAnalyzer
    51  }
    52  
    53  // externalImportList returns a list of the unique imports from the root data.
    54  // Ignores and requires are taken into consideration, stdlib is excluded, and
    55  // errors within the local set of package are not backpropagated.
    56  func (rd rootdata) externalImportList(stdLibFn func(string) bool) []string {
    57  	rm, _ := rd.rpt.ToReachMap(true, true, false, rd.ir)
    58  	reach := rm.FlattenFn(stdLibFn)
    59  
    60  	// If there are any requires, slide them into the reach list, as well.
    61  	if len(rd.req) > 0 {
    62  		// Make a map of imports that are both in the import path list and the
    63  		// required list to avoid duplication.
    64  		skip := make(map[string]bool, len(rd.req))
    65  		for _, r := range reach {
    66  			if rd.req[r] {
    67  				skip[r] = true
    68  			}
    69  		}
    70  
    71  		for r := range rd.req {
    72  			if !skip[r] {
    73  				reach = append(reach, r)
    74  			}
    75  		}
    76  	}
    77  
    78  	sort.Strings(reach)
    79  	return reach
    80  }
    81  
    82  func (rd rootdata) getApplicableConstraints(stdLibFn func(string) bool) []workingConstraint {
    83  	pc := rd.rm.DependencyConstraints()
    84  
    85  	// Ensure that overrides which aren't in the combined pc map already make it
    86  	// in. Doing so makes input hashes equal in more useful cases.
    87  	for pr, pp := range rd.ovr {
    88  		if _, has := pc[pr]; !has {
    89  			cpp := ProjectProperties{
    90  				Constraint: pp.Constraint,
    91  				Source:     pp.Source,
    92  			}
    93  			if cpp.Constraint == nil {
    94  				cpp.Constraint = anyConstraint{}
    95  			}
    96  
    97  			pc[pr] = cpp
    98  		}
    99  	}
   100  
   101  	// Now override them all to produce a consolidated workingConstraint slice
   102  	combined := rd.ovr.overrideAll(pc)
   103  
   104  	type wccount struct {
   105  		count int
   106  		wc    workingConstraint
   107  	}
   108  	xt := radix.New()
   109  	for _, wc := range combined {
   110  		xt.Insert(string(wc.Ident.ProjectRoot), wccount{wc: wc})
   111  	}
   112  
   113  	// Walk all dep import paths we have to consider and mark the corresponding
   114  	// wc entry in the trie, if any
   115  	for _, im := range rd.externalImportList(stdLibFn) {
   116  		if stdLibFn(im) {
   117  			continue
   118  		}
   119  
   120  		if pre, v, match := xt.LongestPrefix(im); match && isPathPrefixOrEqual(pre, im) {
   121  			wcc := v.(wccount)
   122  			wcc.count++
   123  			xt.Insert(pre, wcc)
   124  		}
   125  	}
   126  
   127  	var ret []workingConstraint
   128  
   129  	xt.Walk(func(s string, v interface{}) bool {
   130  		wcc := v.(wccount)
   131  		if wcc.count > 0 {
   132  			ret = append(ret, wcc.wc)
   133  		}
   134  		return false
   135  	})
   136  
   137  	return ret
   138  }
   139  
   140  func (rd rootdata) combineConstraints() []workingConstraint {
   141  	return rd.ovr.overrideAll(rd.rm.DependencyConstraints())
   142  }
   143  
   144  // needVersionListFor indicates whether we need a version list for a given
   145  // project root, based solely on general solver inputs (no constraint checking
   146  // required). Assuming the argument is not the root project itself, this will be
   147  // true if any of the following conditions hold:
   148  //
   149  //  - ChangeAll is on
   150  //  - The project is not in the lock
   151  //  - The project is in the lock, but is also in the list of projects to change
   152  func (rd rootdata) needVersionsFor(pr ProjectRoot) bool {
   153  	if rd.isRoot(pr) {
   154  		return false
   155  	}
   156  
   157  	if rd.chngall {
   158  		return true
   159  	}
   160  
   161  	if _, has := rd.rlm[pr]; !has {
   162  		// not in the lock
   163  		return true
   164  	}
   165  
   166  	if _, has := rd.chng[pr]; has {
   167  		// in the lock, but marked for change
   168  		return true
   169  	}
   170  	// in the lock, not marked for change
   171  	return false
   172  
   173  }
   174  
   175  func (rd rootdata) isRoot(pr ProjectRoot) bool {
   176  	return pr == ProjectRoot(rd.rpt.ImportRoot)
   177  }
   178  
   179  // rootAtom creates an atomWithPackages that represents the root project.
   180  func (rd rootdata) rootAtom() atomWithPackages {
   181  	a := atom{
   182  		id: ProjectIdentifier{
   183  			ProjectRoot: ProjectRoot(rd.rpt.ImportRoot),
   184  		},
   185  		// This is a hack so that the root project doesn't have a nil version.
   186  		// It's sort of OK because the root never makes it out into the results.
   187  		// We may need a more elegant solution if we discover other side
   188  		// effects, though.
   189  		v: rootRev,
   190  	}
   191  
   192  	list := make([]string, 0, len(rd.rpt.Packages))
   193  	for path, pkg := range rd.rpt.Packages {
   194  		if pkg.Err != nil && !rd.ir.IsIgnored(path) {
   195  			list = append(list, path)
   196  		}
   197  	}
   198  	sort.Strings(list)
   199  
   200  	return atomWithPackages{
   201  		a:  a,
   202  		pl: list,
   203  	}
   204  }