| I've seen a lot of developers, especially developers with C backgrounds, reach for Makefiles when approaching Go development, and I think it's a harmful practice. So, I'd like to offer a respectful but firm rebuttal to this article. :) I dislike using make(1) with Go for two reasons. The first is that make was developed for building C projects, and therefore is oriented around that task. Building C projects is a lot different than building Go projects, and it involves stitching together a lot of pieces, with plenty of intermediate results. make(1) has first class support for intermediate results, which are expressed as targets. If you look at the article, the author has to use a workaround just to avoid this core feature of make(1). The second reason I dislike using make(1) for Go projects is that it harms portability. A Go project should only require the Go compiler to build successfully. Go projects that need make(1) to build will not work out of the box for Windows users, even though Go is fully supported on Windows. For me, this puts Makefiles into the "nonstarter" category, even though I do all of my own development work on Linux. There is just no reason to complicate things for people who don't have make(1) installed. For code generation and other ancillary tasks, Go includes the 'go generate' facility. This feature was created specifically to free developers from depending on external build tools. (https://blog.golang.org/generate) For producing several binaries for one project, use several different main packages in directories that are named what you want your binary to be. Edit: corrected some terminology. |
* Makefiles can act as shortcuts for common existing functionality of the build toolchain
* Makefiles can add new functionality that is not part of the build toolchain
* Makefiles can add new functionality that replicates existing functionality in the build toolchain
An example of the first case is one of the first examples in the article: using `make build` to run `go build`. The second includes things like the later example for `make docker-push`. The third includes things like makefiles that generate intermediate files or other things that `go generate` could do.
Only the 3rd thing can really meaningfully harm productivity, but in my experience it’s the least common usage of `make`. A Makefiles that wraps `go generate && go build` into `make build` seems fully outside the scope of the portability concern, since a user without Make could just run the same commands themselves. Likewise, a Makefiles that adds `make release` which uploads the build artifact to GitHub Releases or similar isn’t replacing something the go toolchain could do, so it’s also not affecting portability. The user without Make couldn’t have used docker-push anyways, since the go compiler doesn’t support pushing release assets.