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 }