The replace directive
At the risk of revealing how long I was not reading the docs about this, I recently learned how to use the replace
directive.
I was following along with a Go project tutorial when I hit a wall I was familiar with: needing to import a local package as a dependency.
Before enabling modules, I’d gone through Jon Calhoun’s wonderful Gophercises. As practice, I’d at some point made all of the exercises into packages usable from the outside, and put the external and consumer packages in their own separate directories under $GOPATH/src/github.com/kristakoch/gophercises
. This worked just fine at first, but later on after Go 1.11 was released and I started defaulting to modules enabled, I went in to try to use those same consumer packages and got errors from the compiler saying it couldn’t find what I was telling it to. After a couple hours of disorganized struggle, I threw up a white flag and crammed the external package files all the way back into the same packages where they were used.
But that was months ago, and this time I was a bit less intimidated. I took some time to read up on replace
and analyze the go.mod
s I’d been using that’d been set up by other people, and this time things worked out.
What I ended up doing
-
Ran
go mod init
incode/src/github.com/kristakoch/somepkg/cmd/somepkg
to generate ago.mod
in the directory where my runnable file was. (cmd
is nested like that for structure and executable naming) -
Ran
go mod init
incode/src/github.com/kristakoch/somepkg
to generate ago.mod
in the directory of the package I was planning to use. -
In my
main.go
file, I added the import path for my package as:"github.com/kristakoch/somepkg"
because that’s where I knew the package would be if I had pushed it up to GitHub.
-
I was getting errors saying that the package couldn’t be found, which made sense because the code didn’t actually exist remotely, where Go was looking. In my
go.mod
, I then added:replace github.com/kristakoch/somepkg => ../../../somepkg
which told Go to
replace
the import for the package with the package at a relative path three directories up from where thego.mod
was. So three up fromcode/src/github.com/kristakoch/somepkg/cmd/somepkg/go.mod
.
What I could have done
Of course there are other ways to do this. Following along with How to Write Go Code gives a different runthrough for how to import a local package.
In the tutorial, you:
- Create a package at
$HOME/hello/hello.go
- Run
go mod init [desired import path]
to create a new module - Create the package you’d want to use in the root directory of the
go.mod
- Add your dependency in
main.go
as"[desired import path]/[ext package name]"
Here, you don’t have to create a go.mod
in your external package. The external package is already contained in that module, which can contain multiple packages. Creating the new module also lets you develop the code outside of your GOPATH.
A module declares its identity in its
go.mod
via themodule
directive, which provides the module path. The import paths for all packages in a module share the module path as a common prefix - Go wiki
So I could have instead declared a module path where my go.mod
was and changed my directory structure so that the package was nested inside of that folder, and imported it using the module path I defined in the go.mod
, in that case not needing replace
at all. Huh!