Homebrew 使用中的一些问题

2023-03-31 • 更新于 2025-05-28

编译 Go 项目超时

众所周知,国内网络环境编译 Go 项目会超时,只要设置国内镜像即可:

export GO111MODULE=on
export GOPROXY=https://goproxy.cn,direct

我想了十天十夜也不明白,源码服务器也要屏蔽?多少有点脑瘫哦。

然而即使设置了镜像,Homebrew 在编译 Go 项目时,不知何故不会读取用户设置,于是也会超时:

==> go build -o=/usr/local/Cellar/v2ray/5.3.0/libexec/v2ray -ldflags=-s -w -buildid= ./main
Last 15 lines from /Users/username/Library/Logs/Homebrew/v2ray/01.go:
proxy/vmess/encoding/auth.go:8:2: golang.org/x/crypto@v0.4.0: Get "https://proxy.golang.org/golang.org/x/crypto/@v/v0.4.0.zip": dial tcp 142.251.42.241:443: i/o timeout
common/protocol/dns/io.go:7:2: golang.org/x/net@v0.4.0: Get "https://proxy.golang.org/golang.org/x/net/@v/v0.4.0.zip": dial tcp 142.251.43.17:443: i/o timeout
app/dns/nameserver_quic.go:14:2: golang.org/x/net@v0.4.0: Get "https://proxy.golang.org/golang.org/x/net/@v/v0.4.0.zip": dial tcp 142.251.43.17:443: i/o timeout
transport/internet/http/hub.go:11:2: golang.org/x/net@v0.4.0: Get "https://proxy.golang.org/golang.org/x/net/@v/v0.4.0.zip": dial tcp 142.251.43.17:443: i/o timeout
common/strmatcher/matchers.go:9:2: golang.org/x/net@v0.4.0: Get "https://proxy.golang.org/golang.org/x/net/@v/v0.4.0.zip": dial tcp 142.251.43.17:443: i/o timeout
common/protocol/headers.go:6:2: golang.org/x/sys@v0.3.0: Get "https://proxy.golang.org/golang.org/x/sys/@v/v0.3.0.zip": dial tcp [2404:6800:4012:2::2011]:443: i/o timeout
transport/internet/filelocker_other.go:9:2: golang.org/x/sys@v0.3.0: Get "https://proxy.golang.org/golang.org/x/sys/@v/v0.3.0.zip": dial tcp [2404:6800:4012:2::2011]:443: i/o timeout
app/commander/commander.go:10:2: google.golang.org/grpc@v1.51.0: Get "https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip": dial tcp 142.251.43.17:443: i/o timeout
transport/internet/grpc/dial.go:13:2: google.golang.org/grpc@v1.51.0: Get "https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip": dial tcp 142.251.43.17:443: i/o timeout
app/instman/command/command_grpc.pb.go:6:2: google.golang.org/grpc@v1.51.0: Get "https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip": dial tcp 142.251.43.17:443: i/o timeout
transport/internet/grpc/dial.go:14:2: google.golang.org/grpc@v1.51.0: Get "https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip": dial tcp 142.251.43.17:443: i/o timeout
transport/internet/grpc/dial.go:15:2: google.golang.org/grpc@v1.51.0: Get "https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip": dial tcp 142.251.43.17:443: i/o timeout
transport/internet/grpc/encoding/conn.go:13:2: google.golang.org/grpc@v1.51.0: Get "https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip": dial tcp 142.251.43.17:443: i/o timeout
app/commander/service.go:7:2: google.golang.org/grpc@v1.51.0: Get "https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip": dial tcp 142.251.43.17:443: i/o timeout
app/instman/command/command_grpc.pb.go:7:2: google.golang.org/grpc@v1.51.0: Get "https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip": dial tcp 142.251.43.17:443: i/o timeout

Homebrew 编译时实际上调用的是 Ruby 脚本,那就可以直接修改脚本。以 v2ray 为例:

cd $(brew --repository homebrew/core)
git diff
diff --git a/Formula/v2ray.rb b/Formula/v2ray.rb
index 297d0759440..baef313f96f 100644
--- a/Formula/v2ray.rb
+++ b/Formula/v2ray.rb
@@ -39,6 +39,9 @@ class V2ray < Formula
   end

   def install
+    ENV['GO111MODULE'] = 'on'
+    ENV['GOPROXY'] = 'https://goproxy.cn,direct'
+
     ldflags = "-s -w -buildid="
     system "go", "build", *std_go_args(ldflags: ldflags, output: libexec/"v2ray"), "./main"

