为什么比库文件夹更喜欢包管理器?


68

当我考虑静态库文件夹和程序包管理器的优缺点时,我觉得库文件夹是一种更好的方法。

我看到的带有库文件夹的优点:

  1. 无需外部工具即可管理软件包。
  2. 无需互联网连接即可构建。
  3. 更快的构建(无需软件包检查)。
  4. 更简单的环境(所需知识更少)。

我在套件管理员中看到的优点:

  1. 帮助处理复杂的依赖关系树(可以通过下载依赖关系及其所有​​依赖关系进行管理)。
  2. 帮助检查是否有可用的新版本。

业界似乎已经决定对当今构建的几乎所有产品都遵循包管理器的路径。那么,我想念什么?


33
我认为您真的是在问捆绑需要库的好处。如果使用这些术语,您将获得更多相关的答案。
凯莉安·佛斯

3
是什么让您无法为构建代理创建docker容器,该容器包含所需的所有内容,甚至可能根本不允许Internet访问(构建时不需要)?我认为与实际考虑参数相比,您更反对学习新工具。您是否曾经从事过使用手工管理的依赖项的大型项目?它并不像您看起来的那么好。
Kayaman

3
@Kayaman:学习新工具会花费团队的时间(金钱),我想证明我们在正确的事情上进行了投资。我在大型项目中的经验是,依赖项相当稳定(它们几乎从未改变),这也许就是为什么包管理器对我来说很昂贵的原因。无论如何,我在与Nuget合作一段时间后花了一些时间,只是列出了赞成和反对的内容。
Ignacio Soler Garcia

2
@sebkur您可以保留所有软件包的本地副本。我们将所有依赖项的当前版本置于本地源代码控制下。包管理器唯一需要连接的时间是我们进行升级时。
18年

1
@ 17of26:您不会学习如何配置构建代理以安装nuget并在10分钟内按要求运行它。如果您有一个多解决方案项目,而在不同的解决方案中使用了相同的项目,那么您都不会这样做。
Ignacio Soler Garcia

Answers:


122

其他答案缺少的重要一点:

使用软件包管理器意味着具有一个配置,配置指示您正在使用的库版本,并确保配置信息实际上是正确的。

如果您了解以下情况,那么了解使用的库和版本是非常重要的:

  • 由于严重的错误/安全漏洞,需要更新库;
  • 或只需要检查已宣布的安全漏洞是否对您有影响。

另外,当您实际进行更新时,程序包管理器(通常)确保按需更新所有传递依赖项。

而使用lib文件夹,您只有一堆(可能是二进制文件,并且可能已修改)文件,并且您必须猜测它们来自何处以及它们是什么版本(或信任某些自述文件,该文件可能正确也可能不正确) )。


解决您的其他问题:

无需外部工具即可管理软件包。

没错,但是a)作为软件开发人员,无论如何,您都需要安装大量工具,因此通常并不重要; b)在任何给定的领域(Maven / Gradle for Java, npm for JS / TypeScript等),因此您不需要安装数十个。

无需互联网连接即可构建。

我知道的所有软件包管理器都必须在下载所需的依赖项后脱机工作(下载项目本身后立即发生)。

更快的构建(无需软件包检查)。

可能是正确的,但脱机软件包检查似乎不太可能花费大量时间(它只是比较一些版本号)。一个在线检查可能需要一段时间,但如果需要的话,可以关闭(如果它甚至在默认情况下-的Maven例如永不检查更新的发行版本)。

更简单的环境(所需知识更少)。

是的,但是如上所述,lib文件夹也需要知识。而且,如上所述,您可能只会使用少数几个不同的软件包管理器,您已经知道了。


170
“不需要外部工具来管理软件包” —是的。现在,这是您的大脑工作。祝你好运,大脑!
Paul D. Waite,

57
认真认为拥有lib文件夹是“更轻松的环境”的任何人都应该继续尝试尝试找出Microsoft.AspNetCore.All nuget这样的依赖树。继续,不要等我,我会在大约一天后再检查一次。
Voo

13
另外,您用于手动查找库的Internet浏览器将被视为管理程序包的外部工具。连同您使用的所有其他内容(OS文件管理器等)一起准备和放置库。因此,真正的选择是一个外部工具(程序包管理器)与许多外部工具。
NemesisX00

