微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在继续使用分发包的同时迁移到 Go 模块

如何解决在继续使用分发包的同时迁移到 Go 模块

我目前在 Fedora 上构建了一个基于 GOPATH 的项目:

sudo dnf install golang-etcd-bbolt-devel golang-x-sys-devel golang-x-text-devel
GOPATH=$HOME/go:/usr/share/gocode go build

我的项目 (gonzofilter) 实现了一个命令行实用程序,因此源文件位于主包中(即它们具有 package main 声明)。

在 Fedora 34 及更高版本中,Go 似乎取消了对构建 GOPATH 风格项目的支持,并且确实必须使用 Go 模块:

go build
go: cannot find main module; see 'go help modules'

那个 Go blog post 涵盖了我的情况(-> '没有依赖管理器'),但它没有明确提到如何处理主包项目或分发提供的依赖项。

那么,我该如何迁移这样的项目?

如何告诉 Go/go mod tidy/usr/share/gocode 下查找我的依赖项?


编辑:准确地说:Fedora 34 随附于 Go 1.16,它“只是”将 GO111MODULE 认值从 auto 更改为 on。因此,仍然可以通过设置 GO111MODULE=auto 来恢复旧的行为。

然而,Golang 开发人员已经announced表示他们希望在 Go 1.17 中放弃对 GOPATH 风格项目的支持

我们计划在 Go 1.17 中放弃对 GOPATH 模式的支持。换句话说,Go 1.17 将忽略 GO111MODULE。如果您的项目不是在模块感知模式下构建的,那么现在是迁移的时候了。

解决方法

您可以在生成的 mod 文件中明确定义使用 replace 关键字来引用您的本地模块。

replace packagename => /usr/share/gcode

,

将这样的 GOPATH 项目迁移到 Go 模块感知的一种方法如下:

首先,使用直接依赖项手动创建 go.mod

module github.com/gsauthof/gonzofilter

go 1.15

require (
    go.etcd.io/bbolt  v1.3.5
    golang.org/x/sys  v0.0.0-20210423185535-09eb48e85fd7
    golang.org/x/text v0.3.5
)

我从我系统上的当前版本中获得了最小版本:

$ rpm -q golang-etcd-bbolt-devel golang-x-sys-devel golang-x-text-devel golang-bin
golang-etcd-bbolt-devel-1.3.5-2.fc33.noarch
golang-x-sys-devel-0-0.39.20210123git9b0068b.fc33.noarch
golang-x-text-devel-0.3.5-1.fc33.noarch
golang-bin-1.15.8-1.fc33.x86_64

现在的问题是不能简单地告诉模块感知 Go 工具在文件系统路径 /usr/share/gocode/src 下查找这些模块,它们都位于。

可以将 replace 指令添加到我们的 go.mod 文件中,以设置单个依赖项的查找路径,例如像这样:

replace (
    go.etcd.io/bbolt  => /usr/share/gocode/src/go.etcd.io/bbolt
    golang.org/x/sys  => /usr/share/gocode/src/golang.org/x/sys
    golang.org/x/text => /usr/share/gocode/src/golang.org/x/text
)

但是,go build 仍然不使用来自 /usr/share/gocode间接依赖项,例如由包 golang-x-text-devel 提供的依赖。在此示例中,go build 确实找到了 /usr/share/gocode/src/golang.org/x/text/go.mod,但该文件不包含任何 replace 指令。

因此,这失败了:

$ GOPROXY=off go build -mod=readonly
go: golang.org/x/text@v0.3.6 requires
    golang.org/x/tools@v0.0.0-20180917221912-90fa682c2a6e: module lookup disabled
       by GOPROXY=off

为了解决这个问题,我们还必须为所有间接依赖添加替换指令行。

当然,手动执行此操作既乏味又容易出错。

因此,我们可以使用一个小的 shell one-liner 来自动执行此操作:

我们从这个 go.mod 文件开始:

module github.com/gsauthof/gonzofilter

go 1.15

require (
    go.etcd.io/bbolt v1.3.5
    golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7
    golang.org/x/text v0.3.6
)

replace (
//replace-this
)

以下固定点迭代然后添加依赖项,直到构建成功:

while true; do
    GOPROXY=off go build -mod readonly 2> t
    if [ $? -eq 1 ] && grep 'module lookup disabled' t >/dev/null; then
        x=$(grep disabled t |  tr ' \t' '\n'   | grep '@' | cut -d'@' -f1 )
        echo "Adding $x"
        sed -i "s@^\(//replace-this\)@\t$x => /usr/share/gocode/src/$x\n\1@" go.mod
        continue
    fi
    break
done

这会导致此示例项目的以下 require 指令:

replace (
    go.etcd.io/bbolt => /usr/share/gocode/src/go.etcd.io/bbolt
    golang.org/x/sys => /usr/share/gocode/src/golang.org/x/sys
    golang.org/x/text => /usr/share/gocode/src/golang.org/x/text
    golang.org/x/tools => /usr/share/gocode/src/golang.org/x/tools
    github.com/yuin/goldmark => /usr/share/gocode/src/github.com/yuin/goldmark
    golang.org/x/mod => /usr/share/gocode/src/golang.org/x/mod
    golang.org/x/net => /usr/share/gocode/src/golang.org/x/net
    golang.org/x/sync => /usr/share/gocode/src/golang.org/x/sync
    golang.org/x/xerrors => /usr/share/gocode/src/golang.org/x/xerrors
    golang.org/x/crypto => /usr/share/gocode/src/golang.org/x/crypto
    golang.org/x/term => /usr/share/gocode/src/golang.org/x/term
//replace-this
)
,

