Skip to content
This repository has been archived by the owner on Sep 9, 2020. It is now read-only.

Commit

Permalink
Merge pull request #641 from ibrasho-forks/symlink-project-roots-changes
Browse files Browse the repository at this point in the history
Handling symlinks as project root
  • Loading branch information
sdboyer authored Jun 17, 2017
2 parents 708b53d + 13f512f commit a42708e
Show file tree
Hide file tree
Showing 11 changed files with 336 additions and 284 deletions.
15 changes: 9 additions & 6 deletions FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ Overrides are also discussed with some visuals in [the gps docs](https://github.
Sometimes the revision specified in the lock file is no longer valid. There are a few
ways this can occur:

* When you generated the lock file, you had an unpushed commit in your local copy of package X's repository in your GOPATH. (This case will be going away soon)
* When you generated the lock file, you had an unpushed commit in your local copy of package X's repository in your `GOPATH`. (This case will be going away soon)
* After generating the lock file, new commits were force pushed to package X's repository, causing the commit revision in your lock file to no longer exist.

To troubleshoot, you can revert dep's changes to your lock, and then run `dep ensure -v -n`.
Expand Down Expand Up @@ -255,14 +255,17 @@ There's another major performance issue that's much harder - the process of pick

## How does `dep` handle symbolic links?

> because we're not crazy people who delight in inviting chaos into our lives, we need to work within one GOPATH at a time.
> because we're not crazy people who delight in inviting chaos into our lives, we need to work within one `GOPATH` at a time.
-[@sdboyer in #247](https://github.com/golang/dep/pull/247#issuecomment-284181879)

Out of convenience, one might create a symlink to a directory within their `GOPATH`, e.g. `ln -s ~/go/src/github.com/golang/dep dep`. When `dep` is invoked it will resolve the current working directory accordingly:
Out of convenience, one might create a symlink to a directory within their `GOPATH/src`, e.g. `ln -s ~/go/src/github.com/user/awesome-project ~/Code/awesome-project`.

- If the cwd is a symlink outside a `GOPATH` and links to directory within a `GOPATH`, or vice versa, `dep` chooses whichever path is within the `GOPATH`. If neither path is within a `GOPATH`, `dep` produces an error.
- If both the cwd and resolved path are in the same `GOPATH`, an error is thrown since the users intentions and expectations can't be accurately deduced.
- If the symlink is within a `GOPATH` and the real path is within a *different* `GOPATH` - an error is thrown.
When `dep` is invoked with a project root that is a symlink, it will be resolved according to the following rules:

- If the symlink is outside `GOPATH` and links to a directory within a `GOPATH`, or vice versa, then `dep` will choose whichever path is within `GOPATH`.
- If the symlink is within a `GOPATH` and the resolved path is within a *different* `GOPATH`, then an error is thrown.
- If both the symlink and the resolved path are in the same `GOPATH`, then an error is thrown.
- If neither the symlink nor the resolved path are in a `GOPATH`, then an error is thrown.

This is the only symbolic link support that `dep` really intends to provide. In keeping with the general practices of the `go` tool, `dep` tends to either ignore symlinks (when walking) or copy the symlink itself, depending on the filesystem operation being performed.

Expand Down
3 changes: 2 additions & 1 deletion cmd/dep/ensure.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ func (cmd *ensureCommand) Run(ctx *dep.Ctx, args []string) error {
if ctx.Verbose {
params.TraceLogger = ctx.Err
}
params.RootPackageTree, err = pkgtree.ListPackages(p.AbsRoot, string(p.ImportRoot))

params.RootPackageTree, err = pkgtree.ListPackages(p.ResolvedAbsRoot, string(p.ImportRoot))
if err != nil {
return errors.Wrap(err, "ensure ListPackage for project")
}
Expand Down
7 changes: 1 addition & 6 deletions cmd/dep/hash_in.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,7 @@ func (hashinCommand) Run(ctx *dep.Ctx, args []string) error {
defer sm.Release()

params := p.MakeParams()
cpr, err := ctx.SplitAbsoluteProjectRoot(p.AbsRoot)
if err != nil {
return errors.Wrap(err, "determineProjectRoot")
}

params.RootPackageTree, err = pkgtree.ListPackages(p.AbsRoot, cpr)
params.RootPackageTree, err = pkgtree.ListPackages(p.ResolvedAbsRoot, string(p.ImportRoot))
if err != nil {
return errors.Wrap(err, "gps.ListPackages")
}
Expand Down
39 changes: 26 additions & 13 deletions cmd/dep/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
}
}

var err error
p := new(dep.Project)
if err = p.SetRoot(root); err != nil {
return errors.Wrap(err, "NewProject")
}

ctx.GOPATH, err = ctx.DetectProjectGOPATH(p)
if err != nil {
return errors.Wrapf(err, "ctx.DetectProjectGOPATH")
}

mf := filepath.Join(root, dep.ManifestName)
lf := filepath.Join(root, dep.LockName)
vpath := filepath.Join(root, "vendor")
Expand All @@ -98,11 +109,13 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
return errors.Errorf("invalid state: manifest %q does not exist, but lock %q does", mf, lf)
}

cpr, err := ctx.SplitAbsoluteProjectRoot(root)
ip, err := ctx.SplitAbsoluteProjectRoot(root)
if err != nil {
return errors.Wrap(err, "determineProjectRoot")
}
pkgT, directDeps, err := getDirectDependencies(root, cpr)
p.ImportRoot = gps.ProjectRoot(ip)

pkgT, directDeps, err := getDirectDependencies(p)
if err != nil {
return err
}
Expand All @@ -115,12 +128,12 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {

// Initialize with imported data, then fill in the gaps using the GOPATH
rootAnalyzer := newRootAnalyzer(cmd.skipTools, ctx, directDeps, sm)
m, l, err := rootAnalyzer.InitializeRootManifestAndLock(root, gps.ProjectRoot(cpr))
p.Manifest, p.Lock, err = rootAnalyzer.InitializeRootManifestAndLock(root, p.ImportRoot)
if err != nil {
return err
}
gs := newGopathScanner(ctx, directDeps, sm)
err = gs.InitializeRootManifestAndLock(m, l)
err = gs.InitializeRootManifestAndLock(p.Manifest, p.Lock)
if err != nil {
return err
}
Expand All @@ -130,8 +143,8 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
params := gps.SolveParameters{
RootDir: root,
RootPackageTree: pkgT,
Manifest: m,
Lock: l,
Manifest: p.Manifest,
Lock: p.Lock,
ProjectAnalyzer: rootAnalyzer,
}

Expand All @@ -149,10 +162,10 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
handleAllTheFailuresOfTheWorld(err)
return err
}
l = dep.LockFromSolution(soln)
p.Lock = dep.LockFromSolution(soln)

rootAnalyzer.FinalizeRootManifestAndLock(m, l)
gs.FinalizeRootManifestAndLock(m, l)
rootAnalyzer.FinalizeRootManifestAndLock(p.Manifest, p.Lock)
gs.FinalizeRootManifestAndLock(p.Manifest, p.Lock)

// Run gps.Prepare with appropriate constraint solutions from solve run
// to generate the final lock memo.
Expand All @@ -161,7 +174,7 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
return errors.Wrap(err, "prepare solver")
}

