Skip to content
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: go generate "quotes" unquoted environment variables in directive #18573

Closed
myitcv opened this issue Jan 9, 2017 · 2 comments
Closed

Comments

@myitcv
Copy link
Member

myitcv commented Jan 9, 2017

What version of Go are you using (go version)?

go version go1.7.4 linux/amd64

(but same behaviour observed at tip)

What operating system and processor architecture are you using (go env)?

$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/myitcv/gostuff"
GORACE=""
GOROOT="/home/myitcv/gos"
GOTOOLDIR="/home/myitcv/gos/pkg/tool/linux_amd64"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build336417079=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"

What did you do?

Place the code found here https://play.golang.org/p/PKeAQFaVSe in a package directory called playground and then run:

cd /path/to/playground
go build && PATH=.:$PATH HELLOWORLD="hello world" go generate

What did you expect to see?

len(os.Args): 3, os.Args: []string{"playground", "main.go", "main.go"}
len(os.Args): 2, os.Args: []string{"playground", ""}
len(os.Args): 4, os.Args: []string{"playground", "hello", "world", "hello world"}

What did you see instead?

len(os.Args): 3, os.Args: []string{"playground", "main.go", "main.go"}
len(os.Args): 3, os.Args: []string{"playground", "", ""}
len(os.Args): 3, os.Args: []string{"playground", "hello world", "hello world"}

Maybe my bias arises from my understanding (or lack thereof) of the behaviour of bash etc in these situations, but I see two problems here:

  • in the second instance, the unquoted $VARNOTSET should not have resulted in the empty string being passed as os.Args[1] to playground
  • in the third instance, the unquoted $HELLOWORLD should have resulted in hello and world being passed as os.Args[2] and os.Args[3] respectively, not hello world

I stumbled across this because I was looking to use environment variables to pass common flags to my go generators (to adjust log levels). But when that environment variable is not set, the empty string is passed as an argument... and flag parsing stops just before the first non-flag argument, and the empty string matches that condition.

I can think of one workaround: putting these flag environment variables at the end of all my go:generate directives).. and then ensure my go generators do not do any handling of flag.Args() (or equivalent).

Assuming for one second the current behaviour is not desirable/expected, I can't actually think of a good solution here that is backwards compatible...

cc @robpike perhaps?

@robpike
Copy link
Contributor

robpike commented Jan 10, 2017

It's working as intended. As documented, it doesn't run the shell, but rather uses Go quoting rules, and $VARNOTSET is an empty string. For the behavior you want, you need to run the shell, something like this (untested):

//go:generate sh -c 'playground $VARNOTSET "$VARNOTSET"'

Please reopen if this is not the right solution.

@robpike robpike closed this as completed Jan 10, 2017
@myitcv
Copy link
Member Author

myitcv commented Jan 14, 2017

Thanks for the quick response @robpike

As documented, it doesn't run the shell, but rather uses Go quoting rules, and $VARNOTSET is an empty string

I guess the key bit here is that this happens after tokenising (from go generate -help):

The arguments to the directive are space-separated tokens or
double-quoted strings passed to the generator as individual
arguments when it is run.

...

As a last step before running the command, any invocations of any
environment variables with alphanumeric names, such as $GOFILE or
$HOME, are expanded throughout the command line.

So it's definitely working as documented, and given that you've confirmed it's also working as intended, you've confirmed my bash bias is the root cause of my bad expectations...

The solution using sh -c does indeed work, but such an approach then makes parsing of go generate directives that bit more tricky (instead of simply using the basename of the first word after the directive, post expansion). I've opted for an alternative solution for now

@golang golang locked and limited conversation to collaborators Jan 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants