Brew 包管理器的工作流程分析
最近将自己的站点从 Node.js 迁移到了基于 Python 实现的 ark。在迁移过程中主要遇到了 ark 的包变更问题,它从之前的 ivy 包更名为 ark,而且 Python 版本也随之进行了变更。
在机器上安装多个版本的 Python 还是要自己捣鼓捣鼓的。所以就想,有没有类似管理 Node.js 版本的工具呢?于是问了下 ChatGPT,得到如下的回复:
Python 的版本管理工具主要有以下三种: 1. pyenv:pyenv 是一个简单的 Python 版本管理工具,可以在不同的 Python 版本之间切换,并且可以为每个项目自定义 Python 版本。 2. Anaconda:Anaconda 是一个流行的 Python 数据科学平台,它包括一个 Python 版本管理工具,可以创建和管理不同版本的 Python 环境。 3. virtualenv:virtualenv 是另一个流行的 Python 版本管理工具,它可以为每个项目创建独立的 Python 环境,并允许在这些环境之间切换。这些工具都可以帮助你管理 Python 版本并避免版本冲突问题。你可以选择其中任何一种工具来管理你的 Python 环境。
我选择安装 pyenv。由于我用的是 Mac OS,所以可以使用 brew 进行安装。借助 brew 很快就安装完了。
在安装的过程中我突然意识到,我以前一直在用 brew,但是我并没有想过它是基于什么原理实现的。于是我看着 brew 的输出信息开始思考。
最先想到的是 brew 肯定要具有下载的能力,而后具备安装的能力,至于下载进度什么的都是次要信息。紧接着问题又来了,brew 从哪里知道 pyenv 的下载地址?pyenv 的安装脚本和 awk 的安装脚本一样吗?不一样的话,brew 是怎么知道在哪里找到安装脚本呢?
带着这些疑问,我先去问了 ChatGPT,brew 实现原理是什么?
Brew是一款开源的软件包管理器,它是专门为Mac OS X设计的。它的实现原理主要是基于Ruby编程语言的。Brew的主要功能是下载,编译和安装各种软件包,这些软件包都可以在Mac OS X上运行。Brew使用一个简单的命令行界面,用户可以使用它来搜索和安装他们需要的软件包。Brew的实现原理基于Git和Ruby,它使用Git作为软件包存储库的管理工具,而Ruby则提供了一个强大的编程环境,使得Brew可以轻松地实现各种操作,例如软件包下载,编译,安装和卸载等等。此外,Brew还使用了一些其他的开源组件,例如curl,xz和zlib等等,这些组件可以协同工作,使Brew的功能更加强大和灵活。总的来说,Brew的实现原理是利用开源的组件和技术,通过Ruby编程语言编写的软件包管理器,使得用户可以轻松地安装和管理他们需要的软件包。
在这个回答中,我比较关心 brew 是在哪里下载的,所以我又问 ChatGPT:brew 是怎么知道某个包的下载地址?
Brew是通过自己的中央仓库来获取软件包的下载地址的。当用户使用Brew安装某个软件包时,Brew会根据用户提供的软件包名称,从自己的中央仓库中查找相应的软件包信息,包括软件包的下载地址和其他相关信息。Brew的中央仓库包含了大量的软件包信息,这些软件包都是经过Brew团队认证和测试过的,保证了软件包的安全性和质量。如果Brew的中央仓库中没有用户需要的软件包信息,Brew还可以通过用户提供的自定义仓库地址来获取软件包的下载地址。总的来说,Brew是通过自己的中央仓库和用户提供的自定义仓库地址来获取软件包的下载地址的。
在这个回答中,我比较关心中央仓库的地址,于是我接着问了下地址,得到了如下链接:https://github.com/Homebrew/homebrew-core
图中是仓库的目录结构,从上往下点开看一下。Aliases 中的文件,指向了 Formula 目录中的文件。例如:
因为 Aliases 中的文件是指向 Formula 中的文件的,所以可以不用关心 Aliases 目录中的文件了。接着去看 Formula 。
Formula 目录下文件非常多,找个我比较熟悉的包看看,比如 awk。
class Awk < Formula
desc "Text processing scripting language"
homepage "https://www.cs.princeton.edu/~bwk/btl.mirror/"
url "https://github.com/onetrueawk/awk/archive/20220122.tar.gz"
sha256 "720a06ff8dcc12686a5176e8a4c74b1295753df816e38468a6cf077562d54042"
# https://fedoraproject.org/wiki/Licensing:MIT?rd=Licensing/MIT#Standard_ML_of_New_Jersey_Variant
license "MIT"
head "https://github.com/onetrueawk/awk.git", branch: "master"
bottle do
rebuild 1
sha256 cellar: :any_skip_relocation, arm64_ventura: "47f3d16ebd3eb5767c9053091532d69d2c8288a262f9007301b757639833ffdb"
sha256 cellar: :any_skip_relocation, arm64_monterey: "6bb8fdb607cba55ad0f173881e83f8f83ffbd3f2cbf84ca26a494cf614d6d3f6"
sha256 cellar: :any_skip_relocation, arm64_big_sur: "253a8eb03a628051cc748336648bc984e4a2bb04322a06c8d8e2e43798d5f581"
sha256 cellar: :any_skip_relocation, ventura: "4c14575b3b5c52aa4b7064bfa737e7a2cce5bf00643883f71be87f509fceb546"
sha256 cellar: :any_skip_relocation, monterey: "7ab4bfa6706a1cad2f1990962b8067e359da9f49079803c7628483e97a0e396d"
sha256 cellar: :any_skip_relocation, big_sur: "d566709d5824930ffecb61d05d86e724bf54c3956964ed39b3a3fffaac3b60e0"
sha256 cellar: :any_skip_relocation, x86_64_linux: "6b9a89dc8999517cda6105d5c5e0e94294aba8f507aa85b21ee9a3a75c08a0a7"
end
uses_from_macos "bison" => :build
on_linux do
conflicts_with "gawk", because: "both install an `awk` executable"
end
def install
system "make", "CC=#{ENV.cc}", "CFLAGS=#{ENV.cflags}"
bin.install "a.out" => "awk"
man1.install "awk.1"
end
test do
assert_match "test", pipe_output("#{bin}/awk '{print $1}'", "test")
end
end
再换个包看一下 Formula/autorestic.rb,中的 install 是通过 go 进行安装的:
def install
system "go", "build", *std_go_args, "./main.go"
generate_completions_from_executable(bin/"autorestic", "completion")
end
不过 brew 不能算是约束先行。像 NPM 这样的包管理工具,是约束先行的,首先我认为 NPM 具有发布包的概念,而 brew 没有发布包的概念,brew 只有编写安装脚本的概念(Flrmula 中的 rb 脚本),其次 NPM 由于有了发布的概念,所以在包的设计阶段就要按照规范编写相关的 packge.json,而 brew 的设计中,这个步骤是解耦的。正像 brew 的 homebrew-core 是由一群志愿者维护的,而不是谁写的包谁来负责。
通过 ChatGPT 可以轻松地对信息进行聚合,减少自己去 google 的时间。而且还能锻炼自己提出更好的问题。