Compilation error only building a module, not main package - go

I'm making a Go program and have created a module to divide it. Here is my working tree (the minimal directory is in $GOPATH/src/):
minimal/
├── main.go
└── ui
├── go.mod
├── go.sum
└── ui.go
In the ui module I have the following:
package ui
import "github.com/satori/go.uuid"
func SomeFunction() {
id, err := uuid.NewV4()
if err == nil {
print(id.String())
} else {
print(err)
}
}
The main package uses this module as follows
package main
import "minimal/ui"
func main() {
ui.SomeFunction()
}
Here is the go.mod file:
module minimal/ui
go 1.14
require github.com/satori/go.uuid v1.2.0
When running go build in the main package folder, everything works and the binary is generated. However, when building only the ui module, it gives the following compilation error:
ui$ go build
# minimal/ui
./ui.go:6:10: assignment mismatch: 2 variables but uuid.NewV4 returns 1 values
You can check https://github.com/satori/go.uuid to see that the function returns two values. What has me puzzled is that building the main package works, but the module doesn't. Why is that?

github.com/satori/go.uuid documentation appears to support GOPATH builds. go module builds, however, produce inconsistent results.
Take the simple API usage from it's README.md:
package main
import "github.com/satori/go.uuid"
func main() {
u, err := uuid.NewV4()
_, _ = u, err
}
And try to compile it with go modules:
go mod init
go build
go: finding module for package github.com/satori/go.uuid
go: found github.com/satori/go.uuid in github.com/satori/go.uuid v1.2.0
./main.go:6:9: assignment mismatch: 2 variables but uuid.NewV4 returns 1 values
it pulled tagged v1.2.0 and it failed to compile.
So now pull the latest commit:
go get github.com/satori/go.uuid#master
go: github.com/satori/go.uuid master => v1.2.1-0.20181028125025-b2ce2384e17b
Now it compiles:
go build && echo $?
0
So what is happening here?
github.com/satori/go.uuid is tagged with a mature v1 release, so all dot releases should be feature enhancements with no breaking changes.
The latest tagged version v1.2.0 returns only one value from uuid.NewV4().
The latest commit (which go modules infers a pseudo dot release version of v1.2.1-0.20181028125025-b2ce2384e17b) has code that matches the README.md.
If this repo ever becomes tagged as v1.2.1 - this would be a breaking change, as it changes the signature of a function published in a previous v1 release. This would be a violation of semver rules.
Conclusion:
The documentation matches a GOPATH (i.e. non-go modules) build. GOPATH builds always pull the latest commit.
From a go modules perspective, while one may be able to coerce a working build, it does not look like it's supported. Yes, the git repo uses semver tags, but there is no go.mod.
I would not trust this package with a go modules build. Perhaps consider using github.com/google/uuid which does.

The uuid package has different versions. In the latest version, function NewV4() returns just one variable but in the previous versions, it returns two variables which one of them is an error.
You create a go.mod file in the subdirectory of your project, So when you compile your project in the main directory, go-compiler uses one version, and in your ui directory it uses another version to compile. You should just edit the version of uuid package in your go.mod file.

Related

Content of go.sum and modules really used by a go application

