
做Android系统开发、ROM定制、甚至只是单纯想把AOSP源码拉到本地研究的人迟早都要过“配置repo编译环境”这一关。repo这个命令行工具是Google用来管理Android源码仓库的管家它不替代Git而是把几百上千个Git仓库按照一份manifest清单文件组合成一套完整、可编译的源码树。这篇文章是我个人从零搭建repo编译环境的完整记录覆盖repo安装、仓库初始化、源码同步、编译依赖自检顺带把社区里流传的repo汉化补丁也讲清楚怎么装、值不值得装。无论你是第一次碰AOSP的新手还是要给团队搭一套内部构建环境的老工程师这套流程都适用。1. 项目全貌一套可用的repo编译环境由什么组成1.1 repo的定位不只是“多仓库版Git”很多人刚听到repo的反应是“这不就是Git多仓库管理工具吗”严格来说这个理解不准确。Git解决的是单个项目里的版本控制问题而repo解决的是“一堆Git仓库如何按照指定版本组合在一起”的问题。Android源码树由几百上千个独立Git仓库组成每个仓库都有自己的提交历史、分支和标签但发布某个系统版本时必须把所有这些仓库锁定到互相兼容的commit上。这个“锁定组合”的工作就是repo的核心职责。打个比方Git是你手里的工具箱repo则是整个工地的施工总调度。你负责的每一个独立模块可以各自迭代但到了打地基、盖楼的时候调度员会按图纸把每个模块调到指定的工程版本。这里的“图纸”就是manifest文件它明明白白写着每个仓库该指向哪个分支、哪个提交甚至哪个远程地址。理解了这一点就明白为什么pull一个AOSP工程时第一步永远是repo init——它在拉取那张“图纸”。1.2 一套完整编译环境的组成清单配置repo编译环境远远不止“装好repo命令”这么简单。我在实际项目里见过太多人卡在后面的编译阶段回头排查才发现是基础环境有隐患。一套完整的、能跑通AOSP编译的环境至少要包含下面几块组成部分建议配置说明操作系统Ubuntu 20.04 / 22.04 64位Google官方以Ubuntu/Debian系为基准依赖包最顺手CPU8核以上并行编译和repo sync都吃多核核心越多用时越短内存16GB起步建议32GB低于8GB编译时极容易OOM加swap都救不回来磁盘至少200GB可用空间SSD源码加编译产物轻松超过100GB机械硬盘同步时想哭文件系统ext4大小写敏感Android构建依赖大小写敏感NTFS/exFAT挂载盘直接劝退基础工具Git、Python 3.6、curlrepo 2.x依赖Python 3老教程里Python 2早就过时了JDK版本随源码版本走后面会专门讲对应关系装错版本报错到你怀疑人生repo工具2.x最新版建议从官方渠道下载安装步骤见下文很多人忽视文件系统这一点。Android编译系统假设文件系统区分大小写所以同目录下a.txt和A.txt是两个文件。ext4默认大小写敏感没问题但如果你把源码放在NTFS移动硬盘或exFAT的U盘上编译时会出现各种莫名其妙“file not found”或头文件找不到的报错。这一条属于“隐藏炸弹”等到构建中期才炸。1.3 动手前的环境自检正式配置之前我习惯先跑一轮快速自检把基础项一次过一遍省得后面抓瞎# 系统版本 lsb_release -a # Python版本repo 2.x要求3.6以上 python3 --version # Git版本 git --version # 磁盘空间 df -h # 内存 free -h # 文件句柄限制太小会引发莫名其妙的IO报错 ulimit -n这一轮检查的意义不是走形式而是把“环境问题”和“操作问题”隔离开。后面repo init、repo sync出错时你至少可以确认基础面是干净的排查范围一下就缩小了。我在给别人远程排障时第一句话永远是“先跑一遍这个自检把输出发我。”2. 安装repo与初始化仓库4个步骤搞定基础配置2.1 安装repo一条命令的事但细节藏坑repo本质上是一个Python脚本官方推荐把它放到用户目录下的~/bin里。安装命令本身非常短mkdir -p ~/bin curl https://storage.googleapis.com/git-repo-downloads/repo ~/bin/repo chmod x ~/bin/repo export PATH~/bin:$PATH echo export PATH~/bin:$PATH ~/.bashrc source ~/.bashrc把repo放进~/bin而不是直接丢到/usr/local/bin是我个人比较推荐的做法。用户级工具就该待在自己家里系统级升级不会误覆盖换机器迁移时整个~/bin拷走就行。当然直接放/usr/local/bin也完全没问题纯粹看个人偏好。这里有一个隐蔽的坑repo脚本的第一行shebang默认是#!/usr/bin/env python老版本或#!/usr/bin/env python3新版本。如果你的系统里还存在Python 2并且python这个命令还指向它那么执行repo时会直接崩报错往往是SyntaxError: invalid syntax。遇到这种情况先查一下repo脚本第一行把它改成#!/usr/bin/env python3或者干脆用python3 ~/bin/repo显式调用。这个坑在新手群里出现频率非常高。2.2 repo init初始化清单仓库装好repo后下一步就是拉取manifest清单。我把操作目录固定为~/aosp你也可以换成任意路径但注意别放在有中文和空格的路径下后面构建脚本对路径敏感mkdir -p ~/aosp cd ~/aosp repo init -u https://android.googlesource.com/platform/manifest -b android-13.0.0_r1这里三个参数各有用意-u指定manifest仓库的Git地址Android官方清单仓库就放在这个地址下-b指定分支或标签android-13.0.0_r1是Android 13的release标签之一具体版本号可以到官方release分支列表里查选一个你目标设备对应的版本如果不加-b默认拉取master主线那是给贡献代码的人用的天天在变不适合做稳定构建。repo init执行完后目录下会生成一个.repo隐藏目录里面保存了manifest仓库的本地副本、符号链接manifest.xml以及后续同步Git对象用的元数据。这时候源码目录还是空的因为你只是拿到了“图纸”还没开始“施工”。repo init本身很快几秒钟到十几秒除非网络环境异常。2.3 读懂manifest文件为什么local_manifests是定制神器.repo/manifests目录里放着清单仓库的完整内容其中default.xml就是那张核心图纸。我截取一个简化片段帮助理解manifest remote nameaosp fetchhttps://android.googlesource.com / default revisionrefs/tags/android-13.0.0_r1 remoteaosp sync-j4 / project pathbuild/make nameplatform/build groupspdk / project pathframeworks/base nameplatform/frameworks/base groupspdk-cw-fs / project pathpackages/apps/Settings nameplatform/packages/apps/Settings / /manifest每个project标签里的path决定源码在本地目录树中的位置name决定从远程Git服务器拉取的仓库名。init -b指定的分支会被写进default的revision属性。所以说repo init -b android-13.0.0_r1实际上做的事情是拉下manifest仓库本身然后让default.xml里的所有project都指向这个release标签对应的提交。真正的进阶玩法是local_manifests。很多人不知道.repo目录下可以建一个local_manifests文件夹里面放自定义xml文件repo sync时自动合并到整体清单里。做ROM定制、加私有vendor仓库、替换某个仓库的远程地址都靠它。比如你要加入自己公司的配套仓库只需要新建.repo/local_manifests/local_manifest.xml?xml version1.0 encodingUTF-8? manifest remote namecompany fetchhttps://git.example.com/ / project pathvendor/company namevendor_company remotecompany revisionmain / /manifest下次repo sync就会自动把vendor/company拉下来。这是最干净的做法千万不要去改官方default.xml——下次repo sync就可能被覆盖或者产生冲突维护起来全是眼泪。3. 源码同步实战repo sync参数详解与编译环境检查3.1 repo sync的常用参数别傻傻地全默认初始化完成后核心操作就是同步源码。我常用的完整命令长这样repo sync -j8 -c -d --no-tags逐个说参数-j8表示同时启动8个并发同步任务这个数字不是越大越好。同步瓶颈通常在磁盘IO和网络带宽开到二三十的时候经常出现一堆子进程排队等待反而更容易触发个别仓库超时失败。我个人经验是8到16之间比较稳。-c表示只同步当前清单中指定的分支历史不把每个仓库的所有分支都拉下来能省大量时间和磁盘空间。-d强制切回清单指定的revision防止本地残留的检出状态污染整体构建。--no-tags跳过分发各仓库的标签对象对普通构建没人关心标签省下的时间和网络流量很可观。首次全量同步AOSP android-13在千兆有线网络加SSD的环境下大概需要一到两个小时。如果你用机械盘或网络波动大跑三五个小时也不稀奇。所以我的建议是第一次同步时用tmux或nohup把进程挂到后台人不用死盯终端。同步中途断了也没关系直接再执行一次repo sync它会从断点继续这个设计在早期版本里不完善但现在已经很成熟了。3.2 编译前系统依赖安装一个apt命令解决大部分问题源码同步完之后别急着编译先检查系统依赖包。Ubuntu 22.04上AOSP构建官方要求的那堆依赖包一条命令基本能装齐sudo apt-get update sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev \ libc6-dev-i386 libncurses5-dev lib32z1-dev x11proto-core-dev libx11-dev \ libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig这里有几个细节值得说。第一build-essential必须装它里面包含gcc、g、make这些编译工具链很多新人漏了这个就跑去跑make然后被幺蛾子报错淹没。第二老版本Ubuntu上能直接装libncurses5-dev但22.04的默认源里可能已经移除了这个老包如果报“Unable to locate package”就用apt-cache search ncurses确认当前源里可用版本装libncurses dev的最新兼容包即可不必硬磕老版本号。第三libc6-dev-i386也常被遗漏它是32位兼容库的一部分某些构建工具链需要它。至于JDK版本我用一张表总结对应关系具体的官方要求以source.android.com上的构建要求页面为准源码版本JDK要求示例repo分支Android 8.0及以下OpenJDK 8android-8.1.0_rXXAndroid 9 ~ Android 13OpenJDK 11android-13.0.0_rXXAndroid 14及以上OpenJDK 17android-14.0.0_rXX装错JDK版本的典型症状是编译到某个阶段javac或jack报出一串完全看不懂的类型错误、Unsupported class file version之类。这时候别去查代码先检查java -version大概率是JDK版本和源码要求不匹配。3.3 ccache让增量编译快出一个数量级规模稍大一点的编译工程ccache是必须配的。它对C/C编译结果做缓存第二次构建时如果源文件没变直接命中缓存跳过编译步骤。AOSP本身在prebuilts/misc/linux-x86/ccache/目录下带了ccache可执行文件所以不需要额外装只需要在编译前设置环境变量export USE_CCACHE1 export CCACHE_EXEC/usr/bin/ccache prebuilts/misc/linux-x86/ccache/ccache -M 50G source build/envsetup.sh lunch target make -j$(nproc)ccache -M 50G是设置缓存上限50GB这个量级对AOSP的增量构建比较合理。初次完整编译还是要等几个小时到十几个小时视机器性能而定但编译过一次之后哪怕只改动一小块代码再重新构建速度快得会让你怀疑是不是编译出错了。强烈建议把这个环境变量写进~/.bashrc免得每次开新终端忘了导出。3.4 不只手机repo环境在各类定制系统里的通用性这套环境配置好之后不只是Google官方AOSP能用。很多基于Android或Linux的定制系统工程项目只要源码是用repo管理的走的流程一模一样。比如某些类Switch掌机、电视盒子、平板、车载方案的项目其manifest通常由方案商提供你只需要把repo init里的-u换成它们给的manifest仓库地址后面sync、lunch、make的套路完全相同。这类项目里vendor和device相关仓库往往闭源由硬件方案商单独提供你在local_manifests里把它们引进来就行。所以“配置repo编译环境”这个技能是通用的掌握一次后续换项目只是换URL和分支号的事。4. 高频报错实录从repo init到make的排查手册4.1 repo: command not found这个报错几乎每天都有人在群里问。排查顺序很固定先echo $PATH看~/bin在不在路径里再ls -l ~/bin/repo确认文件存在且有-rwxr-xr-x权限最后source ~/.bashrc让刚写入的PATH生效。还有一个低级的坑新开的终端窗口不会自动加载刚才改的.bashrc必须重新打开一个终端或手动source。如果都正常还报not found检查一下repo文件内容是否完整curl下载中断会留下一个几KB的残缺文件执行时就会报语法错误。这种情况重新下载覆盖就好。4.2 repo init阶段就报错多半是Python或证书问题repo init一执行就报SyntaxError九成是repo脚本被Python 2解释执行了。按前文方法改shebang或显式用python3调用即可。另一个常见报错是repo: error: SSL certificate problem: unable to get local issuer certificate这通常不是网络问题而是系统CA证书过期或时间不同步。先跑sudo apt-get install ca-certificates更新证书再用date确认系统时间正确。证书和时间这类基础项出问题不只是repo后面git拉取、curl下载都会连环爆。还有一个需要留意的情况repo init提示RuntimeError: Python version X.Y is too old。这说明repo版本太新而你系统的Python版本不满足最低要求。升级Python到3.6以上通常能解决。如果不想动系统默认Python也可以用虚拟环境装一个高版本把~/bin/repo的shebang指过去灵活度更高。4.3 repo sync阶段中断、对象损坏的处理同步过程中最常见的崩溃是磁盘满了。很多人只在df -h里扫一眼其实根目录满和家目录所在分区满不是一回事。我见过最离谱的情况是/分区只剩几百MB源码放在/home但缓存和临时文件全往/tmp写同步到一半直接报No space left on device。先确认源码所在分区的剩余空间再确认~/.cache和/tmp所在分区该清理清理该换盘换盘。磁盘没满但某些仓库报error: object file is empty或fatal: Not a git repository这说明单个仓库的本地git对象损坏了。常规手段是只针对出错仓库重同步# 删除损坏的源码目录比如frameworks/base rm -rf frameworks/base # 单独重新同步这个仓库 repo sync frameworks/base -j4rm -rf frameworks/base只会删除工作树.repo里的元数据还在所以repo sync能把它重新拉回来不会影响其他仓库。如果.repo/projects/frameworks/base里的对象也坏了那才需要连.repo里的对应项目目录一起删。这一招是保底手段正常情况别乱动.repo。另外如果本地改过某些源码文件导致repo sync失败加--force-sync可以强制重置到清单状态。这个参数有破坏性会把本地未提交的修改丢弃用之前一定确认自己没在改重要代码。4.4 编译阶段的四个经典坑第一个坑权限。源码目录如果之前用sudo操作过属主变成root普通用户编译时就会疯狂报Permission denied。解决办法是把整个源码目录chown回当前用户全程不要用sudo make。第二个坑内存不足。编译中突然提示Killed或者cc1: out of memory说明物理内存被吃满了。优先降并发比如make -j4而不是make -j$(nproc)。如果机器只有8GB内存硬扛AOSP编译体验极差老老实实加内存或者加swap但swap只能救急救不了长期。第三个坑磁盘满。编译产物比源码还肥out/目录轻松几十GB。编译前就规划好空间编译中定期df -h看一眼。ccache也会占地方用ccache -M限制上限别让它无限膨胀。第四个坑JDK版本不匹配。前文提过旧源码配新JDK会报Unsupported class file major version之类的错。很多人一看到class version就以为是jar包损坏其实是Java版本对不上。按官方要求装对应JDK比硬调参数靠谱得多。5. 锦上添花repo汉化补丁到底值不值得装5.1 repo汉化补丁的本质一场输出翻译repo平时在终端里输出的全是英文像Fetching project: 12%、Repo command succeeded、warning: uncommitted changes这些。对英语不好的新手来说本来就被各种编译日志搞得头大这些英文提示更加重心理负担。社区里流传的repo汉化补丁、repo汉化包本质就是把repo这个Python脚本里的输出字符串替换成中文让你看到的是中文提示。它确实有存在价值。给非英语背景的新员工做培训时汉化后的输出一眼能看懂流程走到哪了教学效率高不少。对纯英文环境无感的老手装不装都无所谓。需要明确的是汉化补丁是社区爱好者维护的不是Google官方出品。它通常基于某个特定版本的repo脚本制作所以你升级repo后汉化可能失效严重时还会因为字符串处理逻辑不匹配导致脚本报错。5.2 安装repo汉化包的步骤与回滚如果你确实想体验一下我的建议是严格按这个流程来避免翻车# 第一步先备份原版repo这是回滚的唯一凭证 cp ~/bin/repo ~/bin/repo.bak # 第二步查看当前repo版本 repo --version # 第三步从可信任的社区源下载与你版本匹配的汉化包 # 替换或patch到~/bin/repo脚本上 # 第四步验证还能正常运行 repo --version repo help这里必须多说一句安全底线。repo是一个有完整执行能力的Python脚本拿到任何来路不明的repo版本或补丁一定要先打开看看内容或者用diff ~/bin/repo ~/bin/repo.bak对比改动范围确认只涉及输出字符串的翻译而不是被植入乱七八糟的逻辑。这个原则适用于所有开发工具不只是repo。版本不匹配导致脚本报错时回滚就很简单mv ~/bin/repo.bak ~/bin/repo一条命令回到解放前。5.3 我的实际使用建议实际项目里我个人更倾向用官方原版repo不装汉化。原因很简单官方repo报错时任何一个错误信息都可以粘到搜索框里找到成百上千条讨论帖汉化后的报错反而没人认识。汉化包更适合教学演示、适合刚入门的同学在看懂流程的阶段使用不适合成为长期生产环境的依赖。真要提升日常效率与其依赖汉化不如把repo start、repo sync、repo abandon、repo forall这几个高频子命令的英文本身用熟配合repo help查阅用不了几天就顺了。配置repo编译环境这件事说难不难说简单也不简单。我自己这些年折腾下来最大的体会是八成问题出在基础环境没对齐Python版本、JDK版本、磁盘空间、文件系统大小写这些检查项一次性过掉后面能少踩九成坑。真正跑通一次repo sync和完整编译之后你对Android这套构建体系的理解会上一个台阶。建议新人第一次别贪大版本挑一个成熟的release分支先跑起来哪怕从repo help开始一行行读。最后再分享一个小技巧每次改完local_manifests或者切换分支先用repo sync -c -d跑一遍再动手改代码能帮你省掉很多“为什么我改的没生效”的烦恼。