# 7. 软件包管理

内核恐慌 · 2014-12-27

<https://lib.pan.icu/9a5f2b37-2f32-429a-99af-2282616263ab>

本期《内核恐慌》深入探讨了软件包管理（package management）的方方面面。Rio和吴涛首先解释了包管理的概念，包括开发时依赖库和部署时分发软件的区别，并对比了Windows的DLL hell与Linux发行版（如Debian的DEB和Red Hat的RPM）的解决思路。随后讨论了macOS上的Homebrew如何凭借Ruby DSL和GitHub生态取代MacPorts，以及Windows的NuGet包管理器。语言层面的包管理工具是吐槽重点：Java的Maven因XML配置和依赖冲突被痛批，Python的setuptools、distutils、pip和Buildout四分五裂，而Node的npm和Ruby的Gem则因清晰的设计得到好评。最后介绍了Go语言去中心化的go get及其版本管理难题，并展望了跨语言统一包管理的可能性。

## Questions this episode answers

### Homebrew 为什么能在短时间内彻底替代 MacPorts？

吴涛认为，Homebrew 用 Ruby DSL 编写 formula，降低了发布新软件的门槛；与 GitHub 深度集成，贡献者只需 fork 和 pull request；默认将软件装到用户可写目录，无需 sudo，给人“虚假的安全感”。Rio 补充 GitHub 让提交流程极方便，而 MacPorts 使用小众的 TCL、门户网站老式，体验差距明显。这些使 Homebrew 迅速获得程序员社区压倒性支持。

