New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/go: add go get -printdir #18119

Open
bradfitz opened this Issue Nov 30, 2016 · 26 comments

Comments

Projects
None yet
6 participants
@bradfitz
Member

bradfitz commented Nov 30, 2016

I'd like a succinct and portable way to get the first element of the user's GOPATH.

Currently:

$ GOPATH=/foo:/bar go env GOPATH
/foo:/bar

Obviously we can't change that.

I propose a -1 flag to go env to only print the first GOPATH element, using https://golang.org/pkg/path/filepath/#SplitList

$ GOPATH=/foo:/bar go env -1 GOPATH
/foo

@bradfitz bradfitz added the Proposal label Nov 30, 2016

@bradfitz bradfitz added this to the Proposal milestone Nov 30, 2016

@rsc

This comment has been minimized.

Contributor

rsc commented Nov 30, 2016

Is there a good reason for it other than explanatory docs? We've always written cd $GOPATH before without worrying about this. Most people don't know it's a list or don't care. There's also very few times I can think of when you definitively want the first GOPATH entry. Usually you want the source for a particular package, which is

cd $(go list -f '{{.Dir}}' my/pkg/import/path)
@bradfitz

This comment has been minimized.

Member

bradfitz commented Nov 30, 2016

I often seen docs where people include $GOPATH in bash scripts exactly in ways where it would fail if my GOPATH actually were a list, encouraging people to not make it a list.

I'd like there to be some official answer people could use in docs instead.

$(go list -f '{{.Dir}}' my/pkg/import/path) doesn't work if the directory doesn't yet exist, and it's often mkdir -p $GOPATH/src/github.com/org/private-repo I see in docs, before a git clone into that location.

@rsc

This comment has been minimized.

Contributor

rsc commented Nov 30, 2016

Maybe this can wait for Go 1.9?

@bradfitz

This comment has been minimized.

Member

bradfitz commented Nov 30, 2016

Indeed. I didn't intend it for Go 1.8.

@rsc

This comment has been minimized.

Contributor

rsc commented Dec 5, 2016

In general it would be good to express the question we want answered instead of the mechanism. In this case we want to know where a particular package will be installed, without really caring about the fact that GOPATH is how we get there.

