Wandering Thoughts archives

2021-07-19

Making a Go program build with Go modules can be not a small change

In theory, at some point in the future Go will stop supporting the traditional GOPATH mode. When this happens, if you want to still build old Go programs that you have sitting around in checked out version control repositories, you will need to modularize them. Once upon a time, I thought that this would be as simple as going to the root of your copy of the repo, then running 'go mod init ...' and 'go mod tidy'. Unfortunately, life is not this simple and there can be at least two complications.

The first complication is moved and renamed repositories for modules, if the moved module has a go.mod that declares its new name. For example what is now github.com/hexops/vecty was once github.com/gopherjs/vecty. In a non-modular Go build, you can still import it under the old path and it will work. However, the moment you attempt to modularize the program, 'go mod tidy' will complain and stop:

github.com/gopherjs/vecty: github.com/gopherjs/vecty@v0.6.0: parsing go.mod:
module declares its path as: github.com/hexops/vecty
        but was required as: github.com/gopherjs/vecty

In theory you may be able to get this to work with a go.mod replace directive. In practice my attempts to do this resulted in 'go mod tidy' errors about:

go: github.com/hexops/vecty@v0.6.0 used for two different module paths (github.com/gopherjs/vecty and github.com/hexops/vecty)

(You also need to get the version number or other version identifier of the moved repository.)

The general fix is to edit every import of packages from the module to use the new location. Then you can run 'go mod tidy' without it complaining.

The second complication is modules that have moved to versions above v1, possibly very far past v1; for example, github.com/google/go-github is up to v37, and modularized at v18 (it doesn't even have a tagged v1). A GOPATH build of the program you're trying to modularize will use whatever version of the repository you have checked out, which may well be the current one, and the code will import it as a version without a version suffix (as 'github.com/google/go-github'). When you run 'go mod tidy', Go will attempt to find the most recent tag (or version of the repository) that doesn't have a go.mod file, and specify that version in your go.mod with a '+incompatible' tag. Depending on how far Go had to rewind, this may be a version of the package that is far older than the program expects.

(If a go.mod existed for a v1 version, I suspect that 'go mod tidy' will pick that in this case. But I haven't tried to test it, partly for lack of a suitable module to test against. With github.com/google/go-github, I get 'v17.0.0+incompatible', the last tagged version before it was modularized.)

Again the fix is to edit the program's source code to change every import of the package to use the proper versioned package. Instead of importing, say, 'github.com/google/go-github/github', you would import 'github.com/google/go-github/v37/github'.

Although I haven't tested it extensively, it appears that go-imports-rename can be used to make both sorts of changes. I successfully used it to automatically modify my test third party repository.

(There may be other tools to do this package import renaming, but this is the one I could find.)

The unfortunate part of all of this is that it requires you to make changes to files that will be under version control in the repo. If the upstream updates things in the future, this will probably make your life more complicated.

(In some cases, 'go mod tidy' may insist that you clean up imports in code that's in sub-packages in the repository that aren't actually imported and used in the program itself.)

programming/GoModularizationTwoGotchas written at 23:32:20; Add Comment


Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.