再探go modules:使用与细节 (2)

当然如果你不想遵循这一规范或者需要兼容现有代码,那么指定+incompatible会是一个合理的选择。不过如其字面意思,go modules不推荐这种行为。

一点思考

眼尖的读者可能已经发现了,semver很眼熟。

是的,REST api是它的最忠实用户,像xxx.com/api/v2/xxx的最佳实践我们恐怕都司空见惯了,所以golang才会要求v2+的包使用pkg/v2的形式。然而把REST api的最佳实践融合进包管理器设计,真的会是又一个最佳实践吗?

我觉得未必如此,一个显而易见的缺点就在于向后兼容上,主流的包管理器都只采用semver的子集,最大的原因在于如果只提供对版本的控制,而把先后兼容的责任交由开发者/用户相对于强行将无关的信息附加在包名上来说可能会造成一定的迷惑,但是这种做法可以最大限度的兼容现有代码,而golang则需要修改mod文件,修改引入路径,分散的修改往往导致潜在的缺陷,考虑到现有的golang生态这一做法显得不那么明智。同时将版本信息绑定进包名对于习惯了传统包管理器方案的用户(npm,pip)来说显得有些怪异,可能需要花上一些额外时间适应。

不过检验真理的标准永远都是实践,随着go1.12的发布我们最终会见分晓,对于go modules现在是给予耐心提出建议的阶段,评判还为时尚早。

replace的限制

go mod edit -replace无疑是一个十分强大的命令,但强大的同时它的限制也非常多。

本部分你将看到两个例子,它们分别阐述了本地包替换的方法以及顶层依赖与间接依赖的区别,现在让我们进入第一个例子。

本地包替换

replace除了可以将远程的包进行替换外,还可以将本地存在的modules替换成任意指定的名字。

假设我们有如下的项目:

tree my-mod my-mod ├── go.mod ├── main.go └── pkg ├── go.mod └── pkg.go

其中main.go负责调用my/example/pkg中的Hello函数打印一句“Hello”,my/example/pkg显然是个不存在的包,我们将用本地目录的pkg包替换它,这是main.go:

package main import "my/example/pkg" func main() { pkg.Hello() }

我们的pkg.go相对来说很简单:

package pkg import "fmt" func Hello() { fmt.Println("Hello") }

重点在于go.mod文件,虽然不推荐直接编辑mod文件,但在这个例子中与使用go mod edit的效果几乎没有区别,所以你可以尝试自己动手修改my-mod/go.mod:

module my-mod require my/example/pkg v0.0.0 replace my/example/pkg => ./pkg

至于pkg/go.mod,使用go mod init生成后不用做任何修改,它只是让我们的pkg成为一个module,因为replace的源和目标都只能是go modules。

因为被replace的包首先需要被require(wiki说本地替换不用指定,然而我试了报错),所以在my-mod/go.mod中我们需要先指定依赖的包,即使它并不存在。对于一个会被replace的包,如果是用本地的module进行替换,那么可以指定版本为v0.0.0(对于没有使用版本控制的包只能指定这个版本),否则应该和替换包的指定版本一致。

再看replace my/example/pkg => ./pkg这句,与替换远程包时一样,只是将替换用的包名改为了本地module所在的绝对或相对路径。

一切准备就绪,我们运行go build,然后项目目录会变成这样:

tree my-mod my-mod ├── go.mod ├── main.go ├── my-mod └── pkg ├── go.mod └── pkg.go

那个叫my-mod的文件就是编译好的程序,我们运行它:

./my-mod Hello

运行成功,my/example/pkg已经替换成了本地的pkg。

同时我们注意到,使用本地包进行替换时并不会生成go.sum所需的信息,所以go.sum文件也没有生成。

本地替换的价值在于它提供了一种使自动生成的代码进入go modules系统的途径,毕竟不管是go tools还是rpc工具,这些自动生成代码也是项目的一部分,如果不能纳入包管理器的管理范围想必会带来很大的麻烦。

顶层依赖与间接依赖

如果你因为golang.org/x/...无法获取而使用replace进行替换,那么你肯定遇到过问题。明明已经replace的包为何还会去未替换的地址进行搜索和下载?

解释这个问题前先看一个go.mod的例子,这个项目使用的第三方模块使用了golang.org/x/...的包,但项目中没有直接引用它们:

module schanclient require ( github.com/PuerkitoBio/goquery v1.4.1 github.com/andybalholm/cascadia v1.0.0 // indirect github.com/chromedp/chromedp v0.1.2 golang.org/x/net v0.0.0-20180824152047-4bcd98cce591 // indirect )

注意github.com/andybalholm/cascadia v1.0.0和golang.org/x/net v0.0.0-20180824152047-4bcd98cce591后面的// indirect,它表示这是一个间接依赖。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyjydj.html