Maybe go get -dir import/path prints the directory where go get would put import/path after downloading it (even if it doesn't exist, no network activity, and so on). But "get -dir" is probably suggestive of an operation that's not actually happening as just defined. Better names?

@rsc

This comment has been minimized.

Contributor

rsc commented Dec 5, 2016

Other possibilities: -where, -printdir, ....

@josharian

This comment has been minimized.

Contributor

josharian commented Dec 5, 2016

Another possibility is accept a format string. Then this feature could also be used to learn the VCS system that go get would use, the remote url, the destination on disk, the clone command, whether it is a custom import path, etc.

I currently maintain a modified fork of cmd/go just for doing this kind of import path resolution. @rogpeppe does too; see https://go-review.googlesource.com/8725.

@bradfitz

This comment has been minimized.

Member

bradfitz commented Dec 12, 2016

$ DIR=$(go get -printdir github.com/org-may-not-exist)
$ echo "Installing to $DIR ..."
Installing to /home/foo/go/github.com/org-may-not-exist ...
$ mkdir -p $DIR && cd $DIR && git clone http://foo/proj.git
@rsc

This comment has been minimized.

Contributor

rsc commented Dec 12, 2016

OK, so -printdir never does anything but only prints the absolute path of the directory where 'go get' would check out the named package, or where one is already checked out that 'go get -u' would update.

If there are no arguments (packages on command line), then that's an error. There must be at least one. If there are multiple arguments, it prints one line for each.

Must print to standard output.

@rsc rsc changed the title from proposal: cmd/go: add -1 flag to go env to proposal: cmd/go: add go get -printdir Dec 12, 2016

@rsc rsc changed the title from proposal: cmd/go: add go get -printdir to cmd/go: add go get -printdir Dec 12, 2016

@rsc rsc modified the milestones: Go1.9Early, Proposal Dec 12, 2016

@bradfitz bradfitz self-assigned this Dec 12, 2016

@rsc rsc added Proposal-Accepted and removed Proposal labels Dec 12, 2016

@josharian

This comment has been minimized.

Contributor

josharian commented Dec 12, 2016

@bradfitz in your example, go get github.com/org-may-not-exist would fail, not check out a named package, because go get for github requires both an org/user and a repo name. And it can't just using the first arg verbatim (attached to $GOPATH) because that would disagree with 'go get' on custom import paths. At which point you're going through cmd/go's repoRootForImportPath anyway, so we may as well also provide access to the other things it returns--clone url, vcs, isCustom, probably by way of a format string. (Pretty please?)

@bradfitz

This comment has been minimized.

Member

bradfitz commented Dec 12, 2016

The idea was this would be done purely lexically. It would not hit the network or attempt to parse the arguments in any way (including using repoRootForImportPath)

@josharian

This comment has been minimized.

Contributor

josharian commented Dec 12, 2016

Ok, but to be super clear, then, it is definitely not true that:

-printdir [...] prints the absolute path of the directory where 'go get' would check out the named package, or where one is already checked out that 'go get -u' would update.

@bradfitz

This comment has been minimized.

Member

bradfitz commented Dec 12, 2016

"would attempt to check out" ?

Are you just saying we're missing the words "attempt to" or "try to"?

@josharian

This comment has been minimized.

Contributor

josharian commented Dec 12, 2016

I guess? In the case of github.com/foo, 'go get' will always fail, it would never attempt anything. But I guess its something like that. Here are more questions. (None of which are to say this is a bad idea. Just probing edge cases.)

What would go get -printdir rsc.io/pdf/... print? The ... reproduced verbatim? Would go get -printdir context print something in $GOROOT or something in the first $GOPATH?

To handle the the "would update" case, does -printdir check to see whether any GOPATH dir contains code, or does it always just return the subdir of the first $GOPATH dir?

@josharian

This comment has been minimized.

Contributor

josharian commented Dec 12, 2016

As an aside, for the specific mkdir -p && cd && git clone sequence above, you don't need the intermediate dirs to exist. git clone url dest will do a mkdir -p dest for you and put the code in the right place. If that's the primary use case, maybe we don't need github.com/does-not-exist to work?

@bradfitz

This comment has been minimized.

Member

bradfitz commented Dec 12, 2016

Verbatim verbatim.

The code would literally be:

    first := filepath.SplitList(goroot)[0]
    for _, arg := range args {
           fmt.Println(filepath.Join(first, arg))
    }
@rsc

This comment has been minimized.

Contributor

rsc commented Dec 13, 2016

No, that's the wrong implementation. The whole point of going down this 'go get -printdir' path was because the answer varies by import path. If the target already exists in a later entry in GOPATH, go get will use that one. Josh is right that you really need to give it the actual package you care about. The script is wrong. It should be:

$ DIR=$(go get -printdir github.com/org-may-not-exist/proj)
$ echo "Installing to $DIR ..."
Installing to /home/foo/go/github.com/org-may-not-exist/proj ...
$ mkdir -p $DIR && cd $DIR && git clone http://foo/proj.git .

(note dot at end of final command)

@bradfitz

This comment has been minimized.

Member

bradfitz commented Dec 13, 2016

If the target already exists in a later entry in GOPATH, go get will use that one.

Ah, okay. You can tell how often I use multiple entries in my $GOPATH.

@jimmyfrasche

This comment has been minimized.

Member

jimmyfrasche commented Dec 13, 2016

What about a separate command, go dir. (The suggested semantics below apply to a -printdir flag as well but it being on go get seems weird to me, since it never gets anything)

Given no arguments it prints the elements of $GOPATH, one per line (or the default if not set).

Given arguments it assumes they're packages and prints their absolute paths (including /src/), one per line. Failing if any do not exist. This also would allow it work with /... or any of the special names.

If you only care about the first pipe through head -n 1 or sed 1q. You can redirect to /dev/null if you just want to check the exit code. If it's a separate command these could be flags on the command, though there's no need for them on unixy systems, at least.

That seems like it would cover all the cases for shell scripting. The only problem would be something like go dir $EMPTY_VAR, but if it's a separate command no args could be an error and go dir -gopath could print $GOPATH.

@rsc

This comment has been minimized.

Contributor

rsc commented Dec 13, 2016

Honestly, I think this is a non-issue and we should do nothing at all. But I would argue strongly against any command that exposes what GOPATH is directly. Obviously code that cares can find out. But that's almost always the wrong question to be asking. The appropriate question is "where is the code for this particular import path?"

The reason this is on 'go get' is that if you've already got the code you can use 'go list -f whatever-you-need'. The one case that doesn't handle is where the code is yet to be downloaded, and 'go get -printdir' will tell you where it would put the code after the download.

@myitcv

This comment has been minimized.

Member

myitcv commented Dec 14, 2016

The one question I often find myself trying to reliably answer with multiple entries in my GOPATH is where go install github.com/my/pkg will install to, so that I can then definitely run the binary (in this case pkg) that is (re-)built, irrespective of the user's PATH. Seems somewhat related (unless I'm missing an obvious solution of course?)

@bradfitz

This comment has been minimized.

Member

bradfitz commented Dec 14, 2016

@myitcv, there's already an answer for that question:

$ go list -f {{.Target}} golang.org/x/build/cmd/coordinator
/home/bradfitz/bin/coordinator

This bug is about a question for which there's no current way to extract an answer.

@myitcv

This comment has been minimized.

Member

myitcv commented Dec 14, 2016

@bradfitz thanks, and apologies for the noise.

@gopherbot

This comment has been minimized.

gopherbot commented Jul 13, 2017

CL https://golang.org/cl/48410 mentions this issue.

@bradfitz

This comment has been minimized.

Member

bradfitz commented Nov 13, 2017

@rsc, could you review https://golang.org/cl/48410 ?

@bradfitz bradfitz modified the milestones: Go1.10, Unplanned Nov 21, 2017

@bradfitz

This comment has been minimized.

Member

bradfitz commented Nov 21, 2017

@rsc said on the CL:

If possible I'd like to hold off introducing this flag. I understand the desire but package management may potentially invalidate things like this. At this point I'd rather wait on any new features until we're sure that they will make sense in the new world.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment