Some notes on relative imports and
vendor/ in Go
For reasons beyond the scope of this entry, I've become interested
in ways to work with multiple packages in Go without doing it in
the normal way, with a
$GOPATH/src and packages and programs that
go get'd from Github or any of the other places that the
go tool supports. The short version of what I'm interested in is
that I'd like to create self contained program source code trees
that are still modularized into multiple Go packages, instead of
throwing everything into '
(If you put everything in
main, you can put all your source code
in one directory and then tell people to just
cd there and do
go build' to build it.)
Go has two plausible mechanisms for this, vendoring and the rarely
used relative import path (which is more properly a filesystem
path). I'll start with the latter. Relative import paths come from
go help packages':
An import path that is a rooted path or that begins with a
..element is interpreted as a file system path and denotes the package in that directory.
Suppose that you have a program and you want to have two sub-packages,
b. Then you can put together a directory tree like this:
program/ main.go a/a.go b/b.go
main.go will use '
import "./a" to make a relative import
from the current directory, while your
a.go would use '
"../b" to reach package
b (if it needed to). You have to put
b.go into appropriate subdirectories, even if each
package only has a single file, because even this sort of import
applies to the entire directory.
However, relative imports turn out to have one drawback, and that's
that you can't use them in a package or program that has its source
code under your
$GOPATH. If you do, you'll get an error message:
main.go:3:8: local import "./a" in non-local package
(Note that you get this error even if you are in the directory
itself and just running '
go build', to build the current directory.
It's possible that this is new behavior in Go 1.10, but if so, well,
you're going to have to use Go 1.10 or later at some point.)
The other plausible mechanism is vendoring. Given the same main
program and two sub-packages
b, construct your directory
program/ main.go vendor/ a/a.go b/b.go
Now everything simply does '
import "a"' or '
import "b", which
works in both
The drawback of vendoring is the exact reverse of relative imports;
instead of not working under your
$GOPATH, it only works under
$GOPATH. This unfortunately makes vendoring not entirely
useful for my particular purpose here, because if I'm going to
$GOPATH, I might as well use something approximating
a proper Go package hierarchy:
src/cslab/program cmd/program/main.go a/a.go b/b.go
cmd/program subdirectory tree feels a bit excessive
here, but I'm not sure if there's a generally accepted better way.)
With this setup I can refer to things as '
and so on. There's relatively little need to hide them in a
subdirectory just so I can confuse everyone with '
In fact, I think this potential confusion from vendoring makes it better to use relative imports if you have to pick one of these two options, because at least when people see them it's fairly clear what they mean.
(It would be useful if the
vendor/ subdirectory worked even outside
$GOPATH, but it doesn't currently so even if this was accepted
as a bug by the Go people it would be some time before a fix appeared
and was more or less universally usable.)
Comments on this page:Written on 02 January 2018.