Go Make

March 3, 2016

Running uninstalled Go programs can be pretty slow. The build is relatively fast, compared to C, but 8+ sec to start is crazy slow for a regular program, or even a Python/Ruby project. That can really make change/run/change/run cycles painful. So I used a Makefile to avoid rebuilding when there haven’t been any changes. Adding some bash aliases made it seamless to my workflow, so now:

$ cd ~/go/src/github.com/heewa/subparlabs/bonjourno
$ gorun
$ gorun --arg=x
$ gorun --arg=x -v

Builds the program just once, instead of three times. The Makefile lives in a single, central place outside any repo, so I don’t have to do anything to use it in a new project.

  1. Avoids rebuilding on every run if there haven’t been code changes.
  2. Rebuilds on any code change, including updates to vendored packages.
  3. Passes cmdline arguments to binary, instead of make.
  4. Doesn’t need modification or addition to a project to work. You don’t even need to dirty the repo state or add a file to .gitignore.
  5. Doesn’t need setup or changes for new projects, so you can jump around codebases and use it right away.

Getting

Drop this Makefile somewhere. I keep it in ~/.golang.Makefile. Set up convenience bash alias & function in your ~/.bashrc:

alias gomake='make -f $HOME/.golang.Makefile'
gorun() { gomake RUN_ARGS="$*" run ;}

Using

From a pacakge root run gorun [args for your program] to run (and build if necessary) a main.go file. You can change the behavior with env vars:

  • MAIN: the file containing func main(), to be built & run. default: main.go
  • BINARY: a path to the location of the built binary. default: /tmp/$MAIN (without the .go suffix)
  • BUILD_FLAGS: passed to go build. default: (empty)
  • RUN_ARGS: passed to the binary when it’s run. The gorun bash fn you put in your .bashrc handles passing arguments to it through this env var to the built binary.

If I know I’ll be running a project many times, I like to set a specific alias for it, just as I get into the directory (not a permanently saved alias). For example, if I’m working on glide:

$ cd ~/go/src/github.com/Masterminds/glide
$ alias glide='BINARY=/tmp/glide MAIN=glide.go gorun'
$ glide update && glide get some/package && glide list

Then on every change, instead of building the project three times, it’ll just build once.

How does it work?

The bash alias (gomake) lets you keep a single copy of the Makefile in one place and use it from anywhere, rather than having to either manually specify the path or copy the Makefile around to each project. The bash function (gorun) allows you to pass arguments to your program just like you would to go run or the built binary, rather than having them go to make.

To determine what files to trigger a rebuild on, the Makefile runs find -name '*.go', which catches vendored files too. Otherwise it’s a pretty standard, simple Makefile.