I'm trying to compare the behavior of go mod tidy (and the resulting content of go.sum) to the output of go list -m all.
Reading the docs, I understand go.sum contains the whole list of dependent modules declared in go.mod and in dependencies' go.mod files, go list -m all shows the modules really loaded during the execution.
As an example, an application including logrus and prometheus like this:
go.mod
module mytest
go 1.14
require (
github.com/prometheus/common v0.4.0
github.com/sirupsen/logrus v1.8.1
)
main.go
package main
import "github.com/sirupsen/logrus"
import "github.com/prometheus/common/version"
func main() {
logrus.Info("Hello World")
logrus.Infof("Prometheus info: %v", version.Info())
}
After go mod tidy, go.sum shows both logrus v1.8.1, requested by the go.mod, and 1.2.0, dependency of prometheus v0.4.0; go list -m all shows only v1.8.1.
go.sum
[...]
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
[...]
output of go list
[...]
github.com/sirupsen/logrus v1.8.1
[...]
Is it correct to say the modules really used by the application are listed by go list -m all?
The underlying problem is that a static code analysis detects insecure module versions listed in go.sum, but actually these versions don't show up in go list -m all, hence they shouldn't be really used by the application, but only downloaded during build phase to select the proper minimal version.
Some reference:
https://go.dev/ref/mod#go-mod-tidy
go mod tidy acts as if all build tags are enabled, so it will consider
platform-specific source files and files that require custom build
tags, even if those source files wouldn’t normally be built.
https://go.dev/ref/mod#go-sum-files
The go.sum file may contain hashes for multiple versions of a module.
The go command may need to load go.mod files from multiple versions of
a dependency in order to perform minimal version selection. go.sum may
also contain hashes for module versions that aren’t needed anymore
(for example, after an upgrade).
https://github.com/golang/go/wiki/Modules#is-gosum-a-lock-file-why-does-gosum-include-information-for-module-versions-i-am-no-longer-using
[...]In addition, your module's go.sum records checksums for all
direct and indirect dependencies used in a build (and hence your
go.sum will frequently have more modules listed than your go.mod).
https://github.com/golang/go/wiki/Modules#version-selection
The minimal version selection algorithm is used to select the versions
of all modules used in a build. For each module in a build, the
version selected by minimal version selection is always the
semantically highest of the versions explicitly listed by a require
directive in the main module or one of its dependencies.
As an example, if your module depends on module A which has a
require D v1.0.0, and your module also depends on module B which
has a require D v1.1.1, then minimal version selection would choose
v1.1.1 of D to include in the build (given it is the highest listed
require version). [...] To see a list of the selected module versions
(including indirect dependencies), use go list -m all.
Yes, it correct to say the modules really "used" by the application are listed by go list -m all (as per documentation you provided the link of). By "used", it means the package selected at build time for the compilation of the go code of your application.
We had a similar issue with a static analysis tool and we had to change the configuration to use the output of go list -m all (dumped in a file) instead of go.sum.

Golang Module Use

I am trying to link to some code online: https://github.com/TheCacophonyProject/audiobait
I am helping out with this project and have altered that repository to create a new package called audiofilelibrary.
I am then trying to use that code in a very simple program:
package main
import (
"fmt"
"github.com/TheCacophonyProject/audiobait/audiofilelibrary"
"github.com/TheCacophonyProject/audiobait/playlist"
)
func main() {
audio := audioFileLibrary.AudioFileLibrary{}
fmt.Println(audio.soundsDirectory)
sched := playlist.Schedule{}
fmt.Println(sched.Description)
}
You'll see I'm importing 2 packages from that repository, audiofilelibrary and playlist. playlist works, audiofilelibrary does not. They seem to be coded in the same way.
This is the error I get:
$ go build
go: finding github.com/TheCacophonyProject/audiobait v2.0.0
go: finding github.com/nathan-osman/go-sunrise latest
go: finding github.com/TheCacophonyProject/audiobait/audiofilelibrary latest
build audiobaitpackagetest: cannot load
github.com/TheCacophonyProject/audiobait/audiofilelibrary:
cannot find module providing package
github.com/TheCacophonyProject/audiobait/audiofilelibrary
And this is my go.mod file, which is in a directory called audiobaitpackagetest:
module audiobaitpackagetest
go 1.13
require github.com/TheCacophonyProject/audiobait v0.0.0-20191013210352-81b0afd9a085
I created the module with the command go mod init audiobaitpackagetest.
How can I see the audiofilelibrary package please? As in, how can I import it into other code?
The code for this question is all here, so it can be easily cloned and run: https://github.com/Davo36/audiobaitpackagetest
Any help much appreciated.
You are using version v2.0.0 of github.com/TheCacophonyProject/audiobait.
This version doesn't contain audiofilelibrary package. But the master branch contains it.
You can change your go.mod file like this to use the master branch.
module audiobaitpackagetest
go 1.13
require github.com/TheCacophonyProject/audiobait master
Then run this commands:
go mod tidy
go mod vendor # if you want to vendor it

People can't use my v2+ semantic versioning release using gomodules

I am maintaining a company go library which uses Semantic Versioning for releases. I switched to go modules for dependency management within the library and created a new release after v2+. I used the first strategy documented here which involves modifying the go.mod file and import paths.
Now when people use the go get command (in a repo that is also using go modules) an error appears:
invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v3
Is anyone familiar with this issue?
To make things simpler I created a similar and simple example:
I have repo (github.com/Graphmasters/testing-gomod-versioning) with three files (not including .gitignore). These files are:
methods/method.go
package methods
func Method() {
println("Method")
}
main.go
package main
import (
"github.com/Graphmasters/testing-gomod-versioning/v3/methods"
)
func main() {
methods.Method()
}
go.mod
module github.com/Graphmasters/testing-gomod-versioning/v3
go 1.13
In the repo I created a release with the tag v3.0.0 based on the branch with this code.
The aforementioned error is encountered when the following command is run in a repo that is using go modules:
go get "github.com/Graphmasters/testing-gomod-versioning#v3.0.0"
The module name must be github.com/Graphmasters/testing-gomod-versioning/v3 or github.com/Graphmasters/testing-gomod-versioning.v3 and not github.com/Graphmasters/testing-gomod-versioning
Users should be able to do github.com/Graphmasters/testing-gomod-versioning/v3#v3.0.0 instead of github.com/Graphmasters/testing-gomod-versioning#v3.0.0
Example: You can check the mod file of the following v3 package - https://search.gocenter.io/gotest.tools~2Fv3/info?version=v3.0.0

