I’m using Golang for a while. During the development, I was used to repeatedly execute “go build”,“go test” manually. This was a bad habit on which I resign. It is not so painful if you use simple command without any args. But in case of more complex tasks, naturally, it is going to be a pain. There are few options you can consider as a way out. You can use a bash script to do the work for you. Or better, at least for me, you can write a makefile. The make tool is there for this reason and in the makefile you can keep all your common tasks together. I’m not “make tool guru” to be able to educate how to write the proper one, but in this post, I put together the makefile which works for most of my projects. Let’s go through it.
# Go parameters GOCMD=go GOBUILD=$(GOCMD) build GOCLEAN=$(GOCMD) clean GOTEST=$(GOCMD) test GOGET=$(GOCMD) get BINARY_NAME=mybinary BINARY_UNIX=$(BINARY_NAME)_unix all: test build build: $(GOBUILD) -o $(BINARY_NAME) -v test: $(GOTEST) -v ./... clean: $(GOCLEAN) rm -f $(BINARY_NAME) rm -f $(BINARY_UNIX) run: $(GOBUILD) -o $(BINARY_NAME) -v ./... ./$(BINARY_NAME) deps: $(GOGET) github.com/markbates/goth $(GOGET) github.com/markbates/pop # Cross compilation build-linux: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(BINARY_UNIX) -v docker-build: docker run --rm -it -v "$(GOPATH)":/go -w /go/src/bitbucket.org/rsohlich/makepost golang:latest go build -o "$(BINARY_UNIX)" -v
Let’s say I like to keep the idea of the DRY rule. So it is handy to declare commonly used commands and variables at the top of the file. And refer to them further in the file.
# Basic go commands GOCMD=go GOBUILD=$(GOCMD) build GOCLEAN=$(GOCMD) clean GOTEST=$(GOCMD) test GOGET=$(GOCMD) get # Binary names BINARY_NAME=mybinary BINARY_UNIX=$(BINARY_NAME)_unix
The makefile targets are defined bellow labels like “
> make run // call specific task > make // make tool calls "all" task
One of the essential parts of the makefile is the build step. Using the variable $(GOBUILD) the “go build” command is executed. The binary name is defined by “-o $(BINARY_NAME)” and I found useful to switch to the verbose mode with “-v”. With verbose mode, you can see the currently built packages.
build: $(GOBUILD) -o $(BINARY_NAME) -v // expands to: "go build -o mybinary -v"
Just because a lot of us are just lazy, the “run” target is here. The target builds the binary and executes the application consequently.
run: $(GOBUILD) -o $(BINARY_NAME) -v ./... ./$(BINARY_NAME)
Naturally, the test command should be the part of the project makefile. I personally always choose the verbose mode to be able to debug and watch the test run better.
test: $(GOTEST) -v ./...
If the project uses CI/CD or just for consistency, it is good to keep the list of dependencies used in packages. This is done by the “deps” task, which should get all the necessary dependencies by “go get” command.
deps: $(GOGET) github.com/markbates/goth $(GOGET) github.com/markbates/pop
To wrap up this section of useful commands, the clean command is accommodated into makefile. The “rm -f” command is added to remove binary with the custom name in $(BINARY_XXX) variable. Typically another clean-up command could be part of this make section.
clean: $(GOCLEAN) rm -f $(BINARY_NAME) rm -f $(BINARY_UNIX)
If the project is intended to run on another platform than the one where it is developed, it is useful to include cross compilation commands to make file. I usually run the binaries on Linux platform in the container, so the makefile contains the Linux build.
build-linux: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(BINARY_UNIX) -v
If your code use C binding you could get stuck a little bit. The issue with CGO is that you need a gcc compatible for given platform. So if the development is done on OSX/Windows you need to build gcc to be able to compile for Linux. At least for me, the configuration of gcc to cross compile the C code is not so straightforward on OSX. If the CGO is needed, the docker image is the best way how to create Linux build. The only requirement: Docker has to be installed.
docker-build: docker run --rm -it -v "$(GOPATH)":/go -w /go/src/bitbucket.org/rsohlich/makepost golang:latest go build -o "$(BINARY_UNIX)" -v
This way you can make your go development process more effective and fluent. The makefile used in this post is available here. Don’t hesitate to comment or ask questions, I’ll be happy to answer or talk about this. Enjoy!