Cyclic module dependencies typically come about through test dependencies. This example presents a simple cyclic dependency between two packages, through one's external test package. Those two packages are initially part of the same module, but then split into two separate modules where the cyclic dependency is created.
The resulting repository state can be seen at https://github.com/go-modules-by-example/cyclic.
Create a repository root and add a git remote:
$ mkdir -p /home/gopher/scratchpad/cyclic
$ cd /home/gopher/scratchpad/cyclic
$ git init -q
$ git remote add origin https://github.com/go-modules-by-example/cyclic
Define a module at the root of the repo:
$ go mod init
go: creating new go.mod: module github.com/go-modules-by-example/cyclic
Add two subpackages a
and b
, with a dependency from a -> b
and b_test -> a
:
$ catfile a/a.go
$ cat a/a.go
package a
import "github.com/go-modules-by-example/cyclic/b"
const AName = b.BName
$ catfile b/b.go
$ cat b/b.go
package b
const BName = "B"
$ catfile b/b_test.go
$ cat b/b_test.go
package b_test
import (
"fmt"
"github.com/go-modules-by-example/cyclic/a"
"testing"
)
func TestUsingA(t *testing.T) {
fmt.Printf("Here is A: %v\n", a.AName)
}
Our directory structure now looks like this:
.
|-- a
| `-- a.go
|-- b
| |-- b.go
| `-- b_test.go
`-- go.mod
Test:
$ go test -v ./...
? github.com/go-modules-by-example/cyclic/a [no test files]
=== RUN TestUsingA
Here is A: B
--- PASS: TestUsingA (0.00s)
PASS
ok github.com/go-modules-by-example/cyclic/b 0.002s
Commit and push:
$ git add -A
$ git commit -q -am "Commit 1: initial commit of parent module github.com/go-modules-by-example/cyclic"
$ git rev-parse HEAD
0fcb217ecb293ac95fdb1a5ad0b44d7728545055
$ git push -q
Create a submodule from the subpackage b
. Note at this point, only github.com/go-modules-by-example/cyclic
can be resolved over
the internet; github.com/go-modules-by-example/cyclic/b
only exists locally. This will also be the case for any CI run before our
next commit is referenced by master
on our remote. Hence we temporarily use relative replace
directives for the
dependencies a -> b
and b -> a
. However, because of #26241, we cannot
currently use a replace
directive to satisfy the dependency a -> b
. So for now this next step is pushed in a "broken"
state; we don't add relative replace
directives for either dependency. This step will be updated once
#26241 is fixed.
$ cd /home/gopher/scratchpad/cyclic/b
$ go mod init github.com/go-modules-by-example/cyclic/b
go: creating new go.mod: module github.com/go-modules-by-example/cyclic/b
Commit and push:
$ cd /home/gopher/scratchpad/cyclic
$ git add -A
$ git commit -q -am "Commit 2: create github.com/go-modules-by-example/cyclic/b"
$ git rev-parse HEAD
90b499a495c8a2cc907238645b3b4d22d8d39baa
$ git push -q
Until #26241 is merged, this is where the mutual dependency gets created:
$ go test -v ./...
go: finding github.com/go-modules-by-example/cyclic/b v0.0.0-20190513150359-11f10a6d8c2e
go: downloading github.com/go-modules-by-example/cyclic/b v0.0.0-20190513150359-11f10a6d8c2e
go: extracting github.com/go-modules-by-example/cyclic/b v0.0.0-20190513150359-11f10a6d8c2e
? github.com/go-modules-by-example/cyclic/a [no test files]
$ cd /home/gopher/scratchpad/cyclic/b
$ go test -v ./...
go: finding github.com/go-modules-by-example/cyclic v0.0.0-20190513150359-11f10a6d8c2e
go: downloading github.com/go-modules-by-example/cyclic v0.0.0-20190513150359-11f10a6d8c2e
go: extracting github.com/go-modules-by-example/cyclic v0.0.0-20190513150359-11f10a6d8c2e
=== RUN TestUsingA
Here is A: B
--- PASS: TestUsingA (0.00s)
PASS
ok github.com/go-modules-by-example/cyclic/b (cached)
List the dependencies of github.com/go-modules-by-example/cyclic
:
$ cd /home/gopher/scratchpad/cyclic
$ go list -m all
github.com/go-modules-by-example/cyclic
github.com/go-modules-by-example/cyclic/b v0.0.0-20190513150359-11f10a6d8c2e
List the dependencies of github.com/go-modules-by-example/cyclic/b
:
$ cd /home/gopher/scratchpad/cyclic/b
$ go list -m all
github.com/go-modules-by-example/cyclic/b
github.com/go-modules-by-example/cyclic v0.0.0-20190513150359-11f10a6d8c2e
Commit the mutual dependency:
$ cd /home/gopher/scratchpad/cyclic
$ git add -A
$ git commit -q -am "Commit 3: the mutual dependency"
$ git rev-parse HEAD
92e76549dcd2086c088d76092d25e16c4ebb90f4
$ git push -q
go version go1.12.5 linux/amd64