3
仅供参考,我最近尝试在离线PC上使用gradle。运气不好,Android Studio无法运行我的应用程序,并且收到模糊的错误消息,那是在我针对依赖项进行了首次在线运行之后。只有在这种情况下,您才真正注意到依赖软件包管理器来创建软件的方式变得如此……
FRob

7
@ PaulD.Waite在讨论时,我们摆脱了每个人都在谈论的那些讨厌的“语言”。无论如何,这最终都归结为机器代码,所以在我公司,我们裁掉了中间人。现在就是效率!
corsiKa

39

从小规模开发转向大型工作后,lib文件夹的优点迅速消失。

例如,不需要外部工具的“好处”被手动管理依赖项所需的工作所压倒,因此该工具将成为您(从多个角度而言)。

包管理器不需要Internet连接。您可以使用本地存储库。

更快的构建可能是正确的,但几乎不应该决定是否使用软件包管理器。我们毕竟不是在谈论差异的大小,这也取决于您的配置。您可以使用软件包管理器轻松进行缓慢的构建,但这基本上是破坏活动。

更简单的环境(需要更少的知识)?同样,在小规模开发中绝对有可能。您可能可以完全掌握项目,直到所使用的几个库中的每一个。添加一个简单的makefile /其他构建脚本,您将获得一个完整的软件包。

但这并不能使环境更简单,它只能在简单的环境中工作。在大规模开发中,您会很高兴使用标准工具而不是自定义解决方案。毕竟,您只需要学习一次(当打包管理器du jour被新的好东西所取代时,您也必须学习它)。


21
@IgnacioSolerGarcia:这只有在没有任何问题的情况下才容易。如果新版本的库A也需要更新的B和C,该怎么办?如果它仍然运行但引入了细微的错误该怎么办?这就是“管理依赖项”的全部内容。
sleske

18
@IgnacioSolerGarcia说“下载文件”并不能完全描绘出正确的图片。假设“下载20个文件并确保它们的版本兼容”。那里没有工作可以避免。除非您已决定冻结依赖版本并准备接受由此产生的可能问题(错误,安全漏洞),否则更新软件包也不是理论上的情况。
Kayaman

12
@IgnacioSolerGarcia“下载文件”-您的意思是(1)找到正确的项目网站(有些托管在github上,有些托管在sourceforge上,有些托管在自己的网站上),(2)转到下载页面,(3)找到正确的版本,(4)下载,(5)解压缩并拖放到某处。这似乎比的工作还要多blah install libfoo。然后,将其乘以5个依赖项。
el.pescado

5
@IgnacioSolerGarcia好的,我必须“下载”哪些文件才能使此nuget正常工作?这基本上是ASP.NET Core项目最简单的设置。实际上,您将有更多的依赖项。
Voo

6
@Ignacio这是启动和运行最简单的ASP.Net Core应用程序的基本元工具。确实,在整个框架的美好时光中,整个过程“更轻松”,因为您只获得了大型的整体库,这些库都同时进行了版本控制,而发布更新花了您很多年。不仅在.NET世界中,由于非常好的原因,该模型已基本弃用。您必须习惯于许多做某件事的小型库的整个模型,并且诚实地使用包管理器是过渡的最简单部分。
Voo

35

