flyx
flyx

Reputation: 39678

How to get the path to a Go module dependency?

I have two Go modules, let's name them example.com/a and example.com/b.

Let this be example.com/a's go.mod:

module example.com/a

go 1.12

require (
  example.com/b v0.4.2
)

In example.com/b's root directory, there is a file named data.yaml. example.com/a needs to autogenerate some code as part of its build process. This autogeneration needs to read data.yaml.

How can I in the directory of example.com/a query for the path of example.com/b to read that file? I know that after downloading, the module will be somewhere in (go env GOPATH)/pkg/mod but I don't know how the path will be constructed from there as it contains some ! characters that are not part of the import path. I hoped that there is some subcommand of go mod or go list that will output the path, but I haven't found it in the documentation.

I have thought about including data.yaml in Go code via go-bindata (yes I'm aware of //go:embed but I don't want to require Go 1.16 for now) but then I would only have access at run-time when I need it at compile-time.

Upvotes: 5

Views: 10630

Answers (2)

mkopriva
mkopriva

Reputation: 38213

You can use go list with the -m flag and the -f flag like so:

go list -m -f '{{.Dir}}' example.com/b

The -m flag:

causes go list to list modules instead of packages. In this mode, the arguments to go list may be modules, module patterns (containing the ... wildcard), version queries, or the special pattern all, which matches all modules in the build list. If no arguments are specified, the main module is listed.

(reference)

The -f flag:

specifies an alternate format for the output, using the syntax of package template. The struct being passed to the template, when using the -m flag, is:

type Module struct {
    Path      string       // module path
    Version   string       // module version
    Versions  []string     // available module versions (with -versions)
    Replace   *Module      // replaced by this module
    Time      *time.Time   // time version was created
    Update    *Module      // available update, if any (with -u)
    Main      bool         // is this the main module?
    Indirect  bool         // is this module only an indirect dependency of main module?
    Dir       string       // directory holding files for this module, if any
    GoMod     string       // path to go.mod file for this module, if any
    GoVersion string       // go version used in module
    Error     *ModuleError // error loading module }

type ModuleError struct {
    Err string // the error itself
}

[the above quote was altered for context]

(reference)

Upvotes: 6

Jakub Dóka
Jakub Dóka

Reputation: 2625

You can figure out the module path like this:

package main

import (
    "fmt"
    "os"
    "path"

    "golang.org/x/mod/module"
)

func GetModulePath(name, version string) (string, error) {
    // first we need GOMODCACHE
    cache, ok := os.LookupEnv("GOMODCACHE")
    if !ok {
        cache = path.Join(os.Getenv("GOPATH"), "pkg", "mod")
    }

    // then we need to escape path
    escapedPath, err := module.EscapePath(name)
    if err != nil {
        return "", err
    }

    // version also
    escapedVersion, err := module.EscapeVersion(version)
    if err != nil {
        return "", err
    }

    return path.Join(cache, escapedPath+"@"+escapedVersion), nil
}

func main() {
    var path, err = GetModulePath("github.com/jakubDoka/mlok", "v0.4.7")
    if err != nil {
        panic(err)
    }

    if _, err := os.Stat(path); os.IsNotExist(err) {
        fmt.Println("you don't have this module/version installed")
    }
    fmt.Println("module found in", path)
}

Upvotes: 3

Related Questions