diff --git a/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/README.md b/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/README.md new file mode 100644 index 0000000000..f54848a7a4 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/README.md @@ -0,0 +1,5 @@ +# Ensure - Transitive Constraint + +[github.com/carolynvs/deptest-transcons-c](github.com/carolynvs/deptest-transcons-c) +has a bug in the latest release. I am a library and need to define a constraint +so that consumers of my library don't pull in the bad version of C. diff --git a/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/final/Gopkg.lock b/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/final/Gopkg.lock new file mode 100644 index 0000000000..3e5a6d5a8d --- /dev/null +++ b/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/final/Gopkg.lock @@ -0,0 +1,25 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:790b232e551fb110286b1fa62c89c5fe7b03fbfb528cd8d4fb46a7a4293f0d44" + name = "github.com/carolynvs/deptest-transcons-b" + packages = ["."] + pruneopts = "" + revision = "bc9969bda2dd54b67470c2f199b14d2665c0f7a1" + version = "v1.0.0" + +[[projects]] + digest = "1:cb2def9cb92c7efda5d2caca1bfff95ef91562f5645d009b5c21427d80926519" + name = "github.com/carolynvs/deptest-transcons-c" + packages = ["."] + pruneopts = "" + revision = "24ede1a3d5a6a036793bfcadf8802f60148adf90" + version = "v1.0.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = ["github.com/carolynvs/deptest-transcons-b"] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/final/Gopkg.toml b/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/final/Gopkg.toml new file mode 100644 index 0000000000..a7a1930daa --- /dev/null +++ b/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/final/Gopkg.toml @@ -0,0 +1,7 @@ +[[constraint]] + name = "github.com/carolynvs/deptest-transcons-b" + version = "1.0.0" + +[[constraint]] + name = "github.com/carolynvs/deptest-transcons-c" + version = "<1.0.1" diff --git a/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/initial/Gopkg.toml b/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/initial/Gopkg.toml new file mode 100644 index 0000000000..a7a1930daa --- /dev/null +++ b/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/initial/Gopkg.toml @@ -0,0 +1,7 @@ +[[constraint]] + name = "github.com/carolynvs/deptest-transcons-b" + version = "1.0.0" + +[[constraint]] + name = "github.com/carolynvs/deptest-transcons-c" + version = "<1.0.1" diff --git a/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/initial/a.go b/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/initial/a.go new file mode 100644 index 0000000000..49c38a5558 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/initial/a.go @@ -0,0 +1,15 @@ +// Copyright 2016 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. + +package a + +import ( + "fmt" + "github.com/carolynvs/deptest-transcons-b" +) + +func A() { + fmt.Println("a did a thing") + b.B() +} diff --git a/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/testcase.json b/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/testcase.json new file mode 100644 index 0000000000..f9b242b288 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/ensure/transitive-constraint/testcase.json @@ -0,0 +1,9 @@ +{ + "commands": [ + ["ensure"] + ], + "vendor-final": [ + "github.com/carolynvs/deptest-transcons-b", + "github.com/carolynvs/deptest-transcons-c" + ] +} diff --git a/cmd/dep/testdata/harness_tests/init/transitive-constraint/README.md b/cmd/dep/testdata/harness_tests/init/transitive-constraint/README.md new file mode 100644 index 0000000000..30fa4fd9da --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/transitive-constraint/README.md @@ -0,0 +1,7 @@ +# Init - Transitive Constraint + +[C](github.com/carolynvs/deptest-transcons-c) +has a bug in the latest release. I am an end-user of [A](github.com/carolynvs/deptest-transcons-a) +which transitively depends on C. A has a constraint on C which avoids a bad release of C. +End-users like me should be able to use A, and have `dep init` pick a version of C +that doesn't have the bug, without manually adding overrides. diff --git a/cmd/dep/testdata/harness_tests/init/transitive-constraint/final/Gopkg.lock b/cmd/dep/testdata/harness_tests/init/transitive-constraint/final/Gopkg.lock new file mode 100644 index 0000000000..a7f55148f5 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/transitive-constraint/final/Gopkg.lock @@ -0,0 +1,33 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:5da50097777400e8fd06d9266b28f3ba6aa439752914f05ed4ed2e1556989d93" + name = "github.com/carolynvs/deptest-transcons-a" + packages = ["."] + pruneopts = "UT" + revision = "750587f3002d87affe4971e0a0f1652272633e76" + version = "v1.0.0" + +[[projects]] + digest = "1:790b232e551fb110286b1fa62c89c5fe7b03fbfb528cd8d4fb46a7a4293f0d44" + name = "github.com/carolynvs/deptest-transcons-b" + packages = ["."] + pruneopts = "UT" + revision = "bc9969bda2dd54b67470c2f199b14d2665c0f7a1" + version = "v1.0.0" + +[[projects]] + digest = "1:cb2def9cb92c7efda5d2caca1bfff95ef91562f5645d009b5c21427d80926519" + name = "github.com/carolynvs/deptest-transcons-c" + packages = ["."] + pruneopts = "UT" + revision = "24ede1a3d5a6a036793bfcadf8802f60148adf90" + version = "v1.0.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = ["github.com/carolynvs/deptest-transcons-a"] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/cmd/dep/testdata/harness_tests/init/transitive-constraint/final/Gopkg.toml b/cmd/dep/testdata/harness_tests/init/transitive-constraint/final/Gopkg.toml new file mode 100644 index 0000000000..4b5549f08f --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/transitive-constraint/final/Gopkg.toml @@ -0,0 +1,8 @@ + +[[constraint]] + name = "github.com/carolynvs/deptest-transcons-a" + version = "1.0.0" + +[prune] + go-tests = true + unused-packages = true diff --git a/cmd/dep/testdata/harness_tests/init/transitive-constraint/initial/main.go b/cmd/dep/testdata/harness_tests/init/transitive-constraint/initial/main.go new file mode 100644 index 0000000000..e4bd0d335f --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/transitive-constraint/initial/main.go @@ -0,0 +1,15 @@ +// Copyright 2016 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. + +package main + +import ( + "fmt" + "github.com/carolynvs/deptest-transcons-a" +) + +func main () { + fmt.Println("Stand back, I'm gonna do a thing!") + a.A() +} diff --git a/cmd/dep/testdata/harness_tests/init/transitive-constraint/testcase.json b/cmd/dep/testdata/harness_tests/init/transitive-constraint/testcase.json new file mode 100644 index 0000000000..d87ecde5ad --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/transitive-constraint/testcase.json @@ -0,0 +1,10 @@ +{ + "commands": [ + ["init", "--no-examples"] + ], + "vendor-final": [ + "github.com/carolynvs/deptest-transcons-a", + "github.com/carolynvs/deptest-transcons-b", + "github.com/carolynvs/deptest-transcons-c" + ] +} diff --git a/cmd/dep/testdata/harness_tests/status/case1/table/stdout.txt b/cmd/dep/testdata/harness_tests/status/case1/table/stdout.txt index 66b4db1843..46de9f00bd 100644 --- a/cmd/dep/testdata/harness_tests/status/case1/table/stdout.txt +++ b/cmd/dep/testdata/harness_tests/status/case1/table/stdout.txt @@ -1,3 +1,3 @@ PROJECT CONSTRAINT VERSION REVISION LATEST PKGS USED -github.com/sdboyer/deptest ^0.8.0 v0.8.0 ff2948a v0.8.1 1 -github.com/sdboyer/deptestdos v2.0.0 v2.0.0 5c60720 v2.0.0 1 +github.com/sdboyer/deptest ^0.8.0 v0.8.0 ff2948a v0.8.1 1 +github.com/sdboyer/deptestdos v2.0.0 v2.0.0 5c60720 v2.0.0 1 diff --git a/cmd/dep/testdata/harness_tests/status/old_constraints/stdout.txt b/cmd/dep/testdata/harness_tests/status/old_constraints/stdout.txt index ad557f414b..ec1c1a398e 100644 --- a/cmd/dep/testdata/harness_tests/status/old_constraints/stdout.txt +++ b/cmd/dep/testdata/harness_tests/status/old_constraints/stdout.txt @@ -1,2 +1,2 @@ PROJECT CONSTRAINT REVISION LATEST -github.com/carolynvs/go-dep-test ^0.1.0 b9c5511 4069198 +github.com/carolynvs/go-dep-test ^0.1.0 b9c5511 4069198 diff --git a/cmd/dep/testdata/harness_tests/status/override_constraint/stdout.txt b/cmd/dep/testdata/harness_tests/status/override_constraint/stdout.txt index dc9b6946f9..ba05bfcf04 100644 --- a/cmd/dep/testdata/harness_tests/status/override_constraint/stdout.txt +++ b/cmd/dep/testdata/harness_tests/status/override_constraint/stdout.txt @@ -1,3 +1,3 @@ PROJECT CONSTRAINT VERSION REVISION LATEST PKGS USED -github.com/sdboyer/deptest v0.8.1 (override) v0.8.1 3f4c3be v0.8.1 1 -github.com/sdboyer/deptestdos v2.0.0 v2.0.0 5c60720 v2.0.0 1 +github.com/sdboyer/deptest v0.8.1 (override) v0.8.1 3f4c3be v0.8.1 1 +github.com/sdboyer/deptestdos v2.0.0 v2.0.0 5c60720 v2.0.0 1 diff --git a/cmd/dep/testdata/harness_tests/status/revision_constraint/stdout.txt b/cmd/dep/testdata/harness_tests/status/revision_constraint/stdout.txt index cd3c384a9c..12f30604b6 100644 --- a/cmd/dep/testdata/harness_tests/status/revision_constraint/stdout.txt +++ b/cmd/dep/testdata/harness_tests/status/revision_constraint/stdout.txt @@ -1,3 +1,3 @@ PROJECT CONSTRAINT VERSION REVISION LATEST PKGS USED -github.com/sdboyer/deptest v1.0.0 v1.0.0 ff2948a v1.0.0 1 -github.com/sdboyer/deptestdos a0196ba a0196ba 1 +github.com/sdboyer/deptest v1.0.0 v1.0.0 ff2948a v1.0.0 1 +github.com/sdboyer/deptestdos a0196ba a0196ba 1 diff --git a/gps/identifier.go b/gps/identifier.go index cf3ca23513..a7481bc803 100644 --- a/gps/identifier.go +++ b/gps/identifier.go @@ -215,6 +215,8 @@ type completeDep struct { workingConstraint // The specific packages required from the ProjectDep pl []string + // Flags the constraint as being defined on an indirect/transitive dependency + isTransitive bool } // dependency represents an incomplete edge in the depgraph. It has a diff --git a/gps/solve_bimodal_test.go b/gps/solve_bimodal_test.go index a5d34211ef..9fc24b4e5f 100644 --- a/gps/solve_bimodal_test.go +++ b/gps/solve_bimodal_test.go @@ -132,6 +132,78 @@ var bimodalFixtures = map[string]bimodalFixture{ "b 1.0.0", ), }, + "transitive constraint": { + ds: []depspec{ + dsp(mkDepspec("root 1.0.0", "foo 1.0.0"), + pkg("root", "foo"), + ), + dsp(mkDepspec("foo 1.0.0", "bar 1.0.0", "baz =1.0.0"), + pkg("foo", "bar"), + ), + dsp(mkDepspec("bar 1.0.0", "baz >=1.0.0"), + pkg("bar", "baz"), + ), + dsp(mkDepspec("baz 1.0.1"), pkg("baz")), + dsp(mkDepspec("baz 1.0.0"), pkg("baz")), + }, + r: mksolution( + "foo 1.0.0", + "bar 1.0.0", + "baz 1.0.0", + ), + }, + "backtrack drops transitive constraint": { + ds: []depspec{ + dsp(mkDepspec("root 1.0.0", "foo ^1.0.0"), + pkg("root", "foo"), + ), + dsp(mkDepspec("foo 1.0.1", "bar =1.0.1", "baz =1.0.1"), // This transitive constraint should not be applied in the final solution + pkg("foo", "bar"), + ), + dsp(mkDepspec("foo 1.0.0", "bar =1.0.0"), + pkg("foo", "bar"), + ), + dsp(mkDepspec("bar 1.0.1", "baz =1.0.0-notexist"), // This should trigger a backtrack + pkg("bar", "baz"), + ), + dsp(mkDepspec("bar 1.0.0", "baz =1.0.0"), // This will conflict with the transitive constraint if it's not dropped during backtracking + pkg("bar", "baz"), + ), + dsp(mkDepspec("baz 1.0.1"), pkg("baz")), + dsp(mkDepspec("baz 1.0.0"), pkg("baz")), + }, + r: mksolution( + "foo 1.0.0", + "bar 1.0.0", + "baz 1.0.0", + ), + }, + "backtrack adds transitive constraint": { + ds: []depspec{ + dsp(mkDepspec("root 1.0.0", "foo ^1.0.0"), + pkg("root", "foo"), + ), + dsp(mkDepspec("foo 1.0.1", "bar =1.0.1"), + pkg("foo", "bar"), + ), + dsp(mkDepspec("foo 1.0.0", "bar =1.0.0", "baz =1.0.0"), // This transitive constraint should be applied in the final solution + pkg("foo", "bar"), + ), + dsp(mkDepspec("bar 1.0.1", "baz =1.0.0-notexist"), // This should trigger a backtrack + pkg("bar", "baz"), + ), + dsp(mkDepspec("bar 1.0.0", "baz ^1.0.0"), // Normally this would pick baz 1.0.1 but the transitive constraint should force an older version + pkg("bar", "baz"), + ), + dsp(mkDepspec("baz 1.0.1"), pkg("baz")), + dsp(mkDepspec("baz 1.0.0"), pkg("baz")), + }, + r: mksolution( + "foo 1.0.0", + "bar 1.0.0", + "baz 1.0.0", + ), + }, // Constraints apply only if the project that declares them has a // reachable import "constraints activated by import": { @@ -152,12 +224,13 @@ var bimodalFixtures = map[string]bimodalFixture{ }, r: mksolution( "a 1.0.0", - "b 1.1.0", + "b 1.0.0", // Now that constraints can be applied transitively, the constraint from root applies ), }, // Constraints apply only if the project that declares them has a // reachable import - non-root - "constraints activated by import, transitive": { + /* TODO(carolynvs): This was broken by supporting transitive constraints. It will be fixed in a follow-up PR where gps tracks which package introduced a constraint. + "constraints activated by import, non-root": { ds: []depspec{ dsp(mkDepspec("root 0.0.0"), pkg("root", "root/foo", "b"), @@ -177,7 +250,7 @@ var bimodalFixtures = map[string]bimodalFixture{ "a 1.0.0", "b 1.1.0", ), - }, + },*/ // Import jump is in a dep, and points to a transitive dep - but only in not // the first version we try "transitive bm-add on older version": { diff --git a/gps/solver.go b/gps/solver.go index 74e77414b9..e2c2783024 100644 --- a/gps/solver.go +++ b/gps/solver.go @@ -633,6 +633,13 @@ func (s *solver) selectRoot() error { } s.sel.pushDep(dependency{depender: awp.a, dep: dep}) + + if dep.isTransitive { + // Do not add transitive dependencies to the queue immediately, + // instead wait until they are directly used + continue + } + // Add all to unselected queue heap.Push(s.unsel, bimodalIdentifier{id: dep.Ident, pl: dep.pl, fromRoot: true}) } @@ -780,6 +787,17 @@ func (s *solver) intersectConstraintsWithImports(deps []workingConstraint, reach } } + // Include transitive constraints, flagging them as transitive for special handling later on + for _, wc := range deps { + root := wc.Ident.ProjectRoot + if _, ok := dmap[root]; !ok { + dmap[root] = completeDep{ + workingConstraint: wc, // TODO(carolynvs): deal with overrides and package prefix-foo (not sure if that's needed?) + isTransitive: true, + } + } + } + // Dump all the deps from the map into the expected return slice cdeps := make([]completeDep, 0, len(dmap)) for _, cdep := range dmap {