ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

OpenHarmony编译环境搭建:解决prebuilts下载报错Failed to call git

OpenHarmony编译环境搭建:解决prebuilts下载报错Failed to call git

拿到 OpenHarmony 4.0 源码,满心欢喜地敲下bash build/prebuilts_download.sh,结果屏幕上一行红字:Error: Failed to call gi...后面就没了。这个场景是不是似曾相识?这应该是 OpenHarmony 环境搭建里出现频率最高的报错之一,尤其是在全新配置的 Linux 机器上。今天这篇就专门解决这个问题,我会把 prebuilts_download.sh 到底做了什么、为什么 llvm 总是第一个站出来报错、以及完整的排查流程一次讲透。

这类问题有个特点:报错信息看着吓人,但根因往往藏在环境里。你缺的未必是什么高深配置,可能就是一个 git 身份信息、一个 Python 版本、甚至是一块磁盘空间。这篇文章适合刚接触 OpenHarmony 编译、卡在环境准备阶段的开发者,也适合已经在编但反复在工具链下载上翻车的朋友。我会把从定位到解决的完整思路拆开讲,你按顺序走一遍,基本能把这个报错从你的人生里删除。

1. 问题定位:先搞清楚这个报错到底卡在哪一步

很多人在报错面前容易慌,第一反应是重新执行一遍脚本,或者去群里问“有没有人遇到过”。但在动手之前,我建议你花五分钟理解一下这个脚本的工作机制。方向对了,后面所有操作都是顺水推舟。

1.1 prebuilts_download.sh 到底干了什么

OpenHarmony 的编译过程和 Android 类似,极度依赖预编译工具链。整个系统源码仓库默认不会把 clang/llvm、rustc、python、gn、ninja 这些编译器和构建工具塞进去,而是单独存放在预编译(prebuilts)仓库里。prebuilts_download.sh 这个脚本的作用,就是把这些散落在不同仓库里的工具链一次性拉取到本地,统一放到源码根目录下的 prebuilts 目录中。

脚本的执行流程大致是这样的:

  1. 检查当前系统环境,确认基本依赖是否存在。
  2. 读取内置的组件清单,确定需要下载哪几个预编译仓库。
  3. 逐个通过代码托管平台获取对应仓库,或者下载指定版本的压缩包。
  4. 解压到目标目录,并完成一些基础的校验。

如果你的网络环境没问题,这个脚本跑完大概需要二十到四十分钟,具体取决于工具链体积和带宽。llvm/clang 是里面最大的块头之一,下载时间占比最高,也是最容易出错的一环。所以你会发现,网上搜这个报错,十有八九都指向 llvm 相关仓库的拉取失败,这不是偶然。

1.2 "Failed to call gi..." 的完整含义

看到Failed to call gi...这个报错,第一反应别去猜。它其实是脚本内部调用 git 命令时抛出的异常信息,后半段被终端截断了。完整内容通常是类似Failed to call git clone/checkout/ls-remote的格式,后面还会跟上具体的仓库地址或子命令参数。

这个报错从机制上可以拆成两层:

  • 脚本通过 subprocess 方式调用了 git 可执行文件。
  • git 命令返回了非零退出码,Python 脚本捕获后抛出这个异常。

所以问题要么出在 git 本身不可用,要么出在 git 执行的具体操作上(网络不通、仓库不存在、权限不足、磁盘满等)。这里有一个非常实用的建议:执行脚本时把输出重定向到日志文件,别让它在终端一闪而过。你可以这样操作:

cd 你的OpenHarmony源码根目录 bash build/prebuilts_download.sh 2>&1 | tee download.log

跑完或者报错之后,用 grep 在日志里捞关键信息:

grep -i "Failed to call" download.log grep -i "llvm" download.log

这样你能看到完整的错误上下文。很多时候,只看报错截断的那一行,根本没法判断问题在哪个仓库、哪个操作上。日志才是排障的第一现场。

2. 环境自检:90%的问题都出在这四个环节

我之前在多个环境里复现过这个报错,反复踩坑之后总结出一个规律:Failed to call git这个报错本身不是根因,它只是一个结果。真正的病根基本集中在下面四个环节里,你按顺序自查一遍,比盲目重试有效得多。

2.1 Python 环境必须达标,版本错了全白搭

你可能觉得奇怪,git 报错怎么跟 Python 扯上关系了?因为 prebuilts_download.sh 本身是一个胶水脚本,真正干活的 download.py 是用 Python 写的。你的 Python 环境如果不满足要求,脚本可能根本跑不起来,或者跑起来后表现诡异。