您缺少了包管理器的许多优点。

  1. 包管理器使您可以避免将大型(几兆字节或更大)的二进制文件检入源代码管理中。这样做对许多源代码控制工具来说都是令人厌恶的,无所不在的git是其中之一。几个月前,由于开发人员正在检入CocoaPods,我们的存储库达到了Bit Bucket的大小限制。当我们从SVN迁移时,另一个项目已经在那儿了,因为我们已经签入了所有库二进制文件(并且没有使用NuGet)。由于软件包管理器将为每个开发人员即时下载软件包,因此他们无需检入这些二进制文件。
  2. 它们可以防止混合使用不兼容的文件/库。如果有人在升级过程中未正确清除文件夹,则文件夹可以包含库文件的混合版本。我已经看到一种情况,其中文件夹中的一半二进制文件已升级,从而导致非常奇怪的错误。(它甚至没有崩溃!)我们花了几个月的时间(不是工时,只是整个时间)来解决问题。通过让程序包管理器控制整个文件夹,您将无法获得混合版本。您确保一致性。通过自动将所有内容一起更新,在需要的地方安装不同版本,甚至在尝试使用不兼容版本的库时抛出警告或错误,它们也使使用不兼容的依赖项变得更加困难。
  3. 他们建立了共享的库管理约定当新的开发人员进入您的项目,团队,公司等时,他们可能会知道包管理器的约定。这意味着他们不必浪费时间弄清楚如何在代码库中管理库。
  4. 它们为您提供了一种版本控制和分发自己的依赖项以及不属于您的存储库的文件的标准方法。我什至亲自使用了它们来处理我的应用程序所需的一些大型静态数据文件,因此,除了二进制代码外,它还可以很好地用于版本控制。
  5. 一些软件包管理器在安装过程中提供了其他功能。NuGet会将依赖项和内容文件添加到您的csproj文件中,甚至可以将配置元素添加到配置文件中。
  6. 他们的软件包清单文件在一个集中的位置记录了您的库的版本。我不必右键单击DLL并查看版本号即可确定我使用的是哪个版本的库。在Python中,库作者甚至可能没有在py文件中包含版本号,因此我什至无法从它们中分辨出库版本。
  7. 他们不鼓励在机器范围内安装依赖项。程序包管理器提供了一种无需全局安装程序即可安装依赖项的常规方法。当您选择的是lib文件夹和全局安装时,许多库开发人员会选择将其库主要提供为全局安装,而不是您必须自行设置的可下载二进制文件。(MS历史证明了这一点。Linux中的许多库也是如此。)实际上,这使管理多个项目变得更加困难,因为您的项目可能具有版本冲突,并且某些开发人员肯定会选择看似更简单的全局安装,而不是拥有自己的lib。目录
  8. 他们倾向于集中托管和分发。您不再需要依赖该随机库的网站。如果他们停业了,成功的软件包管理者的站点仍然会上传每个版本。开发人员也不必为了下载新库而搜寻许多网站。他们有一个首先查看甚至浏览不同选项的地方。镜像以标准方式组织的软件包比手动托管临时网站上的所有内容的副本还容易。

您还夸大了“好处”的价值。

  1. 无需外部工具即可管理软件包。

    “外部”要做什么?我将NuGet可执行文件检入到存储库中。这是我可以签入的唯一二进制文件,因为它很小,意味着我不需要签入任何其他二进制文件。

    pip不会在这方面造成问题,因为它现在默认情况下已与Python捆绑在一起,并且不兼容和向后不兼容的更改极为罕见。无论如何,如果没有在项目外部安装Python,就不会开发Python代码。

    当它们被广泛采用时,程序包管理器趋向于非常稳定。对于大多数项目,如果没有某种在全球范围内安装的工具,您将无法摆脱,而单个软件包管理器的需求就非常轻。通常,与安装语言运行库相比,它并不麻烦。

  2. 无需互联网连接即可构建。

    没有网络连接,我无法连接到数据库。如果数据库是Amazon托管的,无论如何我都需要完整的Internet连接。我需要Internet连接才能通过源代码控制推送和拉取更改。如果没有某种网络连接,构建服务器也无法签出要构建的代码。没有电子邮件,您就无法发送或接收电子邮件。如果没有一个库,您将无法下载将它们放入lib文件夹中!在没有Internet连接的情况下进行永久开发实际上是闻所未闻的。在极少数必要的情况下,您可以通过将软件包下载到软件包管理器可以使用的位置来解决此问题。(我知道NuGet和pip很高兴从一个简单的文件夹或网络驱动器中拉出;我怀疑大多数其他人也可以。)

  3. 更快的构建(无需软件包检查)。

    在自动构建过程中需要30秒,在本地开发人员构建过程中需要5秒,这是我上面概述的优点的一个很好的折衷方案。与收益解决的问题相比,这些琐碎的时间框架通常甚至都不值得考虑。

  4. 更简单的环境(所需知识更少)。

    针对包管理的一个工具没有对库管理是不是一个真正的公平的比较,反正。没有该工具,您必须了解项目正在使用的任何自定义过程管理其库。这意味着您永远不确定您现有的知识是否适用于您所采用的任何新项目。您必须处理某人想出的任何大杂烩方法,或者自己动手制作。那可能是包含所有库的目录,或者可能更奇怪。也许是为了避免检入库,有人将它们全部放到了网络驱动器上,唯一的版本指示符是文件夹名称。那或全局安装真的更好吗?相比之下,包管理器为您提供了一个干净的约定,该约定将适用于您遇到的大多数项目。

