【问题标题】:How to get the path to a Go module dependency?如何获取 Go 模块依赖项的路径?
【发布时间】:2021-07-16 14:16:10
【问题描述】:

我有两个 Go 模块,我们将它们命名为 example.com/aexample.com/b

假设这是example.com/ago.mod

module example.com/a

go 1.12

require (
  example.com/b v0.4.2
)

example.com/b的根目录下,有一个名为data.yaml的文件。 example.com/a 需要在构建过程中自动生成一些代码。这个自动生成需要读取data.yaml

如何在example.com/a的目录下查询example.com/b的路径来读取那个文件?我知道下载后,模块将位于(go env GOPATH)/pkg/mod 中的somewhere,但我不知道如何从那里构造路径,因为它包含一些不属于导入路径。我希望有一些go modgo list的子命令会输出路径,但我在文档中没有找到。

我曾考虑通过go-bindatadata.yaml 包含在Go 代码中(是的,我知道//go:embed,但我现在不想要求Go 1.16)但是我只能在运行时访问-我在编译时需要它的时间。

【问题讨论】:

  • 不确定您是否考虑过此选项,但是您可以让example.com/b 导出一个函数,该函数使用runtime.Caller 来确定其文件的位置,并使用该函数构造 yaml 文件的绝对路径并返回它。然后example.com/a 可以调用此函数来获取该位置。我自己确实使用这种方法,其中a 导入ba 调用b 的函数,该函数从其自己模块的文件夹层次结构中读取css/js/html 文件,然后将这些文件的副本写入@ 987654347@.
  • @mkopriva 有趣的想法,但仍然是运行时解决方案。代码生成器在我编译并链接到 example.com/b 之前运行,所以我不能从那里调用函数。
  • “但我不知道如何从那里构造路径,因为它包含一些不属于导入路径的 ! 字符。” -- 请参阅表格正上方的段落here“为避免在从不区分大小写的文件系统中提供服务时出现歧义,$module 和 $version 元素通过将每个大写字母替换为感叹号后跟相应的小写字母进行大小写编码-case letter。这允许模块 example.com/M 和 example.com/m 都存储在磁盘上,因为前者被编码为 example.com/!m。"
  • 在我的系统上将包 github.com/BurntSushi/toml 目录位置转换为 /Users/mkopriva/Work/go/pkg/mod/github.com/!burnt!sushi/toml@v0.3.1 -- 所以也许你可以使用该规则来确保在正确的位置找到包。
  • 另外,如果你的生成器是用 Go 编写的,并且你可以为此目的修改它,你可以使用包 golang.org/x/tools/go/packages 来加载一个包带有packages.NeedModule 标志,它将在包的模块上加载信息,包括它的目录​​,如果有的话。

标签: go code-generation go-modules


【解决方案1】:

您可以将go list-m 标志和-f 标志一起使用,如下所示:

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

-m 标志:

导致 go list 列出模块而不是包。在这种模式下, go list 的参数可能是模块、模块模式(包含 ... 通配符)、版本查询或特殊模式 all,其中 匹配构建列表中的所有模块。如果没有指定参数, 列出了主模块。

(reference)

-f 标志:

指定输出的替代格式,使用语法 包模板。使用时传递给模板的结构 -m 标志是:

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
}

[以上引用因上下文而有所改变]

(reference)

【讨论】:

  • 虽然packages.NeedModule 解决方案也是可行的,但这肯定是更简单的解决方案。谢谢!
【解决方案2】:

你可以这样算出模块路径:

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)
}

【讨论】:

  • 很高兴知道用于创建路径的转义例程以这种方式公开,感谢您的展示。我将使用 mkopriva 的解决方案,因为它会自动从 go.mod 获取版本号,我需要在这里手动完成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-14
  • 1970-01-01
相关资源
最近更新 更多