OpenHarmony 4.0 对 Python 版本有明确要求,一般来说需要 3.7 以上,我个人建议 3.8 及以上会更稳。你可以先检查当前环境的 Python 版本:

python3 --version which python3

如果你所在的操作系统比较老,系统默认的 Python 还是 2.7,那脚本大概率会直接报错。有些开发者会遇到一种更隐蔽的情况:系统里同时存在多个 Python 版本,脚本调用的解释器和终端里默认的不一致。比如你在终端里测试python3 --version显示 3.8,但脚本内部用的可能是 PATH 里另一个路径下的旧版本。

排查方法很简单,在源码根目录下直接看脚本头部对 Python 的解释器声明,或者用head命令查看脚本内容:

head -50 build/prebuilts_download.sh

如果脚本内部硬编码了/usr/bin/python3,而你的高版本 Python 装在/usr/local/bin,那就需要调整 PATH 顺序,或者建立软链接。我一般倾向于优先保证系统默认的 python3 满足版本要求,避免脚本环境分裂。

2.2 git 缺失或未配置身份信息,最容易忽略

这个原因排在我踩坑清单的前三名,而且特别容易踩中,因为很多开发者的机器上确实没装 git。你可能会觉得“我要下载源码肯定装了 git 啊”,但还真不一定。有些开发者是通过下载源码压缩包的方式拿到 OpenHarmony 代码的,本地可能压根没有 git 环境。脚本一执行,subprocess 去调用 git,系统直接返回 command not found,于是Failed to call git就出现了。

检查方法:

git --version

如果提示找不到命令,安装一下:

sudo apt-get install -y git

装完之后还有一道隐藏关卡:git 的全局身份信息。有的脚本在执行 git 操作时会触发需要 user.name 和 user.email 的逻辑,比如某些仓库更新流程里有 merge 或 commit 动作。没有配置身份信息时,git 会拒绝执行,报错同样会表现为Failed to call git。

配置方式:

git config --global user.name "你的名字" git config --global user.email "你的邮箱@example.com"

这里的名字和邮箱不需要和代码托管平台账号完全一致,只要 git 能识别到一个合法身份即可。当然,如果你想后续往仓库提交代码,最好填真实信息。这一步花不了三十秒,但能帮你省掉一晚上的折腾。

2.3 网络连通性和下载源,决定成败的核心环节

OpenHarmony 的预编译工具链存放在代码托管平台上。你的网络环境到该平台的连通性、稳定性、带宽,直接决定了下载脚本是顺利完成还是中途暴毙。

很多开发者遇到的Failed to call git clone超时,根源就在这里。你可以先手动测试一个小仓库的克隆,验证基本连通性。比如:

git clone --depth=1 一个体积较小的预编译仓库地址

如果这条命令能顺利跑通,说明网络基本没问题。如果它卡住不动、或者报unable to access、Failed to connect这类错误,那就说明你的网络到该平台存在瓶颈。这时候你需要做的不是反复重跑 OpenHarmony 的下载脚本,而是先解决网络层面的问题。

我个人的建议是:优先使用官方推荐的系统版本(比如 Ubuntu 20.04 或 22.04),在这些系统上网络配置和脚本兼容性都相对成熟。如果你的网络环境对代码托管平台的访问不够稳定,可以参考项目官方环境搭建文档,里面通常有关于下载源切换或镜像加速的说明。还有一个小技巧:如果脚本下载反复失败,你可以不用脚本自动拉取,而是从项目发布渠道手动获取对应版本的预编译工具压缩包,再自己解压到 prebuilts 目录下。这个方法虽然笨,但在网络环境确实受限时,反而是最可控、最省心的方案。

2.4 磁盘空间和文件句柄限制,隐藏的冷门杀手

llvm/clang 预编译工具链的体积远超你的想象。OpenHarmony 4.0 的预编译组件全部拉下来,加上源码本身,轻松突破几十 GB。如果你的磁盘分区规划不合理,根目录空间不够,git 在写入对象时会直接报错,而且这个报错经常被包装成各种奇怪的 git 错误。

查看磁盘空间:

df -h

注意看你的源码目录挂载在哪个分区,剩余空间是否充足。我给你的硬性建议是:OpenHarmony 4.0 完整源码加预编译工具链,至少预留 120GB 可用空间。实际开发中我一般留 200GB 以上才安心,因为后续编译过程还会产生大量中间产物,空间挤牙膏迟早要还的。

另一个容易忽略的是文件句柄限制。默认情况下,Linux 系统的 ulimit -n 通常是 1024。下载脚本在拉取大仓库时会同时打开大量文件,一旦超过系统限制,git 进程就会失败。你可以临时调高限制:

ulimit -n 65535

