From 4a515152c94a6bcfc06709f3ca1bfb7568e0b3af Mon Sep 17 00:00:00 2001 From: Carolyn Van Slyck Date: Thu, 11 May 2017 14:55:16 -0500 Subject: [PATCH] Read other dependency mgrs cfg during init Extract initial root manifest generation into an analyzer which combines evaluating the contents of the GOPATH with importing cfg from other managers. --- cmd/dep/compositeAnalyzer.go | 72 +++++++ cmd/dep/compositeAnalyzer_test.go | 181 ++++++++++++++++++ cmd/dep/glideConfig.go | 68 ++++--- cmd/dep/glideConfig_test.go | 15 +- cmd/dep/glideImporter.go | 17 +- cmd/dep/glideImporter_test.go | 83 ++++++++ cmd/dep/gopathAnalyzer.go | 82 ++++++++ cmd/dep/importAnalyzer.go | 74 +++++++ cmd/dep/init.go | 63 +++--- cmd/dep/rootProjectAnalyzer.go | 21 ++ cmd/dep/testdata/glide.lock | 12 ++ cmd/dep/testdata/glide.yaml | 20 ++ .../init/glide/case1/final/Gopkg.lock | 13 ++ .../init/glide/case1/final/Gopkg.toml | 10 + .../init/glide/case1/initial/glide.lock | 12 ++ .../init/glide/case1/initial/glide.yaml | 20 ++ .../init/glide/case1/initial/main.go | 17 ++ .../glide/case1/initial/samples/samples.go | 12 ++ .../init/glide/case1/testcase.json | 14 ++ 19 files changed, 723 insertions(+), 83 deletions(-) create mode 100644 cmd/dep/compositeAnalyzer.go create mode 100644 cmd/dep/compositeAnalyzer_test.go create mode 100644 cmd/dep/glideImporter_test.go create mode 100644 cmd/dep/gopathAnalyzer.go create mode 100644 cmd/dep/importAnalyzer.go create mode 100644 cmd/dep/rootProjectAnalyzer.go create mode 100644 cmd/dep/testdata/glide.lock create mode 100644 cmd/dep/testdata/glide.yaml create mode 100644 cmd/dep/testdata/harness_tests/init/glide/case1/final/Gopkg.lock create mode 100644 cmd/dep/testdata/harness_tests/init/glide/case1/final/Gopkg.toml create mode 100644 cmd/dep/testdata/harness_tests/init/glide/case1/initial/glide.lock create mode 100644 cmd/dep/testdata/harness_tests/init/glide/case1/initial/glide.yaml create mode 100644 cmd/dep/testdata/harness_tests/init/glide/case1/initial/main.go create mode 100644 cmd/dep/testdata/harness_tests/init/glide/case1/initial/samples/samples.go create mode 100644 cmd/dep/testdata/harness_tests/init/glide/case1/testcase.json diff --git a/cmd/dep/compositeAnalyzer.go b/cmd/dep/compositeAnalyzer.go new file mode 100644 index 0000000000..0a4cd42bc2 --- /dev/null +++ b/cmd/dep/compositeAnalyzer.go @@ -0,0 +1,72 @@ +// 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 ( + "github.com/golang/dep" + "github.com/golang/dep/internal/gps" + "github.com/pkg/errors" +) + +// compositeAnalyzer overlays configuration from multiple analyzers +type compositeAnalyzer struct { + // Analyzers is the set of analyzers to apply, last one wins any conflicts + Analyzers []rootProjectAnalyzer +} + +func (a compositeAnalyzer) DeriveRootManifestAndLock(path string, n gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { + var rootM *dep.Manifest + var rootL *dep.Lock + + for _, a := range a.Analyzers { + m, l, err := a.DeriveRootManifestAndLock(path, n) + if err != nil { + return nil, nil, errors.Wrapf(err, "%T.DeriveRootManifestAndLock", a) + } + + if rootM == nil && rootL == nil { + rootM = m + rootL = l + } else { + // Overlay the changes from the analyzer on-top of the previous analyzer's work + if m != nil { + for pkg, prj := range m.Dependencies { + rootM.Dependencies[pkg] = prj + } + for pkg, prj := range m.Ovr { + rootM.Ovr[pkg] = prj + } + for _, pkg := range m.Required { + if !contains(rootM.Required, pkg) { + rootM.Required = append(rootM.Required, pkg) + } + } + for _, pkg := range m.Ignored { + if !contains(rootM.Ignored, pkg) { + rootM.Ignored = append(rootM.Ignored, pkg) + } + } + } + + if l != nil { + for _, lp := range l.P { + for i, existingLP := range rootL.P { + if lp.Ident().ProjectRoot == existingLP.Ident().ProjectRoot { + rootL.P[i] = lp + } + } + } + } + } + } + + return rootM, rootL, nil +} + +func (a compositeAnalyzer) PostSolveShenanigans(m *dep.Manifest, l *dep.Lock) { + for _, a := range a.Analyzers { + a.PostSolveShenanigans(m, l) + } +} diff --git a/cmd/dep/compositeAnalyzer_test.go b/cmd/dep/compositeAnalyzer_test.go new file mode 100644 index 0000000000..7f5722cd65 --- /dev/null +++ b/cmd/dep/compositeAnalyzer_test.go @@ -0,0 +1,181 @@ +// 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 ( + "testing" + + "github.com/golang/dep" + "github.com/golang/dep/internal/gps" +) + +type testRootProjectAnalyzer struct { + *dep.Manifest + *dep.Lock +} + +func (a testRootProjectAnalyzer) DeriveRootManifestAndLock(path string, n gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { + return a.Manifest, a.Lock, nil +} + +func (a testRootProjectAnalyzer) PostSolveShenanigans(*dep.Manifest, *dep.Lock) { + // do nothing +} + +func TestCompositeAnalyzer_ManifestDependencies(t *testing.T) { + pkg := gps.ProjectRoot("github.com/sdboyer/deptest") + m1 := &dep.Manifest{ + Dependencies: gps.ProjectConstraints{ + pkg: gps.ProjectProperties{Constraint: gps.NewVersion("^1.0.0")}, + }, + } + m2 := &dep.Manifest{ + Dependencies: gps.ProjectConstraints{ + pkg: gps.ProjectProperties{Constraint: gps.NewVersion("^2.0.0")}, + }, + } + c := compositeAnalyzer{ + Analyzers: []rootProjectAnalyzer{ + testRootProjectAnalyzer{Manifest: m1}, + testRootProjectAnalyzer{Manifest: m2}, + }, + } + + rm, _, err := c.DeriveRootManifestAndLock("", "") + if err != nil { + t.Fatal(err) + } + + if rm == nil { + t.Fatal("Expected the root manifest to not be nil") + } + + dep, has := rm.Dependencies[pkg] + if !has { + t.Fatal("Expected the root manifest to contain the test project") + } + + wantC := "^2.0.0" + gotC := dep.Constraint.String() + if wantC != gotC { + t.Fatalf("Expected the test project to be constrained to '%s', got '%s'", wantC, gotC) + } +} + +func TestCompositeAnalyzer_ManifestOverrides(t *testing.T) { + pkg := gps.ProjectRoot("github.com/sdboyer/deptest") + m1 := &dep.Manifest{ + Ovr: gps.ProjectConstraints{ + pkg: gps.ProjectProperties{Constraint: gps.NewVersion("^1.0.0")}, + }, + } + m2 := &dep.Manifest{ + Ovr: gps.ProjectConstraints{ + pkg: gps.ProjectProperties{Constraint: gps.NewVersion("^2.0.0")}, + }, + } + c := compositeAnalyzer{ + Analyzers: []rootProjectAnalyzer{ + testRootProjectAnalyzer{Manifest: m1}, + testRootProjectAnalyzer{Manifest: m2}, + }, + } + + rm, _, err := c.DeriveRootManifestAndLock("", "") + if err != nil { + t.Fatal(err) + } + + if rm == nil { + t.Fatal("Expected the root manifest to not be nil") + } + + dep, has := rm.Ovr[pkg] + if !has { + t.Fatal("Expected the root manifest to contain the test project override") + } + + wantC := "^2.0.0" + gotC := dep.Constraint.String() + if wantC != gotC { + t.Fatalf("Expected the test project to be overridden to '%s', got '%s'", wantC, gotC) + } +} + +func TestCompositeAnalyzer_ManifestRequired(t *testing.T) { + pkg1 := "github.com/sdboyer/deptest" + pkg2 := "github.com/sdboyer/deptestdos" + m1 := &dep.Manifest{ + Required: []string{pkg1}, + } + m2 := &dep.Manifest{ + Required: []string{pkg2}, + } + c := compositeAnalyzer{ + Analyzers: []rootProjectAnalyzer{ + testRootProjectAnalyzer{Manifest: m1}, + testRootProjectAnalyzer{Manifest: m2}, + }, + } + + rm, _, err := c.DeriveRootManifestAndLock("", "") + if err != nil { + t.Fatal(err) + } + + if rm == nil { + t.Fatal("Expected the root manifest to not be nil") + } + + if len(rm.Required) != 2 { + t.Fatalf("Expected the root manifest to contain 2 required packages, got %d", len(rm.Required)) + } + + if rm.Required[0] != pkg1 { + t.Fatalf("Expected the first required package to be '%s', got '%s'", pkg1, rm.Required[0]) + } + + if rm.Required[1] != pkg2 { + t.Fatalf("Expected the second required package to be '%s', got '%s'", pkg2, rm.Required[1]) + } +} + +func TestCompositeAnalyzer_ManifestIgnored(t *testing.T) { + pkg1 := "github.com/sdboyer/deptest" + pkg2 := "github.com/sdboyer/deptestdos" + m1 := &dep.Manifest{ + Ignored: []string{pkg1}, + } + m2 := &dep.Manifest{ + Ignored: []string{pkg2}, + } + c := compositeAnalyzer{ + Analyzers: []rootProjectAnalyzer{ + testRootProjectAnalyzer{Manifest: m1}, + testRootProjectAnalyzer{Manifest: m2}, + }, + } + + rm, _, err := c.DeriveRootManifestAndLock("", "") + if err != nil { + t.Fatal(err) + } + + if rm == nil { + t.Fatal("Expected the root manifest to not be nil") + } + + if len(rm.Ignored) != 2 { + t.Fatalf("Expected the root manifest to contain 2 ignored packages, got %d", len(rm.Ignored)) + } + + if rm.Ignored[0] != pkg1 { + t.Fatalf("Expected the first ignored package to be '%s', got '%s'", pkg1, rm.Ignored[0]) + } + + if rm.Ignored[1] != pkg2 { + t.Fatalf("Expected the second ignored package to be '%s', got '%s'", pkg2, rm.Ignored[1]) + } +} diff --git a/cmd/dep/glideConfig.go b/cmd/dep/glideConfig.go index 88c156ecb6..3b7190dad4 100644 --- a/cmd/dep/glideConfig.go +++ b/cmd/dep/glideConfig.go @@ -17,12 +17,12 @@ import ( type glideFiles struct { yaml glideYaml - lock glideLock - loggers *Loggers + lock *glideLock + loggers *dep.Loggers } -func newGlideFiles(loggers *Loggers) glideFiles { - return glideFiles{loggers: loggers} +func newGlideFiles(loggers *dep.Loggers) *glideFiles { + return &glideFiles{loggers: loggers} } type glideYaml struct { @@ -44,7 +44,7 @@ type glidePackage struct { Repository string `yaml:"repo"` } -func (files glideFiles) load(projectDir string) error { +func (files *glideFiles) load(projectDir string) error { y := filepath.Join(projectDir, glideYamlName) if files.loggers.Verbose { files.loggers.Err.Printf("dep: Loading %s", y) @@ -59,22 +59,24 @@ func (files glideFiles) load(projectDir string) error { } l := filepath.Join(projectDir, glideLockName) - if files.loggers.Verbose { - files.loggers.Err.Printf("dep: Loading %s", l) - } - lb, err := ioutil.ReadFile(l) - if err != nil { - return errors.Wrapf(err, "Unable to read %s", l) - } - err = yaml.Unmarshal(lb, &files.lock) - if err != nil { - return errors.Wrapf(err, "Unable to parse %s", l) + if exists, _ := dep.IsRegular(l); exists { + if files.loggers.Verbose { + files.loggers.Err.Printf("dep: Loading %s", l) + } + lb, err := ioutil.ReadFile(l) + if err != nil { + return errors.Wrapf(err, "Unable to read %s", l) + } + err = yaml.Unmarshal(lb, &files.lock) + if err != nil { + return errors.Wrapf(err, "Unable to parse %s", l) + } } return nil } -func (files glideFiles) convert(projectName string) (*dep.Manifest, *dep.Lock, error) { +func (files *glideFiles) convert(projectName string) (*dep.Manifest, *dep.Lock, error) { manifest := &dep.Manifest{ Dependencies: make(gps.ProjectConstraints), } @@ -105,24 +107,28 @@ func (files glideFiles) convert(projectName string) (*dep.Manifest, *dep.Lock, e } } - lock := &dep.Lock{} + var lock *dep.Lock + if files.lock != nil { + lock = &dep.Lock{} + + lockDep := func(pkg glidePackage) { + id := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot(pkg.Name)} + c, has := manifest.Dependencies[id.ProjectRoot] + if has { + id.Source = c.Source + } + version := gps.Revision(pkg.Reference) - lockDep := func(pkg glidePackage) { - id := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot(pkg.Name)} - c, has := manifest.Dependencies[id.ProjectRoot] - if has { - id.Source = c.Source + lp := gps.NewLockedProject(id, version, nil) + lock.P = append(lock.P, lp) } - version := gps.Revision(pkg.Reference) - lp := gps.NewLockedProject(id, version, nil) - lock.P = append(lock.P, lp) - } - for _, pkg := range files.lock.Imports { - lockDep(pkg) - } - for _, pkg := range files.lock.TestImports { - lockDep(pkg) + for _, pkg := range files.lock.Imports { + lockDep(pkg) + } + for _, pkg := range files.lock.TestImports { + lockDep(pkg) + } } return manifest, lock, nil diff --git a/cmd/dep/glideConfig_test.go b/cmd/dep/glideConfig_test.go index b341011ba7..70b6f91ae1 100644 --- a/cmd/dep/glideConfig_test.go +++ b/cmd/dep/glideConfig_test.go @@ -5,13 +5,14 @@ package main import ( + "github.com/golang/dep" "log" "os" "testing" ) func TestGlideConvertProject(t *testing.T) { - loggers := &Loggers{ + loggers := &dep.Loggers{ Out: log.New(os.Stdout, "", 0), Err: log.New(os.Stderr, "", 0), Verbose: true, @@ -28,7 +29,7 @@ func TestGlideConvertProject(t *testing.T) { }, }, }, - lock: glideLock{ + lock: &glideLock{ Imports: []glidePackage{ { Name: "github.com/sdboyer/deptest", @@ -77,7 +78,7 @@ func TestGlideConvertProject(t *testing.T) { } func TestGlideConvertTestProject(t *testing.T) { - loggers := &Loggers{ + loggers := &dep.Loggers{ Out: log.New(os.Stdout, "", 0), Err: log.New(os.Stderr, "", 0), Verbose: true, @@ -93,7 +94,7 @@ func TestGlideConvertTestProject(t *testing.T) { }, }, }, - lock: glideLock{ + lock: &glideLock{ TestImports: []glidePackage{ { Name: "github.com/sdboyer/deptest", @@ -123,7 +124,7 @@ func TestGlideConvertTestProject(t *testing.T) { } func TestGlideConvertIgnore(t *testing.T) { - loggers := &Loggers{ + loggers := &dep.Loggers{ Out: log.New(os.Stdout, "", 0), Err: log.New(os.Stderr, "", 0), Verbose: true, @@ -151,7 +152,7 @@ func TestGlideConvertIgnore(t *testing.T) { } func TestGlideConvertExcludeDir(t *testing.T) { - loggers := &Loggers{ + loggers := &dep.Loggers{ Out: log.New(os.Stdout, "", 0), Err: log.New(os.Stderr, "", 0), Verbose: true, @@ -179,7 +180,7 @@ func TestGlideConvertExcludeDir(t *testing.T) { } func TestGlideConvertExcludeDir_IgnoresMismatchedPackageName(t *testing.T) { - loggers := &Loggers{ + loggers := &dep.Loggers{ Out: log.New(os.Stdout, "", 0), Err: log.New(os.Stderr, "", 0), Verbose: true, diff --git a/cmd/dep/glideImporter.go b/cmd/dep/glideImporter.go index bafb2240d7..55fe27c251 100644 --- a/cmd/dep/glideImporter.go +++ b/cmd/dep/glideImporter.go @@ -16,7 +16,11 @@ const glideYamlName = "glide.yaml" const glideLockName = "glide.lock" type glideImporter struct { - loggers *Loggers + loggers *dep.Loggers +} + +func newGlideImporter(loggers *dep.Loggers) glideImporter { + return glideImporter{loggers: loggers} } func (i glideImporter) Info() (name string, version int) { @@ -24,16 +28,12 @@ func (i glideImporter) Info() (name string, version int) { } func (i glideImporter) HasConfig(dir string) bool { + // Only require glide.yaml, the lock is optional y := filepath.Join(dir, glideYamlName) if _, err := os.Stat(y); err != nil { return false } - l := filepath.Join(dir, glideLockName) - if _, err := os.Stat(l); err != nil { - return false - } - return true } @@ -50,3 +50,8 @@ func (i glideImporter) DeriveRootManifestAndLock(dir string, pr gps.ProjectRoot) func (i glideImporter) DeriveManifestAndLock(dir string, pr gps.ProjectRoot) (gps.Manifest, gps.Lock, error) { return i.DeriveRootManifestAndLock(dir, pr) } + +func (a glideImporter) PostSolveShenanigans(*dep.Manifest, *dep.Lock) { + // do nothing + // TODO: importers don't need to be full root analyzers +} diff --git a/cmd/dep/glideImporter_test.go b/cmd/dep/glideImporter_test.go new file mode 100644 index 0000000000..298febca8b --- /dev/null +++ b/cmd/dep/glideImporter_test.go @@ -0,0 +1,83 @@ +// 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 ( + "log" + "os" + "path/filepath" + "testing" + + "github.com/golang/dep" + "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/test" +) + +const testGlideProjectRoot string = "github.com/golang/notexist" + +func TestGlideImport(t *testing.T) { + h := test.NewHelper(t) + defer h.Cleanup() + + h.TempDir("src") + h.TempDir(filepath.Join("src", testGlideProjectRoot)) + h.TempCopy(filepath.Join(testGlideProjectRoot, glideYamlName), "glide.yaml") + h.TempCopy(filepath.Join(testGlideProjectRoot, glideLockName), "glide.lock") + + loggers := &dep.Loggers{ + Out: log.New(os.Stdout, "", 0), + Err: log.New(os.Stderr, "", 0), + Verbose: true, + } + projectRoot := h.Path(testGlideProjectRoot) + + i := newGlideImporter(loggers) + if !i.HasConfig(projectRoot) { + t.Fatal("Expected the importer to detect the glide configuration files") + } + + m, l, err := i.DeriveRootManifestAndLock(projectRoot, gps.ProjectRoot(testGlideProjectRoot)) + h.Must(err) + + if m == nil { + t.Fatal("Expected the manifest to be generated") + } + + if l == nil { + t.Fatal("Expected the lock to be generated") + } +} + +func TestGlideImport_MissingLockFile(t *testing.T) { + h := test.NewHelper(t) + defer h.Cleanup() + + h.TempDir("src") + h.TempDir(filepath.Join("src", "glidetest")) + h.TempCopy(filepath.Join("glidetest", glideYamlName), "glide.yaml") + + loggers := &dep.Loggers{ + Out: log.New(os.Stdout, "", 0), + Err: log.New(os.Stderr, "", 0), + Verbose: true, + } + projectRoot := h.Path("glidetest") + + i := newGlideImporter(loggers) + if !i.HasConfig(projectRoot) { + t.Fatal("The glide importer should gracefully handle when only glide.yaml is present") + } + + m, l, err := i.DeriveRootManifestAndLock(projectRoot, gps.ProjectRoot(testGlideProjectRoot)) + h.Must(err) + + if m == nil { + t.Fatal("Expected the manifest to be generated") + } + + if l != nil { + t.Fatal("Expected the lock to not be generated") + } +} diff --git a/cmd/dep/gopathAnalyzer.go b/cmd/dep/gopathAnalyzer.go new file mode 100644 index 0000000000..069064700d --- /dev/null +++ b/cmd/dep/gopathAnalyzer.go @@ -0,0 +1,82 @@ +// 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 ( + "github.com/golang/dep" + "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/gps/pkgtree" +) + +// gopathAnalyzer deduces configuration from the projects in the GOPATH +type gopathAnalyzer struct { + loggers *dep.Loggers + ctx *dep.Ctx + pkgT pkgtree.PackageTree + cpr string + sm *gps.SourceMgr + + pd projectData +} + +func newGopathAnalyzer(loggers *dep.Loggers, ctx *dep.Ctx, pkgT pkgtree.PackageTree, cpr string, sm *gps.SourceMgr) *gopathAnalyzer { + return &gopathAnalyzer{ + loggers: loggers, + ctx: ctx, + pkgT: pkgT, + cpr: cpr, + sm: sm, + } +} + +func (a *gopathAnalyzer) DeriveRootManifestAndLock(path string, n gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { + var err error + + a.pd, err = getProjectData(a.ctx, a.pkgT, a.cpr, a.sm) + if err != nil { + return nil, nil, err + } + m := &dep.Manifest{ + Dependencies: a.pd.constraints, + } + + // Make an initial lock from what knowledge we've collected about the + // versions on disk + l := &dep.Lock{ + P: make([]gps.LockedProject, 0, len(a.pd.ondisk)), + } + + for pr, v := range a.pd.ondisk { + // That we have to chop off these path prefixes is a symptom of + // a problem in gps itself + pkgs := make([]string, 0, len(a.pd.dependencies[pr])) + prslash := string(pr) + "/" + for _, pkg := range a.pd.dependencies[pr] { + if pkg == string(pr) { + pkgs = append(pkgs, ".") + } else { + pkgs = append(pkgs, trimPathPrefix(pkg, prslash)) + } + } + + l.P = append(l.P, gps.NewLockedProject( + gps.ProjectIdentifier{ProjectRoot: pr}, v, pkgs), + ) + } + + return m, l, nil +} + +func (a *gopathAnalyzer) PostSolveShenanigans(m *dep.Manifest, l *dep.Lock) { + // Pick notondisk project constraints from solution and add to manifest + for k, _ := range a.pd.notondisk { + for _, x := range l.Projects() { + if k == x.Ident().ProjectRoot { + m.Dependencies[k] = getProjectPropertiesFromVersion(x.Version()) + break + } + } + } +} diff --git a/cmd/dep/importAnalyzer.go b/cmd/dep/importAnalyzer.go new file mode 100644 index 0000000000..f73f48934f --- /dev/null +++ b/cmd/dep/importAnalyzer.go @@ -0,0 +1,74 @@ +// 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 ( + "github.com/golang/dep" + "github.com/golang/dep/internal/gps" +) + +// importer +type importer interface { + gps.ProjectAnalyzer + rootProjectAnalyzer + HasConfig(dir string) bool +} + +// importAnalyzer imports existing dependency management configuration, +// from both dep and external tools. +type importAnalyzer struct { + loggers *dep.Loggers +} + +func newImportAnalyzer(loggers *dep.Loggers) importAnalyzer { + return importAnalyzer{loggers: loggers} +} + +func (a importAnalyzer) Info() (string, int) { + // TODO: do not merge until this is set to something unique. + // I'm not changing it now because that will cause the memo to change in tests + // which I'll deal with and update later + return "dep", 1 +} + +func (a importAnalyzer) DeriveRootManifestAndLock(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { + var importers []importer = []importer{newGlideImporter(a.loggers)} + for _, i := range importers { + if i.HasConfig(dir) { + tool, _ := i.Info() + if a.loggers.Verbose { + a.loggers.Err.Printf("Importing %s configuration for %s. Run with -skip-tools to skip.", tool, pr) + } + return i.DeriveRootManifestAndLock(dir, pr) + } + } + + return nil, nil, nil +} + +func (a importAnalyzer) DeriveManifestAndLock(dir string, pr gps.ProjectRoot) (gps.Manifest, gps.Lock, error) { + // Ignore other tools if we find dep configuration + var depAnalyzer dep.Analyzer + if depAnalyzer.HasConfig(dir) { + return depAnalyzer.DeriveManifestAndLock(dir, pr) + } + + var importers []importer = []importer{newGlideImporter(a.loggers)} + for _, i := range importers { + if i.HasConfig(dir) { + tool, _ := i.Info() + if a.loggers.Verbose { + a.loggers.Err.Printf("Importing %s configuration for %s. Run with -skip-tools to skip.", tool, pr) + } + return i.DeriveManifestAndLock(dir, pr) + } + } + + return nil, nil, nil +} + +func (a importAnalyzer) PostSolveShenanigans(m *dep.Manifest, l *dep.Lock) { + // do nothing +} diff --git a/cmd/dep/init.go b/cmd/dep/init.go index 95eb01f026..940f26105a 100644 --- a/cmd/dep/init.go +++ b/cmd/dep/init.go @@ -33,6 +33,10 @@ versions available from the upstream source per the following algorithm: - Default branch(es) (sorted lexicographically) - Non-semver tags (sorted lexicographically) +If configuration files for other dependency management tools are found, they +are used to pre-populate the manifest and lock. Specify -skip-tools to disable +this behavior. + A Gopkg.toml file will be written with inferred version constraints for all direct dependencies. Gopkg.lock will be written with precise versions, and vendor/ will be populated with the precise versions written to Gopkg.lock. @@ -46,10 +50,12 @@ func (cmd *initCommand) Hidden() bool { return false } func (cmd *initCommand) Register(fs *flag.FlagSet) { fs.BoolVar(&cmd.noExamples, "no-examples", false, "don't include example in Gopkg.toml") + fs.BoolVar(&cmd.skipTools, "skip-tools", false, "skip importing configuration from other dependency managers") } type initCommand struct { noExamples bool + skipTools bool } func trimPathPrefix(p1, p2 string) string { @@ -113,39 +119,26 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error { sm.UseDefaultSignalHandling() defer sm.Release() - pd, err := getProjectData(ctx, pkgT, cpr, sm) - if err != nil { - return err - } - m := &dep.Manifest{ - Dependencies: pd.constraints, - } - - // Make an initial lock from what knowledge we've collected about the - // versions on disk - l := &dep.Lock{ - P: make([]gps.LockedProject, 0, len(pd.ondisk)), + var rootAnalyzer rootProjectAnalyzer + var analyzer gps.ProjectAnalyzer + if cmd.skipTools { + rootAnalyzer = newGopathAnalyzer(ctx.Loggers, ctx, pkgT, cpr, sm) + analyzer = dep.Analyzer{} + } else { + rootAnalyzer = compositeAnalyzer{ + Analyzers: []rootProjectAnalyzer{ + newGopathAnalyzer(ctx.Loggers, ctx, pkgT, cpr, sm), + newImportAnalyzer(ctx.Loggers), + }} + analyzer = importAnalyzer{ctx.Loggers} } - for pr, v := range pd.ondisk { - // That we have to chop off these path prefixes is a symptom of - // a problem in gps itself - pkgs := make([]string, 0, len(pd.dependencies[pr])) - prslash := string(pr) + "/" - for _, pkg := range pd.dependencies[pr] { - if pkg == string(pr) { - pkgs = append(pkgs, ".") - } else { - pkgs = append(pkgs, trimPathPrefix(pkg, prslash)) - } - } - - l.P = append(l.P, gps.NewLockedProject( - gps.ProjectIdentifier{ProjectRoot: pr}, v, pkgs), - ) + // Generate a manifest and lock for the root project + m, l, err := rootAnalyzer.DeriveRootManifestAndLock(root, gps.ProjectRoot(cpr)) + if err != nil { + return errors.Wrap(err, "Error deriving a root manifest and lock") } - // Run solver with project versions found on disk if ctx.Loggers.Verbose { ctx.Loggers.Err.Println("dep: Solving...") } @@ -154,7 +147,7 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error { RootPackageTree: pkgT, Manifest: m, Lock: l, - ProjectAnalyzer: dep.Analyzer{}, + ProjectAnalyzer: analyzer, } if ctx.Loggers.Verbose { @@ -173,15 +166,7 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error { } l = dep.LockFromInterface(soln) - // Pick notondisk project constraints from solution and add to manifest - for k := range pd.notondisk { - for _, x := range l.Projects() { - if k == x.Ident().ProjectRoot { - m.Dependencies[k] = getProjectPropertiesFromVersion(x.Version()) - break - } - } - } + rootAnalyzer.PostSolveShenanigans(m, l) // Run gps.Prepare with appropriate constraint solutions from solve run // to generate the final lock memo. diff --git a/cmd/dep/rootProjectAnalyzer.go b/cmd/dep/rootProjectAnalyzer.go new file mode 100644 index 0000000000..ef72178619 --- /dev/null +++ b/cmd/dep/rootProjectAnalyzer.go @@ -0,0 +1,21 @@ +// 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 ( + "github.com/golang/dep" + "github.com/golang/dep/internal/gps" +) + +// rootProjectAnalyzer is responsible for generating a root manifest and lock for +// a root project. +type rootProjectAnalyzer interface { + // Perform analysis of the filesystem tree rooted at path, with the + // root import path importRoot, to determine the project's constraints, as + // indicated by a Manifest and Lock. + DeriveRootManifestAndLock(path string, n gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) + + PostSolveShenanigans(*dep.Manifest, *dep.Lock) +} diff --git a/cmd/dep/testdata/glide.lock b/cmd/dep/testdata/glide.lock new file mode 100644 index 0000000000..854432741b --- /dev/null +++ b/cmd/dep/testdata/glide.lock @@ -0,0 +1,12 @@ +hash: 16053c82a71f9bd509b05a4523df6bc418aed2083e4b8bd97a870bbc003256f8 +updated: 2017-03-07T17:02:32.214383898-06:00 +imports: +- package: github.com/sdboyer/deptest + repo: https://github.com/sdboyer/deptest.git + vcs: git + version: 3f4c3bea144e112a69bbe5d8d01c1b09a544253f +- package: github.com/sdboyer/deptestdos + version: 5c607206be5decd28e6263ffffdcee067266015e +testImports: +- package: github.com/golang/lint + version: cb00e5669539f047b2f4c53a421a01b0c8e172c6 diff --git a/cmd/dep/testdata/glide.yaml b/cmd/dep/testdata/glide.yaml new file mode 100644 index 0000000000..c4bc07620d --- /dev/null +++ b/cmd/dep/testdata/glide.yaml @@ -0,0 +1,20 @@ +package: github.com/golang/notexist +homepage: http://example.com +license: MIT +owners: +- name: Sam Boyer + email: sdboyer@example.com + homepage: http://sdboyer.io +ignore: +- github.com/sdboyer/dep-test +excludeDirs: +- samples +import: +- package: github.com/sdboyer/deptest + repo: https://github.com/sdboyer/deptest.git + vcs: git + version: master +- package: github.com/sdboyer/deptestdos + version: v2.0.0 +testImport: +- package: github.com/golang/lint \ No newline at end of file diff --git a/cmd/dep/testdata/harness_tests/init/glide/case1/final/Gopkg.lock b/cmd/dep/testdata/harness_tests/init/glide/case1/final/Gopkg.lock new file mode 100644 index 0000000000..d84347d8e9 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/glide/case1/final/Gopkg.lock @@ -0,0 +1,13 @@ +memo = "c53803413bd0160505cce903e1cba743e0b964088f8cc42a6123f6fe1a0ae9d3" + +[[projects]] + name = "github.com/sdboyer/deptest" + packages = ["."] + revision = "ff2948a2ac8f538c4ecd55962e919d1e13e74baf" + version = "v1.0.0" + +[[projects]] + name = "github.com/sdboyer/deptestdos" + packages = ["."] + revision = "5c607206be5decd28e6263ffffdcee067266015e" + version = "v2.0.0" diff --git a/cmd/dep/testdata/harness_tests/init/glide/case1/final/Gopkg.toml b/cmd/dep/testdata/harness_tests/init/glide/case1/final/Gopkg.toml new file mode 100644 index 0000000000..670905fa97 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/glide/case1/final/Gopkg.toml @@ -0,0 +1,10 @@ +ignored = ["github.com/sdboyer/dep-test","github.com/golang/notexist/samples"] + +[[dependencies]] + branch = "master" + name = "github.com/sdboyer/deptest" + source = "https://github.com/carolynvs/deptest.git" + +[[dependencies]] + name = "github.com/sdboyer/deptestdos" + version = "^2.0.0" diff --git a/cmd/dep/testdata/harness_tests/init/glide/case1/initial/glide.lock b/cmd/dep/testdata/harness_tests/init/glide/case1/initial/glide.lock new file mode 100644 index 0000000000..99b7a180e5 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/glide/case1/initial/glide.lock @@ -0,0 +1,12 @@ +hash: 16053c82a71f9bd509b05a4523df6bc418aed2083e4b8bd97a870bbc003256f8 +updated: 2017-03-07T17:02:32.214383898-06:00 +imports: +- package: github.com/sdboyer/deptest + repo: https://github.com/carolynvs/deptest.git + vcs: git + version: 3f4c3bea144e112a69bbe5d8d01c1b09a544253f +- package: github.com/sdboyer/deptestdos + version: 5c607206be5decd28e6263ffffdcee067266015e +testImports: +- package: github.com/golang/lint + version: cb00e5669539f047b2f4c53a421a01b0c8e172c6 diff --git a/cmd/dep/testdata/harness_tests/init/glide/case1/initial/glide.yaml b/cmd/dep/testdata/harness_tests/init/glide/case1/initial/glide.yaml new file mode 100644 index 0000000000..60ac8f5a2d --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/glide/case1/initial/glide.yaml @@ -0,0 +1,20 @@ +package: github.com/golang/notexist +homepage: http://example.com +license: MIT +owners: +- name: Sam Boyer + email: sdboyer@example.com + homepage: http://sdboyer.io +ignore: +- github.com/sdboyer/dep-test +excludeDirs: +- samples +import: +- package: github.com/sdboyer/deptest + repo: https://github.com/carolynvs/deptest.git + vcs: git + version: master +- package: github.com/sdboyer/deptestdos + version: v2.0.0 +testImport: +- package: github.com/golang/lint \ No newline at end of file diff --git a/cmd/dep/testdata/harness_tests/init/glide/case1/initial/main.go b/cmd/dep/testdata/harness_tests/init/glide/case1/initial/main.go new file mode 100644 index 0000000000..3389cd06b4 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/glide/case1/initial/main.go @@ -0,0 +1,17 @@ +// 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. + +package main + +import ( + "fmt" + + _ "github.com/sdboyer/dep-test" + "github.com/sdboyer/deptestdos" +) + +func main() { + var x deptestdos.Bar + fmt.Println(x) +} diff --git a/cmd/dep/testdata/harness_tests/init/glide/case1/initial/samples/samples.go b/cmd/dep/testdata/harness_tests/init/glide/case1/initial/samples/samples.go new file mode 100644 index 0000000000..3e160f22fe --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/glide/case1/initial/samples/samples.go @@ -0,0 +1,12 @@ +// 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 samples + +import dt "github.com/carolynvs/go-dep-test" + +func Sample1() int { + var x = dt.Thing + return x +} diff --git a/cmd/dep/testdata/harness_tests/init/glide/case1/testcase.json b/cmd/dep/testdata/harness_tests/init/glide/case1/testcase.json new file mode 100644 index 0000000000..15d6b2faad --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/glide/case1/testcase.json @@ -0,0 +1,14 @@ +{ + "commands": [ + ["init", "-no-examples"] + ], + "error-expected": "", + "gopath-initial": { + "github.com/golang/lint": "cb00e5669539f047b2f4c53a421a01b0c8e172c6", + "github.com/sdboyer/deptest": "3f4c3bea144e112a69bbe5d8d01c1b09a544253f" + }, + "vendor-final": [ + "github.com/sdboyer/deptest", + "github.com/sdboyer/deptestdos" + ] +}