go run package

386 views
Skip to first unread message

Paul Jolly

unread,
Mar 30, 2017, 5:28:28 AM3/30/17
to golang-nuts
I've searched golang-nuts and the Go issue tracker pretty extensively and I can't find any mention of my question or the topic, so would appreciate any links if this has been previously covered and I was simply using the wrong search terms.

go install takes a number of packages as arguments:

$ go install -help
usage: install [build flags] [packages]

Let's assume for the sake of discussion we're talking about:


(where banana is a main package)

In writing documentation, particularly when it comes to code generators (but not exclusive to that), I find myself repeatedly asking the user to "ensure your path is correct..." so that when they now run:

banana -help

it invokes the banana binary we just installed.

This is relatively painful and error prone because of any permutation of problems/inconsistencies with PATH values, not to mention the potential for clashes in names of installed Go programs within $GOPATH/bin/

One way to deal with this is to do something like:

$(go list -f {{.Target}} github.com/fruit/banana) arg1 arg2 ...

but:
  1. I'm unsure how cross-platform this is
  2. it's not particularly pretty/friendly
  3. it doesn't work with go:generate directives
  4. it doesn't solve the problem of name clashes in $GOPATH/bin
What about therefore making go run (which currently only takes .go files as arguments to the go command) also understand the case of a single package argument:

go run github.com/fruit/banana arg1 arg2 ...

Arguments that are then passed on to the command as before (here arg1 arg2 ... are passed on to banana)

This would not build/install anything, it would simply exec the program most recently built via 'go install github.com/fruit/banana', or fail if not such file exists.

There are of course other details to work through... just looking to test the water with the thought.

Appreciate any links/feedback etc, particularly if actually others consider that I'm perceiving an issue where in fact there isn't one (i.e. I needn't even both writing about getting PATH correct in docs) and that name clashes are not common. 

But it would seem to be a nice way of further lowering the bar for new starters with the language, work cross-platform and not require any further environment tweaking.

Thanks,


Paul

Jan Mercl

unread,
Mar 30, 2017, 5:39:43 AM3/30/17
to Paul Jolly, golang-nuts
On Thu, Mar 30, 2017 at 11:28 AM Paul Jolly <pa...@myitcv.org.uk> wrote:

> go run github.com/fruit/banana arg1 arg2 ...

I don't like the idea of giving go run any more powers. It is already being horribly abused. Your users can perhaps try

        $ $GOPATH/bin/banana arg1 arg2

if properly setting PATH is beyond whatever.

On *nix, many users have $HOME/bin already in $PATH, so using (my setup) export GOPATH=$HOME in .bashrc/.profile solves everything.

--

-j

Paul Jolly

unread,
Mar 30, 2017, 5:48:29 AM3/30/17
to Jan Mercl, golang-nuts
> go run github.com/fruit/banana arg1 arg2 ...

I don't like the idea of giving go run any more powers. It is already being horribly abused.

I understand the sentiment; it needn't be the run sub-command of course... it's about adding this behaviour to the go command.
 
Your users can perhaps try

        $ $GOPATH/bin/banana arg1 arg2

if properly setting PATH is beyond whatever.

This doesn't work when GOPATH contains multiple values... and doesn't deal with the name clash in the various "${GOPATH//://bin:}/bin" directories.

On *nix, many users have $HOME/bin already in $PATH, so using (my setup) export GOPATH=$HOME in .bashrc/.profile solves everything.

This is another option, sure. 

But I guess this is the point; based on your setup, environment, level of proficiency... there are so many options/permutations.

The idea here is to acknowledge all of them and provide an option that will "just work" in all scenarios, a mode which the user can simply ignore if they know their environment is setup in "the right way"


Paul

Paul Jolly

unread,
Mar 30, 2017, 6:03:08 AM3/30/17
to Jan Mercl, golang-nuts
I don't like the idea of giving go run any more powers. It is already being horribly abused.

I understand the sentiment; it needn't be the run sub-command of course... it's about adding this behaviour to the go command.

Because I'm lacking imagination, it could be an entirely separate sub command:

go exec github.com/fruit/banana arg1 arg2 ...

mhh...@gmail.com

unread,
Mar 30, 2017, 6:48:54 AM3/30/17
to golang-nuts
Another scenario,

There is a package x which requires a bin package Y,
if the bin package is installed as global, a per package dependency reference breaks,
so the bin file needs to exists into a package/vendor/bin which co exists with GOPATH/bin
having the path modified to ./vendor/bin does sound error prone.

A bin util, which would do something similar to your idea
`goexec bin args...` might be smart enough to sort things out
and call for the right binary in the right path,
it is also able to provide detailed information when something wrong happen.

More than that, if a collision happen,
it allows to add precision about the bin to execute until the collision is solved,
 something like
`goexec user/bin args...`
`goexec gh.com/user/bin args...`

And to make it awesome, auto completion support would be just awesome! imho.

Paul Jolly

unread,
Mar 30, 2017, 8:50:28 AM3/30/17
to mhh...@gmail.com, golang-nuts
A bin util, which would do something similar to your idea
`goexec bin args...` might be smart enough to sort things out
and call for the right binary in the right path,
it is also able to provide detailed information when something wrong happen.

It would indeed be unambiguous, because a go import path is unambiguous. 

More than that, if a collision happen,
it allows to add precision about the bin to execute until the collision is solved,
 something like
`goexec user/bin args...`
`goexec gh.com/user/bin args...`

This is not what I'm proposing. 

In slightly more formal terms, considering the package P, I'm proposing a go subcommand that is equivalent to:

======
package main

import (
"bytes"
"flag"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
)

func main() {
flag.Parse()

if v := len(flag.Args()); v == 0 {
errorf("expected at least one argument")
}

p := flag.Arg(0)

if v := mustExec("go", "list", "-f", "{{.ImportPath}}", p); v != p {
errorf("must supply unambiguous package; %v is not", p)
}

if v := mustExec("go", "list", "-f", "{{.Name}}", p); v != "main" {
errorf("must supply main package; %v is not", p)
}

target := mustExec("go", "list", "-f", "{{.Target}}", p)
targetDir := filepath.Dir(target)

specTarget := filepath.Join(targetDir, p)

vs := append([]string{specTarget}, flag.Args()[1:]...)

fmt.Printf("syscall.Exec(\"%v\")\n", strings.Join(vs, "\", \""))
}

func errorf(format string, args ...interface{}) {
format = format + "\n"
fmt.Fprintf(os.Stderr, format, args...)
os.Exit(1)
}

func mustExec(c string, args ...string) string {
cmd := exec.Command(c, args...)

res, err := cmd.Output()
if err != nil {
panic(err)
}

return string(bytes.TrimSpace(res))
}

======

For example, in a particular environment I have, considering the main package github.com/gopherjs/gopherjs I get the output:

syscall.Exec("/home/myitcv/gostuff/bin/github.com/gopherjs/gopherjs", "serve", "-m")

Indeed I would also propose that we augment go list -json to include a {{.SpecificTarget}} for main packages, a value that would be equal to "/home/myitcv/gostuff/bin/github.com/gopherjs/gopherjs" in this example.
 
And to make it awesome, auto completion support would be just awesome! imho.

This would have to be the realm of the user's environment; bash and other shells already have good support for the go subcommands... so I suspect this would readily follow. 


Paul 
Reply all
Reply to author
Forward
0 new messages