install 编译命令前添加 ENV['GO111MODULE'] = 'on'ENV['GOPROXY'] = 'https://goproxy.cn,direct' 就可以了。

./t-git-credential-netrc.sh: No such file or directory

编译安装 git 时出现错误:

# ~/Library/Logs/Homebrew/git/05.make
make
test

/Applications/Xcode.app/Contents/Developer/usr/bin/make -C ../../.. SCRIPT_PERL="contrib/credential/netrc/git-credential-netrc.perl" \
                build-perl-script
    * new perl-specific parameters
\
	INSTLIBDIR='/private/tmp/git-20250521-61559-k2ku4o/git-2.49.0/.brew_home/share/perl5' && \
	INSTLIBDIR_EXTRA='/Applications/Xcode.app/Contents/Developer/Library/Perl/5.18/darwin-thread-multi-2level:/Library/Developer/CommandLineTools/Library/Perl/5.18/darwin-thread-multi-2level' && \
	INSTLIBDIR="$INSTLIBDIR${INSTLIBDIR_EXTRA:+:$INSTLIBDIR_EXTRA}" && \
	sed -e 's=@PATHSEP@=:=g' \
	    -e "s=@INSTLIBDIR@=$INSTLIBDIR=g" \
	    -e 's=@PERLLIBDIR_REL@=share/perl5=g' \
	    -e 's=@GITEXECDIR_REL@=libexec/git-core=g' \
	    -e 's=@LOCALEDIR_REL@=share/locale=g' \
	    perl/header_templates/fixed_prefix.template.pl >GIT-PERL-HEADER+ && \
	mv GIT-PERL-HEADER+ GIT-PERL-HEADER
/bin/sh generate-perl.sh ./GIT-BUILD-OPTIONS ./GIT-VERSION-FILE GIT-PERL-HEADER "contrib/credential/netrc/git-credential-netrc.perl" "contrib/credential/netrc/git-credential-netrc+" && \
	mv contrib/credential/netrc/git-credential-netrc+ contrib/credential/netrc/git-credential-netrc
./t-git-credential-netrc.sh
?!ERR?! ./t-git-credential-netrc.sh: No such file or directory
ok 1 - set up test repository
not ok 2 - git-credential-netrc
#
#			perl "$GIT_SOURCE_DIR"/contrib/credential/netrc/test.pl
#
# failed 1 among 2 test(s)
1..2
make: *** [test] Error 1

提示找不到 ./t-git-credential-netrc.sh 这个文件,这个错误让人很容易以为是路径问题。

从 2.47.1 版本以后就这样,就先 pin 了,以为过一段时间会有 brew 或 git 维护人员发现并修复,然而都 2.49.0 了还是不行。

如果在 git 源码 contrib/credential/netrc 目录下运行 make testverbose,会出现以下错误:

Perl v5.26.0 required--this is only v5.18.4

继续查阅源码可知 git 从 2.48 就开始要求 perl 5.26 了

Catalina 自带的 /usr/bin/perl 只有 5.18,brew 编译 git 时并不使用 brew 版 perl,而是系统自带,于是我这个 Catalina 老同志就被偷袭了。

如果指定较新的 perl export PERL_PATH=/usr/local/bin/perl,就可以正常编译。

