Skip to content

Go List Command and x/mod Package

This blog try to introduce the go list command and brief a refinement at go1.16. Moreover, we will explore the differences between go list -m all and go list all, and answer question why some of the module from go list -m all cannot be found by go mod why.

Finally, we will see the x/mod package and when it's useful.

Usage of 'go list'

The go list is used to list all dependencies of a module, no matter direct or indirect. Because of go list retrieves all dependencies of the current module, it requires to download the packages from the internet and the go.mod is tidy.

For example, given the go.mod below, we can use go list to see the module information:

module github.com/xieyuschen/example

go 1.17

require (
    github.com/gin-gonic/gin v1.10.0
)

  • get the module only:
$ go version
go version go1.22.2 darwin/arm64
$ go list -m
github.com/xieyuschen/example
  • get all used module:

    $ go list -m all
    github.com/xieyuschen/example
    github.com/bytedance/sonic v1.11.6
    github.com/bytedance/sonic/loader v0.1.1
    github.com/cloudwego/base64x v0.1.4
    ... ignore many lines...
    nullprogram.com/x/optparse v1.0.0
    rsc.io/pdf v0.1.1
    

  • get all direct dependencies:

    $ go list -m \
      -f '{{if and (not .Indirect)}}{{.Path}} {{.Version}}{{end}}' all
    github.com/xieyuschen/example
    github.com/gin-gonic/gin v1.10.0
    

Differences Between 'go list' and 'go mod why'

Not all modules output by go list -m all could be found via go mod why -m, as the go issue states as well.

$ go mod why -m nullprogram.com/x/optparse
# nullprogram.com/x/optparse
(main module does not need module nullprogram.com/x/optparse)

The module reporting is inconsistency, go list -m all lists all modules implied by the requirements of the main module, while go mod why focuses on the actual packages used in a build.

Because the go mod why actually uses the go list all to filter the result, here we actually are discussing the -m flag of go list.

The -m flag causes list to list modules instead of packages.

The go list -m all includes all modules that are dependencies of the main module, even if it's not used by the main module. This is case usually happened at the module is contained by another module or it's only used in specific build constraint.

Another big difference is that the go list all doesn't contain the version of package, because go module version is based on the module concept.

How 'go list' Got Refined Before

In 2018, one of user complained the side effects caused by go list. In short, he complained:

  • download the imported libraries from internet
  • build CGO packages
  • go.mod is modified

The first and second items here are kept now, the network is needed to run go list because it validates the go.mod and go.sum. Regarding on the third item, rsc has raised a refinement proposal in 2020 during the development of go1.16 to report error rather than modifying go.mod.

I suspect there are many more of these. In general, while the automatic additions to go.mod seemed good on paper, my impression is that they haven't delivered the vision we wanted: they do the wrong thing too often, they slow down the compile-edit-debug cycle for typos, and they are confusing.

After that, most of the go commands use default -mod=readonly. Hence, if you modify your go.mod without running go mod tidy, the go list will yield an error in go1.16.

$ go version       
go version go1.17 darwin/amd64
$ go list -m all
go: github/xieyuschen/exmaple@v0.0.1: 
  missing go.sum entry for go.mod file; to add it:
        go mod download github/xieyuschen/exmaple

Module x/mod

The x/mod module literally provides functionalities to parse and process the go.mod file.

It helps to parse the mod file, extract the dependencies of the projects, and manipulate them.

In practice, when we split the implementations and interfaces of code into different components, during build, we will use CLI tool to combine the implementation code, user's code(import interface) and the generated code together and then trigger the go build.

In this scenario, it's important to ensure the interfaces and implementations are compatible. As a result, we need to scan the go.mod to validate the compatibility. At this stage, we can consider to use the x/mod instead of go list because go list requires the go mod tidy to be run first, which is time-consuming. We should report error as soon as possible.

In contrary, because it doesn't run go mod tidy first, users might manually edit the go.mod without go mod tidy. This could trespass the validation but doesn't make sense, so we don't handle this case.