共同的主题是它们不仅在项目内部甚至在整个项目中都提供一致性,文档和功能。这简化了每个人的生活。


10
“在没有互联网连接的情况下进行永久开发实际上是闻所未闻的”,但愿我对此并不了解。由于安全原因,在完全分离的网络中进行了大量开发。是的,它听起来既有趣,但绝对可行。您只需要设置自己的程序包存储基础结构(即您自己的nuget提要)。
Voo

1
尽管拥有自己的基础结构实际上是在任何情况下都有意义的几件事之一:您不希望在外部基础结构上可靠。如果由于某种原因而无法使用某一种,最好进行后备以保证您的开发人员可以继续进行开发。(在任何人告诉我nuget.org或npm或<插入最喜欢的软件包repo>永远不会遇到此类麻烦之前,也许再三考虑。)
Voo

3
@IgnacioSolerGarcia建立每个项目,每个部门或每个公司的约定并不比仅拥有一个大家都知道而不被告知的约定更好。另外,程序包管理在执行约定方面做得更好,因为遵循约定要比破坏约定少。而且,正如我提到的,我直接提交NuGet并在构建脚本中调用它,因此不需要安装它。我将构建服务器的安装量保持在最低水平。
jpmc26

2
@ jpmc26 Imho您的第一个编号列表将因某些强调而受益。
索伦D.Ptæus

1
@SørenD.Ptæus完成。
jpmc26

16

最近,我们将产品从使用手动下载的库转换为使用Nuget进行自动软件包管理,我可以说使用软件包管理器有很多好处。

我们的产品在27个C#项目中实施,相对于今天的标准而言,这个规模还很小。我们的某些第三方依赖项具有数十个程序集。

在Nuget之前,如果我想将所有依赖项更新到最新版本,则必须:

  1. 跟踪我在哪里可以获得所有更新的库
  2. 下载它们并解压缩/安装
  3. 将新版本添加到源代码管理
  4. 手动浏览项目中的所有引用并更新它们以指向新的程序集

有27个项目和数十个依赖程序集,此过程非常容易出错,可能需要几个小时。

现在我们已经更新为使用Nuget,这一切都由一个命令完成。


同意,这就是专业人士的要点2。无论如何,更改依赖项是我们很少要做的事情(可能是由于缺乏适当的自动化回归测试)。
Ignacio Soler Garcia

9
如果定期执行依赖关系更新,那么要做的事情就不那么麻烦了。
18年

1
这些测试是否自动化?他们需要多长时间才能运行?即使运行完整的测试需要24小时,这仍然允许您每隔几天就更新依赖关系,而几乎没有什么缺点(即使实际上您可能不会经常这样做)。即使它们是手动的,也是不可避免的,但是使用手动安装,您可能要花几天时间进行测试,以发现它们由于缺少某种依赖关系而失败,然后您必须在安装后重新开始,这不会发生使用软件包管理...
肖恩·伯顿

3
您是否不需要对新软件版本进行回归测试?当您已经在测试发行版时,只需更新依赖项即可。
18年

4
“我们没有使它们完全自动化,并且该工具太大了,无法做到(可能需要数月的时间才能对其进行测试或使其自动化)” -这是您的大问题。这些测试应该从开始就已经存在。您的问题不是使用包管理器不会带来任何好处,而是您正在使用的上下文在其他方面过于混乱,无法让您享受它们。
蚂蚁P

14

无需外部工具即可管理软件包

那不是很重要吗?如果使用软件包管理器,则不需要lib文件夹。我也不必自己管理软件包。

无需互联网连接即可构建

除了今天在开发时没有互联网连接的情况很少见(也许在运输过程中除外)之外,体面的软件包管理器不要求您必须具有最新版本才能构建应用程序。它可能会抱怨,但是没有理由不使用已经安装的版本进行构建

更快的构建(无需软件包检查)

那是一个极小的边际加速,但可以说是正确的。

更简单的环境(所需知识更少)

如今,大多数软件包管理器是如此简单,以至于不值得尝试通过这些方法解决它们。如果您愿意,甚至还有视觉客户。实际上,他们隐藏了许多正在发生的小问题。