对此,考虑以下几种解决方法:

  1. 如果不追求新版,可以 pin 在 2.47.1。

    如果还没有用 brew 安装 git,本地有 homebrew-core 源码的话,可以提取历史版本并编译:

    git show aecf26f53d0:Formula/g/git.rb >git.rb
    brew install ./git.rb --formula
    rm ./git.rb
    

    或者去官方仓库下载历史版本。

    brew 脚本我认为是有 bug 的,它编译出的 git-credential-netrc 中模块路径错误,用的是编译时的临时目录,例如 /private/tmp/git-20250527-25582-blyz61/git-2.49.0/.brew_home/share/perl5

    如果不需要 git-credential-netrc,可以将相关脚本注释掉。或者参考方法 3 修改路径。

  2. 如果想升级 git,可以将脚本中与 perl 相关部分注释掉,包括 git-credential-netrc、diff-highlight、git-send-email 相关代码,以及 Net::SMTP::SSL 模块(git-send-email 依赖它)。这也意味着这些工具将无法使用。

    git 源码中还有一个 NO_PERL 环境变量,但我没试过。

    git-credential-netrc 可能用的人不多,毕竟 macOS 上一般用 SSH、git-credential-osxkeychain。但 diff-highlight 还是很有用的,可以在 diff 高亮显示不同的部分。git-send-email 用于发送邮件。

  3. 如果想升级 git 且需要 perl 相关工具,可以使用 brew 版 perl。

    在 brew 脚本中做如下修改:

    git diff Formula/g/git.rb
    diff --git a/Formula/g/git.rb b/Formula/g/git.rb
    index a58a53ea0d6..82200f7576f 100644
    --- a/Formula/g/git.rb
    +++ b/Formula/g/git.rb
    @@ -23,6 +23,7 @@ class Git < Formula
    
       depends_on "gettext"
       depends_on "pcre2"
    +  depends_on "perl" => :build
    
       uses_from_macos "curl", since: :catalina # macOS < 10.15.6 has broken cert path logic
       uses_from_macos "expat"
    @@ -125,12 +126,13 @@ class Git < Formula
    
         # Generate diff-highlight perl script executable
         cd "contrib/diff-highlight" do
    -      system "make"
    +      system "make", "PERL_PATH=#{ENV["PERL_PATH"]}"
         end
    
         # Install the netrc credential helper
         cd "contrib/credential/netrc" do
    -      system "make", "test"
    +      system "make", "PERL_PATH=#{ENV["PERL_PATH"]}", "test"
    +      inreplace "git-credential-netrc", %r{(?<=\$ENV\{GITPERLLIB\} \|\| ')[^:']+}, share/"perl5"
           git_core.install "git-credential-netrc"
         end
    

    depends_on "perl" => :build 指定 brew 版 perl,脚本会将路径保存在 PERL_PATH 环境变量。

    子模块编译时此环境变量无效,因此 make 命令还是需要 PERL_PATH=

    inreplace 一行会将 git-credential-netrc 脚本中的临时路径

    use lib (split(/:/, $ENV{GITPERLLIB} || '/private/tmp/git-20250527-25582-blyz61/git-2.49.0/.brew_home/share/perl5:/Applications/Xcode.app/Contents/Developer/Library/Perl/5.40/darwin-thread-multi-2level:/Library/Developer/CommandLineTools/Library/Perl/5.40/darwin-thread-multi-2level'));
    

    修改为形如 /usr/local/Cellar/git/2.49.0/share/perl5,这也是编译脚本中模块安装位置。

    另外,临时路径之后的几个路径也是错误的。它在 brew 脚本里通过 PERLLIB_EXTRA 设置,这可能是新版 macOS 的路径。但不影响使用。

    最后可将 /usr/local/opt/git/share/git-core/contrib/diff-highlight 加入 PATH 环境变量,并添加到 git 配置:

    git config --global pager.diff 'diff-highlight | less'
    git config --global pager.log 'diff-highlight | less'
    git config --global pager.show 'diff-highlight | less'
    

sha256 mismatch

这类问题出现频率很高,在官方仓库可以看到大量相关的 issue、commit 和 PR。

除了网络问题外,通常有两种原因。

上游重新发布同一版本

这时依旧修改脚本,例如 launchcontrol

cd $(brew --repository homebrew/cask)
git diff
diff --git a/Casks/launchcontrol.rb b/Casks/launchcontrol.rb
index 5c2db44947..db25b721a8 100644
--- a/Casks/launchcontrol.rb
+++ b/Casks/launchcontrol.rb
@@ -1,7 +1,7 @@
 cask "launchcontrol" do
   on_catalina :or_older do
     version "1.52.7"
-    sha256 "16c3d89e41a99cbf43e6996681358e8e7a4bc63fa770b9f8c0bc72c5356a0b8a"
+    sha256 "760edc3f3238ecbbc9f0c14b17ced9ac2a46c46a4ed8feec6bfb532fced37b7e"

     livecheck do
       skip "Legacy version"

确认没问题的话可以去提 PR。

源码归档在服务端重新生成

GitHub 和 Gitlab 都使用 git archive 将源码归档为 tar 包,但不管是 GitHub1 还是 Gitlab2,都不保证源码归档的稳定性。

例如我发现的 Gitlab 校验变化问题:

curl -fsSL https://gitlab.com/AOMediaCodec/SVT-AV1/-/archive/v1.4.1/SVT-AV1-v1.4.1.tar.bz2 | sha256sum
2ddef549e1eaeecc1fc48f0d8332ea3545809e46509db69beb3a0a4bf19ef906  -
# 过一段时间
curl -fsSL https://gitlab.com/AOMediaCodec/SVT-AV1/-/archive/v1.4.1/SVT-AV1-v1.4.1.tar.bz2 | sha256sum
0e988582f315fe76c909accf5e7f81b975c5bd2b850ee760d8e9fac297f70b5d  -