这个命令只对当前终端会话有效,重新打开终端后需要重新设置。如果你想永久生效,需要修改/etc/security/limits.conf,但作为临时排障,直接在终端里设置就够了。

3. 实操排查:从报错信息到最终解决

环境自检那部分讲的是“可能的原因”,这一部分我要带你走一遍完整的实操流程。不做纸上谈兵,直接按步骤来,每一步都有明确的命令和判断标准。

3.1 第一步:把报错信息补全,别让线索断在半路

现在的终端窗口一般只能看到有限的滚动缓冲区,脚本报错后前面的日志早被冲掉了。所以排查的第一步,一定是从日志文件里把完整的报错信息捞出来。

cd 你的OpenHarmony源码根目录 bash build/prebuilts_download.sh 2>&1 | tee download.log

这条命令会把 stdout 和 stderr 都合流输出到终端,同时写入 download.log 文件。等脚本结束或报错后,执行下面的命令来定位关键行:

grep -n "Failed to call" download.log

假设你看到了类似这样的输出:

[ERROR] Failed to call git clone https://xxx/prebuilts_llvm.git

那问题就非常明确了:脚本在克隆 llvm 工具链仓库时失败。接下来你需要判断是网络问题还是仓库地址问题。可以在终端里手动执行同样的 clone 命令,观察真实报错。这一步能帮你把问题从“脚本报错”缩小到“git 操作层面”。

3.2 第二步:逐项检查环境,快速定位病根

在拿到完整报错之后,你可以做一个一分钟快速自检。下面这一串命令,每一条都有明确的期望结果,凡是和期望结果不符的,就是需要修复的点:

# 检查 Python 版本,期望 3.8 及以上 python3 --version # 检查 git 是否安装 git --version # 检查 git 身份信息,期望有输出 git config --global user.name git config --global user.email # 检查磁盘空间,重点看源码目录所在分区 df -h # 检查文件句柄限制 ulimit -n

我见过太多人跳过自检直接重跑脚本,结果就是死在同一个坑里反复循环。比如 git 身份信息没配置的情况,跑一百次也是同样的报错。自检这一步虽然“看起来”简单,但在排障效率上,它的价值比任何高级技巧都高。

3.3 第三步:修复问题后重新执行,注意断点续传行为

环境修复之后重新执行脚本,这里有一个很多开发者不知道的行为细节:prebuilts 下载脚本不是完全从零开始的。那些已经成功下载并校验过的组件,再次执行时会被跳过或者快速通过,只有缺失或失败的部分会重新拉取。所以重跑之前,不需要删除之前已经下载好的目录。

重跑命令:

bash build/prebuilts_download.sh 2>&1 | tee download2.log

如果你之前的报错出在 llvm 组件上,重跑时脚本会优先尝试继续拉取 llvm,而不是把前面的 gn、ninja 等小组件再来一遍。这个过程可能还是会比较慢,因为 llvm 仓库本身巨大,你要有点耐心。

如果第二次仍然在同一个地方失败,那就不是偶发问题了。这时候我建议你检查一下是不是下载中断导致目录残留了不完整的仓库状态。一个比较粗暴但有效的办法是:找到 prebuilts 下对应的 llvm 目录,删除后重新下载。

# 先看目录结构 ls prebuilts/ # 找到 llvm 相关目录,删除后重跑 rm -rf prebuilts/llvm相关目录名

删除前确认一下这个目录确实是下载失败产生的,免得误删之前已经验证可用的部分。

4. 背后的原理:为什么 llvm 偏偏容易出问题

你在网上搜这个问题的时候,大概率会发现一个规律:大部分人的报错都集中在 llvm/clang 工具链上。为什么偏偏是它?理解了这一点,你以后遇到类似问题就能更快地判断“这是偶发抖动”还是“必须干预的顽固故障”。

4.1 llvm 工具链的特殊地位和巨大体积

llvm/clang 是 OpenHarmony 编译流程中的核心编译器组件,系统源码的所有 C/C++ 代码都要经过它来生成目标文件。因此,它必须完整、必须可用,缺了它整个编译流程直接瘫痪。

问题在于,这个“必须可用”的工具链体积巨大。一个完整的 llvm 预编译仓库,包含 clang、lld、各种架构的后端、头文件库文件,压缩后都有几个 GB 的量级,仓库内的 commit 历史也非常长。相比之下,gn、ninja 这类构建辅助工具可能只有几十 MB。下载一个几十 GB 的仓库和下载一个几十 MB 的仓库,对网络稳定性的要求完全不是一个量级。

所以你看到的失败频率集中在 llvm 上,本质上是一个概率问题:越大的传输任务,越容易在传输过程中受到网络波动、连接超时、磁盘写入瓶颈等影响。这不是脚本写得不好,是它在做的事情本身就比别的组件更容易失败。

