Go Without Gopath

I’ve been writing Go for a while now – according to my git log I committed my first go code in August of 2013. Go 1.0 was released in March of 2012. So things have changed quite a bit since I first started writing.

I’m not one to say that any language is perfect, where there exists code and time there will be warts. But I have genuinely enjoyed my experience with the language and still consider it my go-to nearly 9 years later.

Yet!

Things have changed significantly since I first started slinging errs. Dep is no more and go mod rules the land of dependencies. GOROOT is no longer recommended to be set – in fact it’s best not to. go get is no longer the standard way to install binary packages, rendering much of the documentation on the web, and quite a few Makefiles, as no longer working.

This article is about the no longer needed environment variable GOPATH. If you are familiar with Go development from the days of yore, you may remember setting it with every project. It was, honestly, kind of annoying. I’m quite happy to see it no longer necessary.

But it does leave new package layouts in a bit of a quandary. So let’s look at a sample project’s layout to see how to make this work as expected.

cschmidt@kitai ~/src/myproj_src   ❄ tree
.
├── go.mod
├── mylib
│   ├── libfile1.go
│   └── libfile2.go
├── Makefile
└── cmd
    ├── cmd.go

in my go.mod file, you find:

cschmidt@kitai ~/src/myproj_src   ❄ cat go.mod 
module myproj_src

go 1.18

Now something that tripped me up is that you might expect this to work:

go build cmd/
package cmd is not in GOROOT (/home/cschmidt/go/go1.18.1/src/cmd)

but it doesn’t because it’s assuming the prefix is where I installed go, since there’s no GOPATH.

So then I think this might work:

cschmidt@kitai ~/src/myproj_src   ❄ go build ./cmd/
go: build output "cmd" already exists and is a directory

but it doesn’t because the binary that it builds is output to the same name as the existing directory, and the compiler refuses to do that.

One option at this point is to drop to a Makefile. This simple Makefile looks like:

cschmidt@kitai ~/src/myproj_src   ❄ cat Makefile 
default:
	cd cmd/ && go build

clean:
	rm -rf cmd/cmd

which will certainly work.

Another cleaner option is to do

mkdir -p bin/
go build -o bin ./...

and that will build all the binaries that are in the packages in this directory, and put them in bin. This is the closest to what we had with $GOPATH, so this is what I want.

🎉