2022-10-17
The proper setup of a Go module, as I understand it
I have been a little bit confused about some aspects of Go modules in practice for a while, and to get things more nailed down in my head, I'm writing some entries (writing entries here can be a form of talking to the duck). This is the first one, because before I understand the unusual corner cases I need to cover the normal and proper cases.
Suppose that you have a Go module example.org/cks/mymod
, or a
pre-modular Go package that would be a module if you modularized
it, and this module is in a VCS repository of its own (which is the
common case). As far as I know, there are two proper modern cases,
one acceptable modern corner case, and one proper legacy case for
your module or package.
(Go expects to find the repository for your module starting at the URL using a documented approach that allows you to redirect to another location.)
First, your module can have either no version tags or version tags
that say it has major version 0 or 1. In both cases, the module's
import path is 'example.org/cks/mymod
' and your go.mod
should
have that as the module's name in the 'module
' line.
Second, your module can have a tag with a major version above 1,
such as '2.1.0', but you have a normal source code layout with your
code directly in example.org/cks/mymod
. This makes the module's
import path be 'example.org/cks/mymod/v2
' and your go.mod
should
have this as the module name in the 'module
' line. This is the
normal way to have a proper Go module that's above major version 1.
Third, as what I think of as a corner case your module can have a
tag with a major version above 1 but put the code for that version
in an appropriately named subdirectory, such as example.org/cks/mymod/v2
or github.com/pborman/getopt/v2_. This is still
the module's import path, but now your go.mod
for that major
version goes in .../v2/go.mod
and
declares the module name as the v2 name. To avoid really confusing
people, I think that the code outside of the subdirectory should
be the v1 (or v0) code and thus have a go.mod
file that just calls
it 'example.org/cks/mymod
'.
(As far as I can tell from the documentation, Go doesn't accept version subdirectories for v0 and v1 major versions.)
Technically I think you can have the example.org/cks/mymod/v2
subdirectory be major version 2 and the example.org/cks/mymod
root directory be, say, major version 3 and have it all work out
with appropriate go.mod
contents in the root directory and in the
v2/
subdirectory. However, this is likely to be rather confusing
to people, and it's possible that the Go tools don't like this
setup. If you started out with a v2/
subdirectory but want to
move away from version subdirectories for your next major version,
I think that it would work to release a v3 major version with the
v2/
subdirectory deleted from the tree (as of that version tag).
I believe that the one proper legacy case is that your package has
major version 0 or 1, but no go.mod
because you haven't modularized
yet. In this case, the import path is 'example.org/cks/mymod
'.
Go's module tooling handles the case of a non-modular package having
a higher major version than v1, but that's for another entry.
It's possible to have modules be in subdirectories of the VCS repository root. In this case VCS version tags get complicated, as covered in the official documentation. However I believe the rest of it stays the same.
Ultimately all of this (and much more) is documented in the "Version control systems" section of the official documentation. If you need to know the full details, expect to spend a while reading it carefully, and possibly taking notes as you go back and forth between sections to work out how various scenarios (probably) get handled.