背景
在 mac osx 下, 大部分的軟件都是使用 homebrew 進行管理的, 可以方便的進行軟件的安裝,更新,刪除等等, 大部分情況下 homebrew 的倉庫只會存在一份最新的軟件版本, 有時可能也會同時存在多個版本, 比如 python 就會有多個.
在有些情況下我們可能需要安裝某些軟件的歷史版本, 接下來提供幾種方式實現.
Homebrew 術語説明
在安裝歷史版本介紹之前, 先簡單介紹一下 Homebrew 的一些名詞術語及結構, 以便大家能更好的理解, Homebrew 中文可以翻譯成 "家釀", 所以這個軟件是對酒相關的一個抽象. 主要的結構包括:
Formula ( 配方 )
: 主要使用 ruby 文件描述的軟件信息, 包含軟件基本信息, 依賴, 編譯等等. 如 /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/foo.rb
Tap ( 酒吧 )
: 所有 Formula 或命令的 Git 倉庫, 比如: /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core 對應 https://github.com/Homebrew/homebrew-core 倉庫.
我們平時使用 brew update 就是使用同步 Tap 對應分支的更新. 使用 brew install formula 也是從所有的 tap 中查找formula 安裝.
Bottle ( 酒瓶 )
: 基於對應操作系統已編譯好的二進制包, 可以直接使用, 在 rb 文件中會描述各個版本的二進制. 使用二進制包可以省去自己編譯. 如: qt-4.8.4.catalina.bottle.tar.gz
Cask ( 木酒桶 )
: 用於描述和安裝 macOs 原生 app 軟件, 使用 brew cask 安裝就和使用 dmg 安裝到 Applications 一樣的效果.
Keg ( 小桶 )
: 描述的是一個已安裝的軟件版本路徑, 如: /usr/local/Cellar/foo/0.1
Cellar ( 酒窖 )
: 所有已安裝軟件的存儲倉庫路徑, 如: /usr/local/Cellar
理解上面的一些概念後, 其實要安裝歷史版本的話, 我們只需要拿到歷史版本的 formula 就可以安裝了.
歷史版本安裝
存在多版本軟件
前面説過, 有的軟件在 homebrew 的倉庫會存在多個版本, 我們可以直接指定相應的版本進行安裝, 我們可以使用 brew search 查詢下要安裝的軟件的版本.
➜ brew search python
# output
==> Formulae
ipython python-markdown python@3.11 ✔ cython
micropython python-tabulate python@3.7
pr0d1r2/python2/python@2.7.17 ✔ python-tk@3.10 python@3.8 ✔
ptpython python-tk@3.11 python@3.9 ✔
如上有很多 python 的其它版本, 如: python@3.7, python@3.8 等. 我們使用 brew install 指定版本安裝.
➜ brew install python@3.7
# output
Running `brew update --auto-update`...
無多版本軟件
對於 tap 中無多版本的軟件, 我們可以通過在 tap 對應的 git 倉庫中查看歷史 formula 版本, 通過下載到本地進行安裝.
先通過 https://formulae.brew.sh/ 找到軟件信息, 如我需要找到 folly 的歷史版本, 可按照下面的步驟.
歷史版本的 .rb 文件保存到本地之後, 可以使用 brew install 安裝. 如:
# 在 .rb 文件保存的目錄執行
# 需要先刪除原始版本的鏈接再進行安裝
brew unlink folly
brew install folly.rb
這種方式安裝有些情況下會出現安裝失敗, 比如我下載了一個 13.0.1_1 llvm.rb 文件安裝:
➜ brew install -s llvm.rb
Running `brew update --auto-update`...
==> Auto-updated Homebrew!
Updated 1 tap (homebrew/core).
==> Fetching llvm
==> Downloading https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.1/llvm-project-13.0.1.src.tar.xz
==> Downloading from https://objects.githubusercontent.com/github-production-release-asset-2e65be/75821432/e556705d-3a90-49f1-909f-c1c5e
######################################################################## 100.0%
...
Warning: llvm 15.0.6 is available and more recent than version 13.0.1_1.
Error: An exception occurred within a child process:
NoMethodError: undefined method `user' for nil:NilClass
上面會出現 ruby 的執行錯誤, NoMethodError: undefined method 'user' for nil:NilClass, 問題的原因是 llvm.rb 中使用了 tap.user 等變量, 而這些變量來自於當前 formula 對應 tap 的 git 倉庫信息, 而你下載的 rb 文件是沒有放在倉庫中.
# llvm.rb 信息
-DPACKAGE_VENDOR=#{tap.user}
-DBUG_REPORT_URL=#{tap.issues_url}
-DCLANG_VENDOR_UTI=org.#{tap.user.downcase}.clang
解決辦法就是修改 llvm.rb 信息, 把變量的值直接寫成固定值. 如: tap.user 為 "Homebrew", tap.issues_url 為 "https://github.com/Homebrew/homebrew-core/issues", tap.user.downcase 為 "homebrew".
另外一個解決方案就是把下載的 llvm.rb 文件覆蓋到 tap 對應的倉庫中原文件. 如:
# 進入到本地默認 tap 倉庫
cd $(brew --repository homebrew/homebrew-core)/Formula
# 把 llvm.rb 文件覆蓋到此倉庫原文件
mv ~/Downloads/llvm.rb llvm.rb
# 安裝
brew install llvm
安裝完成後, 記得在倉庫中還原這個文件.
References
- https://docs.brew.sh/Formula-Cookbook
- https://rubydoc.brew.sh/Tap.html#user-instance_method