[51:22](https://lib.pan.icu/9a5f2b37-2f32-429a-99af-2282616263ab?t=3082000)

### Python 的包管理工具为什么如此混乱？

Rio 指出，混乱源于历史遗留：早期 easy_install 只能安装不能卸载，后又出现互不兼容的 setuptools、distutils、pip；Python 2 与 3 的分裂加剧碎片化；官方包索引 PyPI 频繁宕机，且在国内常无法访问，迫使企业自建镜像。吴涛补充，虽宣称“唯一明显的做事方式”，实际却有五六种不兼容的工具，社区共识薄弱。

[1:27:56](https://lib.pan.icu/9a5f2b37-2f32-429a-99af-2282616263ab?t=5276000)

### Go 语言如何进行包依赖管理？它没有传统的包管理器吗？

Rio 解释，Go 采用去中心化方式：import 路径直接等于代码仓库地址（如 github.com/user/repo），go get 自动识别并递归获取依赖。官方没有集中仓库，也不内置版号概念，鼓励 API 永久兼容；若需版本控制，社区常用 GoDep 等第三方工具，或直接把依赖源码提交到本地的版本库，以保证构建可重现。这体现了 Google 内部构建系统的哲学。

[2:10:02](https://lib.pan.icu/9a5f2b37-2f32-429a-99af-2282616263ab?t=7802000)

### Maven 给 Java 开发者带来了哪些痛苦？

吴涛根据亲身经历批评 Maven：强制 XML 配置和固定目录结构，小项目也需承受；一旦依赖冲突，项目可能彻底失控。他曾因 Maven 导致产品发布推迟两次。网上一幅漫画描绘其“生产力先小幅上升再跌入深渊”的曲线，DevOps 圈常引用 gif 动图“得知项目需要用 Maven”来表达绝望。

[1:10:34](https://lib.pan.icu/9a5f2b37-2f32-429a-99af-2282616263ab?t=4234000)

## Key moments

- **[0:00] 开场**
- **[1:06] 包管理**
  - [1:06] 本期主题是 package management，作为上一期生命周期管理的自然延伸
  - [2:04] 吴涛指出 package 在不同语境下含义模糊，例如 macOS 的 .app 和 .pkg、Java 的 jar/war
  - [3:11] Rio 解释开发时的 package 指代码依赖，部署时的 package 指可分发的安装包
  - [3:59] 吴涛回忆小时候直接复制粘贴第三方代码，称之为 “package management done wrong”
- **[5:35] Windows部署**
  - [5:47] 吴涛说明 Windows 安装过程神秘，Rio 描述标准安装路径 (Program Files) 和注册表
  - [7:29] 吴涛质疑 Windows 安装进度条从 99% 到 100% 需要 10 分钟的现象
  - [9:10] 吴涛解释 Unix 程序遵循 “沉默规则”：无事可说时便不说，而初学者难以忍受
  - [10:12] Unix 沉默哲学 vs Windows 详细反馈：Rio 认为 Unix 用户只要求不出错别打扰，专业用户居多
  - [13:21] 吴涛提出 DLL hell 问题：A 软件需要库甲版本，B 软件需要乙版本，且同一程序内不同依赖可能要求同一库的不同版本
  - [14:41] Rio 说明 Linux 发行版通过包管理器解决依赖冲突，依赖的多个版本通过符号链接管理
- **[15:21] DLL地狱**
  - [16:57] 吴涛描述升级一个包时遇到连锁依赖冲突的困境，认为各平台都难以避免
- **[17:33] Linux发行版**
  - [19:09] Rio 提出为何不把依赖全打包进应用以避免冲突，吴涛解释出于节省空间和传输成本的考量
  - [21:09] Rio 认为现代硬件下存储空间已不成问题，但 Linux 的共享库文化也鼓励社区贡献和上游合并修复
  - [23:35] Rio 指出选择尽可能少的共享依赖可享受平台的安全更新，而全打包则需等待开发者重新发布修复
  - [25:54] Linux 发行版两大包管理体系：Debian 使用 DEB (dpkg/APT)，Red Hat 使用 RPM
  - [27:20] 吴涛介绍 RPM 名称从 Red Hat Package Management 变成递归缩写 RPM Package Management System
  - [27:57] Debian vs RPM：运维偏向 RPM，开发者偏向 Debian，因为 Debian 包较新且可打补丁
- **[46:06] Mac包管理**
  - [47:06] Mac 上最早的第三方包管理器 Fink 已被废弃，MacPorts 使用 TCL 脚本，编译安装体验不佳
  - [48:43] Homebrew 用 Ruby DSL 写 formula，降低贡献门槛，且默认安装不需要 sudo，给用户“虚假的安全感”
  - [49:54] Rio 认为 GitHub 的 PR 流程和友好 UI 对 Homebrew 的流行至关重要，对比 MacPorts 的门户式网站
  - [52:32] Homebrew 用酿酒术语：编译过程类比发酵，装瓶 (bottle) 是为预编译二进制
  - [54:10] Linuxbrew 将 Homebrew 移植到 Linux，吴涛惊讶于已有成熟方案却去移植一个因缺失才造的工具
  - [55:54] Windows 的包管理器 NuGet：微软官方支持，可安装 jQuery、Bootstrap 等，Rio 表示 “Holy. Really?”
- **[1:04:00] 语言包管理**
  - [1:04:44] 吴涛描述 Maven 采纳后生产力走势漫画：开始直线上升，遇到依赖冲突后跌落深渊无法恢复
  - [1:07:00] sbt 用 Scala DSL 写构建文件，比 XML 简洁，但门槛极高，需理解 Scala 的复杂特性如 object、lazy val
  - [1:09:15] 吴涛预测 Scala 可能因过度繁琐被历史抛弃，同时承认这或许是 Go 等简单语言支持者的无望希冀
- **[1:10:07] Maven**
  - [1:10:56] Python 包管理历史遗留问题多：setuptools、distutils、pip、buildout 共存且互不兼容，违背了 “只有一种明显方式做事” 的哲学
  - [1:12:44] 吴涛将 Python 的包管理比作刚出龙潭又入虎穴，并讽刺一边高唱 Only One Way 一边出现 Python2/3 分裂
  - [1:14:20] easy_install 只能安装不能卸载，吴涛调侃 “easy install, hard manually uninstall”，Rio 称为管杀不管埋
  - [1:16:52] 吴涛对比 Ruby 社区：Matz 振臂一呼大家升级到 2.0，而 Python 社区在投票 Python3 是否是错误
  - [1:18:52] Rio 批评 Python 的 “自备电池” 理念，标准库中的 setuptools 如同过期的腐烂电池
  - [1:20:40] pip 加入了 uninstall 和 freeze 功能，Rio 表示一个包管理器终于支持卸载令人惊讶
- **[1:27:56] Python**
  - [1:28:10] Buildout 配置用 .ini 文件，开发者甚至在 .ini 中插入 shell 脚本来解决依赖，Rio 惊呼 “这是什么黑魔法？”
  - [1:29:50] 尽管古老，Buildout 有本地缓存机制，避免重复下载，类似 Maven 的本地仓库
  - [1:30:57] Ruby 的 Gem 和 Bundler 生态：Bundler 默认将 Gem 装到项目目录，避免全局污染，是行业最佳实践
  - [1:32:15] CocoaPods 和 Homebrew 都是 Ruby 写的，Ruby 社区擅长用 DSL 简化包管理配置
  - [1:33:25] Scala vs Ruby 写 DSL：Scala 的 DSL 可以写得连开发者都认不出，吴涛认为这是优点也是严重缺点
  - [1:35:20] 吴涛分享 Stack Overflow 风滚草勋章故事：提了一个 Swift 问题，却收到一个 Rake file 解答，运行失败，感叹 “不懂 Ruby 就是到处被欺负”
- **[2:01:27] Node**
- **[2:09:29] Go**
- **[2:24:15] 跨语言**

## Speakers

- **Rio** (host)
- **吴涛** (host)

## Topics

软件开发

## Mentioned

APT (product), Fink (product), FreeBSD ports (product), Gem (product), Go (product), Godep (product), Homebrew (product), MacPorts (product), Maven (product), NPM (product), NuGet (product), PyPI (product), RPM (product), buildout (product), bundler (product), distutils (product), easy_install (product), pip (product), sbt (product), setuptools (product)

## Transcript

### 开场

**Rio** [0:01]
大家好 ， 欢迎收听第 7 期的 《 内核恐慌 》， 我是 Rio。

**吴涛** [0:05]
我是吴涛 。 哎呀 ， 终于第 7 期了 ，7 是个我很喜欢的数字 。

**Rio** [0:10]
呵呵 。其实今天这期节目的时间会比较奇怪 ， 我们是提前了很久 。 就你们听到的时候应该是 ——

**吴涛** [0:19]
是那节之后了 。

**Rio** [0:20]
对 。 你 ， 吴涛 ， 解释一下为什么要这么干 。

**吴涛** [0:24]
啊对 ，因为在这个神秘的时间点 ， 我们还没有办法得到足够的听众反馈 。 我们相信我们在上一期里面遇到了 、 犯了很多很低级的错误 ，有可能是非常常识性的错误 。

但是在目前这个 timespace 的 point 上面 ， 我们还没有这些反馈信息 ， 所以我们这一期暂时先不做 follow up。 但是我保证下一期大家会听到我们两个在这边 ， 嗯 ， 怎么说 ， 痛哭流泪 、

捶胸顿足地道歉 ， 说我们犯了哪些低级的错误 ， 包括上一期以及这一期 。

**Rio** [1:05]
OK。

**吴涛** [1:05]
OK。

### 包管理

**Rio** [1:06]
所以这一期我们的主题是什么 ？

**吴涛** [1:10]
Package management。Yeah。其实有一点点像上一期的主题的自然延伸 。 就是上一期我们讨论了软件 ， 或者说程序生命周期的管理 ， 主要还是从比较小的一个转点入手 ， 就是内 —— 主要是 ，其实我一开始只是想谈内存管理 ，但是没有想到后面扯到那么多 。

那这一期的内容其实就是 ， 当你的软件写出来 ， 或者说你在写软件的时候 ， 你会需要用到许许多多内部外部的工具 ，并且你希望你的软件可以以某种比较方便的方式被别人使用 ， 那你就需要把它包装起来 。

我们这一期主要讨论的就是这个问题 。

**Rio** [1:53]
嗯哼 。 所以在开始之前 ， 我们要先解释一下什么叫做这个概念哈 ，是 package management。

**吴涛** [2:00]
对 。package 实际上是一个非常

泛泛的称呼 ， 就是它是一个指涉非常不清楚的词 ，在很多不同语境之下会有很多不同的意义 。 比如说

，以 OS ten 来说 ，package 可以是指 ， 比如说一个 application bundle 可以是一个 package， 对吧 ？ 然后 ，但是与此同时， 你又可以通过一种叫做 .pkg 的文件来安装一个程序 ， 然后那个 .pkg 文件也是一个 package。

**Rio** [2:34]
对 ，有点混 。 就在不同的语境下，package 的概念其实是有点比较不清晰的嘛 。

**吴涛** [2:41]
对 。 然后 Java 程序员可能会非常习惯于一个 package， 就是一个 jar file， 或者是一个 war file。 然后它既可以指拿来 ， 可以拿来用的第三方库 ，也可以指自己写出来的什么东西在部署的时候所打包成的一个文件 。

**Rio** [3:03]
对 。 就是在对开发的时候我们讲的 package 和部署的时候的 package，其实又不是同一个 ， 就相关的但并不完全一致 。

**吴涛** [3:11]
对 。 那什么是开发时的 package 呢 ？

**Rio** [3:15]
对 ， 这个要解释一下吧 。 就大概可以理解为你要写一个软件 ， 然后你需要依赖 ， 你不是从头到尾都是用全部自己写嘛 ， 你肯定要用到别人的一些什么第三方的代码啊 ， 或者是你那个语言的原生的标准库里面那些东西 。

然后这些你要用的那些东西 ， 大概可以理解为一个 package。 这个时候你就要去调用那些 package， 才能完成你这个软件的功能嘛 。

**吴涛** [3:42]
对 。 我记得小时候写程序 ， 就如果需要用到别人的第三方代码 ， 我就直接把那代码拷贝一遍 ， 复 —— 粘贴到我的文件里就可以了 。

**Rio** [3:52]
这个叫什么 ？

**吴涛** [3:53]
这是 package management done wrong。

**Rio** [3:59]
不过其实在今天我们做的很多事情跟这个其实在本质上也是很类似的 ， 就把别人的代码挪到自己的代码库里面去 ， 这样可以保证以后你就不用再管别人他怎么改他 ， 反正你有一份副本了嘛 ，在你的本地的这个目录里面去 ， 对吧 ？

**吴涛** [4:16]
对 。 现在的 package management， 或者说在开发时的 package management 所解决的问题就是 ， 怎样让你不尽量 ， 怎样让你尽量不 dirty 地去拷贝别人的文件到你的工程里面去 。

就如果你需要依赖别人的代码而通过简单的复制粘贴 ， 像我们童年做的那样的话 ， 那假如对方升级了一下他的一个功能 ， 你怎么用呢 ？

你还要再拷贝 ， 再粘贴一遍 。 这还不是最痛苦的部分 。 最痛苦的部分是 ， 嗯 ， 假如你想要把别人的库扔出去的时候 ，有可能你会非常苦恼地发现你已经不记得哪一部分是你拷贝 、 复制 、 粘贴进来的 ，而哪一部分是你自己写的了 。

**Rio** [5:03]
这个要什么 ， 要有这个版本管理才能发现 ， 要回去找那个 log。

**吴涛** [5:07]
对 。 而在你发明版本管理 ， 自己发明一个版本管理系统之前 ， 你可能已经会先发现这个原来世界上有 package management 这种东西 。

**Rio** [5:19]
对 。 然后另外一个我们常说的状态就是说在部署的时候 ， 就所谓把软件分发给 ， 如果你是卖给消费者的话 ， 你是分发给别人， 或者是你是像我部署的服务器的软件的话 ， 你是要怎么把它弄 ， 倒腾到这个服务器上去运行 。

这个时候的这个要求又有点不一样啊 。 这个时候我们要求这个 package 尽可能的是什么 ？ 可重复的 。

### Windows部署

**Rio** [5:43]
就是你整个 deploy 的过程是应该是可以重复的 。

**吴涛** [5:47]
对 。 那这个这么说可能稍微有点抽象 ，但是如果你是一个 Windows 用户的话 ， 你可能会习惯于安装什么软件 ， 就是打开一个安装软件的 installer 的 .exe， 然后还是或者叫 setup.exe， 然后你一路回车执行一个叫做 setup.veza 的东西 ， 然后指定一个安装的目标路径 。

**Rio** [6:10]
目标路径 。

**吴涛** [6:11]
对 。 然后它会把所有东西给你塞到那个目录下面之后， 然后你去那个目录找 ， 就可以找到你要运行的程序 。

所以在 Windows 的世界里面 ， 我们安装程序的过程是用一个程序去安装另一个程序 ， 或者说你为了安装你的程序 ， 你必须创建另外一个 ， 你用另外一个程序把你的程序打包在一个 package 里面 ， 然后交给别人。

别人再同样要利用另外一个程序来解压你的程序 ， 然后把它安装到你对方的电脑下面 。

**Rio** [6:41]
对 。 这个就跟在 OS ten 下面的常用的安装方式还不太一样 ， 刚才你也讲了 。 就是只是 ， 比如说我们通常的一种方式是通过一个 dmg 的镜像 ， 或者是纯粹是一个 .zip 的压缩包 ， 然后解压出来一个 .app 的这么一个目录吧 ， 然后这个目录整个是一个完整的程序 ， 你直接把它拖到你的 application 的 folder 里面 ， 这个事情就算完成了 。

所以但是刚才你说那种 Windows 上面安装的方式在 OS ten 下的对比 ， 就是你之前也提到那个 .pkg 那种方式 ， 运行一个脚本 ， 然后把那些文件塞到你的系统的各个角落 。

**吴涛** [7:16]
对 。 主要来说 ，其实我们说 Mac 下面把一个 .app 的文件拖到你的 application 下面就可以得到这个 ， 或者说以这种方式你就完成了安装 。

这种安装是一种 ， 仅仅是一次拷贝而已 。 你安装一个程序的方法就是通过拷贝 。 而什么是 .app 呢 ？ 就是可能 ， 我觉得这是一种不同的封装层级的思路的体现 。

就是在 Mac 下面 ， 所谓 app，app 文件实际上只是一个 ， 只是一个 folder 而已 ， 它只是一个文件夹而已 。 但如果你把它改名成了 .app，并且在里面加上一个叫做 info.plist 的文件 ，Mac 就会试图把它作为一个整体处理 。

**Rio** [8:05]
就是说你不能很方便地直接打开它的内 ， 看到那个 app 那个目录里面的内容 ，但是你如果右键可以叫什么 ， 叫 showing。

**吴涛** [8:14]
Show content 应该是 。

**Rio** [8:15]
Show package content。

**吴涛** [8:16]
对 。

**Rio** [8:17]
这个功能就可以把它当成目录展开了 。

**吴涛** [8:19]
对 。

**Rio** [8:20]
这个是我们在分发给消费者的时候的一种 ， 分发时候的 package。 然后如果在服务端的话 ， 这种方式就非常非常多的不同的安装方式了哈 。

但是我们待会讲到不同的发行版的时候 ， 一个一个说吧 。

**吴涛** [8:38]
对 。

**Rio** [8:39]
所以其实你们看到哈 ， 我们在这个 package management 的两个不同的应用场合 ，但其实它们还是有一定的这个共性的哈 。

就比如说至少我们需要 ， 我们要求说我们能够清楚地知道我们安装的是什么版本的一个 package， 对吧 ？

**吴涛** [8:55]
对 。

**Rio** [8:56]
我们知道有什么方法可以能够删掉它 ， 能够重复地安装 。

**吴涛** [9:01]
对 。

**Rio** [9:02]
或者说比如说你把某一个文件修改 ， 就是意外修改了 ， 你能够把那个东西再拿回来 。 还有就是说你安装的多个 package 之间最好不要互相有影响 ， 对吧 ？

比如说我安装了 A 这个包 ， 那我再安装 B 这个包 ， 它们会不会冲突啊 ？ 我们最好能避免这种事情 。

就是这是一些基本的需求吧 。

**吴涛** [9:24]
对 。 你在用 Windows 的时候有没有感觉到 Windows 安装程序的过程是一个很神秘的过程 ， 或者是很 ， 是一个很复杂的过程呢 ？

**Rio** [9:36]
其实我到现 ， 我到今天为止也一直不是很清楚它们到底在做什么 。 那我想想有 ，有些什么事我能想得起来啊 。

就主程序它们就作为一个 ， 就是常见的 Windows， 就符合 Windows 规范的安装程 ， 这个程序它是把自己塞到那个叫 C 下面的 。

**吴涛** [9:54]
Program files。

**Rio** [9:54]
Program files， 然后建一个自己公司的名字的目录 ， 然后在下面建一个你这个程序的名字的目录 ， 然后再把那些东西放进去吧 ， 包括你的那些什么需要依赖的什么软件库啊 ， 还有什么静态文件 ， 对吧 ？

都放到那里面去 。 然后还要加什么 ， 就是用户配置写到什么注册表里面 ， 对不对 ？ 现在 Windows 8 里面好像又有一些什么 ， 又有点什么不一样 ，有个什么 app，有个 appsdata 这个目录了 。

**吴涛** [10:21]
What？ 所以 Windows 8 要抛弃注册表了吗 ？ 那不可能吧 。 注册表是 Windows 核心技术一部分 。

**Rio** [10:28]
这个我不太清楚具体怎么样子 ，但是我上次在虚拟机里面装的时候是发现有这么一些新的 ， 就是新是相对于之前 Windows XP 或者是 Windows 7 的时代的东西啊 。

但这个因为我们 ， 我不是这个 Windows 的重度用户 ，也不太清楚它怎么一回事 。

**吴涛** [10:44]
AppData 会不会是 app 在运行时产生的数据 ？

**Rio** [10:47]
有可能 。

**吴涛** [10:48]
那个是什么 data， 我记得有个什么 data roaming folder 是拿来做这件事情的 。

**Rio** [10:54]
OK。

**吴涛** [10:55]
Anyway， 这我也不是很清楚 ，但其实我觉得目前让我觉得回想起来很疑惑的一点就是 ，Windows 安装过程之中所产生的那些数据

，有时候在有些安装程序里面是可以看到的 ，而另外一些安装程序里面是看不到的 。 就比如说我记得 WinApp 在安装的时候会跳出来一个列表 ， 然后不停地告诉你哪个文件被拷到哪个地方 ， 然后你最后可以重新地去审查一遍这个安装程序到底干了些什么 。

当然 ， 当然我相信 99% 的人不会这么做 ，但至少它提供了这种可能性 。 而另外一些安装程序好像从头到尾就 。

**Rio** [11:35]
就一直 next。

**吴涛** [11:37]
对 。 而且 ， 对 ，有一个很著名的现象就是进度条从 0 到 99% 需要大概一秒钟 ， 然后从 99% 到 100% 需要 10 分钟 。

**Rio** [11:48]
没算哈 。

**吴涛** [11:49]
对 。

**Rio** [11:49]
对 。 刚才你讲那个 WinApp 那种形式 ， 它是有一种 ， 我记得印象中是有一种专门的这种生成安装程序的程序 。

**吴涛** [11:58]
对 。 就是其实就是不同的 installer 的供应商不同而已 。

**Rio** [12:02]
然后有 ， 之前有这种很常用的那种 installer， 就是可以让你有一个列表框显示每一个步骤在干嘛 ，但其实这个是非常用户不友好的一个东西 。

作为一个普通用户 ， 你为什么要知道 WinApp 装了多少个文件 ，在哪个位置 ，是吧 ？ 这个是对你来说是没有什么实际价值的信息 。

当然除非你有什么洁癖之外说你卸载的时候一定要逐个对照着那个列表把那个文件一个一个删掉 。

**吴涛** [12:32]
这还是一个约定俗成 ， 我也不知道 。 就有时候 Windows 上面提供的过度信息在有些人看来是好事 。

**Rio** [12:41]
好处 。

**吴涛** [12:41]
是一种 ， 是一种它时刻要看到电脑在做什么 ，而反过来你给它一台比较不那么

intrusive 的操作系统的话 ， 它会觉得这操作系统怎么这么安静啊 。 我 ， 我在用你 ， 你给我点回应好吗 ？

就比如说我记得刚刚学那个 Linux 命令行的时候 ， 执行一个文 ， 执行一个命令 ， 它什么都不告诉你 。

**Rio** [13:11]
它就完了 。

**吴涛** [13:12]
对 。 默认是 ， 默认这种状态是哦 ， 没什么可做的 。 所以它就什么也不说 。 这让我觉得当时非常的难以忍受 。

因为在 DOS 下面你不管干嘛 ， 它都会告诉你哦 ， 我干了什么 ， 哦 ， 没什么可干的 。 就算没什么可干的 ， 它也告诉你说没什么可干的 。

对 。 但就切换到 ， 刚刚切换到 Linux 的时候觉得哇 ， 好酷啊 。

**Rio** [13:33]
所以我觉得这可能也是那个什么 Unix 用户和 ， 或类 Unix 系统的用户和那个 Windows 或 DOS 用户的本质区别吧 。

就是可能用类 Unix 用户就觉得你把这个事情做了就好了 ， 只要没出什么问题就别来烦我 ， 对吧 ？

我还有别的烦心的事多着呢 ， 对吧 ？

**吴涛** [13:53]
对 。

**Rio** [13:54]
然后因为他们很多都是所谓早期都是什么专业用户啊 ， 或者这种程序员 ，他知道在干嘛 ， 然后也知道结果是怎么样子 。

但是像 DOS 啊 ，Windows 这种面向消费者的 ， 它可能需要一些什么存在感 。 它是需要知道 ， 诶 ， 我花了 20 刀买了这个程序 ， 它干嘛了呢 ？

最后， 对吧 ？

**吴涛** [14:15]
对 。 有回应是用户友好的一种表现 ，但没有回应则是 pragmatical 的一种表现 。 可以这么说 。

**Rio** [14:24]
我不确定是不是一定用户友好啊 ，但有可能说那有些人不要来烦我啊 ， 就慢慢割忙 。

**吴涛** [14:31]
不 。 至少对于一个初学者来说 ， 没有回应其实是一个非常费解的 behavior。 你要 ， 你必须要一上来就跟他说 ， 这个程序如果没有回应的话 ， 意味着它没有 ， 它认为自己没有什么可做的 ， 或者是它完成了该做的事情 ， 它就没有什么回应 。

它只有 ， 它唯一有回应的时候就是报错 。

**Rio** [14:50]
嗯哼 。 对 。 这是两种不同的这个思想嘛 。

**吴涛** [14:55]
对 。

**Rio** [14:55]
觉得就看场景 ， 或者看你的这个应用吧 。 有时候 ， 就我的感觉倾向于就是你用一个新的软件 ， 或者是新的产品的时候 ， 你可能需要了解一下一些具体的状况 。

但这肯定也是 ， 你可以从另一个角度说 ， 这个是产品做得不好 ， 需要我了解这么多细节 ，其实没必要嘛 ， 对吧 ？

但另外一种就是说 ， 反正你这个工作就好了 ，不要那么多废话 。

**吴涛** [15:19]
对 。

**Rio** [15:20]
嗯 。

### DLL地狱

**吴涛** [15:21]
对 。 说回到我们的这个 package management 的话题上来 ， 就是还有一个问题 ，Windows 上面当时一直让我疑惑的是 ， 假如一个程序同时需要一个库的两个版本 ，Windows 到底是怎么解决的呢 ？

**Rio** [15:39]
这不是没法解决吗 ？ 它们后来才会出现那个叫做什么 DLL hell 嘛 。

**吴涛** [15:44]
对 。 那 DLL hell 具体来说其实就是

A 软件需要某一个库的甲版本 ，B 软件需要这个库的乙版本 ， 那 Windows 会同时保留甲 、 乙两个版本 ， 对不对 ？

**Rio** [16:00]
应该是这样的 。 对 。

**吴涛** [16:02]
那甲 、 乙两个版本的差别在哪 ？ 是在用户名 ， 文件名上有一个版本号的差别吗 ？

**Rio** [16:08]
这个我不太清楚 Windows 怎么具体怎么做 ，但是这个在不同 ， 就在不同的这种环境里面都是存在的 。 就是这个最麻烦的问题是因为同一个软件不同版本它可能是互不兼容的嘛 。

然后如果 ， 就你刚才说那情况其实还好啦 。 就是它 A 程序的时候它调 A 甲版本好了 ，B 程序调乙版本好了 ， 互相不冲突 。

比较坑的是这种 ，是比如你自己要写一个 A 程序 ， 然后你可能需要这个某个库的 1.0 版本 ， 然后你同时还要使用另外一个库的 ， 另外一个库 B 吧 。

然后 B 它可能需要这个同样你依赖的那个库的 1.2 版本 。 这个时候你就很坑了 。 你就是要同时支持不同的 ， 同一 ，在同一个程序里面你要依赖同一个软件库的不同的版本 。

就是会出现这种交叉 ， 还有这种 ， 这种 ， 这种 ， 这个怎么形容比较好 ？

**吴涛** [17:08]
循环依赖 。

**Rio** [17:10]
也不叫循环 ， 会产生循环 。

**吴涛** [17:12]
对 。 有可能会产生循环啊 。

**Rio** [17:14]
对 。 就反正就很糟糕嘛 ， 这样 。

**吴涛** [17:17]
对 。 脑中立刻出现了一个错综复杂的树状图 。

**Rio** [17:24]
树 。 如果是个树还好啦 。

**吴涛** [17:26]
对 。 它就有可能是 。

**Rio** [17:27]
不会有 circular reference。

**吴涛** [17:28]
对 。 它可能是个有向图 。

**Rio** [17:31]
就很搞死你 。

**吴涛** [17:32]
对 。

### Linux发行版

**Rio** [17:33]
对 。

**吴涛** [17:34]
那 Linux 上面就没有 dependency hell， 就没有 DSL hell 吗 ？ 其实也有吧 。

**Rio** [17:40]
其实也是有的 。 是现在我们大部分用的发行版 ， 它都是通过这个原生的 ， 它们一般每个发行版会自带一个包管理系统嘛 。

然后那个包管理系统会想办法去解决这些冲突啊 ， 这些之类的问题 。 而且那个发行版的维护者 ，他在打包的时候他也会考虑说这个问题 。

比如说你 ，他比如说 ， 比如拿 Debian 来举例子吧 。他可能我发行的这个版本里面我把所有的都测过 ， 它们互相安装应该是没有问题的 。

当然你如果有那种不同版本的这种库的需求的话 ， 它会通过不同的文件名啊 ， 不同的路径来实现嘛 。

**吴涛** [18:18]
具体来说其实就是假如在 Linux 上面有一个就是和 Windows 一模一样的困境的话 ，Linux 会支持你链接到不同版本的库上面 。

而这些库 ， 每一个库会有一个默认的版本 。

**Rio** [18:41]
对 。

**吴涛** [18:41]
是这个 。

**Rio** [18:42]
一般会是用 ， 会用那个吧 ， 会用那个软链接来实现 。

**吴涛** [18:45]
对 。 会用一个符号链接来实现 。

**Rio** [18:46]
嗯 。

**吴涛** [18:49]
Anyway。 就有时候我在用 Debian 的时候需要升级某一个我想要的工具到某一个特定的版本 ， 比如说 Win 吧 。 然后使用其内置的包管理工具升级的时候 ， 包管理工具说啊 ， 这个东西依赖于那个东西 ，但是那个东西的版本已经过旧了 ， 所以你需要一起升级那个东西 。

那时候好吧 ， 那就一起升级它好了 。 可是你升级的时候又会说啊 ， 这个东西会安装那个 ， 就是 B 会再安装 C 的另外一个版本 ，而 C 的另外一个版本在你这边会跟 D 的什么东西冲突 。

然后我就觉得啊 ， 怎么也是有这样的问题 。 所以这样的问题其实在各个平台之下都是很难避免的 ， 可以说 。

**Rio** [19:35]
对 。其实我一直有一点想不明白哈 ， 比如说为什么我们 ， 就是这点可能在 Linux 上面比较明显啊 。 就是我们经常会 ， 你装一个软件你会发现它会依赖一堆其他乱七八糟的其他的软件或者是库也好 。

然后 Linux 的大部分的发行版的做法是说这个我们发行版做的时候就会把这个东西考虑好 ， 它们之间的依赖关系 ， 然后我们会告诉你怎么去 ， 会一套算法去解决这个依赖问题 。

但其实你会发现这个时候你也会遇到那种情况 ， 就是如果你升级到某一个它没有被发行版在那个版本支持的时候 ， 你就没有办法升级 ， 你得冒很大的风险去打破这个或者用自己编译的包嘛 。

那我就不明白了 ， 为什么在现在这个 ， 就你直接装一个软件 ， 你把所有的依赖都打包好 ，不要去和别人共享不就好了吗 ？

**吴涛** [20:30]
这个我觉得还是出于一个

节省空间的考虑吧 。 就如果你打包进去你自己需要那个库 ，而这个库其实很多很多其他程序都需要 ， 你总要找出一个界限吧 。

就是哪 ， 到具体到什么库上你是一定要自己打包进去的 。

**Rio** [20:53]
这个有这么一个问题啊 。 就是说如果你这个的理由是因为为了节省空间的话 ， 那在现代硬件条件下根本就已经不成立了嘛 。

你现在磁盘你动辄多少一两个 T， 对吧 ？ 其实那个程序多大点空间 。

**吴涛** [21:07]
不 。 但并不仅仅是存储空间 ， 还有传输空间你需要考虑进去 。 假如说我 ， 对啊 ， 我这个包从事无巨细 ， 从 libc 一直到所有我可能用到的包 ， 我全都打包在一起去给用户下载 ， 这个好像有点二吧 。

**Rio** [21:26]
嗯 。 虽然是这么说 ，但是其实你看在 OSX 下面的大部分都也是这么干的呀 。

**吴涛** [21:34]
嗯 。

**Rio** [21:34]
对吧 ？ 其实倒是它们 ， 我后来也专门去想过这个问题 ，也看了一些别人的说法 。其实有一个事情倒是我觉得是可以理解的 。

就是因为你知道 Linux 这种开放的系统嘛 ， 它希望如果你在使用某个软件 ， 某个软件库的时候发现了有问题 ， 你可以去修正它 。

那么它希望你把这个修正 ， 比如是 bugfix 能够也 ，因为你做 bugfix 其实在从某种意义上来说是在帮助编写这个软件嘛 ， 对吧 ？

那么它希望把这种成果 ， 这种劳动的工作不要浪费掉 。 就你能够在你使用那个版本修正之后， 你也向它的这个上游 ， 就是这个软件库的原作者那里能够把这个整合进去 。

这样的话大家都可以享受到你修改这个 bugfix 带来的好处嘛 。

**吴涛** [22:23]
对 。 但我是觉得就是这是一种人人为我 ， 我为人人的精神状态 。

**Rio** [22:30]
对 。 我就说这个 ， 它们的 ， 它们的 ， 就究其原因 ， 它们的理念就是这样子嘛 。 那么同样的道理 ， 如果每个程序都是打包自己的依赖的那东西 ， 那么它肯定会出现 ， 比如说同样你的这个两个程序会依赖 ， 比如说同一个 OpenSSL 的这个包 ， 对吧 ？

然后它可能是依赖了不同的版本 。 但是可能比如说它 OpenSSL 发现一个安全漏洞 ， 然后某个系统升级的时候就把它删了 。

但是如果你用的是自己打包的包的话 ， 那你可能你必须再重新发布一下你这个软件 ， 你这个才会有那个安全补丁打上去 。

但如果你把这个事情交给系统来做 ， 就是说这个系统说 ， 诶 ， 我把这个整个你们需要用到的 OpenSSL 这个包的补丁都打了 ， 你们只要重启一下， 就重新这个 App 关掉 ， 重新启动一下， 就可以享受到这个带来的安全的好处 ，而不是说要等每一个开发者重新去做一遍嘛 。

就这里其实也是有一定的劳动力上面的节省嘛 。

**吴涛** [23:23]
对 。 所以说其实这是一个取舍 ，有一个取舍在里面 ，有个 tradeoff。 就是你要么选择同时发布更多的依赖 ， 来避免用户可能产生的包冲突的问题 。

而另一方面你也可以选择尽量减少自己所发布的软件其内置的内部包库的依赖 ， 来享受这种有一个平台吧 ， 加个括号 ， 加两个括号 ， 平台在那边的便捷和稳定性 ， 或者说安全性 。

**Rio** [24:02]
对 。 这里可以看到就是刚才讲这两种不同的思路和这个做法 。 就一般像那种像什么商用的 ， 就是消费者级别用的 ， 然后是为了简单方便 ， 然后互相不冲突的话 ， 会采用像刚才讲的把所有的东西都打包到自己里面去 ， 然后你就依赖于一个比较稳定的系统提供你的那些包 。

像 Mac 上面就是这样子 ，Windows 上面应该很多也是这样子的 ，因为它要减少这个下载的体积嘛 。 刚才你说这是一个原因之一 。

但是像 Linux 啊 ， 这种发行版它很多就是说我们整个系统都是开源的 ， 对吧 ？ 那我们肯定是希望能够把这种 ， 就是能够把各种合力都集中起来 ， 就不要大家都去自己搞一份了 。

**吴涛** [24:45]
对 。 但实际上现在 Linux 社区面临的一个问题是 ，Linux 已经越来越不是一种人人为我为人人的模型了 。 就是有很多的企业级的用户在用 Linux，而从来他们基本上是不会去考虑 ， 或者是有这个利益驱使让他们能够把自己的某些修正反馈给上游的 。

对于他们来说 Linux 最重要的就是不要有冲突 ， 然后部署起来简单快捷好用 。

**Rio** [25:17]
就能够稳定可靠的运行就好了 。 不要 ， 我才不跟你扯那个什么会不会回到上游去 ，是不是 ？

**吴涛** [25:24]
对 。 没错 。

**Rio** [25:26]
这其实这个行为是会被谴责的吧 ？

**吴涛** [25:29]
也不是吧 。 因为毕竟你看很多 Linux 发行商 Red Hat 直流吧 ， 它们其实很大一部分工作 ， 或者很大一部分利润的来源就来自于对于企业级用户提供支持 。

所以其实是有人来付钱让它们来做事 ， 让用户满意的 。

**Rio** [25:49]
嗯哼 。 对 。 对 。 就是还是根据你这个需求来嘛 。 就是说如果你需求是稳定可靠的话 ， 你可能要考虑一下采取刚才那种方式 。

就是你宁可放弃别人帮你修复了这个 bug 带来的好处 ， 你也要说我不管了 ， 那个风险太大了 ， 对吧 ？

你改一改 ， 说不定我这个应用就 crash 了 。

**吴涛** [26:10]
对 。

**Rio** [26:11]
对 。 还是追求那个是第一目标嘛 。

**吴涛** [26:13]
嗯 。

**Rio** [26:14]
不过这里说到 Linux 哈 ， 我们可以这里就顺便提一下， 就是在大部分的这种比较大的 Linux 发行版 ， 它都有自己的一个一套系统吧 ， 一套这个什么包管理系统 。

**吴涛** [26:27]
对 。

**Rio** [26:29]
然后这里可能比较著名的两个 ， 一个是以 Debian 为首的那个 ， 那个叫 DEB 那个体系 ， 然后一个是 Red Hat 为首的叫什么 RPM， 对吧 ？

**吴涛** [26:40]
对 。RPM 最开始是 Red Hat Package Management 的缩写吧 。 但后来现在好像 somehow 缩写变了 ， 变成就叫 RPM Package Management System。 就是它变成了一个像 GEO 那样的 ， 叫什么 recursive acronym。

**Rio** [26:59]
啊 。 对 。 对 。

**吴涛** [27:00]
对 。

**Rio** [27:01]
就不是很扯 。 嗯 。 那么其实有一个问题啊 ， 我其实是没有怎么接触过这个 RPM 这套体系的 。 我因为我大部分都是在 Debian 或者是 Ubuntu 系统上面用 ， 所以我觉得用 Debian 还挺顺手的 。

就我不知道你有没有接触过这个 RPM 这个 。

**吴涛** [27:20]
我在进入目前的公司之前也是从来没有接触过 RPM， 或者说接触过但非常有限 。 但是我们公司目前在用 CentOS， 所以

在服务器的环境上面的确是在跑 RPM。 但我的所有的私人的 ， 包括 VPS 或者是虚拟机全都是 Debian， 所以我也可以说算是一个 Debian 的阵营的人吧 。

就其实挺可笑的 ， 就是 Debian 和 RPM 都可以用 FlameWare， 这也是一个 。

**Rio** [27:55]
这个还是很大的一个坑呢 。

**吴涛** [27:56]
对 。

**Rio** [27:58]
知乎上最之前有一个挺 ，也是就这个坑吧 ， 一个有人写的 ， 就是两边都有这个 ，有支持 Debian 这一派的 ， 然后有这个支持 RPM 这一派的 ，他们在那边吵 。

当然这个吵也不是中国首次了 ，也是什么很多国外地方就已经吵过 ， 然后中国的 Linux 社区早就吵过无数遍了 。

不过最近就是前一阵子吧 ， 知乎上又在吵这个事 。 但是因为我自己个人并没有什么太多的经验呢 ， 所以我觉得没什么特别感觉 。

如果你也没有什么经验 ， 我们可以直接跳过这个话题 。

**吴涛** [28:33]
对 。 总结来说就是我的印象好像是做运维的一方好像会比较偏向于 RPM。

**Rio** [28:41]
对 。

**吴涛** [28:41]
然后做开发这一方会觉得 Debian 比较好 。 因为 RPM 比较强调于

， 怎么说呢 ， 就是强调一个包一定要来自于 upstream， 然后一定要正统 ， 然后一定不能是 patch 过的 。 然后

就是节节的部署用 RPM 相对来说无痛一点 。 而 Debian 包的优点则是它相对来说一般比较新 ， 然后它可以在包里面加上 patch， 然后 。

**Rio** [29:14]
就其实我作为一个用户 ， 我看了这么久 ， 我的理解就是说 ，因为作为开发者你可能会经常需要一些比较稍微新一点的工具 ， 对吧 ？

**吴涛** [29:23]
对 。

**Rio** [29:24]
或者你需要用到一些比较方便安装的东西 。 但是就是 Debian 可以让你比较方便的安装 ，也不叫最新吧 ， 或者是较新的版本 ， 然后能够比较方便的做一些修改 ， 然后再发布出去 。

**吴涛** [29:37]
对 。

**Rio** [29:37]
但 RPM 的话 ， 你刚才讲就是首先它包的数量应该是比 Debian 的包要少得多 。

**吴涛** [29:43]
小心说话啊 ， 说不定就要被人砸 。

**Rio** [29:46]
小心说话 。OK。 我应该 ，但这点我是看到就是在吵的两边对这个事情都没有异议的 。 这个应该还是比较安全的一个说法 。

**吴涛** [29:54]
OK。

**Rio** [29:55]
然后就说但是它每一个就是它有的包的那个 ， 就是如果它有这个包是有的话 ， 它是做得不错 ， 就是稳定可靠嘛 。

**吴涛** [30:04]
对 。 而且反复经过很多次测试 。 就我听过一个版本就说所谓 Debian 的 stable， 就是 Debian 的 stable 版本 ，其中的测试其实也不是很多啊 ， 就真的不算不得真的是 stable 啊 ，因为还是比较不稳定的 。

**Rio** [30:23]
不 。 那个 Debian 的哲学其实也很简单 ， 它也不叫什么测试 ， 就是它先测一测 ， 然后放到那个什么 unstable 吧 ， 还是哪里 ， 然后再到 test 里面再看看有什么 bug 没有 。

稳定的话 ， 这就挪到那个 stable 里面去了吧 。 应该是这么一个流程 。 因为它本来就是一个并不是像 Red Hat 那种有这种企业能够去专门去做这种事情 ， 还是靠这个大家这个维护者 ， 虽然有很多很多哈 ， 去这么一个做 。

还是一个比较相对松散吧 ， 我觉得 。

**吴涛** [30:55]
对 。 对 。 对 。 说到这个 ，Debian 的发行版后面好像的确没有一个怎么说 ， 财团在支持 。

**Rio** [31:04]
对 。 它就是 Debian 是一个就靠社区嘛 。

**吴涛** [31:06]
对 。

**Rio** [31:07]
就不像那个 Red Hat 的 ， 它有这个公司的商业支持 。 但这里的问题就是说 ， 到底商业支持能到什么程度 ， 或者是它支持的是不是刚好你需要的 ， 这就是另外一回事 。

但反正这事情就是只要你肯花钱 ， 问题总是可以解决的 。 现在问题是说没钱了以后怎么办 ？

**吴涛** [31:25]
对 。

**Rio** [31:26]
对 。 那就有这么两个问题 。 还有一个叫 Yum 是哪里面的 ？

**吴涛** [31:34]
Yum 是 ， 就是这么说吧 ， 就是 APT 是 Debian 的高层嘛 。Debian 包系统就是最底层的工具是 DPackage， 然后 RPM 包的管理器就叫 RPM。

但是 APT 是基于 DPackage 的一个比较高层管理工具 ， 它会去帮你做什么 dependency management 之类的 。 而 Yum 和 Zypper 则是分别来自于 Red Hat 和 Suze 的两个基于 RPM 管理工具的高层工具 。

**Rio** [32:08]
OK。

**吴涛** [32:08]
对 。

**Rio** [32:09]
对 。 我觉得这个也是一个挺有趣的一个事情啊 ， 就是它们我们会把这个包管理分的层级分得很细 。 就刚才你说有些底层的工具去做一些最基础的事情 ， 然后上面高层的工具去做一些把这个整个流程变得没有那么繁琐 ， 然后界面对用户友好那么一丁点的事情 。

**吴涛** [32:28]
对 。 但我觉得这个友好的其实不是 ， 就是不是一丁点 。 因为比如说 APT 这一套工具 ，APT 这套工具可以帮你自动去分析这个包需要依赖于哪些东西 ， 然后自动帮你给出一个就是解决依赖的解决方案 ， 诸如此类的 。

如果你自己要一个一个用这个 DPackage 的工具去搞的话 ， 或者 RPM 自己去弄的话 ，其实非常非常繁琐 。 我记得我刚上大学的时候 ， 摆弄 Linux 的当时的疑点之一就是安装程序真的一定要用 RPM 吗 ？

然后为什么就不能有一个什么东西给我 ， 然后我双击一下就安好了呢 ？

**Rio** [33:12]
到现在还是不能 。 你还是得敲几个命令 ， 然后让它安装这个和那个 。 你得知道那个命令是什么 ， 然后你得知道那个命令的参数怎么样 。

然后就如果一切正常 ，在大部分情况下应该是一切正常 ， 它能够顺利的给你安装完 ， 没问题 。 但如果它报错了 ， 然后你怎么办 ？

**吴涛** [33:30]
对 。 但这个时候就是经验就要 kick in 了 ， 经验就要派上用场 。 对 。 我有这样一个印象 ， 就是在早期 Windows，在 Windows 上面安装程序 ， 或者说在 Mac 上面安装程序 ， 要远远方便于在 Linux 上面 。

但是从某一个时间点开始 ， 我 somehow 得到的这种印象就是在 Linux 上面得到我想要的工具反而要比 Windows 上面简单了 。

因为 Linux 比如说 Debian 吧 ， 我想要用什么东西 ， 然后比如说我在网上看别人说这个工具很好用 ， 然后我就直接 apt cache， 然后 search 我要找的东西在哪 ， 然后 apt get install， 我有了 。

可是在 Windows 上我就要去先去一个网站下载一个东西 ， 然后双击那个文件 ， 然后一路 next， 然后最后才要去双击什么一个快捷方式啊之类的 ， 才可以运行我需要的东西 。

就是在某一个时间点 ，Linux 社区或者 Linux 文化创造出了这么一种自动化安装某一个 。

**Rio** [34:34]
傻瓜性 。

**吴涛** [34:35]
对 。 傻瓜性 。 傻瓜是一条龙安装软件的功能 。

**Rio** [34:39]
对 。

**吴涛** [34:39]
而 。

**Rio** [34:40]
或者你可以从某种角度这么讲 ， 就是我们现在包括 iOS 上面那个 App Store 也好 ， 还是在 Mac 上面的 App Store 也好 ， 还包括 Windows 现在也在 ，他们也有 Windows App Store 里面 ，其实他们做的事情就是把 Linux 这种包管理器 ， 就各个发行版的包管理器的功能 ， 通过一种图形化的方式比较友好的呈现给普通消费者 ，而不是说只是像我们这种维护什么后端的 ， 或者用 Linux 的这种小众的 geek

用户 。

**吴涛** [35:08]
对 。 没错 。 所以从这个意义上讲 ，Linux 对于人类的最大贡献之一就是发明了 App Store 的某种 geek 方式 。

**Rio** [35:19]
对 。

**吴涛** [35:20]
某种 hardcore 形式 。

**Rio** [35:22]
嗯 。 所以就是我们刚才现在讲 ， 现在在谈的这些各种发行版的就是包管理器的话 ， 就是其实我们就等同于是在讲某个发行版的 App Store 一样 。

**吴涛** [35:33]
没错 。

**Rio** [35:34]
然后对 ， 我觉得你刚才说那点很好啊 ， 就是之前最开始我接触 Linux 的时候 ， 就是确实是非常对普通用户不友好 ，因为那个时候也不是特别熟悉 ， 然后就慢慢去摸索 ，有些东西完全不懂嘛 ， 就是连概念上都不一样 ， 跟 Windows 上面的那种逻辑啊 ， 那种你对这个世界的认知都是不一样的 。

后来它慢慢改嘛 。 我印象中大概是在 ， 就我拿一个 Ubuntu 这个 ， 可能它有那个年份的时间比较清楚啊 ， 我觉得大概是在 Ubuntu 9 和 10 大概之后， 就是 10 年左右开始 。

所以在起码跟我个人使用的经验来讲 ，在 Ubuntu 这种就是 Debian 系统上安装程序是一个相当还蛮愉悦的体验 。

就是越来越少遇到包管理器不能帮你解决的这种版本冲突的时候 ， 就现在基本上很少遇得到 。

**吴涛** [36:24]
对 。其实可以感受到它的整个工具有一套慢慢磨合好的这样一个过程在里面 。

**Rio** [36:32]
对 。 对 。 不过但是这里还是有一个小小的代价哈 ， 就是刚才你讲的说 ， 现在你什么去一个 APT 里面搜一下， 然后找装个软件很方便 。

但问题是说这个是有前提的 。 前提就是首先这个软件得是在 APT 那个仓库里面能找到的 ， 就是有人把它提交上去了 ， 要么是这个软件的作者也好 ， 要么是一个第三方的这个发行版的这个维护的人员把它加进去了 。

但如果你要找一个不是在那里面的程序的话 ， 就这个过程还是蛮辛苦的 。

**吴涛** [37:04]
对 。 这个就比较算有一个叫什么积攒动量的过程吧 。 就是当你这个软件包管理系统大到一个一定程度 ， 那么你可以理解为任何一个不会在这个 APT source， 就是 APT 的源仓库之中出现的软件 ， 都不是什么太大或者是值得你去信赖的软件 。

因为 。

**Rio** [37:29]
不 。 你说这个要小心哦 ，因为有些很小众的软件 ， 它可能只是因为作者也没有怎么去维护它嘛 ，因为可能比较简单或者什么也好 。

**吴涛** [37:37]
对 。

**Rio** [37:38]
或者是比较偏 。

**吴涛** [37:39]
这就是一个问题 ， 就是如果这个软件简单到不需要 APT 来安装的话 ， 那你很可能安装它的过程也不会比使用 APT 安装其他软件复杂多少 。

**Rio** [37:51]
嗯 。 对 。 这是一个 ， 这个是一个不错的这个 argument。 但是有些是软件挺复杂的 。 这个我印象还蛮深的 。

就是我不知道 ， 你有一段时间我在经常折腾一些视频的东西嘛 ， 然后你知道在 Linux 上面处理视频到目前为止还不是一个比较好的体验 ， 所以它在这一块还是比较弱的 。

比如说最简单一个例子 ， 你要重新封装像 mp4 啊 ， 这些视频的一些格式 ， 它会用到一些软件包 。 然后这些软件包呢 ，因为一个是因为版权的原因 ， 它不能够直接提供这个二进制的发行版 。

另外一个就是它的有些可能用到的这种代码 ， 它可能跟某一个 Linux 发行版的这个授权协议有一点不兼容 。

就是它就处于这种比较尴尬的状态 ， 就是爹娘都不爱 ， 就是发行版的维护者没法把它装 ， 没法把它加进去 ，因为有这个软件协议的问题 。

然后它自己就没有办法提供二进制的版本 ， 你得自己去下载原版来包 ，因为有那个法律的问题 。

**吴涛** [38:51]
对 。

**Rio** [38:52]
就遇到这种情况你就很沮丧 。 你作为一个用户的话 ， 你只能想办法说 ， 啊 ， 好吧 ， 那我琢磨说怎么去把这个它的 source 抓回来 ， 然后根据它的手册一个去安装 。

这里就是提到这么一个概念 ， 就是说从安装别人给你包好的软件包 ， 还有是从这个直接那个软件的原始出处去下载它的源文件回来 ， 从自己自行编译安装 。

这是两种比较大的区别吧 。

**吴涛** [39:20]
对 。 这是 ， 这可能也是当初从 Windows 切换到 Linux 上面的一个很大的迷思 ， 就是当时我的理解是在 Linux 上面你要安装一个程序的唯一方法就是先找到这个程序的源代码 ， 然后自己在 Linux 上面编译它 。

但是我一方面觉得这个方法实在是太酷了 ， 然后另一方面又觉得这不是有一点 ， 就是 。

**Rio** [39:46]
你不觉得 crazy 吗 ？

**吴涛** [39:47]
为什么呢 ？ 为什么你不能给我一个已经编译好的二进制程序让我来运行呢 ？

**Rio** [39:52]
嗯哼 。

**吴涛** [39:52]
对 。

**Rio** [39:53]
这里其实又有涉及到另外一个问题啊 ， 就是有些人会觉得我要有源代码 ， 然后因为你知道很多软件在编译的时候就会有各种各样的选项嘛 。

有的人会觉得说我装这个软件 ， 我只需要编译我需要的那一部分功能 ， 然后我不需要那部分功能 ， 我就不编译 。

这样我的代码会出来 ， 结果会小 ， 然后可能运行的会快一点 。 或者说更从一个务实的角度来说 ， 如果我这个软件包里面没有那些我不需要的冗余的代码 ， 那么是不是我可以在某种程度上减小我的安全的这个叫做暴露的被攻击的面 ， 对吧 ？

**吴涛** [40:26]
对 。

**Rio** [40:28]
啊 ， 就这也是一种想法 。 然后另外一种想法就是说 ， 如果每一个软件你都要根据你自己的方法去定义一遍这个安装的 ， 就编译的选项 ， 那么会导致同一个软件所有安装的那个选项 ， 安装的最终结果都是有一点点不一样的 。

这样的话会导致支持起来 ， 就是软件作者要给你提供一定的支持 ， 或者是你要除错起来就非常困难 ，因为别人很可能没有办法在它那个编译的结果上重现你的问题嘛 。

**吴涛** [40:57]
对 。Cannot reproduce won't fix。 这是所有的软件维护者最喜欢选的一个选项 。

**Rio** [41:04]
对 。 对 。 所以这里其实也看 ， 就是在自由灵活和这个 ， 就是你要什么 ， 你要定制化和你要用这种大众产品的之间也是有一个取舍的 。

**吴涛** [41:16]
对 。

**Rio** [41:16]
就是这个取舍就是说 ， 啊 ， 定制化当然好 ， 就刚才也讲了 ， 比如说有可能更安全 ， 可能性能更好 ， 可能更灵活 ， 对吧 ？

但是你要牺牲的就是这个可维护性嘛 。 然后另外一个角度就是说大众产品说 ， 那我大家都用一个版本 ，有问题的话我们都在这个一个版本里面修 ， 就一个编译方式出来修 ， 修好大家都能够受益 ， 对吧 ？

所以这是一个也是两种不同的思路吧 。

**吴涛** [41:40]
对 。

**Rio** [41:40]
嗯 。 但是其实并不是所有的包管理器都是从这个二进制的 ，其实大部分都不是二进制的方式吧 。

**吴涛** [41:50]
嗯 。 现在很难说了 。

**Rio** [41:52]
一半一半 。

**吴涛** [41:52]
一半一半吧 。 但至少在 BSD 上面 ，BSD 的 ports 基本上还全都是以获得原码的方式 ， 线编译的方式来

安装软件的 。

**Rio** [42:06]
对 。 这点我觉得其实当时我接触的时候觉得还是蛮特别的哈 ， 就是因为首先一个就是 BSD 的那个 ， 就是以那个 FreeBSD 这个例子来说 ，因为我以前用过 ， 就它那个叫做 port system， 就比较 ， 它做的比较好 。

因为首先这个 FreeBSD 这个系统整个维护的比较好 ，因为它是一个 ， 它不像 Linux 这种是一个以内核和这个 user space，user land 是分开的 。

对 。 它是两波源码在维护 ， 然后这个发行版是说要把这两个包在一起 ， 然后给你一个定制的产品 ， 那就会导致很多各种各样奇奇怪怪的发行版 ，也会导致各种各样的什么包管理的问题 。

但是 FreeBSD 这个起码它是有一套人马在维护的 ， 然后它可以确保说它给你的这套这个 port， 这个 port file 你是一定可以在你那天的机器上编译成功然后装上的 。

**吴涛** [42:59]
对 。其实 Linux 的生态有一点像 ， 就是有一个人， 或者有一波人马做出了这么一个 kernel 来 ， 然后围绕这个 kernel 有一群人在上面插各种各样的插件 。

**Rio** [43:11]
嗯 。

**吴涛** [43:11]
然后会造成 ，其实我倒觉得 Linux 在这方面

， 它的所谓 kernel space 和 user space 的分也是比较模糊的 。Linux 真正意义上的 kernel 其实只有那么一点点 ，而后面外面很大一个外围都是 user land， 可以这么理解 。

而在 BSD 这边则你可以说每一个 BSD 发行版都有一个明确的 kernel space 和一个明确的 user land。

**Rio** [43:43]
而且他们都是同一帮人。

**吴涛** [43:44]
对 。 而且是同一帮人维护 。

**Rio** [43:45]
这个很重要 。

**吴涛** [43:46]
而且版本号 ， 版本号好像是比较一致的 ，不像你在一个 Linux 下面 ， 你可能有一个软件的版本特别新 ，而另外其他部分则基本上是可能要倒退那么两三年的水平 。

而在 BSD 上面 ， 你会基本上在每个发行版里面拿到比较年份相近的一批工具 。

**Rio** [44:09]
嗯哼 。

**吴涛** [44:10]
对 。 但是我也很久没有用过 BSD 了 。

**Rio** [44:13]
嗯 。 其实现在我们看从那个实际使用的情况来看 ，也是就是 BSD 的市场份额应该是比较小的已经 。

**吴涛** [44:21]
对 。

**Rio** [44:22]
而且另外有一个 ， 我有一个比较明显的例子 ， 就是上一节目我们不是提到一个叫做 Varnish 的那个反向代理缓存软件嘛 。

**吴涛** [44:29]
对 。

**Rio** [44:30]
它那个本来最开始的时候是在 FreeBSD 上写的 ， 那个开发者也是 FreeBSD 的这个内核开发者之一嘛 。 然后后来发现他们在做商业化的时候 ，其实大部分人都是在 Linux 上面跑他们的系统的 。

然后他们现在连做新的那些扩展的功能都是先考虑 ， 或者是只考虑 Linux 版本了 ， 然后 FreeBSD 那边都已经不怎么管 。

**吴涛** [44:51]
对 。 这也是一个多少有点可惜的现状吧 。

**Rio** [44:56]
对 。 因为其实如果 FreeBSD 流行像 Linux 上面流行的话 ， 那我们如果在 Mac 用会就比较爽 。 这都是 BSD 内核它切换起来 ， 就是包括一些那种系统调用啊 ， 还有那种软件的包啊 ， 还有这种 ， 包括很多这种规范啊 ， 行为方式都是比较接近的嘛 。

**吴涛** [45:14]
对 。

**Rio** [45:14]
所以会爽一点 。 但其实现在就比较糟糕了 。 比如说我们现在跑服务器上面大部分都是 Linux，但是 Mac 上面又是另外就是 BSD 的内核嘛 。

这样的话就经常会不 match， 你要搞一下这个还是挺折腾的 。

**吴涛** [45:31]
对 。 我觉得不管怎么说 ，Mac 作为一套 ， 或者是 Darwin 这个东西作为一套 BSD 系统 ， 还是可以从开源界受益的 。

这点已经是很不错的了 。 就无论至少我们有 ， 至少我们有第三方工具可以用 ， 就至少我们有 Linux 上面的很多很多工具可以通过 Homebrew 或者是 MacPorts 来直接拿来用 。

这已经是很好的一点了 。 反观 Linux。 反观 Windows 用户可能就没有那么幸运 。

**Rio** [46:06]
啊 ， 这里就说到了在 Sun 上面的 ， 就除了那个官方苹果那个 App Store 之外哈 ， 我们还其实有很古老的 ， 最早的叫什么 Fink 这么一个包管理器 ，Fink 哈 。

### Mac包管理

**吴涛** [46:19]
对 。Fink。

**Rio** [46:19]
这个其实我自己是没有用过 。

**吴涛** [46:21]
我也没有用过 。 我只是听说过 。

**Rio** [46:24]
对 。

**吴涛** [46:24]
咱们两个都没有用过 。

**Rio** [46:24]
对 。 在用 MacPorts 的时候听说过 ， 说已经 ， 叫什么 ，deprecated。

**吴涛** [46:29]
对 。It's a better thing。Forget about Fink。Use MacPorts。OK。

**Rio** [46:33]
对 。 所以我接触到在 Mac 上面用的这种类似 Linux 包管理器 ，其实就是从 MacPorts 开始的 。 然后 MacPorts 又是可以 ，因为你看名字也看得出来 ， 它是那种 port 系统哈 ， 就是跟 BSD 的 port 是有一点点相似之处的 ， 我觉得 。

**吴涛** [46:50]
很大程度上是同一套系统 ， 只不过 BSD port， 如果我 ， 当然我的 BSD 可能我已经有三五年没碰过 BSD 了 ，但如果我的 BSD 知识没有欺骗我的话 ，BSD 上面的 port 其实就是一个 Make file， 就是你要进到那个 port 的子目录下面 ， 然后手动运行 Make，Make install 啊之类的 。

然后 MacPorts 跟这套系统唯一差别就是 MacPort 会自动帮你运行 Make，Make 这个过程 ， 仅此而已 。

**Rio** [47:20]
嗯 。 对 。 对 。 就它 ， 你整个 ， 你下回来的还是软件的原始代码 ， 然后你要去通过这个手工编译 。

当然这编译之前你还要先装一些什么编译器啊 ， 那些是另外一个基础需求 。

**吴涛** [47:32]
这些都是 。 然后 MacPorts 唯一的

特点就是它是用 TCL 把整套编译和安装的过程帮你自动化了 。 然后当时还有一个优点就是当时有一些那个苹果的员工在直接的贡献这个系统 ， 所以他们有很大的优势 ， 就他们知道自己干什么 ，而且他们可以做的比较 Apple like。

**Rio** [47:58]
嗯哼 。

**吴涛** [47:59]
对 。

**Rio** [47:59]
对 。其实就是在相当长一段时间里面哈 ， 那个 OST 的用户就是做软件开发者的话 ， 或多或少的也会接触到这个 MacPorts 这个系统 ， 要么你自己写一点 ， 要么就装别人的东西 。

不过在我有限的记忆里面 ，MacPorts 的使用体验并不是特别好 ，因为经常会有这种各种各样的问题导致安装不成功啊 ， 编译失败 ， 然后这个时候就会进入一个比较混乱的状态哈 。

你要去找那个 ， 你要去看那个它的那个 port file， 然后去看安装到哪一步了 ， 哪一步失败了 ， 要清理哪些垃圾文件 ， 怎么怎么的 。

**吴涛** [48:33]
对 。 它的用户界面不是那么的友好 。

**Rio** [48:36]
嗯哼 。 所以后来就有一个叫做这个 Homebrew， 我们通常简称它叫 Brew 嘛 。

**吴涛** [48:43]
横空出世啊 。

**Rio** [48:45]
对 。 然后一刀解决了这个问题 ， 然后现在基本上 MacPorts 也是被淘汰掉了 。

**吴涛** [48:51]
还是有一 ， 对 。 怎么说呢 ， 就是 MacPorts 还是一批死忠的拥趸在那边用 ， 然后他们会觉得 Homebrew 就有点像 ， 就像所有沾染了 Ruby 的东西一样 ， 都是一群喝了 cookies 的 ， 喝了 cookies 的这个小屁孩们在那边闹 ， 说这个比较好 。

**Rio** [49:13]
嗯哼 。 所以这里你提到哈 ， 就是刚才你讲那个 MacPorts 的那 port file 是用 TCL 脚本来写的 。

**吴涛** [49:19]
嗯哼 。

**Rio** [49:20]
然后 Homebrew。

**吴涛** [49:21]
TCL 脚本 。

**Rio** [49:23]
是吗 ？ 你这个 。

**吴涛** [49:25]
应该是 ， 应该是没错 。 对 。MacPorts port file 是 TCL 写的 ， 没错 。

**Rio** [49:29]
对 。 然后这个 Homebrew 的这个安装的 ，他们叫一个 formula， 对吧 ？ 是一个 Ruby 的 class 文件其实是 。

**吴涛** [49:38]
对 。

**Rio** [49:39]
啊 ，因为其实我个人对 Ruby 没什么特别感觉 ， 我不知道你觉得怎么样 。

**吴涛** [49:44]
啊 ， 具体到 Homebrew 的 formula 上面 ， 它其实你可以把它理解当成一门 DSL 来学习嘛 。 所以你可以完全 ， 你可以完全不懂那个 Ruby 里面的那个什么两个竖线中间加一个变量名到底是什么意思 ， 你也可以去操作 Homebrew 的 formula， 你就一壶的话瓢嘛 。

所以它其实是把它做得相当傻瓜化的这么一个 formula。 我刚刚写 ， 就我记得你还在推特上问过我说 ， 你难道没有那个写过自己的 formula 吗 ？

我当时很老实而且很骄傲的说没有 。

**Rio** [50:25]
我都还写过一两页 ， 还是两三页 。

**吴涛** [50:28]
对 。 但后来我也写了 ，但我觉得就是即便像我这样对 Ruby 其实没有什么太深了解的人， 也是可以

驾轻就熟的去操作一个 formula file 的 。 所以不是很 ，不是没有什么太大问题 。

**Rio** [50:44]
对 。 不过说回来这个 Homebrew 本身哈 ， 就是我觉得它 ， 就现在它 Homebrew 已经现在就是这个 OS 上面的叫做什么继承事实的 ， 打引号的官方这个包管理器了吧 。

因为你如果你现在去关注一下 GitHub 上面的那些各种软件也好 ， 你会发现它只要涉及到跟 OST 有关的 ， 大部分会选用 Homebrew 的方式 。

**吴涛** [51:10]
对 。 而且尤其是新工具会倾向于提供一个 Homebrew formula， 比如我们上次提到的那个 Neovim 就提供了一个 Homebrew 的 tap。

**Rio** [51:20]
嗯哼 。

**吴涛** [51:21]
对 。

**Rio** [51:22]
所以这里就有一个延伸的问题 ， 就是你觉得为什么 Homebrew 会在如此短的时间内完全就替代掉了 MacPorts？

**吴涛** [51:32]
啊 。

**Rio** [51:34]
因为这个和当年那个 MacPorts 取代 Fink 还是有蛮大区别 。 当时我记得印象中 MacPorts 和 Fink 还是共存过相当长一段时间的 。

**吴涛** [51:42]
我觉得第一是它把提供一个新软件的门槛降低了 。 因为 Ruby 的强项就是写一个 DSL 嘛 ， 然后它用的这种 DSL 使得你发布一个新的软件到 Homebrew repository 的过程变得荒唐的简单 。

而 MacPorts 它因为用 TCL，TCL 毕竟是一个从来没有大众过的 。

**Rio** [52:11]
比较小众的 。

**吴涛** [52:13]
然后你写它还是需要花一点脑筋的 ， 你要必须去明白它是一个什么样的运作原理 。 然后我觉得还有一个可能会成为一个决定性因素的是 ，Homebrew 给你一种虚假的安全感 ， 就是你每次 brew 安装什么东西 ， 你不需要速度 。

**Rio** [52:33]
前提是你要 。

**吴涛** [52:33]
前提是你安装的时候用了速度 ， 对吧 ？

**Rio** [52:36]
不是 。 你要把那个 user locale 设定为你自己账户可读写嘛 。

**吴涛** [52:40]
对啊 。 但是一般来说 。

**Rio** [52:42]
就是他们推荐的一种方式 。

**吴涛** [52:43]
对 。 一般来说每一个 ， 就是如果你默认装一台新电脑默认直接用它的安装脚本去安装 Homebrew 的话 ， 它就自动会帮你这样做 。

**Rio** [52:52]
啊 ， 现在已经是这样了吗 ？

**吴涛** [52:53]
应该是这样的吧 。 我反正安装这台新的

MacBook 的时候 ， 好像是完全没有自己去改 。

**Rio** [53:02]
它会推荐你这么干 ， 我记得确实是这样的 。 但我不太确定是不是它会默认吧 。

**吴涛** [53:08]
就它可能会在安装过程中询问你要不要这样干 ， 然后你应该我也是就是草率的打了一个 y。

**Rio** [53:15]
嗯哼 。

**吴涛** [53:16]
对 。

**Rio** [53:16]
OK。其实有一点我一直在想哈 ， 就是 GitHub 对这个 Homebrew 的帮助有多大 。其实我觉得这个应该是挺关键的一个 。

**吴涛** [53:25]
那应该说是相当大 。 因为 。

**Rio** [53:28]
对 。

**吴涛** [53:28]
就是你刚才说它是一个 de facto 继承事实的软件包安装工具 ， 我就想说它其实仅仅是对程序员这样而言而已 。

但正是因为此 ， 几乎每一个稍微有点自尊的程序员肯定都会去搞搞 GitHub， 然后 Homebrew 就在 GitHub 上面 。

**Rio** [53:48]
对 。 对 。 这点我觉得就是跟那个像 MacPorts 很不一样 。MacPorts 那些我都不知道它那 port file 文件是从哪里来的 ， 然后怎么去提交 ， 然后整个看了一下那个网站也做得很丑 ， 对吧 ？

但是 Homebrew 不一样 ， 它所有的这个那个所谓的 ，他们叫 formula， 就是我们类似于 make file 或者 port file 这么一个东西 ， 它都是整个包是放在这个 GitHub 上面的 ， 然后因为 GitHub 是比较友好的一个工具嘛 。

**吴涛** [54:15]
对 。

**Rio** [54:15]
对程序员来说至少 。 然后都会大家觉得 ， 诶 ， 那我给你添加 ， 我不就是 fork 一下你这个吗 ？ 然后把这个加进去 ， 然后再给你发一个这个 pull request， 这个东西就算搞定了 ， 对吧 ？

整个流程是非常方便的 。 所以这里其实就是说 ， 我们要说 UI 做得好 ，但 UI 是广义上的哈 ，不只是说用户 ， 最终消费者我们看到一个按钮 。

**吴涛** [54:38]
应该是这个 ， 这个就应该叫做 UX 了 ， 就是 user experience， 用户体验非常好 。

**Rio** [54:43]
啊 ， 这个叫 ， 对 ， 这个用户就是我们那种 geek 宅程序员 。 对我们的友好的话 ， 我们也会更加乐意去给它什么添砖加瓦嘛 。

**吴涛** [54:53]
你刚才说的这一点很好 ， 就是当你打开

Homebrew 的页面的时候 ， 它会用一上来就告诉你 ， 一上来给你讲一个展示给你这个工具能干嘛 ， 给你讲一个例子 ， 给你讲一个故事 ， 然后用大字 ， 用易读的简单的文字告诉你这个工具是干嘛的 ， 这工具怎样安装 ， 怎样使用 ， 感兴趣的话去干这个 。

而反观 MacPorts， 你打开它网页 ， 它网页是一个很典型的这种

门户网站的感觉 ， 就是一上来密密麻麻一大堆信息 ， 什么 。

**Rio** [55:30]
你知道在说什么 。

**吴涛** [55:32]
Documentation，update，recent news，blah blah blah blah。 你想要干嘛 ？ 你想知道这个工具到底是干嘛的 ， 你不知道 。

**Rio** [55:41]
对 。 对 。 所以其实 ， 所以刚才你提到哈 ， 就是说那个什么 ， 我们都讲那个什么 cool a drinking kit。

**吴涛** [55:48]
对 。

**Rio** [55:49]
其实这个我觉得是所有的开发者 ， 包括这个产品经理都要学习的一个东西 ， 就怎么把这个叫做 ， 我们这个其实有一个专门的叫做什么 ，user landing 吧 ， 还是叫什么 ？

**吴涛** [56:02]
就是第一批用户 ， 当用户第一眼目睹你的产品的时候 ， 你给他一个什么样的印象 ？

**Rio** [56:08]
就是在用户已经知道有你这么一个东西 ，但是还不知道怎么用的时候 ， 你怎么让他能够快速的去上手 ， 怎么让这个过程变得比较爽 。

我觉得这个是可能是 Ruby 社区 ， 就是 Ruby 或者 Rio 的这个社区给整个其他软件这个社区的一个最大的 ， 叫什么 ， 财富吧 ， 就是他们展示了这一点 ， 就是让一个哪怕他只是对这个有兴趣 ，但是可能并不知道一些具体的细节的人， 也能够很快的上手 ， 然后之后再去想办法去摸清那些细节嘛 。

而不是说你先上来甩给他一个啊 ， 我这个什么很高冷的形象 ， 然后你看不懂就自己去吃屎吧 。 这样就不太好嘛 。

**吴涛** [56:51]
以这种姿态能最终取得成功的人，

旷古至今只有 Linuxtreo 是一个人吧 。

**Rio** [57:01]
所以是不是我们节目的姿态要放低一点 ？

**吴涛** [57:05]
哎呀 ， 好啊 。 下次放低一点吧 。

**Rio** [57:09]
啊 ， 那接着说这个 Homebrew 这个事哈 。 所以就现在我们看到就是基本上那些新的包啊 ， 最新的版本 ，他们就因为用户很多嘛 ， 然后大家都会为它去贡献啊 ， 去出错啊 ， 就使得它变成一个很良性的一个循环 。

**吴涛** [57:23]
对 。

**Rio** [57:24]
就是你作为一个新软件的作者 ， 或者你要用的一个软件里面没有 ， 你会想啊 ， 我去 Homebrew 里面找一下， 如果没有 ， 我就去提交这个 formula， 然后把那个提交的 formula 也加回到这个 upstream 上面去 ， 然后这样别人也可以因此而受益 。

所以其实到今天的话 ， 现在 OST 上面这个 Homebrew 的包管理基本上已经可以在某种程度上达到了在 Linux 上面做得比较好的那些包管理器的那种规模或者是效果 。

**吴涛** [57:50]
对 。 而且 Homebrew 甚至还被 port 到 Linux 上面 。 我当时就听刚听到这个新闻的时候 ， 我觉得还挺吃惊的 ， 就是 Linux 上， 我们在 Mac 上面用 Homebrew 是因为 Mac 上面没有 apt get 或者是没有 yum。

而为什么今天这个时代居然要把 Homebrew 这种因为没有 ， 所以不得不造出来的工具 backport 到你们那边已经比较成熟的解决方案的地方呢 ？

这也让我挺吃惊的 。 但仔细想想 ， 就是 Homebrew 的理念其实是相当先进的吧 ， 可以这么说 。

**Rio** [58:28]
对 。 对 。 而且它的整个就刚才就讲了 ， 我们这个整个用户体验是蛮非常友好的嘛 ， 就是让大家能够很快上手 。

这其实我觉得也是一个很好的类比 ， 比如说那个 formula 刚才讲的 ， 你是一个 DSL 的一个 Ruby 的文件 。

**吴涛** [58:39]
对 。

**Rio** [58:39]
你可以写起来 ， 你可以就我你看一下就大概知道它在干嘛 ，但我到现在为止也没搞懂这个怎么去自己编 ， 搞一个这个什么 Debian 的 package 出来 ， 然后能够很方便的发布出去 。

就是这个过程还是蛮繁琐的 。

**吴涛** [58:52]
对 。 而且 Homebrew 更优雅 ， 加引号的一点就是它是 ， 它跟 Git 结合的非常紧密 ， 它本身 host 在 GitHub 上面就不用说了 。

而这个 Homebrew 本身的升级也只不过是一个 Git pull 的某种被包装过的 Git pull 而已 。 就是它这个工具本身就是以一套版本管理系统的方式 ，以一个经过版本管理的软件

repository 形式出现在你电脑上 。 它需要升级的时候就只需要从它的 upstream 上更新一下就可以了 。 这个也是挺优雅的一个部分吧 。

**Rio** [59:42]
对 。

所以刚才讲的那么都是根据某一个操作系统上面而选择的不同的这个包管理器哈 ， 比如说刚才讲的 Debian 上面有没有一个 apt， 然后 RPM 系统的话是 yum， 然后有 bsd 的 ports， 然后 Mac 上面现在最新的是这个 Homebrew。

下面我们接下来要讨论 。

**吴涛** [1:00:03]
你刻意跳过了 Windows。

**Rio** [1:00:06]
啊 ，Windows。Windows 有吗 ？

**吴涛** [1:00:08]
有啊 ，有啊 ，有啊 。 看来你是 。

**Rio** [1:00:09]
有什么 ？

**吴涛** [1:00:10]
看来你真是应验了我们听众那句话 ， 就是对 Windows 的了解连大学毕业生都不如 。

**Rio** [1:00:17]
那肯定啊 ， 已经彻底放弃好多年了 ， 根本不可能 。

**吴涛** [1:00:20]
我其实很惭愧的就是我也是现学现卖 ，也是因为前阵子去关注那个 .NET 的开源才注意到的 ， 就是 Windows 下面有一叫做 ， 我不确定这个应该怎么念 ，nugget。Nugget。

**Rio** [1:00:35]
What？

**吴涛** [1:00:36]
N-U-G-E-T。Nugget。

**Rio** [1:00:38]
OK。

**吴涛** [1:00:40]
鸡块嘛 。

**Rio** [1:00:42]
OK。

**吴涛** [1:00:43]
和鸡块谐音 。 总之它就是 ，nugget 这就是一个

包括 .NET 平台在内的专门为微软开发而研发的一套 package manager。

**Rio** [1:00:57]
等一下， 等一下， 它是跑在命令行下面的吗 ？

**吴涛** [1:00:59]
它有一个命令行界面 ， 然后也有一个图形界面 ，as always。Windows 程序怎么可能没有图形界面 ， 你让 Windows 程序员怎么活 ？

**Rio** [1:01:07]
对 ， 我就在想它怎么会有这种需求 ？ 他们不是都直接在那个什么 ， 跑一个这个什么 GUI 的东西 ， 然后做个 500 个按钮的软件出来 。

**吴涛** [1:01:20]
此处省去对 Windows 程序员冷嘲热讽 5000 字 。 总之这个 。

**Rio** [1:01:26]
那谁在用呢 ？ 我从来 。

**吴涛** [1:01:28]
所有微软程序员 ， 所有现在 up to date 的微软程序员都在用应该 。

**Rio** [1:01:32]
啊 ，OK。 所以他们是一个叫什么 ，parallel universe， 我们不知道 。

**吴涛** [1:01:37]
对 。在这个平行宇宙里面 ， 比如说什么 jquery 啊 ， 或者是 。

**Rio** [1:01:42]
啊 ， 真的可以 。

**吴涛** [1:01:43]
对 ，jquery 之类的东西你都可以通过在那个 nugget 的图形界面之下搜 jquery， 然后点一下 install， 它就帮你装好了 。 虽然 。

**Rio** [1:01:53]
天哪 。

**吴涛** [1:01:53]
对 。 而且它可能有两三万个那个 package 了已经 。

**Rio** [1:01:59]
那还不挺大规模的 。 这个是有什么微软官方的维护啊 ？

**吴涛** [1:02:04]
对啊 ， 这是微软官方做的嘛 。 官方加持 。

**Rio** [1:02:06]
Holy。

**吴涛** [1:02:07]
Yeah。

**Rio** [1:02:07]
Really？

**吴涛** [1:02:08]
Yes。 而且在那个你安装了

.NET 在 macOS 上的那个 Homebrew 的工具之后， 你就有了 nugget。

**Rio** [1:02:23]
等一下，nugget 这个是不是一个脏话 ？

**吴涛** [1:02:27]
不是吧 ？nigger 是脏话 ，nugget 不是吧 ？ 微软不会蠢到这么政治不正确的 。

**Rio** [1:02:35]
这个时候就要祭出 urban dict。Nugget, a person with no arms or legs.

**吴涛** [1:02:43]
鸡块嘛 ， 就一个人把一个人。

**Rio** [1:02:46]
这是人棍吗 ？

**吴涛** [1:02:47]
人质啊 。

**Rio** [1:02:48]
人棍 ？

**吴涛** [1:02:49]
那个是人质吧 。 我忘记了 ， 就是那个吕后做的那件事嘛 ，是吗 ？

**Rio** [1:02:53]
对啊 ，不是忠啊 ， 那个叫人质是吧 ？

**吴涛** [1:02:56]
应该是人质吧 。

**Rio** [1:02:57]
好吧 。Anyway。

**吴涛** [1:03:00]
对 。Anyway， 反正从 nugget 上面你可以下载比如什么 jquery 啊 ， 包括 spnet 的 mvc 库啊 ， 甚至还有很多什么 bootstrap 好像也可以从上面下下来 ， 我记得 。

**Rio** [1:03:17]
这是面针对前端的吗 ？ 还是 ？

**吴涛** [1:03:19]
针对一切开发者 。

**Rio** [1:03:21]
OK，因为刚才你举的几个例子都是跟前端开发有关系的 。

还是说你暴露了你是在上面做前端开发这件事 ？

**吴涛** [1:03:30]
我在上面做了很简单的 Windows 用户 ，Windows 编程摸底而已 ， 没有在上面做过任何开发 。

**Rio** [1:03:41]
OK。

好吧 ， 这个反正我也没问过 ， 没法接着讨论下去它到底怎么样 。

**吴涛** [1:03:50]
总之有这么一个东西存在就是 。

**Rio** [1:03:52]
对 。 刚才我们讲的就是基于每一个各个操作系统或者是这个 Linux 发行版本的这个包管理工具的一些相关的情况哈 。

### 语言包管理

**Rio** [1:04:00]
现在我们要切换到另外一个比较重要 ，也是我们吐槽比较多的 ， 就是基于各个语言的这个包管理的东西了 。

**吴涛** [1:04:11]
对 。

**Rio** [1:04:11]
啊 ， 这里有一个比较显著的区别啊 ， 就是刚才讲的那些像 Homebrew 也好 ， 还是这个 Debian 也好 ， 就是它也很大程度上是为了这个我们讲部署的时候用的嘛 。

就是部署就是可能最终用户自己安装 ， 或者是我们把它弄到服务器上运行的时候这样去的一个用法 。

然后包括你用 Homebrew 安装的很多这种所谓的命令行软件 ， 你也是作为它的一个用户 ， 你不是作为它的开发者去安装的 。

当然也有例外 。

**吴涛** [1:04:37]
其实这是一个比较大的 、 比较明显而但是目前还没有人做出来的区别吧 。 就是你通过一个包管理工具安装的一个东西 ，有可能是有一个直接可执行文件的这么一套东西 ，也有可能它仅仅是一个 。

**Rio** [1:04:57]
Lib， 对吧 ？

**吴涛** [1:04:58]
仅仅是一个 library 而已 。

**Rio** [1:04:59]
对 ，有很多这种情况的 。

**吴涛** [1:05:01]
对啊 。在我的有限认知里面 ， 好像 Homebrew 其实是对此有一个比较模糊的 ，而且是一个很稍微有点绕人的概念的 ， 就是有些东西是所谓 task only。

就 Homebrew 有自己一套 jargon， 对吧 ？ 虽然我对此也不是非常理解 ， 就是比如说它因为它整个借用了这个酿司酒的这么一套术语 。

**Rio** [1:05:25]
语汇 ， 对 。

**吴涛** [1:05:26]
对 。 比如说 task 就是一个酒桶 ， 然后你从 task 里面 pour 出来什么东西 ， 然后再自己 brew 这个套 ，其实就是类比 ， 或者说它把整个编译的过程类比为发酵的过程 。

而 task only 的意思就是这套东西是不发酵的 ， 这套东西只是存在酒桶里面 。

**Rio** [1:05:47]
发酵 。

**吴涛** [1:05:48]
OK， 我有点错字了 ，sorry。

**Rio** [1:05:51]
我先纠正了 ， 免得你又被搞 。 对 ， 它有那个 ， 就当然有个事情刚才没有解释啊 ， 它有这个就之前我们讲那个 Debian 的包一般是发行的二进制版本嘛 ， 然后 ports 一般是发行源码嘛 。Homebrew 的话其实是比较诡异的 ， 就是它两种都有 。

**吴涛** [1:06:11]
它两种都支持应该说 。

**Rio** [1:06:13]
对 ， 然后它会有 ， 刚才讲有不同的词去描述它 。 我记得它的二进制版本是叫做 bottle， 就是瓶子 。

**吴涛** [1:06:22]
对 。

**Rio** [1:06:24]
这个逻辑你想想很简单 ， 就是你编译的过程是在 brew 发酵嘛 ， 然后发酵完了之后就出现一个成品的酒瓶 。

**吴涛** [1:06:30]
你就装瓶了 。

**Rio** [1:06:30]
就你叫装瓶 ， 然后就直接可以给别人用了 。 就它是有这么一个概念 ， 然后但是它对于很多那种软件就不方便装瓶的 ， 一个是可能因为法律的问题 ， 或者是因为版权的问题 ， 它没法装瓶的 ， 没法编辑的 。

**吴涛** [1:06:44]
或者甚至最简单的情况就是这个东西它根本就不是拿来用的 ， 它只是拿来作为一种一个建筑材料而已 。

**Rio** [1:06:51]
对 ， 它就会直接发布这个 source。

**吴涛** [1:06:54]
对 。

**Rio** [1:06:55]
这个我想想能够比较明显那个 fmpeg，fmpg 这个工具 ， 它就是发布的源码 ， 然后你要自己编译的 。 但是你像 vim，Macvim 好像是它是弄成一个 bottle 了 ， 我记得 。

**吴涛** [1:07:09]
Macvim 是 bottle 吗 ？

**Rio** [1:07:11]
如果我没记错的话应该是这样子 。

**吴涛** [1:07:13]
我怎么记得 Macvim 也是要编译的呀 。

**Rio** [1:07:15]
是吗 ？ 那可能我记错了 。 它有很多这种是 bottle 过的 ， 就你可以装 ， 发现安装很快 ，也不需要没有那个编译的过程 。

**吴涛** [1:07:24]
Anyway， 继续说我们这个和编程语言相关的包管理系统 。

**Rio** [1:07:30]
对 ， 这里就可能比较奇特的一个事情 ， 就是在具体到某个语言来讲 ， 它还有通常是一个有自己的一套这个包管理器的 ， 然后这个包管理器要解决的绝大部分场合是作为就为了帮助开发者写程序的时候用的 。

**吴涛** [1:07:48]
对 。

**Rio** [1:07:49]
对 ， 这么说安全吧 。

**吴涛** [1:07:51]
应该是安全的吧 。 但就是其实这里也有一个很有趣的事实 ， 就是不管是 Python 也好还是 Ruby 也好 ，有很多包其实它不是拿来 ，不是帮你提供一个开发工具的 ， 它是拿来 。

**Rio** [1:08:08]
它就是提供一个命令行工具 。

**吴涛** [1:08:09]
对 ， 它是给你提供一个命令行工具的 。 比如说我很喜欢的一个叫做 lolcat，LOLcat， 它的效果是把你可以把任何字符串 pip 给它 ， 然后它把你 pip 进去的那个字符串以七彩的形式显示在屏幕上 。

所以你可以用 ， 比如说你可以 call say， 就是 Linux 上有一个有趣的小工具就是 。

**Rio** [1:08:35]
牛说 。

**吴涛** [1:08:36]
对 ， 牛说工具 ， 它在屏幕上显示一头牛 ， 然后这个牛会头上会冒出来一个气泡框 ， 然后里面写着你要想说的话 ， 然后你先把你要说的话 cat 进 call say， 然后再 cat 进 lolcat， 然后最终的结果就是一个七彩的牛说了一句七彩的话 。

这个东西就是用 npm， 就是用 。

**Rio** [1:08:58]
好 fancy 啊 。

**吴涛** [1:08:59]
对 ， 就是用 Gem 来发布的 。 它本质就只是给你提供一个额外的工具而已 。

**Rio** [1:09:05]
对对 ， 就所以就是刚才我就说要特别限定一下， 就是说只能说是大部分场合包管理器是为了做这个开发用的工具 ，但是其实你看也提到有这种场合是它那个本来就是拿来发布成一个可直面向最终用户使用的一个工具 。

所以这里面的界限不是那么清晰啊 ， 只能说是一个大概的这么一个分类 。

**吴涛** [1:09:27]
而且你比较难的 ， 很难去一刀切说我就要硬性在我的包管理系统里面提供 ， 比如说 Gem，Gem as a tool 和 Gem as a library 的区别 ，因为有些是两者都提供 。

**Rio** [1:09:43]
对对对 ， 它提供了一个让你调试的工具 ， 比如说对吧 ？

**吴涛** [1:09:47]
对 。

**Rio** [1:09:48]
你可以很方便的启动它 ，但是它的主要作用还是提供一个库 。

**吴涛** [1:09:52]
比如 Pray 或者是 iPython 这种 ， 它既是一个工具 ， 它又是一套库 ， 对 ， 还是比较 。

**Rio** [1:10:02]
对 。Software is hard, so confusing。

好 ， 那现在我们就来 ， 就我们先着眼来讲 ， 就我们先不讲那个用这种某一个语言独特的包管理器来发布命令行工具这么一个情况 ， 我们就先只看用它来做这个开发者的事情的时候会有一些什么样的东西啊 。

### Maven

**Rio** [1:10:26]
我们还是按语言来分吧 ， 对吧 ？

**吴涛** [1:10:28]
对 。

**Rio** [1:10:30]
呃 ，Java 程序员吴涛同学 ， 请你开始吐槽 ， 开启你的吐槽模式 。

**吴涛** [1:10:34]
好 ，Maven。 对 。

**Rio** [1:10:38]
所以 Maven 为什么会给你带着这么惨痛的这个感觉 ？

**吴涛** [1:10:43]
其实我 Maven 没有真正的伤害过我啊 。 但是 。

**Rio** [1:10:47]
那你为什么这么仇视它 ？

**吴涛** [1:10:50]
公司里面 ， 公司至少因为 Maven 的一些林林总总的事情 ，在我印象里面可能有两次推迟过 release。 就 release 推迟一天其实是非常

，并不是什么天塌下来的灾难性事件 ，但至少也是让所有人不爽的一件事 。

**Rio** [1:11:13]
因为你们是做游戏的 ， 对吧 ？

**吴涛** [1:11:14]
我们不是做游戏的 ， 我们是做比游戏更海洛因的东西的 。 但对 ，anyway，不重要了 。 但就是推迟一天的话就意味着首先运维人员要从鸡飞狗跳 ， 鸡飞狗跳 24 小时变成鸡飞狗跳 48 小时 。

然后开发人员呢 ，也会因为

发布的被推迟而不得不要么临时被抓壮丁去解决导致这个推迟的问题 ， 要么就因为这一次发布不能及时运行而不及时进行而得不到一些比较宝贵的资源 ， 比如说 QA， 比如说 staging server 之类的 。

因为所有这些 server 都还在备战状态 ， 就是要去迎合这个发布的 deadline。 而 Maven 至少已经导致过两次这样的 ，在我看来已经是事故级别的问题了 。

**Rio** [1:12:15]
OK。

**吴涛** [1:12:15]
对 。 而另外一方面 ，Maven 在使用的时候本身你也会觉得一阵阵的蛋疼啊 ， 真是 。

**Rio** [1:12:25]
我又问一个问题 ，是不是还是要手工写 XML？

**吴涛** [1:12:28]
是的 。 欢迎来到 1995 年 。

**Rio** [1:12:35]
我能理解你的痛苦 。

**吴涛** [1:12:36]
而且当然你可以说 ， 你可以站出来说 ， 你可以用各种各样的图形界面去管理 XML。

**Rio** [1:12:44]
三层大门 。

**吴涛** [1:12:44]
对 ，但对啊 ，有一句名言是 XML 就像 violence，XML 就像暴力 。 如果它不能解决问题 ， 如果它不能解决你的问题 ， 那一定是你用的还不够多 。

**Rio** [1:13:01]
好吧 。

**吴涛** [1:13:02]
对 。

**Rio** [1:13:02]
所以但是 Maven 其实是一个比较 ， 就是在 Java 这个开发圈里面是一个比较什么通用常见的一个方案了嘛 。

**吴涛** [1:13:11]
可能有一半程序员会反驳你的看法 ，但

要我说 Maven 其实是一种

一个过度过早优化的典型 ， 可以这么说吗 ？

**Rio** [1:13:26]
为什么这么说呢 ？

**吴涛** [1:13:27]
因为比如说 Maven， 一个 Maven projects 一上来就强制你使用一种特定的项目的格式 ， 就是你所有的包 。

**Rio** [1:13:38]
文件格式 ？

**吴涛** [1:13:40]
就是你组织源代码的格式 ，在所有的 Maven project 里面是一模一样的 。

**Rio** [1:13:44]
就 directory layout。

**吴涛** [1:13:45]
对 ，directory layout 是一上来要有一个 source，source 里面要有 main，main 里面要有 ，sorry，main 里面要有一个 main， 然后有一个 test， 然后 main 和 test 下面的所有的文件结构要一模一样 。

对 ， 总之是 。

**Rio** [1:14:02]
其实这个没什么 ， 这个有这个规范是好事嘛 ， 我不觉得这个是一个特别不好的一个事情 。

**吴涛** [1:14:08]
但是在你仅仅想要写一些比较小的东西的时候 ， 你就会觉得这种强加于你的结构是非常让人不爽的 。

**Rio** [1:14:18]
对 ， 我觉得这就是问题所在了 。 你为什么用 Java 写一个很小的东西呢 ？ 为什么不用一个脚本语言去写 ？

**吴涛** [1:14:24]
好吧 ， 这里有个审美观念的问题在里面 ， 就是你可以说 Maven 是一种专门为了企业级应用而推出的一种硬性的解决方案 ， 就是它通过

永常繁琐的配置和

强制加在你身上的工序 ， 保证了你是一个可以被替换的公司 。 因为不管你是谁 ， 你只要用 Maven， 那么你能做的东西都是可以预定的 ，而都是可以预见到的 。

而不像你说比如说早期的洪荒时代在用 Ant 的时候 ， 你可能你的 Ant file 可能是一个天书一样的东西 ， 你的接收者是没有办法去理解你 ， 去替代你的 。

但是反过来我会觉得 ， 就像我对所有的带有 enterprise 字样的东西天生的反感一样 ， 这是一种 ， 我很难说它到底错在哪 ，但我就觉得它不是我希望的那样 。

**Rio** [1:15:28]
但如果你是抱着这种心态的话 ， 你是不是不应该写 Java 程序呢 ？At all。

因为 Java 本来就是为了 enterprise 这种场景设计的 。

**吴涛** [1:15:38]
我只能说我是为了谋生不得不做这件事情的 。 但就像所有的 ， 就像 Paul Graham 说过的那句话一样 ， 就是在他知道的所有的程序员里面 ，在他认识的十个 ，在他能想出 ， 原话我不太记得了 ， 反正大意就是说在他能想到的十个 Java 程序员里面 ，有九个不是出于资源的目的而去写 Java 的 。

对 ， 我可能也是 。

**Rio** [1:16:09]
明白 ， 生活所迫嘛 。

**吴涛** [1:16:12]
对 ，而且这也 ， 我觉得这可能也是我辞职的原因之一吧 ， 就是去寻找更绿的 。

**Rio** [1:16:19]
更不干了 。

**吴涛** [1:16:19]
对 ， 去寻找更绿的草坪的动机之一 ，也就是我终于受够了 Java 这一套 。 对 ，而且 。

**Rio** [1:16:27]
所以这里要

， 所以这里要插问一句 ， 你下一份工作是准备写什么 ？

**吴涛** [1:16:33]
可能是 Python， 应该是 Python。Python 和 C++， 然后 Java 可能会成为一个选项 ，但应该不是必须的 ， 至少目前我掌握的资料是这样的 。

**Rio** [1:16:46]
OK。

**吴涛** [1:16:46]
对 。

**Rio** [1:16:48]
好 ， 那 Java 里面除了 Maven 这一个比较你比较沮丧的这个包管理器之外， 还有什么其他选项吗 ？

**吴涛** [1:16:55]
Java 有其他别的选项吗 ？

**Rio** [1:16:57]
没有吗 ？

**吴涛** [1:16:58]
应该是有的 ，但 Maven 基本上可以视为任何一个比较上了规模的

企业级应用的开发企业都不得不 ， 或者说都会选择的一个解决方案 。 因为围绕着 Maven 产生出了一套还是相对来说比较成熟的生态系统 ， 就每一个都很繁琐 ， 每一个都很丑陋 ，但是搭载在一起的时候 ， 它在 50% 的时间里面还是可以做事的 。

**Rio** [1:17:34]
好吧 ， 就所以这是一个大家都知道很不爽 ，但是大家都知道起码这个不爽是可以预期的 ， 对吧 ？ 有多不爽也就那样了 。

**吴涛** [1:17:43]
对 ， 网上有一幅漫画我觉得一定要放在我们的 show notes 里面 ，以响读者 。 对 ， 大意就是你 ， 大意是一个你采用 Maven 之后的生产力曲线 ， 刚刚开始这个曲线是有一点点向上的 ， 就是刚刚采用 Maven 之后你会觉得哇 ， 一切包都井井有条 ， 然后一切 ， 就是 Maven 还有一个特点就是它给

你预先定义了一套非常永常繁琐啰嗦的那个编译步骤 。 你比如说你可以运行 Maven clean， 就是帮你删掉所有东西 ， 然后 Maven test 就是帮你从 precompile、compile， 然后 assembly， 然后 distributes， 诸如此类整整一套全都跑下来 ， 然后自动帮你运行测试 。

你会觉得自己的生产力好像提高了 ，因为一切都尽可能自动化了 。 但是好景不长 ， 当某一个时间点你遭遇到某一个安装包的依赖冲突问题的时候 ， 你会发现你已经失去了对整个项目的控制 ， 然后整个情况急转直下， 然后生产力曲线就跌落了一个万劫不复的深渊 。

你会发现哇 ， 我把整个 project 已经怎么说 ，mavenized， 然后在这个 。

**Rio** [1:19:10]
就是感觉这个状态像得了癌 ， 就像癌症感染一样 。

**吴涛** [1:19:13]
对 ， 我已经骑虎难下再也回不去了 。

**Rio** [1:19:18]
所以还有一个什么 ， 还能够恢复的阶段吗 ？

**吴涛** [1:19:22]
好像那幅漫画里面是没有恢复的阶段的 。

**Rio** [1:19:25]
直接死了 。

**吴涛** [1:19:27]
而且在有一个 ， 就像我上次提到过的一个 Tumblr 的一个以 gif 网站 ，以 gif 动画为主的 Tumblr 叫做 DevOps Reactions 里面就有一个

，有一期就是讲 Maven 的人如何令人痛苦的 。 很简单 ， 就是一个人在雨中流泪 ， 然后另外一个人从后面贴近他的耳朵说 ，

好像是

"It's gonna be painful"， 大概是 ， 就是大概说了这么一句话 。 然后这个 gif 的标题就是 " 得知项目里面需要用到 Maven"。

**Rio** [1:20:18]
好吧 ， 这个我们知道你的痛苦了 。

**吴涛** [1:20:21]
不过我马上就要解脱了 ， 还有一个礼拜 。 而且各位听众听到这个 ， 听到这期节目的时候我 。

**Rio** [1:20:27]
说明你已经解脱了 。

**吴涛** [1:20:28]
我已经解脱了 ， 我已经开开心心的在意大利游玩 。

**Rio** [1:20:34]
好吧 ， 就说到 Java 要说一个相关的语言啊 ， 就是这个名字叫做 Scala 的语言 。

**吴涛** [1:20:43]
或者说 next generation Java。

**Rio** [1:20:47]
我觉得不能这么说 ，因为它并不是它的一个超级 ， 首先 。

**吴涛** [1:20:51]
不 ， 它是 ， 怎么说呢 ，Scala 的目的就是取代现有的 Java 成为下一代的企业级应用的标准语言嘛 。

**Rio** [1:20:59]
我觉得这件事是不会发生的 。

**吴涛** [1:21:01]
对 ， 你觉得不会发生 ，但至少我在我这边 ，在我看到的新闻里面 ，Scala 这是 Scala 的目标 ，而且欧盟也的确 ， 欧盟给 Scala 扔了很多很多钱 ，因为 Scala 是某种意义上是一个欧洲语言 。

就它是德国人开发 ， 瑞士人制造 ， 德国人开发 ， 瑞士人制造 ， 基本上是你所能得到的编程语言里面最复杂的一种可能性 。

**Rio** [1:21:26]
对 ， 就为什么我讲那个 Scala 不可能替代 Java 成为主流哈 ， 我觉得这个在工业定性上就已经有这一点了 。

光说一点吧 ， 它那个 Scala 的语言规范叫一个大部头的书 ， 才能讲得清楚 ， 才能讲得完 。 然后我觉得这个世界上能够跟它比的估计只有 C++ 了吧 。

**吴涛** [1:21:46]
对 ，C++ 的现状就是没有任何一个 ， 纠正我如果我错了 ， 如果我说错的话 ， 世界上没有任何一个 C++ 编译器是完全

就是可以符合这个 C++ 标准的 ，是不是这样 ？

**Rio** [1:22:01]
我觉得应该是吧 。

**吴涛** [1:22:03]
OK。

**Rio** [1:22:04]
对 ， 然后 Scala 也是一个就是属于一个 ， 就是我在我的心目中归类是把它归于 C++ 同类复杂度的语言 。

但是它确实有很多这种很爽的语言特性 ，但这不是这次讨论的重点哈 。 但这次要提到它是因为要说它的一个包管理器叫做 sbt， 傻变态 。

**吴涛** [1:22:27]
还好你没有选择另外一种更 。 那么它特别在哪呢 ？

**Rio** [1:22:32]
它的特别在于它的 ， 你知道你写 Maven 你是要写这个 XML 嘛 。

**吴涛** [1:22:37]
对 。

**Rio** [1:22:37]
那你知道 XML 本来就不是拿给人写的嘛 ， 对吧 ？ 因为它本来是要给机器生成机器读取的嘛 。sbt 的那个文件是一个 Scala 的 DSL， 你可以这么想 。

对 ， 它是相 ， 就比 XML 是绝对要简洁很多的 。 不过呢 ， 它作为一个 DSL 呢 ， 它也有它自己的问题 ， 就是你首先得知道你能够在这里面写什么 ，不能在里面写什么 。

就是你要用 sbt 这个包管理器 ， 首先你得比较熟悉的理解 Scala 那一套复杂诡谲的语言规范怎么一回事 。

**吴涛** [1:23:13]
OK，Scala 里面反正有很多很难忘文生义的东西 ， 这个我是知道的 。

**Rio** [1:23:19]
对 ，因为它是一个 ，you know， 它是一个来自学术圈的语言 ， 你可以这么想吧 。

**吴涛** [1:23:26]
很难说吧 ， 作者毕竟也是 JVM 的 。

**Rio** [1:23:32]
对 。

**吴涛** [1:23:32]
Anyway， 你可以理解它是一个工业界和学术界都双修的大牛吧 。

**Rio** [1:23:36]
对 ，因为这个人起码到现在为止还是待在这个大学里面教书嘛 。

**吴涛** [1:23:41]
对 ， 待在他美丽的瑞士小城洛桑 ， 每天看着日内瓦湖 ， 然后写写编译器 ， 好悠闲的生活 ， 教教课 。

对 ，他在那个 。

**Rio** [1:23:52]
洛桑理工啊 ？

**吴涛** [1:23:53]
对 ，他在联邦理工洛桑分院 。 对 ， 然后他应该是在 Coursera， 应该是在 Coursera 上面教一门那个公开课 。 对 。

**Rio** [1:24:03]
Functional Programming 嘛 。

**吴涛** [1:24:05]
Functional Programming Principles in Scala。

**Rio** [1:24:07]
OK。

**吴涛** [1:24:08]
Martin Odersky， 对 。

**Rio** [1:24:10]
对 ， 这个大牛哈 ， 德语口音 ， 长得一口德语口音很重的一个 。

**吴涛** [1:24:16]
长了一张德国人的脸 。

**Rio** [1:24:18]
但在瑞士的法语系教书 。

**吴涛** [1:24:22]
对 ， 很奇怪的事 。

**Rio** [1:24:24]
Sbt 的 ， 它的 ， 就我当时学 Scala 的时候就是用它来编译东西嘛 。 它能够也 ， 它可以支持纯 Java 的项目也可以 ， 然后 Scala Java 混合项目也可以 。

就给我感觉它那套就是这个文件 ， 就是那个配置文件还是比较好写的 ， 就起码比 Maven 好多了我觉得 。

当然吐槽的刚才也吐了 ， 就是我对这个东西的理解就 ，因为我没有用它用过 ， 只是它用来学习而已 ， 没有用过什么作为大规模的应用 ， 所以不太清楚具体使用的时候会怎么样 。

但是据 sbt 的用户的描述来讲 ， 应该是比 Maven 之流要好很多的 。

**吴涛** [1:25:03]
OK， 你确定你说的 DSL 好写是真的吗 ？ 因为我目前在看这个 sbt 教程上面的一段 ， 就是哇 ， 这是什么 ， 为什么一个 build 要有 object hello build extends build， 就在 build 文件里面居然要用到继承 ， 或者说非常显式的写出继承关系来 。

**Rio** [1:25:25]
这个原理很简单 ， 就好像在 Java 里面你没有办法不写一个 class 运行一个程序一样 。 你在 Scala 里面你至少 ， 对 ， 你在 Scala 里面你至少得有一个 object， 就是 in Scala everything is an object。

**吴涛** [1:25:38]
Object 在 Scala 里面相当于如果我没记错的话相当于 singleton 吧 。

**Rio** [1:25:43]
对对对 ， 差不多这个概念 。

**吴涛** [1:25:45]
然后为什么 settings 这个东西前面要有 override lazy valve。

**Rio** [1:25:53]
那这个时候你就要去研究 Scala 这个语言到底本身怎么一回事 ， 你还得理解它这个文件 ， 就说它的门槛是蛮高的 ， 然后但是一旦你入了门之后，which is a long long time， 一旦你入了门之后， 就之后还是写起来还行 ， 反正就那套 DSL 你一旦学会之后， 你能够以一个非常简练的方式把它写出来 ，但难度在于你要达到那个简练方式 ， 你得先读它肯那本 ， 那个 Scala 的那

本书你就要啃上个什么小半年了吧 。

**吴涛** [1:26:25]
对 。

**Rio** [1:26:28]
对 ， 然后用那个来管理 Java 的这个

project 也是可以的 ，但是我很怀疑这件事情的可行之处 ，因为这确实实在太难学了 。

**吴涛** [1:26:40]
我是觉得它应该像很多很多欧盟看好的所有其他项目一样 ，因为过度的繁琐而最后被抛弃在历史的车轮下面 。

**Rio** [1:26:54]
很有可能 。

**吴涛** [1:26:56]
对 ， 所以我们 ， 或者从另外一个角度理解也是 ， 我们这些选择了 Go 或者是选择了更多更简单的菜鸟们的一种无望的希冀吧 ，也许它最后还是会像 Java 一样 take over the world， 像 C++ 一样盘踞在这个

TIOBE 的那个排行搒上终年阴魂不散 。

**Rio** [1:27:24]
也不是没有可能啊 ， 现在比如说你像 Twitter 还有一些什么重度用户都在用这个写它的后台 ， 那也 ， 这种也是可以 ， 就如果它出现了我觉得也不难理解嘛 。

好 ， 下面要进入一个终于我们两个都可以共同吐槽的一个东西 ，Python，也就是你的下一份工作要做的事情 。 不要以为你从 Java 里面那个什么泥泽里面挣脱出来你就爽了 ，Python 里面坑也不少我跟你讲 。

**吴涛** [1:27:56]
对 ， 刚出龙潭又入虎穴 。

### Python

**Rio** [1:27:58]
对 ，Python 这个问题其实也是怎么讲 ， 历史遗留问题比较多吧 。 就首先 ， 就这个事情是蛮讽刺的哈 ， 就是 Python 我们都知道它有一个哲学嘛 ， 就是 there is one and hopefully only one obvious way to do things。

**吴涛** [1:28:15]
对 ， 然后一边高唱着这个口号 ， 一边出了 Python2 和 Python3， 然后又有了 setuptools 和 pip。

**Rio** [1:28:26]
不止啊 ， 还多着呢 ， 最开始有这个有 setuptools，有 distillutils，有 pip，有 buildout， 这些算是比较老的 。

**吴涛** [1:28:35]
Buildout 是啥啊 ？

**Rio** [1:28:37]
我会给你解释 ， 这是一个很神奇的存在 。

**吴涛** [1:28:40]
OK。

**Rio** [1:28:41]
然后后来有 ， 后来觉得这个事情玩不下去了 ， 后来出现那个叫什么 ，distutilr。

**吴涛** [1:28:49]
OK。

**Rio** [1:28:49]
所以你看这个标搒者这里 ， 这一个标搒只有通常一个正确的方式做事的语言 ，有着五六七八种包管理工具 。

**吴涛** [1:29:02]
对 ，有五六七八种不同的做事的方式 。

**Rio** [1:29:04]
而且它们互相之间是不太兼容的 。

**吴涛** [1:29:07]
不兼容的 ， 对 。 但我觉得这也可能也不能完全怪 Python， 它的设计理念并不能保证它所确认的那种方式就是大家最喜欢的或者是大家最觉得与时俱进的方式 。

**Rio** [1:29:24]
对 ， 所以其实这就是一个 ， 跟你讲嘛 ， 是一个历史遗留的问题 ，因为它刚才提到那些工具 ， 它出现的时， 就它的最开始的版本出现都比较早 。其实在过去这么十几年中我们对这个包管理的很多认识啊 ， 包括这个网络基础设施 ，其实发生过很大很大的这个变化嘛 。

但是它那些工具呢 ， 就没有太跟得上时代吧 ， 你可以这么想 。 而且另外一个比较显著的问题是说 ，因为 Python 它通常是在 Linux 发行版里面作为系统组件之一发行的 。

那么这就导致一个很严重的问题 ， 就是如果那个系统发行版不升级的话 ， 那个绑定的 Python 的版本也是不升级的 。

这样在实际使用的时候就会出现一个非常多的不同的版本要支持 ，而这个互相之间不同版本它们之间的兼容性又是有问题的 。

所以在这么一个就说在漫漫长的历史长河中散落下这么多版本 ， 然后你作为一个包管理器的编写者 ， 你也去很难说能够什么 ，one thing to fix them all， 对吧 ？

**吴涛** [1:30:28]
对 。

**Rio** [1:30:30]
对 ，但是这并不能改变我们要吐槽它这件事实啊 ，因为它确实就是很糟糕啊 。 对 ， 它 Python 的这个每个 ，Python 它自己有一个这个发行的这个一个渠道吧 ， 就是它自己会有可以官方下载 ，但是更多的时候我们用的是从系统里面自带的 。

它每个系统里面自带的就是某一个版本 ， 它会标准库里面会有这么一个叫做 setuptools 吧 ， 对 。 然后这个东西的命令行的体现应该是叫什么 ，easy install。

**吴涛** [1:30:59]
Easy install，easy underline install。

**Rio** [1:31:01]
对 ， 它的问题是

它只能 install，不能 uninstall。

你会发现 easy install， 哎 ， 好简单好方便啊 ， 那怎么 easy uninstall 好像没有这个命令哎 。

**吴涛** [1:31:18]
Easy install, hard manually uninstall。

**Rio** [1:31:21]
对 ， 就你会觉得这个就很奇怪 ， 你怎么能只进不出呢 ？ 这会导致什么什么样的后果 ， 你装完之后没法重来 ， 没法说啊装错了删掉 ，因为它一旦装了之后大概整个东西就赖死在你的那个系统的面目了 ， 除非你自己挨个分析它们那个安装的脚本的语句 ， 看源码 ， 看它到底装到哪里去了 ， 然后一个一个去手动搞 ， 就觉得这怎么这么变态

， 对吧 ？

**吴涛** [1:31:48]
对 ， 它会很无辜的告诉你说我不是跟你说了吗 ， 我只是负责 easy install 而已 ， 只有 easy install 而已 。

**Rio** [1:31:56]
对 ， 就是给人的感觉就很像中国的很多这种流氓软件是吧 ， 很方便的装上去 。

**吴涛** [1:32:02]
管杀不管埋 。

**Rio** [1:32:03]
这不叫一键式安装 ， 你就无键式安装 ， 你不装它都给你装了 。 但是发现装的时候就没法回头了 。

对 ，有这么一个问题 。 而且它这个 easy install 它并不支持全部的 ， 就 Python 的安装包 ， 安装的这个包很多 ， 你发现装完直接报错 ， 装不了 ，不支持这个格式 。

它因为它也是对软件包的这种所谓的这个 layout 或者这种封装方式 ， 包括那些文件里面要填什么什么 meta 的信息 ， 它都是有一些要求的 ，但是并不是所有 ，因为这个四分五裂的这么一个生态圈的状况 ，并不是所有的软件包都是觉得要用这个方式来写的 。

而且据我所知应该大部分人都不是不用那个方式来写的 。

**吴涛** [1:32:48]
对 ， 我记得当年听说 Guido 是什么 dictator 的时候 ， 我心里还觉得我靠 ，有个 dictator 难道不会站出来管一管这件事情吗 ？

但哎 ， 事实上 。

**Rio** [1:33:00]
又遇见一个不管事的 。

**吴涛** [1:33:02]
对 ，在我们如果对比一下 Python 社区和 Ruby 社区的时候 ， 就会发现 Ruby 的 ， 虽然我们默默鄙视着 Ruby 社区的人 ，但是 Ruby 社区有一点好 ， 就是当 Mats 说一句大家升级到 2.0 吧 ， 所有都升级到 2.0。

而我们现在 ， 我记得刚刚前几天还收到一封邮件让我们在投票说你觉得 Python3 是一个错误吗 ？

**Rio** [1:33:29]
是啊 ， 按现在的这个状况来看 。

**吴涛** [1:33:32]
对 。

**Rio** [1:33:33]
Anyway。

**吴涛** [1:33:33]
Python3 本意是好的 ，但目前的现状毫无疑问是个错误 。

**Rio** [1:33:36]
说法是错的 。Python 语言吐槽这一块也可以 ， 这个单独再吐槽 ， 这里只吐槽它的发布工具啊 。 所以这里其实也是体现了这一个问题 ， 就是 Python 还有一句口号叫做 batteries included， 叫什么 ， 自带电池 。

**吴涛** [1:33:52]
自带电池 。

**Rio** [1:33:52]
就说它这个标准库里面自带了这个 setuptools 这么一个标准的安装包 ，但是就它的标准库里面其他很多 batteries 一样 ， 它们都是 dead on arrival。

**吴涛** [1:34:03]
已经至少是五年旧的电池了 ，而且装进去的时候根本没有做 。

**Rio** [1:34:06]
直接没电 。

**吴涛** [1:34:08]
装进去的时候根本没有做这个绝缘 ， 导致你拿到机器拆开之后发现电池已经烂在里面了 ， 那个电池电浆已经跑出来的感觉 。

**Rio** [1:34:20]
对 ， 就是它们带了一个很烂的工具 ， 然后没法用 。 然后后来有人想办法 ， 那我们再解决吧 ， 后来就出了这个什么 ， 我们这么说 ， 我这么说其实不对啊 ，但是为了把这个故事讲下去就这么说吧 。

后来就出了什么 distutil 这么一个工具 ， 说试图解决 setuptools 的各种问题 ，但其实后来也发现也没解决了 ， 然后又多了这么一个 ， 就碎片吧 ， 又多了一个工具要支持 ， 然后也 ， 然后 distutil 也不是特别友好我觉得 。

**吴涛** [1:34:52]
我印象 ， 我几乎从来没有怎么用过 distutil。

**Rio** [1:34:56]
Trust me， 你肯定用过 ， 只是你不知道而已 。

**吴涛** [1:34:58]
What?

**Rio** [1:34:59]
它 3 号应该和 setuptools 有点这种扯不清道不明的关系 ， 最新的说法是这两个东西要融合了 。

**吴涛** [1:35:06]
啊哈 。

**Rio** [1:35:07]
就是在 。

**吴涛** [1:35:08]
就它们催生出第三个怪胎来是吗 ？

**Rio** [1:35:10]
对 ， 就是因为它们各 ， 就是 setuptools 那帮人和 distutil 这帮人开发的 ， 后来发现哎呀不行啊 ， 这么我们各自为政搞一套新版本 ， 首先和自己过去不兼容 ， 然后和对方不兼容 ， 这版本就二变四了嘛 ， 对吧 ？

这就很沮丧 ， 所以后来他们说算了 ， 我们还是合作一把 ， 所以以后的那个什么 distutilr 和 setuptools 的下一个版本应该是融合在一起的 。

然后具体叫什么我现在已经不知道了 ， 我已经放弃理解它们的行为了 ， 我觉得已经 beyond my comprehension。

**吴涛** [1:35:42]
OK。

**Rio** [1:35:43]
但是那个美好的局面还没有出现 ， 就是起码我到目前为止还是一个挺令人沮丧的情况 。 然后后来也有人受不了了 ， 就是出现了一个大牛吧 ， 名字忘了 ，他写了这个叫 pip 的这个工具 ，pip。

**吴涛** [1:35:57]
对 ，其实你刚才说我们要一起吐槽 Python， 我心里默默嘀咕一句 ， 我现在其实不需要怎么吐槽 Python。

**Rio** [1:36:04]
我也是 。

**吴涛** [1:36:05]
我已经放弃了 。

**Rio** [1:36:06]
Painful 的部分 ， 我把 painful 的部分就默默绕过了 ， 比如说我会用 Pythonz， 就是 Pythonz 装好几个版本的 Python， 然后每次跑什么东西的时候就选择一个 Python， 然后再如果需要什么 package 的话就用 pip，在那个 ，在该版本的 Python 之下装只供该版本的 Python 使用的 package。

**吴涛** [1:36:30]
啊 ， 你是这样玩 。

**Rio** [1:36:31]
好像也没有什么太痛苦的部分 。

**吴涛** [1:36:33]
那你肯定是装的还不够多嘛 ， 总会装多了总会遇到坑的 ， 这个是一定的 。 这个当年， 这个之前我前几年在知乎工作的时候就有遇到这个比较令人沮丧的状况 ，因为我们用的那个 Python 的包比较多比较杂嘛 ， 然后确实是挺痛苦的 。

**Rio** [1:36:51]
对 ， 一定是我还没有遇到企业级 ， 我还没有进入到企业级 Python 的世界 。

**吴涛** [1:36:57]
对对 。

**Rio** [1:36:58]
不过我的解决方案比你更加决绝 。 首先我现在已经不在知乎了嘛 ， 已经离职好久了 。 然后另外我也不再写 Python 相关的东西了 ， 就全面转向 Go 和 Node。

**吴涛** [1:37:11]
OK。

**Rio** [1:37:12]
就直接避免了这个问题 。

**吴涛** [1:37:14]
你现在在搞学术的时候用的是什么工具啊 ？

**Rio** [1:37:18]
我的 Node， 你敢信吗 ？

**吴涛** [1:37:23]
Node？

**Rio** [1:37:24]
Demo 的 。

**吴涛** [1:37:24]
Seriously？

**Rio** [1:37:25]
因为要做一个 demo 嘛 ， 然后这个东西 ， 这个场景比较适合在浏览器里面搞 。

**吴涛** [1:37:30]
OK。

**Rio** [1:37:31]
所以就 JavaScript， 然后必然就跟 Node 扯不掉关系了 。 这个以后再说 。 现在接着说回 Python 这个东西啊 ， 就有人也受不了这个那种各种四分五裂的局面 ， 它又搞出一个又不兼 ，也不叫又不兼容 ， 就大部分情况下兼容 ， 只有小部分时候不兼容的工具叫 pip，pip。

**吴涛** [1:37:50]
嗯哼 。

**Rio** [1:37:51]
叫 pip 还是 pip？pip 吧 ， 对 。

**吴涛** [1:37:52]
Pip 吧 ， 我一般念 pip。

**Rio** [1:37:54]
然后它的好处就是它支持 uninstall。

**吴涛** [1:37:59]
Yeah。

**Rio** [1:37:59]
Yay。

**吴涛** [1:37:59]
这是非常 。

**Rio** [1:38:01]
一个包管理器终于加入了一个 uninstall 的功能也好哎 。

**吴涛** [1:38:05]
Yeah。

**Rio** [1:38:08]
这听起来有点奇怪 。

**吴涛** [1:38:09]
对 ， 为什么好像有点 ， 对啊 ， 这难道不是 。

**Rio** [1:38:12]
很潮热 。

**吴涛** [1:38:13]
Sorry， 对 ， 普通用户已经用了十几年的功能嘛 。

**Rio** [1:38:17]
Pip 有什么好呢 ， 就是它首先

支持这个卸载 ， 这个是很大的一个进步 。 然后第二个我觉得比较重要的 ， 它支持一个所谓的版本冻结 。

**吴涛** [1:38:29]
对 ， 就是 pip freeze 这个 。

**Rio** [1:38:31]
对 ， 你可以装好一堆包之后， 你可以说好 ， 我这个什么 ， 我这个开发环境搭建好了 ， 看一下有什么版本的包我是依赖的 ， 然后你就 pip freeze 一下， 然后返回一个列表 ， 说你当前装了哪些 Python 的这个包 ， 然后那个包是哪些版本 ， 对吧 ？

你形成一个列表 ， 下次你再用那个列表 ， 然后再 pip 再装一遍 ， 它就会按照那个列表一行一行的去给你装对应的版本就好了 。

就 hopefully it works。

**吴涛** [1:39:01]
而且以我有限的 hobbyist 的经验来看 ， 基本上 99% 的时间它是 works。

**Rio** [1:39:08]
对 ，但这也是因为你在国外， 你不知道中国人民的痛苦 。

**吴涛** [1:39:15]
可是我听说贵网站接下来是机密内容 ， 听众请勿互相泄露 ， 就是贵站难道不是内部直接 VPN 翻墙的吗 ？

**Rio** [1:39:27]
但是你知道即便是这样翻墙 ， 网络也是不稳定的嘛 。 就是你如果用到东西是网络的话 ， 你首先要假设一点 ， 网络是可以随时断掉的 ， 它可能会在你最不希望它断断时间断掉 。

**吴涛** [1:39:40]
OK， 墨菲定律 。

**Rio** [1:39:42]
对 ， 基本上是这样 。 如果你一旦做了什么企业的运维之后， 你发现这件事情是一定成立的 。

**吴涛** [1:39:48]
对 ，而其实这就是有一次 Maven 导致我们发布推迟的原因之一 ， 就是我们上 Nexus 挂掉了 。 不是连不上网络 ，而是 Nexus 挂掉了 。

就 Nexus 是 Maven 这一套系统里面的一个 ， 或者说支持 Maven 系统很重要的一个特征 ， 就是你可以自定义一个包管理服务器 ， 然后把你写好的软件作为一个源挂在这个包管理服务器上， 然后当你在发布新的软件的时候 ， 你已经做好那些组件 ， 可以就像从其他任何源一样 ， 从你自己的服务器上面下载下来 ， 然后成为发布好的系统软件的一部分 。

但是如果这个服务器挂掉了 ， 那也就意味着整个发布过程将无法进行 。 然后有一次我们的 Nexus 服务器就挂掉了 。

**Rio** [1:40:45]
所以你们这个是有什么 single point of failure， 单点失败嘛 。

**吴涛** [1:40:48]
对 ， 把所有的鸡蛋放在一个篮子里是非常愚蠢的一件事 。 但对 ， 当然 ，yeah。

**Rio** [1:40:57]
对 ，但其实刚才讲的 。

**吴涛** [1:40:59]
I don't blame you。

**Rio** [1:41:03]
刚才讲的那些就是 Python 的三个三大工具吧 ， 都是它们都依赖同样一个东西 ， 叫做 PYPI， 叫 PYPI 吧 ， 叫 Python Package Index。

**吴涛** [1:41:15]
对 ，Python Package Index。

**Rio** [1:41:17]
对 ， 我不知道到底 PyPi 还是叫 PyPI， 我觉得叫 PyPI 吧 。

**吴涛** [1:41:20]
PyPI 吧 。

**Rio** [1:41:21]
因为 PyPi 是另外一个东西 ，PYPI 又是另外一个东西了 。

**吴涛** [1:41:24]
对 。

**Rio** [1:41:25]
这里就不说了 ， 先讲这个 PyPI 吧 。PyPI 呢是一个什么啊 ， 就是它是一个 Python 这个

foundation, Python Software Foundation 维护的一个中央服务器 ， 就是你们那个 Nexus 差不多 ，但是它是通过网络访问的 。 然后你每次 pip install 或者 distutil， 就是什么 easy install 的时候 ， 它都要去那里去下载 ， 搜一遍你那个要装的那个软件名字 ， 然后在哪里找到那个位置去下载回来 ，不是找到 ， 它应该是传到那里一个服务器上， 直接下载回来 。

它有一个 FTP 服务器吧 ， 你可以这么想 。 然后它经常挂掉 。

**吴涛** [1:42:02]
OK。

**Rio** [1:42:03]
它挂掉的 ， 就它挂掉的这个概率之频繁真是令我瞠目结舌 。

**吴涛** [1:42:10]
是在你们公司挂掉 ， 就是在你们 ， 你从你们公司访问的时候挂掉的 。

**Rio** [1:42:14]
不不不 ， 它自己就挂掉了 ， 所有人都不能访问 。

**吴涛** [1:42:17]
What?

**Rio** [1:42:18]
真的真的 ， 前几年的时候这个事情超级严重 。

**吴涛** [1:42:22]
OK。

**Rio** [1:42:22]
也是因为就两个原因嘛 ， 它自己经常挂掉 ， 一个是我们访问它的网络的情况经常会挂掉 。 你知道有段时间 Python .org， 哎 ，.com，.org 吧 ， 这个网站是被封掉了嘛 。

**吴涛** [1:42:35]
对 ， 现在也是被封掉了 ，不然它为什么要在 。

**Rio** [1:42:38]
加个中文 。

**吴涛** [1:42:39]
首页上面出现中文的下载两个字 。

**Rio** [1:42:43]
这就不是你不觉得很奇怪吗 ？ 你都被封掉了 ， 你出现中文又有什么用 ？

**吴涛** [1:42:47]
这是 parody 嘛 ， 就是它故意 ， 我觉得这就是它一个技术 ， 从一个技术社区来表达对于某些政府行为的微弱的反抗呗 。

**Rio** [1:42:57]
Whatever.

**吴涛** [1:42:58]
就是 ， 对 。

**Rio** [1:43:00]
这个就不能说 。

**吴涛** [1:43:00]
秀个姿态嘛 。

**Rio** [1:43:02]
反正就是双重原因嘛 ， 它自己本身也不是很稳定 ， 就它自己的 availability 也不是很高 ， 再加上网络的原因导致我们经常访问不了它 ， 所以大部分用 Python 做生产的公司也好 ， 团队也好 ， 基本上都自己在内部局域网维护了一个 PyPi 的镜像 。

**吴涛** [1:43:21]
OK。

**Rio** [1:43:21]
简单说就是把 PyPi。

**吴涛** [1:43:22]
很大吗 ？

**Rio** [1:43:23]
你不一定 ， 你不一定需要全部的嘛 ， 你只需要你常用的那些就行了 。

**吴涛** [1:43:27]
OK， 可是你怎么知道哪些常用 ？

**Rio** [1:43:29]
它是这种 ， 它有一种 ， 这个做得还比较不错 ， 就是有这个 ， 就因为 PyPi 经常挂掉 ， 所以有人写出这种工具 ， 可以很方便的让你在自己的服务器上架设一个镜像 ， 然后这个镜像可以说就代理嘛 ， 就是你设置一下你装的时候 ， 它会先去这个镜像服务器上找 ， 如果它找不到它这个镜像 ， 它会让这个镜像服务器去 PyPI 的官方去把那个抓的回镜

像服务器 ， 这样下次的话你就在那个镜像服务器上有了嘛 。 就是等于也是一个缓存代理的意思啊 。

**吴涛** [1:44:00]
对 。

**Rio** [1:44:01]
对 ， 就是就到了这种程度了 。 当然不过从这个可用性的角度来讲 ， 你有一个本地的这个镜像也是好的 ， 就可以避免网络和你们遇到的那种情况单点失败嘛 。

**吴涛** [1:44:14]
嗯哼 。

**Rio** [1:44:15]
OK，但是事情还是没有结束 。

**吴涛** [1:44:20]
为什么呢 ？

**Rio** [1:44:20]
因为不管是 easy install 也好 ，distutil 也好 ，pip 也好 ， 它们基本上不能安装非纯 Python 的包 。Python 作为一个 。

**吴涛** [1:44:37]
这个多少有点强人所难了 ， 说实话呢 。

**Rio** [1:44:40]
不不 ， 这个就要解释为什么嘛 ， 为什么要出现这种一个应用场景啊 ， 就是说首先是因为 Python 语言本身是一个比较龟速的语言啊 ， 它是一个解释型不带 JIT 的语言 ， 大部分情况下 。

**吴涛** [1:44:54]
或者说它的 JIT 支持的特征还不够全面 。

**Rio** [1:44:58]
对 ， 简单来说就是慢 ， 那么导致它做很多事情的时候就不能 。

**吴涛** [1:45:03]
都需要 native pining。

**Rio** [1:45:04]
对 ，不能用原生的 Python 的这个代码来实现 ， 要用这个 ， 比如用 C 或者是 C++ 来实现 ， 然后通过一个这种什么 bridge 的方式再通过 Python 去调用它 。

比如说最常见的两个这种例子是一个叫做 numerical py，numeric py，是做数值计算的 ， 比如说什么矩阵运算啊 ， 乱七八糟的 。

另一个叫做 Python Image Library， 然后这个是干嘛 ？ 就处理图像的 。

**吴涛** [1:45:35]
OK。

**Rio** [1:45:35]
就处理图像其实用图还蛮多的 ， 比如说你做一个社交网站 ， 你要用户传个头像上来的时候 ， 你得想办法检查一下， 压缩一下， 对吧 ？

你就要用这个 PIL，Python Image Library。

**吴涛** [1:45:47]
PIL 说实话已经好像曾经非常喜欢它 ，但是现在好像已经有一阵子从来没有用过了 。

**Rio** [1:45:53]
爱不起来了是吧 ？

**吴涛** [1:45:55]
对 。

**Rio** [1:45:56]
我理解你 。

**吴涛** [1:45:57]
那么 PY 也是 ， 就是其实有一阵子我非常痴迷于在 Mac 上面时刻保持一个 。

**Rio** [1:46:04]
IPython。

**吴涛** [1:46:05]
IPython 的 Qt 版本的 Console 在跑 。 因为用 Qt 你可以 。

**Rio** [1:46:10]
可以用图形化的 。

**吴涛** [1:46:11]
内联显示很多很酷 ， 就有点像那个 Mathematica 或者是那个 Maple 的那种感觉 ， 就是你潇洒的写下几行代码 。

**Rio** [1:46:22]
真正的科学计算环境 。

**吴涛** [1:46:23]
对 ， 冒出一个图表来 。 我当时有一阵子非常痴迷于一定要保持我的 Mac 上面跑着这么一个东西 ，但是 。

**Rio** [1:46:30]
其实现在也很多人这么用 ， 它现在那个 IPython 出了一个叫做 Notebook。

**吴涛** [1:46:35]
对 ，其实 Python Notebook 是网页版的嘛 ，但我就痴迷于一定要有一个原生的图形界面的这么一个东西 。

**Rio** [1:46:42]
它不就是为了解决这个跨平台的问题嘛 。

**吴涛** [1:46:45]
看起来非常高大上， 主要是 。

**Rio** [1:46:47]
简单一点 。

**吴涛** [1:46:48]
但是后来 ， 对 ，但是后来我因为我在 GitHub 上面维护一个小 Note， 就是怎样解决一些安装一台新 Mac 的时候需要面对的小问题 ， 然后我写在这个 ， 我在 IPython

Qt 版本的这一段 Notes 里面倾注了大量的心血 ， 然后最后最后最后的时候我绝望在里面写说这一切都需要太多努力了 ， 还是直接 give it up and use IPython Notebook instead。

**Rio** [1:47:29]
好吧 ， 明白了 。OK， 这就说回来这个好东西啊 ， 就是说前面那几个工具 setuptool、distutil 还有 pip 都不能解决 ，不能很好的解决这个就带有这种 C binding 或者这种其他语言 binding 的 Python 的包的安装 。

就是说你有两种方案 ， 就是像 Python Image Library 这种巨无霸的 ，因为它很大嘛 ， 这种巨无霸的话 ， 它通常给你提供一个官方的安装包 ， 当然你就装上那个官方安装包 ， 通常是一个比较靠谱的办法 。

但是如果你又想从某种方式去把这个东西管理起来 ， 或者是能够帮忙自动化起来 ， 就没有办法能够帮到你了 。

所以后来就有另外一个工具叫做 Buildout， 这个你估计没有用过 。其实我之前也没有听说过 ， 我也是去了知乎之后发现 ， 哎 ， 还有这么一个东西 。

这个东西来源其实还挺古老的 ， 你知道 Python 在和 Web 1.0，不 ，Web 0.9 的时代出现过一个叫做 Zope 的这么一个机构 ， 你不知道你有印象没 ？ZOPE。

**吴涛** [1:48:33]
我知道 ， 对 ， 我知道 。 而且号称 ZOPE 是当时如日中天的这么一个史诗级伟大的库 ，是这样吗 ？

**Rio** [1:48:45]
它是一个 ， 它是一个 Sem， 说它是 SemS， 好像不太严谨 。

**吴涛** [1:48:51]
它其实算一个 framework 吧 ，toolkit， 它自称一个 toolkit。

**Rio** [1:48:57]
对 ， 它是一个本来是用来建一个 CMS 的一个 framework， 确实可以这么理解 。 但是后来它也越来越臃肿 ， 已经可以做很多事情了 。

然后 Buildout 这个工具其实就是最开始 ， 如果我没记错的话 ， 就是 ZOPE 那帮人为了解决他们那个巨无霸的 framework 的安装和这个开发的问题 ， 就写出来的这么一个工具 。

然后这个工具的特色是什么呢 ？ 它能支持这种带 。

**吴涛** [1:49:27]
Native binding。

**Rio** [1:49:28]
Native binding 的 Python 的包的安装 。 因为这里说起来就要提另外一件事情 ， 就是比如说这个 Python Image Library， 它虽然说是一个非纯 ， 就它虽然说需要有 native binding，但你安装的时候并不是安装它自己本身这么简单 ， 你还得再装一些其他的什么 C 的一些库 ， 比如说 zip、png 啊 ， 什么乱七八糟的 。

然后在 Linux 上面的话 ， 你就要通过 ， 就在你装这个 PIL 之前 ， 你还得用什么 APT 或者是 YUM 的方式 ， 把那些的依赖给先给装上去 ， 然后你再可以再装这个 Python Image Library 才可以成功 。

**吴涛** [1:50:02]
对 。

**Rio** [1:50:03]
然后这样的话就其实手工操作是非常繁琐和容易出错的一个过程 。 你可以 ， 如果你不信你可以去试一下 。

**吴涛** [1:50:12]
我对此还是有所体会的 ，因为装 ， 就像我说的 IPython 的 Qt Console 其实也是这么一个过程 ， 你首先要用包管理工具去安装一些 library， 然后再安装 ， 对啊 ，Qt 之类的 ， 然后再去再用 Python 去安装对应的组件 ， 然后还有什么 ZeroMQ 啊之类的东西 ， 都要一个一个都装上 。

**Rio** [1:50:34]
对对对 ，其实我当时在知乎用的时候也是因为就是最主要原因就是尝试用那个 ZeroMQ 要装一些 C 的东西嘛 。

**吴涛** [1:50:43]
对 。

**Rio** [1:50:44]
然后 Buildout 就可以用一个这种方法 ， 它就可以解决嘛 ， 它可以指定一些脚本 ，在你安装某个包之前 ， 你可以先声明它依赖哪些非 Python 的东西 ， 然后系统先帮你安装好 ， 比如支持一个 APT 什么的 ， 帮你装好 ， 然后你再装 ， 然后再报你的过程 。

但是 Buildout 是一个神一样的存在 ， 你知道吗 ？ 当时， 现在我不知道情况会不会好转一些 ，但是我用的时候我想去学它 ， 就是一个三无产品 ， 就你学它完了之后你知道你看不出这个 。

**吴涛** [1:51:11]
没有文档 。

**Rio** [1:51:12]
没有官方文档 ， 所有的能找到的都是只有网上稀稀拉拉一些教程 。 然后你不知道这个东西最开始不知道是谁在维护的 ，因为它现在没有说这个东西是找 ， 你都不知道去找谁 ， 它有一个 mailing list， 然后那凑 ， 那凑 ， 那凑 ， 就是你没有办法再去 。

**吴涛** [1:51:30]
等等 ， 三无产品 ， 我们可以定义一下三无产品到底是哪三无 ？ 没有文档是肯定的 。

**Rio** [1:51:35]
没有文档 ， 没有这个稳定的开发者 ， 然后你出了问题不知道去哪里问 。

**吴涛** [1:51:44]
无文档 ， 无稳定维护者 ， 还有一个无 。

**Rio** [1:51:47]
就是不知道去哪里问问题 ， 就不知道出了问题找谁 ，不知道 。

**吴涛** [1:51:51]
无社区 ， 对 。

**Rio** [1:51:53]
然后就发现哇 ， 这个也可以 ，而且这既然是就我所知道的能够解决这个 Python 这种就安装非纯 Python 包的唯一的一个比较好的 ， 能用的工具都不能说它好了 。

**吴涛** [1:52:07]
能用的自动工具 ， 应该说 ， 对 。

**Rio** [1:52:09]
对 ， 然后它的体验是相当 ， 使用体验是相当糟糕的 。 它的配置文件是一个 .ini 的文件 。

**吴涛** [1:52:16]
What？

**Rio** [1:52:16]
然后你需要在 。

**吴涛** [1:52:17]
开发者是什么背景 ？

**Rio** [1:52:19]
ZOPE 嘛 ， 你自己想想这么古老的 ，Ancient 的东西 ， 就有出现这种情况也不奇怪了 ， 就还好 ， 还好 ， 可以不是太糟糕 ，但糟糕的是你需要在这个 .ini 文件里面 ， 你 .ini 文件的格式它你想来应该也很明白 ， 就是有它分几个 ， 就是一小节一小节 ， 一个 section， 每个 section 是一个方括号标分开嘛 ，有个标 ，有个 section 的名字 。

**吴涛** [1:52:39]
.ini 不管怎么说还是要比 XML 好 。

**Rio** [1:52:41]
对对 ， 还是比 XML 好一点 ，但是他们把这个已经用到了极致了 。 就是然后每个 section 里面是它是一个就是 key value 的一个 pair 嘛 ， 一个对 ， 就是一个组件 ， 然后那个数值 。

为了解决 Buildout 的中间的各种问题 ， 你需要在里面插入用一些很诡异的方法去写版本字符串 ， 你需要在里面插入这个 shell 脚本 。

**吴涛** [1:53:10]
在 .ini 里面插入 shell 脚本 ， 这是什么黑魔法 ？

**Rio** [1:53:14]
所以你知道 Buildout 是多么一个惨痛的存在了吧 ， 对 。

**吴涛** [1:53:17]
Holy。

**Rio** [1:53:19]
这是我一直对它吐槽不能 ，但是又不得不用的一个很令人沮丧的工具 。

**吴涛** [1:53:25]
哎 ， 可是我刚刚在 Buildout， 我刚刚在 GitHub 上面发现了 Buildout/buildout 这个 repository。

**Rio** [1:53:34]
什么玩意儿 ？

**吴涛** [1:53:35]
这难道是他们官方的库吗 ？

**Rio** [1:53:37]
所以它已经去了 GitHub 了 ， 那所以可能事情有所好转 。

**吴涛** [1:53:42]
有可能 。

**Rio** [1:53:43]
好吧 ，但起码你可以去它的这个什么 buildout.org 这个网站 ， 你可以发现它有一个 learn 这个选项 ， 里面就是一堆 tutorial， 然后完了 。

**吴涛** [1:53:55]
OK。

**Rio** [1:53:57]
然后它教你用它的唯一的方式就是给你一堆例子 。

**吴涛** [1:54:03]
而且在 Git repository 里面的贡献最多的这个人叫做 Jim Falton，他显示的工作地点是 ZOPE Corporation， 所以有可能他就是你一直苦苦在寻找的主要维护者 。

**Rio** [1:54:22]
对啊 ，因为这个 Buildout 是他写的嘛 ， 最开始是他写的 ，但是我相当确定应该没有 ，他没有再更新这个东西了 。

**吴涛** [1:54:29]
OK。

**Rio** [1:54:30]
所以这是一个半死不活的项目 ， 靠着社区也不知道谁在维护 ，anyway。

**吴涛** [1:54:37]
就像游魂一样的软件 。

**Rio** [1:54:38]
对 ，但是你又不得不用它 ，而且你发现 somehow 你竟然能够用它来 ， 还能用 。

**吴涛** [1:54:48]
所以软件不再新能用就行嘛 。

**Rio** [1:54:51]
但是其实很多可能经常会出问题 ，不过它还有一些比较令人， 就是对于一个这么古老的软件 ， 它有些令人蛮惊艳的特性的 。

比如说刚才讲的那些什么 pip 那些工具啊 ， 就是经常那个 pip 那个组件挂了就下不了了嘛 。Buildout 它竟然有自带的这种本地缓存机制 ， 你知道吗 ？

就是你一旦下载过某一个安装 ， 用过 ，refer 过某个版本 ， 它会把那个版本的这个源码就从 pip 或者是某个地方抓回来的 ，在你本地的它管理的一个隐藏的目录下面去存起来 ， 这样你下次装它的 ， 你下次再运行 Buildout 的时候 ， 它就不用再去网上抓 ，而且直接本地从里面复制一份过来就好了 。

**吴涛** [1:55:33]
OK， 为什么好不惊艳呢 ？ 因为 Maven 也有这个功能 。

**Rio** [1:55:36]
对对对 ，但是你想它是一个多古老的软件 ， 就是有这种功能还是相当不错的 ， 挺好的一个事情 ，但是它的使用体验实在是太糟糕了 。

好 ， 我对 Python 的吐槽 ， 就为了不要拉太多仇恨 ， 吐槽到此为止 。

**吴涛** [1:55:55]
还好吧 ， 只要我们是保持自黑的姿态 ， 应该就不会招来太多的 。

**Rio** [1:56:00]
不要黑别人呢 ， 黑自己就行 。

**吴涛** [1:56:02]
对 ，而我们来看一看敌对阵营 。

**Rio** [1:56:06]
敌对阵营 ，不不 ， 现在我已经叛逃了 ， 我已经加入了这个阵营 。Node， 这个 JavaScript 的怎么 。

**吴涛** [1:56:14]
哦 ， 你觉得 Node 是敌对阵营吗 ？ 我不觉得 ， 我觉得 Node 是兄弟阵营 ，不是吗 ？

**Rio** [1:56:18]
你的敌对阵营是哪里 ？Ruby。

**吴涛** [1:56:20]
敌对阵营那是 Ruby 了 。

**Rio** [1:56:21]
其实 Ruby 我不是很熟 。

**吴涛** [1:56:23]
我也不熟 ，但至少 Gem 这个包管理系统在我有限的使用经验里面要比 Python 的体验好很多 。

**Rio** [1:56:33]
首先那只有一套 ， 对吧 ？

不像 Python 那种四分五裂的搞五套出来 。

**吴涛** [1:56:39]
对 ，Ruby 社区不管怎么说最好的一点就是团结 ， 这是我们 Python 社区做不到的 。

**Rio** [1:56:45]
对 ， 就是我觉得他们更加乐于去为了达成一个共识吧 ， 去改变自己 。

**吴涛** [1:56:53]
对 。

**Rio** [1:56:54]
这点是挺好的 。 所以 Gem 的体验 ， 我其实体验不多的 ， 你有什么体验吗 ？

**吴涛** [1:57:01]
我老实说我也不是特别多 ， 只不过

Ruby 简直是你接触一点稍微新一点的东西绕不开那个工具嘛 。

**Rio** [1:57:12]
那最简单的 ， 刚才上面提到那个 Homebrew 是 Ruby 的 ， 你必须要 。

**吴涛** [1:57:16]
对 ，Homebrew 是 Ruby 的 ， 然后比如说写那个 iOS 的 ，iOS 下面的包管理工具 ， 或者 Object C 的包管理工具 ，CocoaPods 也是 Ruby 写的 。

**Rio** [1:57:29]
What？

**吴涛** [1:57:29]
Yes， 我没有说错 ，CocoaPods 本身是一个 Ruby Gem， 很神奇吧 ？

**Rio** [1:57:35]
OK， 那它其实跟 Homebrew 的形式比较类似了吗 ？ 还是就它的 CocoaPods 的一个 pod 文件也是一个 Ruby 的文件吗 ？

**吴涛** [1:57:45]
应该是这样了吧 ， 我忘了 ， 我忘了 pod， 你这么一说我还真 ，podfile 的语法是 Ruby 的语法吗 ？ 还是一个 JSON， 还是一个什么东西 ？

它是一个也是用 Ruby 写的那个 。

**Rio** [1:57:58]
DSL。

**吴涛** [1:57:59]
DSL， 对 。

**Rio** [1:58:00]
这个 Ruby 开发者最喜欢做的事情就是写 DSL。

**吴涛** [1:58:04]
对 ，Ruby 毕竟是一个非常适合于写 DSL 的语言 ， 这也不能怪它 。

**Rio** [1:58:07]
你知道这句话我听的最熟的地方是在哪里吗 ？

**吴涛** [1:58:09]
哪 ？

**Rio** [1:58:10]
Scala。

**吴涛** [1:58:11]
Scala 写 DSL？

**Rio** [1:58:13]
对 ，Ruby 写 DSL 你还觉得从看出来一眼就能看出来它是一个 Ruby 的 。

**吴涛** [1:58:17]
Scala 写 DSL，my ass。

**Rio** [1:58:19]
不 ， 你听我解释 ， 就是你说 Ruby 写 DSL， 它还有一些东西是很明显的 Ruby 的语法痕迹在里面 ， 对吧 ？ 你可以看出它是一个 class， 然后它那个什么 block 的语法就是很鲜明的特点 ， 对吧 ？Scala 写出来 DSL 你甚至看不出它 ，有时候你不仔细看你还看不出来它是 Scala。

**吴涛** [1:58:37]
这是它的优点还是缺点 ？ 我怎么觉得越听越像缺点 。

**Rio** [1:58:40]
这都是 ， 就是优点就是说它能够可以让你定义一些很诡异的语法 。

**吴涛** [1:58:45]
它的元编程能力非常强大 。

**Rio** [1:58:46]
对 ， 非常强大 。

**吴涛** [1:58:47]
缺点就是它可以编的连亲爹都不认识 。

**Rio** [1:58:50]
对 ， 就是 Scala 那种写的 DSL， 就是如果你不知道这个 DSL 的这个实现方式或者模板是怎么样的话 ， 你根本别想 ， 你别想往下写下去 ， 这根本不现实 。

对 ，有这种情况发生 。

**吴涛** [1:59:05]
还有什么呢 ？ 我前两天还用了一个什么东西 ， 啊对对对 ，Rake file，Rake file 这种

也可以理解为 DSL， 它本身也是一个 DSL， 说实话 。Rake file 这种 ， 或者说 Rake 这个工具在很多开源工具里面已经取代了 Make 或者是其他一些更加冷僻的工具 。

**Rio** [1:59:33]
对对对 。

**吴涛** [1:59:33]
这个我有 。

**Rio** [1:59:34]
成为了一种默认的安装工具 ， 或者说默认的执行工具 ， 我觉得这个也挺有意思的 。 就比如说前两天我在 Stack Overflow 上面获得了风滚草勋章 ， 就是

提问了一个问题 ， 没有人关注 ， 没有人 upload， 没有人回答 。

**吴涛** [1:59:54]
这叫风滚草 。

**Rio** [1:59:56]
持续过了一个星期 ， 对 ， 然后你就会获得风滚草勋章 ，因为你提这个问题实在是太小众了 ， 没有人去管 。

**吴涛** [2:00:04]
三无 。

**Rio** [2:00:05]
三无问题是吧 ？

**吴涛** [2:00:06]
对 ， 然后一个兄弟非常热心的来帮我 ， 然后

折腾出了一个 Git repository， 说你看我做了一个活生生的例子 ， 你去试试吧 。 然后我兴高采烈的屁颠屁颠跑过去一看 ， 哎 ， 里面是个 Rake file， 相关问题是一个 Swift 的问题 ，但最后它 somehow 给了我一个 Rake file 的解决方案 ，而且对 ， 它默认我是能理解这个东西的 。

然后我愚蠢莽撞的 Rake 了一下， 然后出错了 。

**Rio** [2:00:43]
理解你的痛苦了 。

**吴涛** [2:00:45]
Yeah， 就不懂 Ruby 脊柱筋就是到处被人欺负的感觉 。

**Rio** [2:00:51]
好吧 ， 那什么 ，Ruby 的吐槽我没法吐 ，因为我不用 ， 然后也不是很熟 。

**吴涛** [2:00:57]
Ruby 没有什么可吐槽的 ， 说实话 ， 真的 。

**Rio** [2:00:59]
好 ， 现在我们来说另外一个敌对阵营 ，Node。Node 有一个很不错 ， 就在我看来在这些就是主流的动态语言里 ， 动态语言里面比较好的实现的一个包管理器 ， 这个叫 NPM。其实它甚至都不完全是一个包管理器 ， 它还能做一些别的事 ， 什么那个运行态的东西啊 ， 部署态也能做 ，其实还挺好的 。

不过我觉得 。

### Node

**吴涛** [2:01:28]
它有点像一个 configuration tool，有点 。

**Rio** [2:01:31]
对对 ， 它的范畴有点还不太一样 ，但是这个先不去纠结这些细节了 。 但是就说它作为这个包管理器的这个优点 ， 我觉得还是比较明显的 ， 就它干净 。

这干净什么意思呢 ？ 就是我们用这个 pip 安装也好 ， 还是 Ruby Gem 安装也好 ， 默认是安装进系统目录的 。

换句话说 ， 你只要没什么事 ， 你只要在这里装一个了 ， 你的系统那个 ， 就系统的 Python 那个包的目录就已经被打引号的污染了 ， 你没法再回到之前那个状态了 ， 特别是你用了 Python 那种只可 install、 不可 uninstall 的状态了 。

这种工具的情况下， 哎 ，Gem 可以 uninstall 吗 ？ 可以啊 。

**吴涛** [2:02:12]
Gem， 等等啊 ，Gem 当然可以 uninstall，而且 Gem 是可以装在本身的那个 。

**Rio** [2:02:18]
Local project 下面 。

**吴涛** [2:02:20]
加目录或者是本地目录吗 ？

**Rio** [2:02:21]
对 ， 直接装在那个 Windows 下面的 。

**吴涛** [2:02:24]
但是它的默认也是装到那个系统吧 ？ 是吗 ？

**Rio** [2:02:28]
默认 。

**吴涛** [2:02:29]
我记得是这样子 。 就系统可能是说一个用户嘛 ， 或者是你如果是 sudo 的话 ， 就是系统的根里面那些什么 ，user local 里面 。

**Rio** [2:02:37]
等等啊 ， 这不是一个非常 ， 就是如果你用 bundler 的话 ，bundler 本身是个 Gem， 然后你用 bundler 来管理你的 Gem 依赖的话 ，bundler 默认是安装在当前目录下的 。

**吴涛** [2:02:50]
啊 ，OK。

**Rio** [2:02:52]
就如果你只要用一个它给的那个 ，不是它给的 ， 就是它 ， 如果你用 bundler 这个 Gem 来管理你的项目的依赖的 Gem 的话 ，which is an industry standard， 就是几乎每一个好一点的 Ruby 项目几乎都是用 bundler 的 。

然后你就不会去污染你的全局 Gem。

**吴涛** [2:03:15]
所以还是有一个 best practice 在那里 。

**Rio** [2:03:17]
对 ，有一个 best practice 在那里 。

**吴涛** [2:03:19]
OK。

**Rio** [2:03:19]
不像我 ，不像彼阵营 。

**吴涛** [2:03:22]
我已经不是了 ， 我已经宣布脱离了 。

**Rio** [2:03:24]
好吧 。

**吴涛** [2:03:25]
OK。

**Rio** [2:03:26]
讲回 Node 这个东西啊 ，NPM， 就它是默认是装在当前目录下面的 ， 就是你可能每个工程 ， 每个项目是一个有一个自己的一个 Node module 这么一个目录 ， 然后你在里面执行 npm install 的之后， 它装的所有东西都会在 ， 默认是在那里面 。

然后如果你是要装在系统级别的话 ， 你这要加一个就 -global 这个选项 ， 它才会装到你的这个加目录 ， 或者是你 sudo 的情况下装到你的系统级别的目录里面去 。

**吴涛** [2:03:52]
对 。

**Rio** [2:03:53]
说它在一开始就鼓励一种就是所谓分 ， 完全分离嘛 ， 就是我觉得他们觉得磁盘空间的损失算不得什么 ， 重点是不要互相干扰 。

所以这个就是这种方式 ，但是它有它有自己的原因哈 ， 就是因为在所有的这个软件包里面 ， 可能 Node 的软件包的版本跳跃是最快的 。

我上个月装的一个什么开发一个软件的版本 ， 用的一个库可能是 0.2 点几 ， 我这个月升级看就已经 1.2 点几了 。

**吴涛** [2:04:27]
对 ， 如果你想最快最短 ， 最短时间之内在屏幕上获得最多的数据流 ，并且多多少少能让人看得懂的话 ， 那请执行 npm update。

**Rio** [2:04:40]
对 ， 所以也是因为这个残酷的现实使得这个 ， 可能我是这么猜的啊 ， 就使得这个 Node 社区必须要把这种版本依赖 ， 就是这个要 ， 就是叫什么 ，contained， 就是要局部化 ，因为一旦安装全局这个事没法玩了 。

**吴涛** [2:04:59]
对 ， 这就是他们拥抱改变的一种方式 。

**Rio** [2:05:01]
对 ， 然后他们这个 ，而且他们对这个版本的 ， 刚才我们讲那个交叉依赖解决的也很好 ， 就是它是说用我说的那种方式 ， 就是各个包去用装自己的依赖 ， 互相不要干扰 。

就是刚才我们就举一个最简单的例子 ， 假设我写这个程序叫 A， 然后它依赖 B， 然后也依赖 C， 然后这个依赖 ， 我的程序依赖 B 和 C 这两个包嘛 ， 然后这个 C 这个包它又依赖于 B，但是可能它是依赖于 B 的另外一个较早或者较新的版本 。

那个 NPM 的解决方案就是说 ， 那我这个程序装的时候 ， 我就写我要这个 B 的包的这个版本 ， 我要 C 的包的那个版本 ， 然后它就会去装这个 B， 然后就会把这个在我当前目录的 Node module 里面 ， 这个有一个目录名字是 B 这个包的名字 ， 还有一个名字是 C 的包的名字 ， 然后你进到这个 Node module， 然后 B 那个包的那个名字的目录里面去 ， 你发现它

又是一个带有 Node module 的一个包 ， 所以它就是在每一个包里面 ， 它都是独立的一个

一个 Node package 的层级关系在里面 ， 就不会出现说我依赖 A， 我依赖 B， 然后我也依赖 C，C 又依赖 B 的另外一个版本 ， 这种交叉依赖会给我导致的影响 ， 对我来说我是看不见 C 依赖了什么东西的 。

对 ， 就这点是我觉得它做的比较好的地方 ， 就是整个是很干净 ， 就很这个明确 ， 很干净 ，而且是就是局域化的

。 而且它的这个删除也很简单 ， 你不想用了 ， 你直接把那个 Node module 里面的那些东西清掉就好了 。

**吴涛** [2:06:34]
就是把目录删掉就可以了 。

**Rio** [2:06:35]
对 ， 就不会在你操作系统的其他任何地方留下垃圾 ， 如果你有洁癖的话 ， 这点是相当令人解放的一件事情 。

**吴涛** [2:06:44]
这也跟 JavaScript 的特性有关吧 ， 就 JavaScript 它不是一种

严格意义上的为

结粘合不同系统组件的 glue language 而出现的 。

**Rio** [2:06:59]
对对 。

**吴涛** [2:07:00]
对 。

**Rio** [2:07:02]
然后它的 。

**吴涛** [2:07:03]
其实这也引出了一个问题 ， 就是 JavaScript 有 native keybinding，native binding 需要 compile 的情况出现吗 ？

**Rio** [2:07:10]
应该是有的 ， 比如说我今天安装的时候 ， 它会去运行一下 C 的什么 compiler 在装什么东西 ，但是具体它在做什么我也不是太清楚 。

**吴涛** [2:07:19]
OK，因为我印象里面 JavaScript 的 NPM 社区极端到连什么 MySQL 的二进制代码都要 ， 二进制的协议都要写一个 JavaScript 的版本 。

**Rio** [2:07:30]
What？

**吴涛** [2:07:31]
就不要去调用 C 库 ， 这是为什么呢 ？

**Rio** [2:07:34]
不能调用 C 库吗 ？ 我不太确定 。

**吴涛** [2:07:37]
就你乍问我 JavaScript 怎么调用 C 库 。

**Rio** [2:07:40]
但是对 ， 确实有一点 ，他们是很愿意把所有东西都用 JavaScript 重新写一遍 ， 这里面其实是有好处的 ， 比如说你可以直接用到那个 v8 的那个 ， 如果你连 Node 里面跑 ， 你可以用 v8 的 JIT 的特性 ， 对吧 ？

然后你可以调试 ， 那些出错都很方便 ， 然后你包括管理起来也会很方便嘛 。

**吴涛** [2:08:00]
嗯 ， 一群极端主义者 。

**Rio** [2:08:01]
对 ， 然后那个 NPM 它在每一个包的里面 ， 它那个就是 package.json 这个文件也很简单 ， 就是你把一些常见东西写进来 ， 然后依赖关系写进来 ， 然后 NPM 这个命令行的工具也是比较强大的 ， 它能 ， 它甚至连那种连你的版本号的写法都有快捷命令帮你去搞定 ，他们是遵从这个叫做 semantic version 这个做法嘛 ， 就是你看这个 a 点 b 点 c， 然后 h 大版本号说变了

，API 肯定变了 ， 然后 b 是小版本号 ， 是一个变了话 ， 肯定是有很多各种新增内容 ，但是应该是向下兼容的 ， 然后 c 是这个小的小升级 ， 什么 bugfix 之类不影响接口的这种情况 。

对 ， 它连这个都已经帮你考虑好了 ，而且如果你这个 NPM 的包是在一个 git 里面的 repository 里面的话 ， 它甚至你做完 NPM version， 然后 patch 或者是 miner 这种命令之后， 就是把那个命令版本号升级一位 ， 它连那个 git 的 tag 的事情都帮你做好了 。

**吴涛** [2:09:03]
哦 ， 真是贴心 。

**Rio** [2:09:04]
对 ， 就是简直就是贴心小棉袄 ， 非常方便 ， 所以用起来很爽 ，而且就 。

**吴涛** [2:09:11]
对 ，也从一个侧面反映了 NPM 的社区其实是一个非常有活力的社区 ，不管多少人怎么样 ， 整个世界都在嘲笑他们 ， 对 ，但可能就是因为这种受难者的姿态迫使他们紧紧团结到了一起 。

**Rio** [2:09:24]
对 。

**吴涛** [2:09:25]
NPM 社区就是软件开发界的犹太人。

### Go

**Rio** [2:09:29]
对 ， 就反正整个来说 ， 这个 NPM 的体验是我目前用过所有的这种就是语言原生的包管理器里面体验最好的一个吧 ， 那可能是我孤陋寡闻 ， 没见过更好的 。

**吴涛** [2:09:43]
我也真的没有见过更好的 ， 更好的可能就只能是什么图形化的 ， 然后给最终用户用的 。

**Rio** [2:09:53]
所以对 ， 所以这个是 NPM 的一个挺好的地方 。 现在再说一个现在本人的大爱 ，Go 里面是怎么做的这件事情 。

**吴涛** [2:10:02]
Go 传教士 ，Rio 同学又来了 。

**Rio** [2:10:04]
对 ，其实你知道 Go 里面怎么做这件事情吗 ？

**吴涛** [2:10:07]
Go get 嘛 ，不是吗 ？

**Rio** [2:10:08]
对 ，但是它的哲学其实蛮奇怪的 ， 就通常来讲前面我们提到的几个像 ， 比如说

Java 也好 ，Python 也好 ，Node 也好 ， 还有 Ruby Gem 也好 ，他们都有一个所谓的这个所谓官方正式认可的这么一个集中发布软件包的渠道 ， 对吧 ？

**吴涛** [2:10:27]
对 ，有一个 repository 在那边 。

**Rio** [2:10:29]
对 ， 像 。

**吴涛** [2:10:29]
而 Go 是没有的 。

**Rio** [2:10:30]
对 ，Go 是没有的 ，Go 是完全是一个去中心化的一个

东 ， 一个这个发布方式 ， 然后就会导致一些很奇怪的一些状况 ， 比如说你 ， 我们通常一个包的名字你要引用它的时候 ，在比如说在 Python 里面 ， 你就是比如说 import JSON 就好了 ， 对吧 ？

那可能用官方标准的 ， 你可以用 import 的一个 JSON 这个例子不好 ， 另外举个什么例子 ，request 吧 ， 这个一个 import request， 你都整件好了 ， 就它你在用的时候和它的 ， 你和怎么拿到它是一个完全分离的过程 ， 对吧 ？

你是在 pip install request 它去那个 PyPI 上面下载回来 ，但你并不知道它到底是在哪 ， 然后你就用的时候你也不需要关心这个东西本来是 host 在哪里的 ，但 Go 里面不一样 ，Go 里面的 import 这个路径 ， 就是官方认可的方式就是你的那个源代码库的路径 ， 就比如说 。

**吴涛** [2:11:29]
所以它和 repository 相关的 ， 它不能做到 。

**Rio** [2:11:31]
对 ， 比如说我自己有一个 GitHub 的目录 ， 我是 github.com/reobar 下面一个什么软件 ， 就是我的一个 Go 的软件 ， 我就直接要把那一段写到这个 Go 的原程序里面去 ， 这样我就可以 ， 就如果我要引用某一个其他软件 ， 我也得把它的那个 repository 的地址放到那个路径里面去 ， 然后 Go get 这个东西有个黑魔法 ， 就是它能够自动识别常见的包管理的地址 ， 比如它知道 GitHub 怎么去取 ，

它知道那个 Bitbucket 怎么去取 ， 它知道那个什么 Mercury 那些东西怎么去取 ， 然后它会自动的给通过这个路径去找到那个 repository 下载回来放到你本地的一个约定好的那个路径里面去 ， 然后这样就把这个事情完成了 ，而且它是这个所谓的是一个 recursive 的一个过程 ， 就是比如说我要去 Go get 一个包 ， 比如说在 GitHub 下面某一个 /bard 这个包 ， 然后这个 /bard 这个包它又引用了 /spam 这个

包 ， 它会连带的把那些东西也拿回来 ，因为它能够分析到里面的 import path， 它能够知道缺什么 ， 然后它也知道因为那个 import path 就是你的那个 git 项目的地址嘛 ， 或者是那个 hg 项目的地址嘛 ， 它就能够去到那里就把所有东西给你抓回来 。

**吴涛** [2:12:49]
哦 ， 所以它通过一种非常复杂的方式使得一个包的 repository somehow 成为了它的命名空间的一部分 。

**Rio** [2:12:59]
对 ， 没错 ， 就是这样的 。

**吴涛** [2:13:01]
有意思 。 那你觉得这种做法是咸的蛋疼呢 ， 还是自有其道理所在 ？

**Rio** [2:13:08]
这个怎么说呢 ， 就是有好 ， 就肯定是有好处有坏处的 ， 好处就是简单嘛 ， 就是首先它别 ， 我们讲的它不是一个集中化的东西 ， 它可是一个分布式的东西 ， 它没有一个所谓就 ， 它不能说你给它一个包的名字 ， 它默认去哪个地方去找 ， 然后帮你下载回来 ， 对吧 ？

因为它没有这么一个中央存储的地方 ， 所以它只能把这种方式把这个地址的信息隐藏 ， 就这个嵌入到这个包的这个源代码里面去 ， 然后通过这种工具 ， 这种 Go get 这种工具去把这个事情帮你解决了 。

但是它不好的地方其实也很明显了 ， 比如说最简单一个例子哈 ， 我用了一个包 ， 然后我 fork 了它 ， 就它的官方功能不太好 ， 我 fork 它 ， 然后我要改的时候 ， 你会发现很痛苦 ，因为你改的时候就会必然 ， 你 fork 的时候会必然改变它的那个 import 路径嘛 。

**吴涛** [2:13:58]
对 。

**Rio** [2:13:59]
但是如果里面又有 import 自己的 ， 或者别人又 import 你的时候 ， 你也去改相关 import 到你的那个包的软件 ， 才能够使得你的这个修改能够被别人看见 。

**吴涛** [2:14:14]
哦 ， 所以它 somehow 增加了软件包维护的难度 。

**Rio** [2:14:21]
对 ， 就有点 ， 这里有点小不爽嘛 ， 就是你得去手工改一下那个 import 路径 ， 如果你 fork 之后， 如果你 fork 没有被这个 merge 回去的话 ， 就说你做了一个本地的不兼容的 fork。

然后另外一个问题就是说 ， 你会发现整个过程中没有出现版本这个概念 ， 比如说你可以在 Python 也好 ， 还是那个 NPM 也好 ， 你去安装一个包的时候 ， 它会告诉你安装了什么版本 ， 对吧 ？

**吴涛** [2:14:47]
对 ， 它甚至可以告诉你说请尽量安装接近于那个版本的版本 ，其实这个还挺 。

**Rio** [2:14:53]
对 ， 就它有一个中央存储 ， 它会有这个包的名字 ， 然后有这个版本号 ， 包括历史的版本号 ， 它会记录下来 ， 然后你可以说我可以指定说 2.x 版本都可以 ， 或者我一定 2.1， 或者一定 2.5， 或者一定 2.5.5， 对吧 ？

你都可以这么去指定 。

**吴涛** [2:15:09]
或者说不高于某个版本的版本 ， 这也是采用这个所谓 semantic versioning 的一个优点嘛 ， 就是 。

**Rio** [2:15:15]
对 ， 没错 。

**吴涛** [2:15:16]
你可以做诸如此类的 convention， 基于 convention 的事情 。

**Rio** [2:15:18]
对 ，Go 里面完全没有版本的概念 ，Go 里面所有东西都是一个 ， 就是一个 git， 就是一个 repository 地址 ， 完了 。 然后他们的说法是说 ， 如果你发布了一个包 ， 你就不要改它的接口 ，forever。

**吴涛** [2:15:36]
这个有点太 。

**Rio** [2:15:38]
太过了 ， 对不对 ？

**吴涛** [2:15:38]
太任性了吧 。

**Rio** [2:15:40]
这简直就是 。

**吴涛** [2:15:41]
Google 就是这么任性 。

**Rio** [2:15:42]
对 ， 简直就是 ， 这哪里是任性 ， 简直就是强硬好不好 ？ 然后其实他们的这个说法也很简单 ， 就是说如果你做了一个叫什么接口不兼容的改进 ， 那么请用一个新的 import 的地址 。

**吴涛** [2:15:58]
这个我觉得有点 ， 那为什么不能再就让它更进一步 ， 就直接指定这个 repository 的 branch 呢 ？

**Rio** [2:16:05]
对 ， 这就是很多人要吐槽的地方 ， 就说那为什么不支持 ， 那我直接说我可以 tag 嘛 ， 对吧 ？ 我可以 tag 嘛 ， 我这样也可以啊 。

但是你想想 ， 按理说还蛮合理的 ， 对吧 ？ 但问题出在哪呢 ？ 问题出在 Go 也没有指定一个默认的软件控制的 ， 软件 version control 的东西 ， 就说比如说 git 里面有 branch 的概念 ， 然后这个 Mercury 里面有这个 branch 的概念 ，SCV 有吗 ？

也有好像 ，有创 。

**吴涛** [2:16:35]
SCV？CSV？

**Rio** [2:16:37]
啊 ，不 ，CSV。

**吴涛** [2:16:38]
啊 ，sorry。

**Rio** [2:16:39]
SCV，SCVS， 错 ，不好意思 。

**吴涛** [2:16:41]
SCV，CSV 是 。

**Rio** [2:16:43]
CVS。

**吴涛** [2:16:43]
Comma separated。

**Rio** [2:16:46]
对 ， 挖矿机 。

**吴涛** [2:16:47]
CSV。

**Rio** [2:16:49]
CSV 不支持 ， 对吧 ？ 还有 subversion， 它里面也什么有 tag 的概念 ， 就很 。

**吴涛** [2:16:53]
Subversion 是有 ，okay。Subversion 其实是有 branch 的 ，不过它 branch 是非常重量级的 branch。

**Rio** [2:16:58]
对 ， 就是说其实你要去兼容那个 ， 你没有这些东西 ， 就是最基础的 ， 你只有一个 URL 而已 ， 就你不能去指定其他东西 。

而且因为 Go get 也是封装了那些命令行工具而已嘛 ， 就是包括封装了 git， 封装了 hg， 封装了那个什么 subversion 是什么命令 。

**吴涛** [2:17:21]
SVN？

**Rio** [2:17:21]
SVN， 对 ， 它封装了那些命令的调用 ， 它也没有办法去强制说你能用一个什么样的方法去大家在不同的版本库之间 ， 用不同的方法管理软件版本的 ，也能够指定同一个版本出来 ， 对吧 ？

所以其实就变得这个很难嘛 。 你知道凡是一旦变成分布式 ， 没有一个中央集权的东西 ， 就变得很难解决 。

对 ， 这也是其中之一 。 所以现在有一些就是社区来了一些方法 ， 就是说那我们不要 ， 就为了解决这个刚才你说这个版本管理的问题 ， 我们要指定某个版本 ， 我们不简单的直接用 Go get， 我们是说写一个文件 ， 声明我需要哪一个软件的哪一个版本 ， 通过一个 branch 也好 ， 还是通过这个 git tag 或者什么方式也好 ， 把它写在一起 ， 然后我再通过这个第三方的社区维护

的工具去安装这个软件版本 ，而不是用这个官方的这个 Go get 这种形式 。

**吴涛** [2:18:27]
Okay。

**Rio** [2:18:27]
对 。

**吴涛** [2:18:28]
那具体来说有什么样的工具 ？

**Rio** [2:18:30]
有一个比较常见的一个叫 GoDep，GoDep， 就是 Go Dependency 的一个 。

**吴涛** [2:18:36]
Go Dependency，okay。

**Rio** [2:18:37]
对 ， 这么一个工具 ， 还有一些其他 ， 就在这一块 Go 里面 ， 就社区里面还是有三四个选项吧 ， 就看 ， 我不知道之后会怎么样 ，但是现在 GoDep 这个是一个比较用的比较多的一个工具 。

然后另外一个要解决的问题就是说 ， 如果你分布式 ， 你也没有一个集中存储代码的地方 ， 那同样的你也不能保证那个代码你今天在哪 ， 明天还在哪 ， 对吧 ？

那个人可能就直接把他的 git 项目删除了 ， 对吧 ？ 之前也有遇到过什么那个什么 Y 啊 ， 还有那几个人他从网上消失的时候 ， 连带着把他的那个什么 GitHub 账号也给清掉了 。

这样如果你一拉他代码 ， 你发现哇 ， 没了 ， 怎么办 ？

**吴涛** [2:19:17]
没错 。

**Rio** [2:19:18]
对 ， 所以这其实是一个比较 ，在这种分布式的场下， 一个比较推荐的方式就是说 ， 你把你需要依赖代码也加入你的本地的版本控制里面去 。

对 ， 然后这样的话有一些什么难度呢 ， 就是那你要想想办法 ， 你怎么去分离你自己写的代码和你依赖的代码的这个历史的版本的管理 ， 你最好是能够区别开来 ， 对吧 ？

这样可以分别升级嘛 。

**吴涛** [2:19:41]
对 ， 这也就无形中增加了你对 ， 要求你对版本管理系统又要有足够多的知识 ，而相比之下其他的语言就可以比较专心的放在开发上面 。

**Rio** [2:19:55]
对对对 ，但其实这个怎么来说 ， 从总体来可用性的角度来讲 ， 这个反而是好的 ，因为这样的话你就可以确保你只要拿到这个软件包 ， 然后这个软件包里面的所有依赖是通过这种方式来管理的 ， 就是你把它 check 进你的本地的这个代码库里面 ， 那么你拿到这个代码库里面 ， 你知道它是一个完整的 ， 就是你可以用代码库编译出来一个最终的结果 ，不需要

去再经过一个网络的下载的这么个过程 。

**吴涛** [2:20:19]
Okay， 所以如果你要用 ， 你要为世界的终结做准备的话 ， 那请用 Go 来编程 。

**Rio** [2:20:26]
对 ， 所以就是它是一个 ， 你可以看到刚才我们这一系列的主题都是一个无中心的这么一个诉求 ， 所以从这一点来讲 ， 它跟 git 的想法其实是还是蛮不谋而合的哈 。

**吴涛** [2:20:40]
对 。

**Rio** [2:20:40]
就 git 说我不要什么 SVN 和 CSV 都是需要有个中央服务器 ， 你才可以 check in 啊 ， 才可以同步软件的代码嘛 ，但是 git 不需要 ， 你可以本地修改 ， 本地 commit， 对吧 ？

**吴涛** [2:20:53]
对 。

**Rio** [2:20:53]
Go 的话也是这样 ， 本地编译 ， 本地出结果 ， 对吧 ， 就比较好一点 。 当然代价也就是用起来会比较繁琐 ，而且在 ， 就是 Go 官方是对这个事情是没有说法的 ， 就是说你们自己想办法解决 ， 我们不管 ，因为这里面有个很务实的情况 ， 就是因为 Go 的大部分开发者还是谷歌嘛 ，Google 的一些工程师 ， 然后他们 Google 内部是用一个非常复杂先进的 build 系统在写程序 ，

哪怕是 ，但是他们是这个跨语言的 ， 所以他们也不用这个 Go get 来装程序 ， 所以他们内部 。

**吴涛** [2:21:28]
所以 Go get 到底是做给谁用的 ？ 如果不吃自己狗屎的话 ， 那么他们是 。

**Rio** [2:21:33]
开发条适用嘛 ， 就是你可以 ，因为它大部分时候还是可以用的 ， 你不是说你每天要 Go get 一遍 ， 对吧 ？

你 Go get 回来一个版本 ， 它大部分时候还是稳定可靠的 ，因为它还是鼓励说这个 API 要尽量稳定可靠嘛 ， 所以你拿回来大部分时候还是可用的 ， 只是说当你要正式发布的时候 ， 你最好把你的依赖那些通过刚才我讲的那些方法 check 到你本地的一个目录里面去 ， 这样的话会 ， 就可靠性会好一些嘛 。

**吴涛** [2:22:01]
Okay。

**Rio** [2:22:03]
对 ， 所以也是就是说因为 Go 它官方没有一个说法 ，也没有一个他们自己内部也用的东西 ，也他们那个内部用那个也不是开源的嘛 ，也不是一个商业系统 ， 所以就没有办法把它共享给我们共享 ， 就是给我们也用不了 ， 太复杂了 。

**吴涛** [2:22:21]
对 ， 这个其实 somehow 跟苹果这边的阵营差不多啊 ， 苹果也是 ，不管 Objective-C 还是 Swift，其实都没有一个官方的版本包管理系统 。CocoaPods 虽然是目前的继承事实的包管理系统 ，但好像官方也没有表现出任何态度是支持它 ， 还是觉得可能要拿出来要自己另做一套的 。

**Rio** [2:22:50]
苹果应该不会管这种事情吧 ， 我觉得 。

**吴涛** [2:22:54]
我觉得对于开发者来说 ， 这个东西其实还是蛮重要的 。

**Rio** [2:22:59]
这肯定毫不怀疑 ，但问题是说苹果会不会觉得这个事情很重要 ？ 我很怀疑 。

**吴涛** [2:23:05]
我是觉得上次 WWDC 之后， 苹果既然对开发者有了这么难得一次的微笑 ， 那也许接下来有些事情也是可以梦想的 。

**Rio** [2:23:15]
啊 ，是吗 ？ 那我强烈建议你听一下还没 ， 应该是明天发布的 IT 公论第 131 期 ， 看一下苹果对开发者的真实态度是什么 。

**吴涛** [2:23:25]
啊 ， 好吧 。 感觉又要被泼冷水了 。

**Rio** [2:23:29]
对 。

**吴涛** [2:23:29]
今年的那个 YouTube 的 Rewind somehow 泼冷水泼了很久 。

**Rio** [2:23:33]
为什么 ？

**吴涛** [2:23:35]
就 YouTube 年末的时候会有个 Rewind 的视频嘛 ， 就是历数那个今年知名的比较 。

**Rio** [2:23:42]
那个动词应该是 。

**吴涛** [2:23:43]
Sorry？

**Rio** [2:23:44]
对 。

**吴涛** [2:23:45]
Rewind？

**Rio** [2:23:46]
Rewind。

**吴涛** [2:23:46]
Sorry。

**Rio** [2:23:46]
我觉得应该是念 Rewind。 动词的情况下 。

**吴涛** [2:23:49]
Rewind。

**Rio** [2:23:50]
Anyway。

**吴涛** [2:23:52]
对 ， 然后它里面会历数今年的比较知名的视频 ， 然后把它们以某种方式重新拍一遍 ， 然后在里面 ，因为今年冰桶挑战很出名嘛 ， 然后在里面有大量那个知名人士互相泼冷水 。

**Rio** [2:24:07]
泼冷水 ， 泼冰水 。

**吴涛** [2:24:08]
泼冰水的情节 ， 然后哇 ， 难道明天我要被泼冰水了 ？

**Rio** [2:24:15]
不过刚才今天刚才前面讲了这么多 ， 就是各个语言自己本身的一些包管理的机制也好 ， 一些软件也好 ，其实我一直有一个问题 ， 为什么没有一个能够跨语言解决这个问题的工具呢 ？

### 跨语言

**Rio** [2:24:29]
就是我们为什么每个语言出来都要自己再重复这么一套很麻烦啰嗦的事情 ， 然后

要把这个已经解决的问题再解决一遍呢 ？

**吴涛** [2:24:44]
如果你要说这个的话 ， 那我觉得 Nugget 可能是一个这样的工具吧 。

**Rio** [2:24:50]
为什么 ？

**吴涛** [2:24:51]
因为你觉得 Nugget 并不限定于是专门为 C# 或者是专门为 .NET 平台所做的一个包管理工具啊 。 它 ， 你像我刚才说的 ， 它可以拿到 JQuery 这种东西 。

**Rio** [2:25:04]
这个我没有异议 ，但我是说比如说像那个什么 Node 那些 NPM， 它只能装 JavaScript 的包嘛 ， 然后 Ruby 也只能装 Ruby 的包嘛 ， 就是说在现在流行的那些东西都是根据各个语言那个独特的一些系统 ，并不能有一个通用的系统解决它们之间的问题 。

你是说如果 Nugget 这种方式流行起来 ， 我们也可以用 Nugget 装那个 JavaScript 的包 ， 然后用 Nugget 装 Go 的包吗 ？ 还是怎么样 ？

**吴涛** [2:25:34]
可是反过来我的理解是 ，有些语言的部分包也是可以通过比如说 Apt 之类的东西来安装的呀 。

**Rio** [2:25:46]
对对对 。

**吴涛** [2:25:46]
但是如果朝这个方向发展的话 ， 你是不是就可以说那整个系统平台上干脆只有一个包管理系统 ， 它既负责软件安装 ， 又负责所有语言的相关包的安装就可以了呢 ？

那我觉得其实对于 C 语言来说 ，C 语言没有一个既成事实的安装包 ， 安装包管理系统 ， 对吧 ？

**Rio** [2:26:08]
对 。

**吴涛** [2:26:08]
那可是反过来说 ， 你可以说任何一个 APD 也好 ， 那个 Yum 也好 ， 你在安装那些 Develop 的 Dev 或者是 Devel 或者是 Develop 包的时候 ， 你不就已经安装了这个语言 ，C 语言所需要的头文件吗 ？

那也许一个语言发展到足够壮大的时候 ， 它也许就可以通过一个泛用的 Package Manager 来解决这个问题了 。

**Rio** [2:26:37]
其实还是一个就是说这个不够成熟的表现嘛 。

**吴涛** [2:26:43]
对 ， 应该是这样的 。 但参差不齐乃是生活之美的来源 ，是这么说吗 ？

**Rio** [2:26:51]
确实 ，但

我不确定这在程序语言这一套 ， 就在包管理器这样是不是一定是好的 ，但是每次跨一个语言都要重新学习一遍 ， 还是蛮高成本的一件事情吧 ， 我觉得 。

**吴涛** [2:27:07]
这就是 Polyglot 的宿命了 。

**Rio** [2:27:10]
好悲伤 。

**吴涛** [2:27:12]
如果你只会 Java 的话 ，也许就没有所有这些痛苦了 ， 你会生活在一种无知的幸福里面 。

**Rio** [2:27:18]
这是一个悲伤的故事 。

**吴涛** [2:27:23]
好吧 ， 已经两个半小时了 。

**Rio** [2:27:25]
好 ， 今天 。

**吴涛** [2:27:26]
就结束吧要不 。

**Rio** [2:27:27]
对 ， 今天我们的吐槽那就到这里为止吧 。

**吴涛** [2:27:31]
对 ， 我们今天的主题是 Package Management System，其实我们本来还想就这个话题延伸到比如说 。

**Rio** [2:27:40]
部署啊 。

**吴涛** [2:27:41]
做好了包怎样部署到服务器上 ，以及围绕着部署所产生的一系列问题 ， 比如说部署并不仅仅简简单单的是把一个 Binary 扔到服务器上去运行这么简单 ， 当然对于某些 Go 程序员来说这是可能的 ，但对于我们其他的熟民来说 ， 这里面还是有很多学问所在 ，但是今天我们也没有时间展开了 ， 所以还是 。

**Rio** [2:28:07]
下次吧 。

**吴涛** [2:28:08]
下次找机会吧 ， 就明年再说吧 。

**Rio** [2:28:12]
See you in 2015。

**吴涛** [2:28:14]
Yes， 就是各位听到我们这一期节目的时候 ， 应该还有两三天就要新年了 ， 那也在这里祝大家新年快乐喽 。

**Rio** [2:28:23]
嗯哼 。

**吴涛** [2:28:25]
好吧 。

**Rio** [2:28:25]
我们 。

**吴涛** [2:28:26]
Okay， 下期再见 ， 连同着前两期的所有的 feedback。

**Rio** [2:28:32]
明年再见 。

**吴涛** [2:28:32]
我随着新年的新气象 ， 对 ， 重新来做一期新的内核攻防 。

**Rio** [2:28:39]
嗯哼 。

**吴涛** [2:28:40]
Okay， 我们今天做了七期嘛 ，不错 。

**Rio** [2:28:45]
拜拜 。

---

本节目库由 PodHood（https://podhood.com）提供支持——播客网站平台。