l.SolveMeta.InputsDigest = s.HashInputs()
p.Lock.SolveMeta.InputsDigest = s.HashInputs()

// Pass timestamp (yyyyMMddHHmmss format) as suffix to backup name.
vendorbak, err := dep.BackupVendor(vpath, time.Now().Format("20060102150405"))
Expand All @@ -172,7 +185,7 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
ctx.Err.Printf("Old vendor backed up to %v", vendorbak)
}

sw, err := dep.NewSafeWriter(m, nil, l, dep.VendorAlways)
sw, err := dep.NewSafeWriter(p.Manifest, nil, p.Lock, dep.VendorAlways)
if err != nil {
return err
}
Expand All @@ -184,8 +197,8 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
return nil
}

func getDirectDependencies(root, cpr string) (pkgtree.PackageTree, map[string]bool, error) {
pkgT, err := pkgtree.ListPackages(root, cpr)
func getDirectDependencies(p *dep.Project) (pkgtree.PackageTree, map[string]bool, error) {
pkgT, err := pkgtree.ListPackages(p.ResolvedAbsRoot, string(p.ImportRoot))
if err != nil {
return pkgtree.PackageTree{}, nil, errors.Wrap(err, "gps.ListPackages")
}
Expand Down
43 changes: 22 additions & 21 deletions cmd/dep/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ func (c *Config) Run() (exitCode int) {
},
}

outLogger := log.New(c.Stdout, "", 0)
errLogger := log.New(c.Stderr, "", 0)

usage := func() {
errLogger.Println("dep is a tool for managing dependencies for Go projects")
errLogger.Println()
Expand Down Expand Up @@ -142,19 +144,15 @@ func (c *Config) Run() (exitCode int) {
return
}

// Set up the dep context.
// Set up dep context.
ctx := &dep.Ctx{
Out: log.New(c.Stdout, "", 0),
Out: outLogger,
Err: errLogger,
Verbose: *verbose,
}
gopaths := filepath.SplitList(getEnv(c.Env, "GOPATH"))
err := ctx.SetPaths(c.WorkingDir, gopaths...)
if err != nil {
errLogger.Printf("%q not in any GOPATH: %s\n", c.WorkingDir, err)
exitCode = 1
return
}

GOPATHS := filepath.SplitList(getEnv(c.Env, "GOPATH"))
ctx.SetPaths(c.WorkingDir, GOPATHS...)

// Run the command with the post-flag-processing args.
if err := cmd.Run(ctx, fs.Args()); err != nil {
Expand All @@ -174,18 +172,6 @@ func (c *Config) Run() (exitCode int) {
return
}

// getEnv returns the last instance of the environment variable.
func getEnv(env []string, key string) string {
pre := key + "="
for i := len(env) - 1; i >= 0; i-- {
v := env[i]
if strings.HasPrefix(v, pre) {
return strings.TrimPrefix(v, pre)
}
}
return ""
}

func resetUsage(logger *log.Logger, fs *flag.FlagSet, name, args, longHelp string) {
var (
hasFlags bool
Expand Down Expand Up @@ -241,3 +227,18 @@ func parseArgs(args []string) (cmdName string, printCmdUsage bool, exit bool) {
}
return cmdName, printCmdUsage, exit
}

// getEnv returns the last instance of an environment variable.
func getEnv(env []string, key string) string {
for i := len(env) - 1; i >= 0; i-- {
v := env[i]
kv := strings.SplitN(v, "=", 2)
if kv[0] == key {
if len(kv) > 1 {
return kv[1]
}
return ""
}
}
return ""
}
2 changes: 1 addition & 1 deletion cmd/dep/prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (cmd *pruneCommand) Run(ctx *dep.Ctx, args []string) error {

// While the network churns on ListVersions() requests, statically analyze
// code from the current project.
ptree, err := pkgtree.ListPackages(p.AbsRoot, string(p.ImportRoot))
ptree, err := pkgtree.ListPackages(p.ResolvedAbsRoot, string(p.ImportRoot))
if err != nil {
return errors.Wrap(err, "analysis of local packages failed: %v")
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/dep/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ type dotOutput struct {
func (out *dotOutput) BasicHeader() {
out.g = new(graphviz).New()

ptree, _ := pkgtree.ListPackages(out.p.AbsRoot, string(out.p.ImportRoot))
ptree, _ := pkgtree.ListPackages(out.p.ResolvedAbsRoot, string(out.p.ImportRoot))
prm, _ := ptree.ToReachMap(true, false, false, nil)

out.g.createNode(string(out.p.ImportRoot), "", prm.FlattenFn(paths.IsStandardImportPath))
Expand Down Expand Up @@ -263,7 +263,7 @@ func runStatusAll(ctx *dep.Ctx, out outputter, p *dep.Project, sm gps.SourceMana

// While the network churns on ListVersions() requests, statically analyze
// code from the current project.
ptree, err := pkgtree.ListPackages(p.AbsRoot, string(p.ImportRoot))
ptree, err := pkgtree.ListPackages(p.ResolvedAbsRoot, string(p.ImportRoot))
if err != nil {
return digestMismatch, hasMissingPkgs, errors.Errorf("analysis of local packages failed: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/dep/testdata/harness_tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ for example, it can be completely left out.

The test procedure is as follows:

1. Create a unique temporary directory (TMPDIR) as the test run's GOPATH
1. Create a unique temporary directory (TMPDIR) as the test run's `GOPATH`
2. Create `$TMPDIR/src/github.com/golang/notexist` as the current project
3. Copy the contents of `initial` input directory to the project
4. Fetch the repos and versions in `gopath-initial` into `$TMPDIR/src` directory
Expand Down
Loading

0 comments on commit a42708e

Please sign in to comment.