README.md
May 13, 2019 ยท View on GitHub
Using submodules
Go modules supports nesting of modules, which gives us submodules. This example shows you how.
The resulting code can be found at https://github.com/go-modules-by-example/submodules.
Background
The official modules proposal
predicts that most projects will follow the simplest approach of using a single Go module per repository,
which typically means creating one go.mod file located in the root directory of a repository. However,
there are cases when multiple modules in a single repository are worth the extra on-going work, and here
we show a runnable example of how to create a multiple module repository with Go submodules.
Example Overview
The end result will be similar to the following, with three modules defined by the three go.mod files:
.
|-- go.mod
|-- b
| |-- go.mod
| `-- b.go
`-- a
|-- go.mod
`-- a.go
In this walkthrough:
- We create a root module and two submodules.
- We version the submodules independently by applying separate git tags (
v0.1.1andv1.0.0) - We have
aimportbto make things slightly more interesting. - We finish by creating a module on our local filesystem to use our
acommand.
Note that the root go.mod is optional here. (In general, you can have a multi-module
repository without a root go.mod, and without any nesting of modules. The techniques
shown in this example also apply to multi-module repositories that do not have any
nested modules).
Walkthrough
Initialise a directory as a git repo, and add an appropriate remote:
$ mkdir -p /home/gopher/scratchpad/submodules
$ cd /home/gopher/scratchpad/submodules
$ git init -q
$ git remote add origin https://github.com/go-modules-by-example/submodules
Define a root module, at the root of the repo, commit and push:
$ go mod init github.com/go-modules-by-example/submodules
go: creating new go.mod: module github.com/go-modules-by-example/submodules
$ git add go.mod
$ git commit -q -am 'Initial commit'
$ git push -q
Create a sub package b and test that it builds:
$ mkdir b
$ cd b
$ cat <<EOD >b.go
package b
const Name = "Gopher"
EOD
$ go mod init github.com/go-modules-by-example/submodules/b
go: creating new go.mod: module github.com/go-modules-by-example/submodules/b
$ go test
? github.com/go-modules-by-example/submodules/b [no test files]
Commit, tag and push our new package:
$ cd ..
$ git add b
$ git commit -q -am 'Add package b'
$ git push -q
$ git tag b/v0.1.1
$ git push -q origin b/v0.1.1
Create a main package that will use b:
$ mkdir a
$ cd a
$ cat <<EOD >a.go
package main
import (
"github.com/go-modules-by-example/submodules/b"
"fmt"
)
const Name = b.Name
func main() {
fmt.Println(Name)
}
EOD
$ go mod init github.com/go-modules-by-example/submodules/a
go: creating new go.mod: module github.com/go-modules-by-example/submodules/a
Build and run that main package:
$ go run .
go: finding github.com/go-modules-by-example/submodules/b v0.1.1
go: downloading github.com/go-modules-by-example/submodules/b v0.1.1
go: extracting github.com/go-modules-by-example/submodules/b v0.1.1
Gopher
$ go list -m github.com/go-modules-by-example/submodules/b
github.com/go-modules-by-example/submodules/b v0.1.1
Notice how we resolve to the tagged version of package b.
Commit, tag and push our main package:
$ cd ..
$ git add a
$ git commit -q -am 'Add package a'
$ git push -q
$ git tag a/v1.0.0
$ git push -q origin a/v1.0.0
Create another random module and use our a command from there:
$ cd $(mktemp -d)
$ export GOBIN=$PWD/.bin
$ export PATH=$GOBIN:$PATH
$ go mod init example.com/blah
go: creating new go.mod: module example.com/blah
$ go get github.com/go-modules-by-example/submodules/a@v1.0.0
go: finding github.com/go-modules-by-example/submodules/a v1.0.0
go: downloading github.com/go-modules-by-example/submodules/a v1.0.0
go: extracting github.com/go-modules-by-example/submodules/a v1.0.0
$ a
Gopher
Version details
go version go1.12.5 linux/amd64