包管理器还允许您在不同项目之间共享这些包。如果我的5个项目使用相同版本的Boost,则无需为每个项目重复此步骤。对于您所说的复杂的依赖关系树尤其如此。

使用lib文件夹,您只能管理该项目的软件包,而软件包管理器允许您使用单个工具在整个开发环境中执行此操作。


配置一个构建代理以在构建期间安装软件包管理器,还原依赖关系等并非易事。lib文件夹不需要任何东西。
Ignacio Soler Garcia

4
我认为这取决于您使用的是哪种语言。使用像Ruby或Rust这样的语言,包管理是如此完美地集成在一起,以至于使用它完全是微不足道的。
肖恩·伯顿

好吧,我故意省略了它,以发表更广泛的评论,但是我在特别谈论NuGet,C#和VSTS云。
Ignacio Soler Garcia

4
@Ignacio无论您使用的是什么构建系统,都无法立即恢复NuGets,因此应该立即将其丢弃。幸运的是,VSTS使此操作变得尽可能容易(文档):有一个NuGet还原任务,您指向解决方案文件并告诉它要使用的NuGet源-对于简单的项目,只需使用nuget.org它就可以了(默认模板应该可以已经以这种方式设置)。
Voo

3
@Ben RVM不是软件包管理器。Ruby的软件包管理器是RubyGems。RVM管理Ruby本身的版本,因此rbenv更好……
Sean Burton

5

只是使用(lib目录)和使用它们来维护元信息 (包管理器)之间是有区别的。这样的元信息涉及版本号,库之间的(传递)依赖性等。

对DLL地狱,库兼容性,java模块系统,OSGi等的讨论至少应该足以说服某种形式的依赖管理形式。

  • 库版本和依赖性问题可能会浪费时间。

另外,还有一个共享(本地)存储库的好处,因此几个项目不需要维护导入的库的副本。如果一个项目有20个子模块,则其中一些模块具有40个奇数依赖项。

  • 更多结构
  • 更多整理库
  • 没有人为的临时图书馆决定

3

在某些情况下,可能需要lib文件夹,例如,在处理过时的库(不再维护/可用的版本),库的本地修​​改版本,...

但是对于其他所有方面,就像开发人员承担了程序包管理器的角色:

  • 开发人员将必须下载库(需要互联网)
  • 开发人员将必须手动检查较新的版本
  • ...

恕我直言,它所需要的知识较少,因为您必须了解外部工具的用法,而无需了解库(即依赖项)。


4
即使对于已过时或已修改的库,到目前为止,我所看到的所有包管理器都提供了将本地依赖项上载到本地存储库的选项。好的,那是您失去一些“它会自动运行”的经验的地方。
绿巨人

@Hulk如果它是一个开放源代码库,则您可以(而且应该)仅发布您的版本,从而使它对程序包管理器可见。通过将修改推送给维护者,或者带出您自己的库分支。
左右约

如果您修改了一个其维护者对补丁邮件无响应的库,那么这将成为弄清楚如何配置程序包管理器的问题,从而使依赖该库的其他程序包也可以对您修改后的库满意。
Damian Yerrick

1

还有其他问题未涵盖的另一个问题:共享部门。

假设您有两个软件包在构建同一个库。在最佳情况下,不会有任何冲突,但是相同的HDD / SSD空间将使用两次。在最坏的情况下,会有版本冲突,例如版本冲突。

如果使用软件包管理器,它将仅安装一次库(每个版本),并且已经提供了它的路径。

PS:当然,您需要动态链接(或您语言中的类似功能)才能获得此专业版。


-5

共享库被视为90年代Unix和Windows系统的一项进步的一个主要原因是,当加载使用同一组库的多个程序时,它们如何降低RAM使用率。仅需要为每个确切的库和该库的版本分配一次代码空间,剩下的每个实例的唯一内存使用情况是静态变量。

许多操作系统以依赖于unix mmap()api之类的机制的方式来实现共享库-这意味着一个库不仅需要完全相同的版本,而且实际上需要完全相同的FILE。要完全利用附带自己的库集的程序,这根本是不可能的。

与90年代相比,鉴于内存便宜得多,并且库版本需要更多的多样性,因此这种说法在今天没有太大的意义。


4
这个问题不是在谈论共享库,而是在库文件夹上的依赖关系。
Ignacio Soler Garcia

1
这不能回答OP的问题
esoterik
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.