Skip to content

Instantly share code, notes, and snippets.

@tschaub
Last active June 11, 2020 03:03
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tschaub/66f5feb20ae1b5166e9fe928c5cba5e4 to your computer and use it in GitHub Desktop.
Save tschaub/66f5feb20ae1b5166e9fe928c5cba5e4 to your computer and use it in GitHub Desktop.
Reproducible module builds with tools (commands) as dependencies

Tools as dependencies

This example is a slight tweak on the best-practices example for working with Go modules as development dependencies.

The downside of the existing example is that someone who git clones your module to make a contribution, would not be able to run go generate (without extra work).

$ go generate
painkiller.go:5: running "stringer": exec: "stringer": executable file not found in $PATH

Instead of requiring that the stringer executable be on everyone's path, the go:generate comment can be changed to use go run instead of calling the executable directly.

For example, in painkiller.go, use this

//go:generate go run golang.org/x/tools/cmd/stringer -type=Pill

instead of this

//go:generate stringer -type=Pill

Walk-through

Create a module:

$ mkdir -p /home/gopher/scratchpad/tools
$ cd /home/gopher/scratchpad/tools
$ git init -q
$ git remote add origin https://github.com/go-modules-by-example/tools
$ go mod init
go: creating new go.mod: module github.com/go-modules-by-example/tools

Add stringer as a dependency by importing the package in a build constraint-ignored file. This file will never be compiled (nor will it compile, because we are importing a main package); it is used simply to record the dependency. The file and the build constraint names are not particularly important, but we choose tools for the sake of consistency:

$ cat tools.go
// +build tools

package tools

import (
	_ "golang.org/x/tools/cmd/stringer"
)

Use go run golang.org/x/tools/cmd/stringer via a go:generate directive:

$ cat painkiller.go
package main

import "fmt"

//go:generate go run golang.org/x/tools/cmd/stringer -type=Pill

type Pill int

const (
	Placebo Pill = iota
	Aspirin
	Ibuprofen
	Paracetamol
	Acetaminophen = Paracetamol
)

func main() {
	fmt.Printf("For headaches, take %v\n", Ibuprofen)
}

go generate and run the result:

$ go generate
$ go run .
For headaches, take Ibuprofen

Commit and push the results:

$ git add -A
$ git commit -q -am 'Initial commit'
$ git push -q origin
diff --git a/010_tools/README.md b/010_tools/README.md
index e545f80..3a36e0e 100644
--- a/010_tools/README.md
+++ b/010_tools/README.md
@@ -106,14 +106,6 @@ $ go mod init
go: creating new go.mod: module github.com/go-modules-by-example/tools
```
-Set `GOBIN` (see [`go help environment`](https://golang.org/cmd/go/#hdr-Environment_variables)) to define where tool
-dependencies will be installed:
-
-```
-$ export GOBIN=$PWD/bin
-$ export PATH=$GOBIN:$PATH
-```
-
Add `stringer` as a dependency by importing the package in a build constraint-ignored file. This file will never be
compiled (nor will it compile, because we are importing a `main` package); it is used simply to record the dependency.
The file and the build constraint names are not particularly important, but we choose `tools` for the sake of
@@ -131,41 +123,7 @@ import (
)
```
-Install `stringer`:
-
-```
-$ go install golang.org/x/tools/cmd/stringer
-go: finding golang.org/x/tools v0.0.0-20190511041617-99f201b6807e
-go: downloading golang.org/x/tools v0.0.0-20190511041617-99f201b6807e
-go: extracting golang.org/x/tools v0.0.0-20190511041617-99f201b6807e
-go: finding golang.org/x/sync v0.0.0-20190423024810-112230192c58
-go: finding golang.org/x/net v0.0.0-20190311183353-d8887717615a
-go: finding golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
-go: finding golang.org/x/text v0.3.0
-go: finding golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
-```
-
-Our module reflects the dependency:
-
-```
-$ go list -m all
-github.com/go-modules-by-example/tools
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
-golang.org/x/net v0.0.0-20190311183353-d8887717615a
-golang.org/x/sync v0.0.0-20190423024810-112230192c58
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
-golang.org/x/text v0.3.0
-golang.org/x/tools v0.0.0-20190511041617-99f201b6807e
-```
-
-Verify `stringer` is available on our `PATH`:
-
-```
-$ which stringer
-/home/gopher/scratchpad/tools/bin/stringer
-```
-
-Use `stringer` via a `go:generate` directive:
+Use `go run golang.org/x/tools/cmd/stringer` via a `go:generate` directive:
```go
$ cat painkiller.go
@@ -173,7 +131,7 @@ package main
import "fmt"
-//go:generate stringer -type=Pill
+//go:generate go run golang.org/x/tools/cmd/stringer -type=Pill
type Pill int
@@ -201,9 +159,6 @@ For headaches, take Ibuprofen
Commit and push the results:
```
-$ cat <<EOD >.gitignore
-/bin
-EOD
$ git add -A
$ git commit -q -am 'Initial commit'
$ git push -q origin
diff --git a/go.mod b/go.mod
index 094f511..a011237 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,5 @@
module myitcv.io/go-modules-by-example
require myitcv.io v0.0.0-20190513145353-42487431adac
+
+go 1.13
@tooolbox
Copy link

Can you use this to run stringer at a specific version?

@Kagami
Copy link

Kagami commented May 10, 2020

@tooolbox I think go.mod handles that?

@tschaub
Copy link
Author

tschaub commented May 10, 2020

Yes.

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