github.com/sdboyer/gps@v0.16.3/manifest.go (about) 1 package gps 2 3 // Manifest represents manifest-type data for a project at a particular version. 4 // That means dependency constraints, both for normal dependencies and for 5 // tests. The constraints expressed in a manifest determine the set of versions that 6 // are acceptable to try for a given project. 7 // 8 // Expressing a constraint in a manifest does not guarantee that a particular 9 // dependency will be present. It only guarantees that if packages in the 10 // project specified by the dependency are discovered through static analysis of 11 // the (transitive) import graph, then they will conform to the constraint. 12 // 13 // This does entail that manifests can express constraints on projects they do 14 // not themselves import. This is by design, but its implications are complex. 15 // See the gps docs for more information: https://github.com/sdboyer/gps/wiki 16 type Manifest interface { 17 // Returns a list of project-level constraints. 18 DependencyConstraints() ProjectConstraints 19 20 // Returns a list of constraints applicable to test imports. 21 // 22 // These are applied only when tests are incorporated. Typically, that 23 // will only be for root manifests. 24 TestDependencyConstraints() ProjectConstraints 25 } 26 27 // RootManifest extends Manifest to add special controls over solving that are 28 // only afforded to the root project. 29 type RootManifest interface { 30 Manifest 31 32 // Overrides returns a list of ProjectConstraints that will unconditionally 33 // supercede any ProjectConstraint declarations made in either the root 34 // manifest, or in any dependency's manifest. 35 // 36 // Overrides are a special control afforded only to root manifests. Tool 37 // users should be encouraged to use them only as a last resort; they do not 38 // "play well with others" (that is their express goal), and overreliance on 39 // them can harm the ecosystem as a whole. 40 Overrides() ProjectConstraints 41 42 // IngoredPackages returns a set of import paths to ignore. These import 43 // paths can be within the root project, or part of other projects. Ignoring 44 // a package means that both it and its (unique) imports will be disregarded 45 // by all relevant solver operations. 46 // 47 // It is an error to include a package in both the ignored and required 48 // sets. 49 IgnoredPackages() map[string]bool 50 51 // RequiredPackages returns a set of import paths to require. These packages 52 // are required to be present in any solution. The list can include main 53 // packages. 54 // 55 // It is meaningless to specify packages that are within the 56 // PackageTree of the ProjectRoot (though not an error, because the 57 // RootManifest itself does not report a ProjectRoot). 58 // 59 // It is an error to include a package in both the ignored and required 60 // sets. 61 RequiredPackages() map[string]bool 62 } 63 64 // SimpleManifest is a helper for tools to enumerate manifest data. It's 65 // generally intended for ephemeral manifests, such as those Analyzers create on 66 // the fly for projects with no manifest metadata, or metadata through a foreign 67 // tool's idioms. 68 type SimpleManifest struct { 69 Deps, TestDeps ProjectConstraints 70 } 71 72 var _ Manifest = SimpleManifest{} 73 74 // DependencyConstraints returns the project's dependencies. 75 func (m SimpleManifest) DependencyConstraints() ProjectConstraints { 76 return m.Deps 77 } 78 79 // TestDependencyConstraints returns the project's test dependencies. 80 func (m SimpleManifest) TestDependencyConstraints() ProjectConstraints { 81 return m.TestDeps 82 } 83 84 // simpleRootManifest exists so that we have a safe value to swap into solver 85 // params when a nil Manifest is provided. 86 // 87 // Also, for tests. 88 type simpleRootManifest struct { 89 c, tc, ovr ProjectConstraints 90 ig, req map[string]bool 91 } 92 93 func (m simpleRootManifest) DependencyConstraints() ProjectConstraints { 94 return m.c 95 } 96 func (m simpleRootManifest) TestDependencyConstraints() ProjectConstraints { 97 return m.tc 98 } 99 func (m simpleRootManifest) Overrides() ProjectConstraints { 100 return m.ovr 101 } 102 func (m simpleRootManifest) IgnoredPackages() map[string]bool { 103 return m.ig 104 } 105 func (m simpleRootManifest) RequiredPackages() map[string]bool { 106 return m.req 107 } 108 func (m simpleRootManifest) dup() simpleRootManifest { 109 m2 := simpleRootManifest{ 110 c: make(ProjectConstraints, len(m.c)), 111 tc: make(ProjectConstraints, len(m.tc)), 112 ovr: make(ProjectConstraints, len(m.ovr)), 113 ig: make(map[string]bool, len(m.ig)), 114 req: make(map[string]bool, len(m.req)), 115 } 116 117 for k, v := range m.c { 118 m2.c[k] = v 119 } 120 for k, v := range m.tc { 121 m2.tc[k] = v 122 } 123 for k, v := range m.ovr { 124 m2.ovr[k] = v 125 } 126 for k, v := range m.ig { 127 m2.ig[k] = v 128 } 129 for k, v := range m.req { 130 m2.req[k] = v 131 } 132 133 return m2 134 } 135 136 // prepManifest ensures a manifest is prepared and safe for use by the solver. 137 // This is mostly about ensuring that no outside routine can modify the manifest 138 // while the solver is in-flight, but it also filters out any empty 139 // ProjectProperties. 140 // 141 // This is achieved by copying the manifest's data into a new SimpleManifest. 142 func prepManifest(m Manifest) SimpleManifest { 143 if m == nil { 144 return SimpleManifest{} 145 } 146 147 deps := m.DependencyConstraints() 148 ddeps := m.TestDependencyConstraints() 149 150 rm := SimpleManifest{ 151 Deps: make(ProjectConstraints, len(deps)), 152 TestDeps: make(ProjectConstraints, len(ddeps)), 153 } 154 155 for k, d := range deps { 156 // A zero-value ProjectProperties is equivalent to one with an 157 // anyConstraint{} in terms of how the solver will treat it. However, we 158 // normalize between these two by omitting such instances entirely, as 159 // it negates some possibility for false mismatches in input hashing. 160 if d.Constraint == nil { 161 if d.Source == "" { 162 continue 163 } 164 d.Constraint = anyConstraint{} 165 } 166 167 rm.Deps[k] = d 168 } 169 170 for k, d := range ddeps { 171 if d.Constraint == nil { 172 if d.Source == "" { 173 continue 174 } 175 d.Constraint = anyConstraint{} 176 } 177 178 rm.TestDeps[k] = d 179 } 180 181 return rm 182 }