How do you handle vendoring in libraries?

polaris · · 545 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Hi,</p> <p>As the subject says, how do you handle vendoring in libraries? Suppose I&#39;m making a package that&#39;s not an application, but does reference some other packages not part of the standard library. I want to vendor those so that my tests are more reliably repeatable (and able to be put in a container easily). But then, ultimately, this library itself is going to be included in another project, which means:</p> <p>vendor/library/one/vendor/library/two</p> <p>From a brief test, it will compile if library/two is referred to by library/one. But if the main application tries to also import library/two (which is in vendor/library/one/vendor/library/two), it won&#39;t be found unless library/two is also located in just vendor/library/two.</p> <p>What&#39;s the ideal way to use library/two directly without having it vendored twice? Perhaps a symlink from vendor/library/one/vendor/library/two to vendor/library/two?</p> <p>Thanks</p> <hr/>**评论:**<br/><br/>shovelpost: <pre><p>The ideal way is to get rid of unnecessary dependencies. I&#39;d be really interested to know which exact libraries are you trying to vendor that are causing this problem. It doesn&#39;t come very often in practice. Speaking theoretically will only cause the usual go vendor drama discussion while the solution is usually much simpler.</p></pre>Winsaucerer: <pre><p>I&#39;m not really sure what you mean by unnecessary dependencies. Here&#39;s a quick example that&#39;s a little more concrete, but still somewhat theoretical. library/one uses UUID&#39;s for something, so makes use of library/uuid to generate those.</p> <p>The core application, for completely independent reasons, also needs to produce uuid&#39;s, and uses library/uuid as well. The &#39;problem&#39; is how to avoid having library/uuid located in both vendor/library/uuid and vendor/library/one/vendor/library/uuid.</p> <p>It doesn&#39;t seem to me that this is going to be something all that uncommon, and that can be reasonably put down to poor design choices.</p></pre>christopherhesse: <pre><p>Ideally, since their use is &#34;completely independent&#34;, the core application should never know that library/one uses library/uuid at all. library/one should not leak any references to library/uuid in its public API and you can safely have two copies of uuid.</p> <p>Imagine if you did have one copy of uuid. Now you have coupling between the dependencies of your core application and the dependencies of library/one. If library/one requires a specific version of uuid or makes some incompatible changes to the library, your core application may no longer work.</p></pre>Winsaucerer: <pre><p>Yep, that might be a reasonable attitude/approach.</p></pre>bmurphy1976: <pre><p>Also, if both versions are the same, a smart enough linker should be able to remove the duplication. I don&#39;t know if go is smart enough today.</p></pre>Winsaucerer: <pre><p>One problem I&#39;m running into now: <a href="https://github.com/golang/go/issues/12432" rel="nofollow">https://github.com/golang/go/issues/12432</a></p> <p>I&#39;m thinking it&#39;s better to avoid vendoring in libraries entirely, and I&#39;ll set up a docker container that comes with all the packages ready. That way, I can easily run tests in a container, but without having to do any vendoring inside the library.</p></pre>joushou: <pre><p>Your specific problem here can seemingly be solved with an interface local to main.go: </p> <pre><code>type Test struct { name string } func (t *Test) Name() string { return t.name } func (t *Test) SetName(s string) { t.name = s } </code></pre> <p>...</p> <pre><code>type wee interface{ Name() string } func main() { var t1, t2 wee t1 = a.Test() t2 = b.Test() } </code></pre> <p>Vendoring is not a Go problem. Everyone has this problem, and the solutions usually boil down to &#34;lock people into using specific version of common libs&#34; or &#34;have all consumers use their own version&#34;. There does not exist a good solution to the problem, but the Go vendoring solution is similar to the NPM solution for the same problem.</p></pre>christopherhesse: <pre><p>I totally agree with your point about vendoring. NPM has been doing recursive dependency downloading for years and as long as the libraries support it, it works fine (except how you get 16 levels deep dependency trees because you made it too easy).</p> <p>For Go I think that means at least: no global registration in library code, and no returning other (non stdlib) libraries&#39; objects. So the interface thing you did above should work fine. But it might be even better if library B returned an interface rather than *a.T, or if library B returned it&#39;s own *b.T that it controls, rather than making your program rely on its version of library A.</p></pre>joushou: <pre><p>But there&#39;s a trick to the interfaces - if you return on interface, you&#39;re returning a specific type form that library. It is not equal to an otherwise identical interface from another library. Furthermore, if local types are used in the definition of the interface, you cannot assert it to the other interface, as the fully qualified types won&#39;t match.</p> <p>In other words, the problem is that you&#39;re returning a type, and in this case it can only be fixed locally by the &#34;confused&#34; party (your application interacting with the type problem here). Returning interfaces would only make things more confusing (&#34;Why are they asserting this to an identical interface?!&#34;).</p> <p>Translating to a local type is a solution, but not a pretty one. However, you can fix it by <em>not</em> having libraries vendor other libraries, and rather require that the main project does all the vendoring. Figuring out what a library depends on is as simple as trying to compile it without having used go get to get everything. It works out rather well for us.</p></pre>shovelpost: <pre><p>In this case you could make a small abstraction in your library &#34;one&#34; and make the uuid &#34;pluggable&#34;. That way, the main application will use the uuid library the way it needs and it will also &#34;plug&#34; that same library in your own library (&#34;one&#34;) that also needs the uuid library.</p></pre>[deleted]: <pre><pre><code>mv vendor/library/one/vendor/library/two vendor/library/two </code></pre></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

545 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传