两个 tar 包内容完全一样,只有文件夹名不同:

curl -fsSL https://gitlab.com/AOMediaCodec/SVT-AV1/-/archive/v1.4.1/SVT-AV1-v1.4.1.tar.bz2 >old
# 过一段时间
curl -fsSL https://gitlab.com/AOMediaCodec/SVT-AV1/-/archive/v1.4.1/SVT-AV1-v1.4.1.tar.bz2 >new
tar xf old
ls
SVT-AV1-v1.4.1
tar xf new
ls
SVT-AV1-018276d714ce65d9b586f6205ee016cbd8d5425d
SVT-AV1-v1.4.1
diff SVT-AV1-v1.4.1 SVT-AV1-018276d714ce65d9b586f6205ee016cbd8d5425d -r
echo $?
0

我也是服了某些 Homebrew 的维护者,复现不了我的问题就说是我配置问题,神他妈配置问题😅

Gitlab 官方人员解释如下:

The root cause was discussed in a confidential issue, but I can share a general idea what went wrong.

One of deployed fixes had a side-effect of changing the hash sum of the archived files.
When it was discovered, we reverted the code.
However, Cloudflare still responded with old archive files for some regions.
We updated the cache and that resolved the problem.

好了,破案了。Gitlab 也是个鬼才,归档生成机制变了也就算了,同一个老 URL,还能返回不同的文件。

要是不幸遇到这种问题,自认倒霉吧,依旧直接改脚本。鬼知道什么时候抽风。

其结果就是,明明服务商不保证自动归档的稳定性,而 Homebrew 却依赖这一点。

要保证源码包的稳定性,应该由作者官方以 release 形式发布,就像各种开源软件官网那样,然后由官方提供校验方法。

那么从全局看,怎么避免这种错误?我觉得 Homebrew 完全可以定期检查哈希值,如果变更则自动提 PR,这一点可以通过 CI/CD 自动化完成。

Warning: You are using macOS 10.15.

We (and Apple) do not provide support for this old version.
It is expected behaviour that some formulae will fail to build in this old version.
It is expected behaviour that Homebrew will be buggy and slow.
Do not create any issues about this on Homebrew's GitHub repositories.
Do not create any issues even if you think this message is unrelated.
Any opened issues will be immediately closed without response.
Do not ask for help from Homebrew or its maintainers on social media.
You may ask for help in Homebrew's discussions but are unlikely to receive a response.
Try to figure out the problem yourself and submit a fix as a pull request.
We will review it but may or may not accept it.

啊对对对,我知道我们老古董不配用高贵的 Homebrew,出了问题后果自负我理解。

但是你每次一大堆烦人的警告跳脸连个他妈的开关都没有?哪怕减到一行呢?

何况苹果只是对系统停止了支持,你一个包管理器,不想支持老系统直接大方说不就行了,和苹果对系统的支持有个毛线关系?难道苹果不让 Catalina 用户用 App Store 了?Ubuntu 就连 12.04 甚至更老的版本,改一下源就能继续用官方的老仓库。

对于一个非盈利的开源软件,我表示支持,但你别又当又立😅

发现我脾气逐渐暴躁,管他的,反正鬼佬看不懂中文。

编辑 $(brew --repository)/Library/Homebrew/extend/os/mac/diagnostic.rb 这个脚本,在 check_for_unsupported_macos 方法第一行直接返回:

cd $(brew --repository)
git diff
diff --git a/Library/Homebrew/extend/os/mac/diagnostic.rb b/Library/Homebrew/extend/os/mac/diagnostic.rb
index 1cbc907f3..37b5b91ba 100644
--- a/Library/Homebrew/extend/os/mac/diagnostic.rb
+++ b/Library/Homebrew/extend/os/mac/diagnostic.rb
@@ -106,6 +106,7 @@ module Homebrew
       end

       def check_for_unsupported_macos
+        return
         return if Homebrew::EnvConfig.developer?
         return if ENV["HOMEBREW_INTEGRATION_TEST"]

整个世界都清净了!

当然,后果自负。

macOSGoHomebrewGit

本作品根据 署名-非商业性使用-相同方式共享 4.0 国际许可 进行授权。

在 Linux 上使用 Exim4 发送邮件

在 Android 上部署 Linux