理想情况下,发行版应在与 GOPROXY protocol 兼容的布局中打包已安装的依赖项。然后您就可以适当地设置 GOPROXY 并运行 go mod tidy 以使用已安装的依赖项。

然而,据我所知,目前没有任何发行版实际提供 GOPROXY 树。您可能需要一种解决方法。


下一个最佳选择是使用 replace 指令自行连接替换。 go mod tidy 已经知道比其他版本更喜欢 replace 指令中的版本,因此执行以下操作就足够了:

go mod init github.com/gsauthof/gonzofilter

go mod edit -replace go.etcd.io/bbolt=/usr/share/gocode/src/go.etcd.io/bbolt
go mod edit -replace golang.org/x/sys=/usr/share/gocode/src/golang.org/x/sys
go mod edit -replace golang.org/x/text=/usr/share/gocode/src/golang.org/x/text

go mod tidy

但是,请注意 replace 指令要求目标包含显式 go.mod 文件,因此这仅在您的依赖项已经具有显式 go.mod 文件或您的发行版打包程序时有效已将它们添加为补丁以支持此用例。


请注意,使用这些 replace 指令后,随着时间的推移,您的构建将不会是 reproduciblerepeatable:如果您更改了依赖项的发行版安装版本,则含义的 go build 将无声地更改为使用这些不同(并且可能不兼容!)的版本。

因此我建议您不要这样做,而是使用 go get 和/或 go mod tidy 从上游或公共模块代理(例如 {{ 1}}).

如果您担心上游篡改,请注意 Go 项目的 proxy.golang.org 命令的 official binary distribution - 以及从原始源构建的 go 命令 - 默认情况下会自动验证下载的校验和针对 https://sum.golang.org 上的可审计公共数据库的模块;但是,go 命令 disables use of the checksum database by default 的 Fedora 发行版。

,

另一种将 Go 指向系统范围可用依赖项的方法是利用 Go 的模块供应商支持:这意味着通过符号链接模块文件路径,创建最小的 vendor/modules.txt 并使用 -mod vendor 进行编译。

因此,对于 -mod vendor,Go build 不会尝试下载任何必需的模块,而是在 vendor/ 目录中查找它们。

人们可能只想创建一个符号链接,该链接从 vendor/ 指向 /usr/share/gocode/src(其中安装了所有发行版 golang-...-devel 软件包),但这不起作用,因为 { {1}} 还需要另一个模块描述文件,即 -mod vendor

因此,除了一些更具体的符号链接之外,我们还必须在使用 vendoring 功能时创建适当的 vendor/modules.txt

首先,创建所有子级符号链接:

modules.txt

然后,创建 awk -vvd=wendor 'BEGIN { PROCINFO["sorted_in"]="@ind_str_asc"; system("mkdir -p "vd) } func push_mod(pkg) { if (!seen[pkg]) { ARGV[ARGC++]="/usr/share/gocode/src/"pkg"/go.mod"; seen[pkg]=1; } sub("/.+$","",pkg); xs[pkg]=1; } /^require[^(]+$/ { push_mod($2); next } /^require/ { inb=1; next } /^\)/ { inb=0; next } inb { push_mod($1); } END { for (i in xs) { print i; system("ln -sfn /usr/share/gocode/src/"i" "vd"/"i) } }' go.mod

vendor/modules.txt

执行这些命令后,我们示例项目的 vendor 目录如下所示:

awk -vvd=wendor 'BEGIN {
    fn=vd"/modules.txt"
}
/^require/ {
    inb=1;
    next
}
/^\)/ {
    inb=0;
    next
}
inb {
    printf("# %s %s\n## explicit\n%s\n",$1,$2,$1) > fn 
}' go.mod

$ ls -l vendor total 16 lrwxrwxrwx. 1 juser juser 32 2021-04-25 11:12 github.com -> /usr/share/gocode/src/github.com lrwxrwxrwx. 1 juser juser 32 2021-04-25 11:12 go.etcd.io -> /usr/share/gocode/src/go.etcd.io lrwxrwxrwx. 1 juser juser 32 2021-04-25 11:12 golang.org -> /usr/share/gocode/src/golang.org -rw-r--r--. 1 juser juser 195 2021-04-25 11:13 modules.txt $ cat vendor/modules.txt # go.etcd.io/bbolt v1.3.5 ## explicit go.etcd.io/bbolt # golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 ## explicit golang.org/x/sys # golang.org/x/text v0.3.5 ## explicit golang.org/x/text 非常小,即它不包含任何替换指令:

go.mod

这足以让

module github.com/gsauthof/gonzofilter

go 1.15

require (
    go.etcd.io/bbolt  v1.3.5
    golang.org/x/sys  v0.0.0-20210423185535-09eb48e85fd7
    golang.org/x/text v0.3.5
)

成功。

请注意,GOPROXY=off go build -mod vendor 等间接依赖项并未在 golang.org/x/tools 中列出,但是,它们仍可通过 modules.txt 获得,因为我们传递地遍历了所有 vendor/ 文件在创建指向顶级目录的符号链接时使用所需的模块。

这种方案的独特魅力在于,您不必弄乱 go.mod 文件中的 replace 指令。这意味着 go.mod 可以保持非常通用,基本上只需将 go.mod 开关添加到构建命令。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。