vfsgen: vfsgendev cannot handle main package

I have a main package for which I want to compile assets with vfsgendev, but it fails:

$ vfsgendev -source="<path-to-my-package>".Assets
-source flag has invalid value: invalid format, expression &{0xc42006aa80 18 / 0xc42005e2e0} is not a selector expression but *ast.BinaryExpr
Usage: vfsgendev [flags] -source="import/path".VariableName
  -n    Print the generated source but do not run it.
  -source string
        Specifies the http.FileSystem variable to use as source.
  -tag string
        Specifies a single build tag to use for source. The output will include a negated version. (default "dev")

The minimum viable demo case is:

$ find
.
./assets_dev.go
./assets
./assets/hello.txt
./main.go
$ cat main.go
package main

func main() {}
$ cat assets_dev.go
/ +build dev

package main

import "net/http"

// Assets contains project assets.
var Assets http.FileSystem = http.Dir("assets")

About this issue

  • Original URL
  • State: open
  • Created 7 years ago
  • Comments: 15 (8 by maintainers)

Commits related to this issue

Most upvoted comments

Thank you very much for taking the time to investigate! I really appreciate your efforts to get to the bottom of this and help me understand!

From what I can grasp from that commit is that my PR #37 in its current form might be a really bad idea, as future Go versions might add further checks that prevent this from working. With some minor changes, however, it might become a bit more reliable and versatile:

  1. The generator template would not write a file that is package main itself, but a vfsgendev_generate_test.go (or any arbitraryly and collisionfreely named _test.go) with a single test function of a random name, e.g. func Test{{.UUID}}(t *testing.T). That function does what main in the template did before.
  2. vfsgendev_generate_test.go has a build constraint // +build vfsgendev{{.UUID}}.
  3. The test is part of the package that it wants to embed files for, e.g. it is not written as package foo_test, but really of package foo itself.
  4. Instead of go run -tags dev, vfsgendev would run go test -run Test{{.UUID}} -tags vfsgendev{{.UUID}}.
  5. vfsgendev_generate_test.go is removed.

The advantages I would come to expect are:

  • The approach does not conflict with the ways that the Go authors had in mind.
  • It works for package main
  • Unexported variables in the target package may be used for the asset generation. I believe that was not possible before, right?

What do you think?

Okay, understood. Take all the time you need. I have a working solution in place that fully covers my use case.

If you are interested, on the weekend I may find time to rerfactor #37 into a draft for the _test.go approach mentioned above. That way you have something to play around with when you are weighing the pros and cons someday.

Just to give my two cents on the rationale: You are already writing to the user’s $GOPATH: the resulting file. IMHO writing a single temporary file in the user’s real $GOPATH and then deleting it after it ran is hardly worse than writing the final resulting file. Using a UUID for the filename virtually eliminates the danger of overwriting a user file. And even then the tool could simply log an error “file already exists” and exit early before doing so. Leaving the file behind will not cause problems during compilation or test thanks to the UUID-protected build constraint. A user can run git clean or similar if the file is found to be bothering.

Hmm, the workaround from https://github.com/shurcooL/vfsgen/issues/36#issuecomment-347504650 only runs if generate.go is in the same directory as the main package. As soon as I move it out of there I get the same error message. At the moment I am at a complete loss why that might be.

Ok, I can reproduce that error by running this in bash:

$ vfsgendev -source="foo/bar".baz
-source flag has invalid value: invalid format, expression &{foo 4 / 0xc4200e2080} is not a selector expression but *ast.BinaryExpr

The issue is how bash interprets that parameter, it drops the quotes. So you’ll need to wrap the entire parameter in single quotes for bash:

$ vfsgendev -source='"foo/bar".foo'
2017/11/28 15:16:05 can't import package "foo/bar"