Update: With the advent of Go 1.11 and Go modules, this whole post is now useless. Unset your GOPATH entirely and switch to Go modules today!
Maybe someday I’ll start writing about things besides Go again.
Go requires that you set an environment variable for your workspace called
your GOPATH
. The GOPATH
is one of the most confusing aspects of Go to
newcomers and even relatively seasoned developers alike. It’s not immediately
clear what would be better, but finding a good GOPATH
value has implications
for your source code repository layout, how many separate projects you have on
your computer, how default project installation instructions work
(via go get
), and even how you interoperate with other projects and
libraries.
It’s taken until Go 1.8 to decide to set a default and that small change was one of the most talked about code reviews for the 1.8 release cycle.
After writing about GOPATH himself, Dave Cheney asked me to write a blog post about what I do.
I set my GOPATH
to always be the current working directory, unless a parent
directory is clearly the GOPATH
.
Here’s the relevant part of my .bashrc
:
# bash command to output calculated GOPATH.
calc_gopath() {
local dir="$PWD"
# we're going to walk up from the current directory to the root
while true; do
# if there's a '.gopath' file, use its contents as the GOPATH relative to
# the directory containing it.
if [ -f "$dir/.gopath" ]; then
( cd "$dir";
# allow us to squash this behavior for cases we want to use vgo
if [ "$(cat .gopath)" != "" ]; then
cd "$(cat .gopath)";
echo "$PWD";
fi; )
return
fi
# if there's a 'src' directory, the parent of that directory is now the
# GOPATH
if [ -d "$dir/src" ]; then
echo "$dir"
return
fi
# we can't go further, so bail. we'll make the original PWD the GOPATH.
if [ "$dir" == "/" ]; then
echo "$PWD"
return
fi
# now we'll consider the parent directory
dir="$(dirname "$dir")"
done
}
my_prompt_command() {
export GOPATH="$(calc_gopath)"
# you can have other neat things in here. I also set my PS1 based on git
# state
}
case "$TERM" in
xterm*|rxvt*)
# Bash provides an environment variable called PROMPT_COMMAND. The contents
# of this variable are executed as a regular Bash command just before Bash
# displays a prompt. Let's only set it if we're in some kind of graphical
# terminal I guess.
PROMPT_COMMAND=my_prompt_command
;;
*)
;;
esac
The benefits are fantastic. If you want to quickly go get
something and not
have it clutter up your workspace, you can do something like:
cd $(mktemp -d) && go get github.com/the/thing
On the other hand, if you’re jumping between multiple projects (whether or not
they have the full workspace checked in or are just library packages), the
GOPATH
is set accurately.
More flexibly, if you have a tree where some parent directory is outside of the
GOPATH
but you want to set the GOPATH
anyways, you can create a .gopath
file and it will automatically set your GOPATH
correctly any time your shell
is inside that directory.
The whole thing is super nice. I kinda can’t imagine doing something else anymore.