4.2 脚本的容错机制和你的应对策略

了解脚本的容错机制,能帮你判断一个失败是该重试还是该修环境。download 脚本的基本容错逻辑大致是这样的:

  • 检查本地目标目录是否存在。
  • 如果目录存在且状态正常,跳过下载。
  • 如果目录不存在或者状态异常,尝试从配置的仓库地址拉取。
  • 拉取失败后抛出异常,终止或者跳到下一个组件(不同版本行为有差异)。

基于这个机制,你可以建立两个基本判断:

如果脚本在某个仓库上第一次失败,第二次重跑却能顺利通过,说明当时只是网络临时抖动造成的偶发失败,环境本身没有硬伤。这类情况不需要做额外修复,耐心重试即可。

如果脚本每次都卡在同一个仓库的同一个位置,那十有八九是环境层面的固化问题。这时候不要跟脚本较劲,停下重试,回到第 2 章的自检清单里逐项排查。

另外还有一个容易被忽略的点:脚本执行时记录的日志里面,你可能会看到一些 HTTP 302、重定向之类的信息。这通常是代码托管平台在做请求调度,属于正常行为,不要被它吓到。关键只有一条:最后的退出状态是否为成功。

5. 常见问题速查表与避坑经验

为了避免你未来再被类似的报错绊倒,我把整个排查过程中最常见的几个场景整理成表格,配合一些我从实践中总结的避坑技巧。这部分内容建议你收藏一下,下次遇到直接对着查。

5.1 高频问题一览表

报错特征常见原因解决办法
Failed to call git clone且手动 clone 超时网络到代码托管平台不稳定检查网络连通性,尝试官方文档中的镜像或下载源配置;或手动下载预编译包
git: command not found系统未安装 git执行sudo apt-get install -y git
git 命令提示身份无法识别未配置 user.name 和 user.email执行git config --global user.name "名称"和git config --global user.email "邮箱"
Python 脚本直接退出或崩溃Python 版本低于要求升级 Python 到 3.8 及以上,并确认脚本调用的是正确解释器
下载到中途磁盘写入失败磁盘空间不足执行df -h检查空间,清理或扩容后再重试
解压阶段报权限错误混用了 root 和其他用户操作保持使用同一个用户身份运行下载和后续编译
下载过程异常缓慢网络带宽受限或 DNS 解析异常检查 DNS 设置,必要时更换网络环境

5.2 我的几条实操建议

第一,优先用官方推荐的操作系统版本。OpenHarmony 官方文档对编译环境有明确的系统版本建议,在这个版本上踩坑的概率最小。我在旧版本系统上试过,光是 Python 和 git 的兼容性问题就能折腾一整天,后来换到建议版本,下载过程异常顺滑。

第二,整个下载和编译过程保持同一个用户身份。我踩过这个坑:第一次用 root 跑了脚本,后面切换到普通用户继续操作,结果 prebuilts 目录下的文件权限全是 root 的,普通用户无法覆盖或者解压,导致各种诡异的报错。统一身份后,这类问题彻底消失。

第三,下载完成后先验证一下核心工具链再继续。不用急着直接开编译,可以先检查 clang 的版本,确认工具链可用:

prebuilts/clang/ohos/linux-x86_64/llvm/bin/clang --version

如果这一步能正常输出版本信息,说明 llvm 工具链本身是好的,后续编译过程会顺畅很多。如果提示缺少动态库,那可能是你的系统基础库版本太老,需要额外安装兼容层。

第四,不要忽略日志的价值。很多人在终端报错之后第一反应是截图发群里,但一张截图的报错信息往往是残缺的。养成重定向日志的习惯,排查问题时先 grep 日志而不是直接重跑。遇到任何脚本类工具报错,这个方法论都通用。

第五,如果自动下载这条路反复走不通,果断切换手动方案。从项目发布渠道获取对应版本的预编译工具包,解压到指定目录后,脚本会觉得工具已经存在而跳过下载。这条路我用过不止一次,在网络受限时反而是最稳的兜底方案。

我个人在实际操作中的体会是,Failed to call git这个报错看起来吓人,但它本质上就是一个“环境不合规”的放大镜。你只要按照环境自检、日志定位、逐项修复这条路线走,九成以上的情况都能在半小时内解决。真正需要警惕的反而是那种“每次位置都不一样”的随机失败,那通常意味着网络波动严重,优先要解决的是网络稳定性,而不是继续跟脚本较劲。希望这篇记录能让你少走几个弯路,早点把精力放回 OpenHarmony 本身的开发上。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进