Consider two modules, one of which is nested in the other:
modcache/pkg/mod/foo.com
├── outer
│ └── inner@v1.0.0
│ ├── go.mod
│ └── p2
│ └── y.go
└── outer@v1.0.0
├── go.mod
└── inner
└── p
└── x.go
Module foo.com/outer/inner
contains p2
, which would be imported as foo.com/outer/inner/p2
. Module foo.com/outer
contains inner/p
, which would be imported as foo.com/outer/inner/p
.
I think most people would find this surprising. I certainly did. I had thought that the presence of a nested module "punched a hole" in the containing module, but I guess that's only true if the go.mod
file is actually present in the source tree of the containing module? So if the two modules are developed on separate branches, as would have happened here, the hole-punching behavior doesn't happen?
Is this a bug, or expected behavior?
@bcmills, @jayconrod
7条答案
按热度按时间5n0oy7gb1#
CC @thepudds@myitcv
whlutmcx2#
我认为这种行为太令人困惑了。在一个构建列表中的每个模块都是包的命名空间的世界里,我会更快乐。你只需要找到最长的匹配前缀,就能知道一个包属于哪个模块。
这也有一个I/O成本。为了确定一个模块是否包含一个包,我们需要列出目录,然后解析并应用构建标签以确认该包是可构建的。我希望在1.13版本中通过增加缓存来降低这个成本,但仍然如此。
guicsvcw3#
“最长匹配前缀”似乎不是一个合适的解决方案。这将使得重新组合已经被拆分的模块变得不可能。(重新组合是很重要的:如果没有它,每次你对两个包之间的内部交互进行更改时,你必须记住标记并更新所有涉及模块的需求。)
I/O成本不应该非常高。我认为我们不需要解析或应用构建标签:如果在两个不同模块中存在相同的导入路径下的Go源文件,即使其中一个变体碰巧在当前配置下无法构建,发出一个模糊的导入错误也是可以接受的。
voase2hg4#
嗯,重组确实很重要。
假设我们有一个更简单的“最长前缀”模块解析,而且我们不会因为模糊的导入而发出错误。如果我导入
foo.com/outer/inner/p
,只要我在go.mod
中就有这个,那么它肯定会被解析为foo.com/outer/inner
。为了重新组合这些模块,foo.com/outer
的维护者需要从foo.com/outer/inner
复制所有内容,发布一个新版本,然后告诉用户停止要求foo.com/outer/inner
。这种方法的缺点是用户进行增量迁移很困难。上面描述的增量迁移方法也相当困难。为了将一个包从模块
foo.com/outer/inner
推广到foo.com/outer
,维护者需要为两者发布新标签,用户需要更新这两个模块以避免模糊导入错误。看起来像是从foo.com/outer/inner
中移除一个包的一个破坏性更改,但如果这两个模块在较新的版本中相互依赖,我认为实际上没有什么会破坏。有没有更好的方法?
无论如何,+1 支持Hana的建议,将类似“如何重新组合模块”的内容记录在“最佳实践”文档中。
a9wyjsp75#
为了将模块
foo.com/outer/inner
升级到foo.com/outer
,维护者需要为两者发布新标签,是的!(但是,重要的是,他们只需要这样做一次,而不是每次更改包后都需要这样做。)
用户需要更新这两个模块以避免模糊的导入错误。
这也是正确的。另请参阅 #27899 。
von4xj4u6#
这是按照设计工作的。考虑一下如果你有一个稳定的
outer
模块和一个不稳定的inner
模块,并且想要将outer/inner/p
提升为稳定状态会发生什么。实现这个目标的一种方法是创建三个模块:
outer@v1.X.0
outer/inner@v0.Y.0
outer/inner/p@v1.0.0
当你在这些模块之间来回移动包时,最终会为每个目录创建一个模块——这对于维护者来说不是一个很好的体验。
相反,使用这种交错方式,你可以有:
outer@v1.X.0
outer/inner@v0.Y.0
然后只需将
outer/inner/p
从v0
模块移动到v1
模块中。我预计这在
inner
(例如)是一个从internal
中提取出来的outer
包因子,以便outer
和inner
可以共享实现时特别有用。xienkqul7#
这值得在模块wiki或未来的模块最佳实践wiki中提及。