Skip to content

Native per-project tools in Go 1.24

There are some exciting news in the Go community as of recently on how to manage tools in Go projects. With the next release of Go (Go 1.24, expected to be released in February 2025), it seems we'll finally have a way to natively define per-project tools! 🎉

This hopefully means no more Makefile and/or tools.go for managing project tooling. And it looks like e.g. Dependabot will support this out of the box too.

Giving this a spin with gotip

You can try it out today using gotip:

go install golang.org/dl/gotip@latest
gotip download

Let's have a look at the docs for go tool (in this case, gotip tool):

$ gotip help tool
usage: go tool [-n] command [args...]

Tool runs the go tool command identified by the arguments.

Go ships with a number of builtin tools, and additional tools
may be defined in the go.mod of the current module.

With no arguments it prints the list of known tools.

The -n flag causes tool to print the command that would be
executed but not execute it.

For more about each builtin tool command, see 'go doc cmd/<command>'.

Then let's create a project and add some tools to it:

gotip mod init go-playground
gotip get -tool github.com/alta/protopatch/cmd/protoc-gen-go-patch
gotip get -tool gotest.tools/gotestsum

There's a new tool section in the go.mod!

$ cat go.mod
module go-playground

go 1.24

tool (
        github.com/alta/protopatch/cmd/protoc-gen-go-patch
        gotest.tools/gotestsum
)

require (
        github.com/alta/protopatch v0.5.3 // indirect
        github.com/bitfield/gotestdox v0.2.2 // indirect
        github.com/dnephin/pflag v1.0.7 // indirect
        github.com/fatih/color v1.16.0 // indirect
        github.com/fatih/structtag v1.2.0 // indirect
        github.com/fsnotify/fsnotify v1.7.0 // indirect
        github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
        github.com/mattn/go-colorable v0.1.13 // indirect
        github.com/mattn/go-isatty v0.0.20 // indirect
        golang.org/x/mod v0.16.0 // indirect
        golang.org/x/sync v0.6.0 // indirect
        golang.org/x/sys v0.18.0 // indirect
        golang.org/x/term v0.18.0 // indirect
        golang.org/x/text v0.14.0 // indirect
        golang.org/x/tools v0.19.0 // indirect
        google.golang.org/protobuf v1.31.0 // indirect
        gotest.tools/gotestsum v1.12.0 // indirect
)

You can now run tools like this:

gotip tool <the-tool>

And if you step out of the project, the tool you just added is no longer available.

You can also see which bundled tools are available by default with Go 1.24:

$ gotip tool

addr2line
asm
buildid
cgo
compile
covdata
cover
dist
distpack
doc
fix
link
nm
objdump
pack
pprof
preprofile
test2json
trace
vet

Summary

I think this development is great. It's something my colleagues and myself have been waiting for.

This new go tool workflow introduces less complexity (than the Makefile/tools.go setup) but it likely won't solve all the use cases, like splitting out test dependencies from the production dependencies (EDIT: this do seem possible, see amendment below) or solve/improve integration with IDEs/editors.

Amendment, 2024-12-04

It seems like it will be possible to split out dependencies from the main go.mod file:

gotip mod init -modfile=tool.mod foo
gotip get -modfile=tool.mod -tool gotest.tools/gotestsum

Amendment, 2024-12-20

!!! quote "Quoting ConradIrwin from GitHub comment"

- Tool executables live in the build cache, and are built on demand (and expired
  when unused). This lets go tool select which version of a tool is used
  depending on the active module.
- You invoke them with `go tool <toolname>`. If you want to (and versioning is
  not a concern) you can install all tools for the current module to `GOBIN`
  with `go install tool`.
- Tool dependencies participate in the same module graph as imported
  dependencies, and require/replace/exclude directives apply in the same way.
- If you want to remove a tool dependency, you can use `go get -tool` followed
  by `go get -u`.

— Source at [golang/go#48429](https://github.com/golang/go/issues/48429#issuecomment-2547760589)

Comments