Can't load package in brand new Go project

I'm trying modules in Go. I'm outside the GOPATH and using version 1.12.6:
GOBIN="/home/x80486/Workshop/go/bin/"
GOPATH="/home/x80486/Workshop/go/"
GOROOT="/home/x80486/.asdf/installs/golang/1.12.6/go/"
I created a new project and initialized it as a module inside: /home/x80486/Workshop/Development/gauge-basics. I then created a file example_spec.go with this content:
package stepImpl
import (
"github.com/getgauge-contrib/gauge-go/gauge"
)
var _ = gauge.Step("Run me before any other", func() {})
...and I ran go test:
[x80486#uplink gauge-basics]$ go test
go: finding github.com/getgauge-contrib/gauge-go/gauge latest
go: finding github.com/getgauge/common latest
go: finding github.com/golang/protobuf/proto latest
go: finding github.com/dmotylev/goproperties latest
? github.com/x80486/gauge-basics [no test files]
Everything is somehow OK, but as soon as I move this file into a folder named stepImpl, nothing works:
[x80486#uplink gauge-basics]$ go build
can't load package: package github.com/x80486/gauge-basics: unknown import path "github.com/x80486/gauge-basics": cannot find module providing package github.com/x80486/gauge-basics
I can't understand why moving a file to a folder with the package name would break the project.
This is the generated go.mod file:
module github.com/x80486/gauge-basics
go 1.12
require (
github.com/dmotylev/goproperties v0.0.0-20140630191356-7cbffbaada47 // indirect
github.com/getgauge-contrib/gauge-go v0.1.3 // indirect
github.com/getgauge/common v0.0.0-20190514095629-619e107433ce // indirect
github.com/golang/protobuf v1.3.2 // indirect
)
There are no .go files under github.com/x80486/gauge-basics, because you moved them under a different directory. You can run go build under the directory containing the source files or add another .go in the gauge-basics directory importing them.

go get/build can't resolve a local-only project package

project.go:6:2: cannot find package "example.com/project/package" in any of:
/usr/local/Cellar/go/1.1.2/libexec/src/pkg/example.com/project/package (from $GOROOT)
/Users/me/go/src/example.com/project/package (from $GOPATH)
Fetching https://example.com/project/package?go-get=1
ignoring https fetch with status code 404 Fetching http://example.com/project/package?go-get=1
Parsing meta tags from http://example.com/project/package?go-get=1 (status code 404)
import "example.com/project/package": parse http://example.com/project/package?go-get=1: no go-import meta tags
package example.com/project/package: unrecognized import path "example.com/project/package"
Why can't go get/build find the local package. I understand go get will fail on my repo because it's bare, but it seems like go get is completely ignoring the local file, forcing me to commit and push my code before I can compile it. This is, per the snippet, OSX 10.8 and Go 1.1.2 installed via brew. GOPATH is set to /Users/me/go and GOROOT is empty.
I should note that I don't have this problem at all when using go get in gitbash on my Windows machine. I've tried all the google-fu I can think of to search this, but everyone claims you can use relative "project/package" imports, which also completely fail in this case.
Upgrading from go1.1.2 to go1.2 through brew upgrade go fixed this problem
You can work with code locally without having pushed it - you just need your folder structure to mimic the import path.
Put your library code inside a src/ directory, with a folder structure mimicking the import path:
[15:42] foa:home $ tree
.
├── myprogram.go
└── src
└── example.com
└── project
└── mypackage
└── mypackage.go
Then set GOPATH to the directory that has src/ in it, and you'll be able to build the project:
[15:42] foa:home $ export GOPATH=`pwd`
[15:42] foa:home $ go build
Here's myprogram.go:
package main
import "example.com/project/mypackage"
func main() {
mypackage.Run()
}
And mypackage.go:
package mypackage
import "fmt"
func Run() {
fmt.Println("It works.")
}
It's common to lay out your working directories like this, and then root the actual git repositories at the lowest level (e.g. in src/example.com/project/mypackage) so that they work cleanly with go get and go install for other users.

Resources