news 2026/10/21 17:30:30

Windows下WSL2编译Android 10源码与Source Insight索引全攻略

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Windows下WSL2编译Android 10源码与Source Insight索引全攻略

花了大概两个星期,终于把家里这台Windows 10机器跑成了“Android源码阅读机”。过程中经历了WSL2环境搭建、AOSP源码下载、整包编译,再到用Source Insight 4把源码索引起来,中间踩的坑足够写一篇长文了。这篇文章把我实际操作的完整链路和那些“教程里不会写”的细节整理出来,给想在Windows下研究Android 10系统源码的开发者一个可以直接照着走的方案。

这篇内容适合这几类人:想深入读一读Android 10系统源码的App开发者、要做framework定制或移植的驱动/系统工程师,以及纯粹想把AOSP编译链路跑通、验证自己机器性能的折腾型玩家。准备好磁盘、内存和耐心,下面开始。

1. 为什么这条路值得走:WSL2、双系统、虚拟机在源码编译下的差异

1.1 摆在面前的几条老路

想在Windows上编译Android系统源码,大多数人第一时间想到的是装双系统或者开虚拟机。先说我的个人结论:双系统确实性能最好,但为了看源码而频繁重启切换,体验实在割裂;你在Windows里查资料、写文档、看微信,编译在Linux里跑,来回报个结果都要重启一次,很多碎片时间就被切碎了。

VMware和VirtualBox这类传统虚拟机倒是解决了“同时使用”的问题,但对编译这种重度IO密集型任务并不友好。磁盘IO经过虚拟化层之后损耗明显,Android源码编译会产生几十万个小文件,虚拟磁盘的随机读写性能会被放大成很长的编译时间。另外虚拟机还需要在内部再挂一层图形环境,资源占用也比较尴尬。

WSL2的定位恰好卡在两者中间:它不是一个传统虚拟机,而是运行在Windows虚拟化平台上的轻量虚拟机,有着完整的Linux内核,又能和Windows文件系统双向互通。最重要的是,WSL2里Linux部分跑在真正的ext4文件系统上,编译Android源码时不再需要面对VMware那种IO损耗。

1.2 WSL2的“不太像虚拟机”的特性

初次接触WSL2的人容易把它当成“Windows里的一个终端”,这会让后面踩不少坑。实际上当你执行wsl进入发行版时,你是在一个真正的Linux环境里操作,只是窗口和Windows无缝集成而已。它有几个对编译非常重要的特性。

第一个是文件系统。WSL2的Linux文件系统存放在一个动态扩展的vhdx虚拟磁盘文件里,挂载后对Linux侧来说就是原生ext4,装依赖、跑repo、跑make都按照原生Linux的方式工作。第二个是网络模式。WSL2默认走NAT网络,Windows可以通过localhost访问WSL2里启动的服务,WSL2里的Linux也能访问外网,只是IP和宿主机不在一个网段。第三个是内存分配。WSL2默认最多使用宿主机物理内存的50%或8GB(取较小者),这个限制在编译Android源码时根本不够,需要单独配置。

这三点会贯穿后面的所有步骤,尤其是内存这块,很多人第一次编译失败都不是代码问题,而是WSL2能吃到的内存太少。

1.3 动手之前的硬预算

在敲第一条命令前,先把预算算清楚。Android 10源码完整同步下来,.repo目录加上工作区会在50GB上下浮动,编译过程产生的out目录又会增加80GB以上。也就是说,你需要给WSL2所在的Windows盘预留至少200GB可用空间,少于这个数,编译到一半磁盘写爆的概率非常高。

内存是另一个硬指标。我个人建议宿主机16GB起步,因为WSL2能用的内存必须通过.wslconfig指定,你总得留出一部分给Windows自身运行。如果机器只有8GB内存,坦白说这条路会很痛苦,swap能缓冲一部分但编译速度会明显拉胯。

网络方面,repo sync要拉取的数据量大约在20GB到30GB之间,耗时取决于你的实际连接质量。这一步没有太多取巧空间,耐心等就好。

2. Windows侧准备:启用WSL2、装Ubuntu 18.04、初始化依赖

2.1 开启WSL2的完整命令

Windows 10上启用WSL2需要两个特性的配合:Microsoft-Windows-Subsystem-Linux和VirtualMachinePlatform。用管理员身份打开PowerShell,依次执行:

dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

执行完第一条和第二条命令后重启系统。重启之后,在PowerShell里把WSL默认版本设为2:

wsl --set-default-version 2

然后在应用商店里安装Ubuntu。这里有个重要的选型建议:如果是编译Android 10,安装Ubuntu 18.04比20.04省心很多。18.04的默认软件源里可以直接装OpenJDK 8和Python 2,而20.04里OpenJDK 8需要额外加第三方源,Python 2也变成了老古董包,凭空多出一堆环境问题。

安装Ubuntu时设置完用户名和密码后,先确认一下WSL版本是否正确切换到了2:

wsl -l -v

如果显示版本还是1,执行wsl --set-version Ubuntu-18.04 2手动转换。

2.2 进Ubuntu后第一件事:换源与基础依赖

进入Ubuntu终端后,先做常规更新,然后安装编译Android 10需要的基础依赖。参考AOSP构建环境的要求,我在18.04上实际执行的安装命令是:

sudo apt update && sudo apt upgrade -y sudo apt install -y git gnupg flex bison build-essential zip curl zlib1g-dev \ gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev \ x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev \ libxml2-utils xsltproc unzip fontconfig

解释一下这些包里几个容易让人困惑的:flex和bison是语法解析器生成工具,编译阶段负责生成很多C/C++解析器代码,少一个会在很后面奇怪的位置报错;gcc-multilib和g++-multilib用来支持32位编译目标,Android构建过程中有相当一部分中间工具是32位的;lib32ncurses5-dev这类32位库解决的是编译时期头文件和库缺失的问题。

如果你用Ubuntu 20.04,这里有个需要注意的差异:lib32ncurses5-dev在20.04里不见了,要用lib32ncurses-dev替代。所以我才反复强调,18.04是这一步的最优解。

2.3 Python 2.7与OpenJDK 8:Android 10的两个隐藏依赖

Android 10的构建系统虽然已经很现代化,但仍有不少脚本硬编码或隐式依赖python命令,默认指向Python 2。Ubuntu 18.04正好保留了Python 2.7,安装好后再确认一下:

python --version

应该是Python 2.7.x。如果输出的是Python 3或者提示找不到命令,后面编译必然出问题,所以这一步先解决。

接着是JDK。Android 10明确要求OpenJDK 8,装高版本反而会引出一堆编译错误。18.04上一条命令搞定:

sudo apt install -y openjdk-8-jdk

验证版本:

java -version

看到openjdk version "1.8.0_..."就对了。这两项是编译前最容易忽视的环境点,建议在进入下一步之前逐个确认。

3. 拉取Android 10源码:repo init与repo sync实操

3.1 分支选择

Android系统源码用repo工具管理,repo init时需要指定一个manifest分支。我建议选择android-10.0.0_r1这样的release标签,它对应的是一套经过验证的稳定代码组合。理论上你可以选择任何一个可用的分支,但实际经验是:release标签相对稳定,编译过程中遇到的意外更少。

我的实际操作:

mkdir ~/bin curl https://storage.googleapis.com/git-repo-downloads/repo -o ~/bin/repo chmod +x ~/bin/repo export PATH=~/bin:$PATH

考虑到每次登录都要能直接使用repo命令,把最后一行export PATH=~/bin:$PATH追加到~/.bashrc里。

接着配置git身份。repo里的项目和普通git项目一样,提交或同步时需要有user.name和user.email:

git config --global user.email "you@example.com" git config --global user.name "Your Name"

这两个值只是占位,你自己替换就行,不必是GitHub真实账号。注意这里的身份信息和后面搞Source Insight没关系,只是repo工具工作时的基础配置。

3.2 repo init与repo sync的完整过程

新建一个工作目录,我习惯叫aosp:

mkdir ~/aosp cd ~/aosp repo init -u https://android.googlesource.com/platform/manifest -b android-10.0.0_r1

repo init阶段只是初始化manifest,不会下载大量代码,很快就完成。真正的重头戏是:

repo sync -c -j8

这里的-c表示只同步当前分支(也就是manifest指定的那个分支),不拉取其他分支或历史,这对节省磁盘和时间很重要。-j8是并发数,8是相对稳妥的选择。如果你的网络连接质量一般,把-j4也会更稳。

repo sync的过程会持续很久,期间终端会不断刷出不同的项目路径。如果中途断网或中断了,不用担心,直接再执行一次repo sync -c -j8即可,repo工具会基于已有的部分状态继续同步,不会从头再来。

3.3 下载完成后的自查

同步结束后,我习惯做三个检查动作:

du -sh ~/aosp du -sh ~/aosp/.repo ls frameworks/base/

第一个命令看总占用,第二个命令专门看.repo目录的大小,第三个命令确认关键源码目录是否完整。根据tag和网络差异,整体大概在50GB左右,有些tag对应的厂商驱动预编译包多一些,体积会更大,不必过于纠结确切数字,关键是确认目录存在、内容不空洞。

另外提醒一点:源码同步完成之后,如果你后续确定不需要再更新源码,.repo目录其实可以删除以释放空间。但如果你还想随着上游tag推进做增量更新,保留它会更方便。

4. 编译Android 10:lunch、并行数与内存控制

4.1 进入构建环境:三条命令的关系

AOSP的编译入口是build/envsetup.sh脚本,它会把一堆辅助函数加载到当前shell里,包括lunch、m、mm等。流程是固定的:

cd ~/aosp source build/envsetup.sh lunch aosp_x86_64-eng

运行lunch时如果不带参数会弹出一个交互式菜单,让你选目标。aosp_x86_64-eng对应的是x86_64架构的模拟器镜像,开发版(eng)带root权限,方便日常调试。如果你是为真机开发,需要选择对应的设备lunch项,但本文场景下,编译出一个模拟器镜像就足够你阅读源码和验证构建链路了。

lunch成功后会打印一些环境信息,比如TARGET_PRODUCT、TARGET_BUILD_VARIANT等。接下来:

make -j8

之所以用-j8而不是make -j$(nproc),这里是有讲究的,下一段专门说。

4.2 并行数怎么定:用内存倒推,而不是一味用满CPU

很多第一次编译Android的同学会在这一步直接用make -j$(nproc),然后编译到一半被系统杀死,或者整个Windows卡到几乎无法操作。原因在于:每个ninja并发任务占用内存大约1.5GB到2GB,WSL2默认内存配额有限,宿主机逻辑核心越多,并发数越高,内存越先爆。

以我的机器为例,宿主机是8核16线程,如果直接-j16,仅编译器进程的内存需求就可能超过20GB,这在16GB物理内存的机器上必然OOM。所以我采用按内存倒推的方式:在.wslconfig里给WSL2分配12GB内存,那么12GB除以单任务约2GB,得出并行数6到8比较安全。最终我定的是-j8,实际编译过程中整体负载稳定,基本跑满CPU而不会爆内存。

如果你的内存更大,比如32GB,给WSL2分24GB,那可以尝试-j16。原则就是:内存优先于CPU核数,宁可让CPU稍微空转,也不要让内核去杀编译进程。

4.3 .wslconfig配置:改造WSL2的资源边界

在Windows侧C:\Users\<你的用户名>\.wslconfig文件里做如下配置:

[wsl2] memory=12GB processors=8 swap=16GB localhostForwarding=true

这个文件的作用是把WSL2的资源配额固定下来。memory=12GB是按上面思路分配的内存,processors=8限制了WSL2最多利用8个逻辑核心,swap=16GB是给编译过程加一层保险——即使内存峰值冲高,还有swap可以兜底,不至于立刻被杀进程。

改了.wslconfig之后要让配置生效,在PowerShell里执行wsl --shutdown,等待几秒后重新进入Ubuntu终端。这一步经常被忽略,结果改了配置没生效,编译还是继续崩,白白浪费一晚上。

4.4 编译时的磁盘速度红线:不要在/mnt/c下编译

这是WSL2编译Android源码最最重要的一条:工作目录绝对不能放在/mnt/c或任何其他Windows盘符挂载点下。

原因很简单:/mnt/c是Windows NTFS通过9P协议挂载到Linux侧的,它的IO性能和真正的ext4差一个数量级,编译过程中数十万次小文件读写会被无限放大。更麻烦的是,NTFS和ext4在文件名大小写、权限模型上的差异会导致某些构建脚本行为诡异。

我见过有人把源码放D:\aosp,然后在WSL2里cd /mnt/d/aosp直接make,结果编译了十几个小时后死在各种莫名其妙的文件访问错误上。最稳妥的做法是让源码住在Linux文件系统里,也就是~/aosp这个位置。

如果你确实觉得“宿主机盘上的源码更方便备份”,那也要在宿主机盘上留下一份单独的拷贝,而不是直接拿/mnt/c下的目录来编译。

首次编译的时间取决于机器性能,我实测在8核16线程、16GB内存的机器上,大约3到5小时能完成。中间过程会有大量编译输出滚动,看到[100% ...]字样时基本就接近尾声了。

5. 编译过程中实际撞见的错误:从日志到解决的完整链路

5.1 错误一:python命令找不到,构建脚本集体报错

先说一个我一开始就撞上的问题。执行make后没跑多久,终端开始弹出类似/bin/bash: python: command not found的报错,或者某个Python脚本直接以异常退出。之所以会出现这个问题,是因为Android 10的构建系统里仍有相当一部分工具脚本是用#!/usr/bin/env python写的,它们必须能解析到Python 2解释器。

排查思路很简单,先确认当前shell解释器状态:

which python python --version

如果输出为空,说明系统里没有python软链接。Ubuntu 18.04默认只有python2.7,有的镜像甚至没有python命令。解决方法是建立软链接:

sudo ln -s /usr/bin/python2.7 /usr/bin/python

注意千万别把/usr/bin/python指向python3,因为部分旧脚本的语法是Python 2专属的,指向Python 3会出现大量SyntaxError,你会以为是源码问题,实际上只是解释器指错了。

我在编译前交叉验证过:AOSP构建系统自带的build/soong/soong_ui.bash和很多生成器都假定存在python这个命令,所以建议在一开始就确认好,而不是等报错再补。这个坑在18.04上基本是一步到位,在20.04上要多装一个python2包并做软链,综合体验差不少。

5.2 错误二:ninja: build stopped: subcommand failed背后的OOM

这个错误可以说是编译Android时最令人沮丧的一种,因为它不告诉你哪个具体任务失败,只在某个瞬间突然中断。我当时第一次遇到时,完整日志末尾就是:

ninja: build stopped: subcommand failed.

再往上翻,能看到某个C++编译命令后面跟着virtual memory exhausted或者干脆被信号杀掉。

排查时我做了三件事:先看是否还有编译进程残留:

ps aux | grep -c cc1plus

再检查内存和swap占用:

free -hg

最后看内核日志里有没有OOM记录:

sudo dmesg | grep -i kill

结果在dmesg里直接看到了Out of memory: Kill process ... (cc1plus)的记录,基本实锤是内存耗尽。

根源就是前面说的并行数开太高。当时我先pkill -9 ninja清理残留进程,然后把make命令改成make -j6,同时扩大了.wslconfig里的swap,重跑之后就能顺利通过。这里我个人的经验是:出OOM后优先降并行数,而不是盲目加swap。因为只要并发任务数还是超过物理内存承载能力,swap再大也只是把崩溃延后。

5.3 错误三:磁盘写满后的诡异表现

磁盘空间不足在编译中段的表现最容易产生误导:它不会直接说“disk full”,反而会给出一些奇怪的模块构建失败。比如说某个jar包写不进去、某个中间文件生成一半就断了、soong日志无法写入,等等。

排查方法很直接,看到任何“无法写入”“打开文件失败”类错误,先运行:

df -h /home

如果容量使用率达到100%,那基本就是磁盘问题。我在准备阶段预留了220GB空间,但没想到机器上还跑着Windows更新、Docker镜像之类的东西,最终编译到中途还是撞上了磁盘耗尽。

处理办法是清理Windows侧的临时文件,并用make clean清掉out目录再重新构建。这里也侧面印证了第3章说的:源码加编译产物总体变化区间很大,给自己的环境多留50GB余量永远不亏。

5.4 编译完成后的产物确认

编译完成后,产物在out/target/product/generic_x86_64/目录下。关键文件是:

system.img vendor.img boot.img ramdisk.img

看到这几个文件都生成齐全,说明整套构建链路已经跑通。如果你只是用Source Insight读源码,那么编译到这里已经达到目标了,不必费劲去启动模拟器。想在WSL2里跑模拟器需要额外搭图形界面转发,那是另外一篇长文,本文不展开。

不过我多说一句:即使不跑模拟器,完整走一遍编译也是很有价值的。它保证了源码的完整性、目录的多平台代码都真实存在,你在Source Insight里跳转符号时,看到的不是静态文本而是实际参与构建的代码。

6. Source Insight 4接入WSL2里的源码:两种方式与索引优化

6.1 最直接的一招:\wsl$路径建工程

编译和源码都留在WSL2里,Windows侧的Source Insight 4想要读取它,最直接的方式是通过\\wsl$网络路径。

在Windows资源管理器地址栏输入:

\\wsl$\Ubuntu-18.04\home\<你的用户名>\aosp

回车后就能看到整个源码目录树,相当于把WSL2的文件系统暴露成网络驱动器。Source Insight 4新建工程时,只需要做到三步。

第一步,Project > New Project,给工程起名并选择工程数据库的存放位置。这个位置建议放在Windows本地磁盘,比如D:\SIWork\aosp,因为Source Insight的同步索引数据库文件放在NTFS本地盘上速度最快。

第二步,在添加源码文件时,把目录切换到\\wsl$\Ubuntu-18.04\home\<你的用户名>\aosp,然后在目录树里只勾选需要阅读的子目录。比如读framework层就勾frameworks/base、frameworks/av、frameworks/native;读系统核心就勾system/core、bionic、art;想看构建逻辑再加build/soong和build/make。

第三步,点击确定后Source Insight会递归扫描源码文件并建立符号数据库。这个过程第一次会比较慢,因为AOSP源码文件数量极其庞大,即便只选了常用目录也是几十万行代码,耐心等它同步完即可。

6.2 更稳定一些:Samba共享方案

\\wsl$虽然方便,但在某些Windows版本上,当访问的源码文件数量特别多或文件特别大时,偶尔会卡顿。如果你打算让Source Insight长期挂载这份源码,我建议在WSL2里装一个Samba服务,把源码目录共享出来。

在Ubuntu 18.04里执行:

sudo apt install -y samba

然后编辑/etc/samba/smb.conf,在文件末尾追加:

[aosp] path = /home/<你的用户名>/aosp browseable = yes read only = no guest ok = yes force user = <你的用户名>

重启服务:

sudo service smbd restart

在Windows资源管理器里访问\\<WSL2的IP>\aosp,就能看到共享目录。IP地址用hostname -I在Ubuntu里查询。然后把共享目录映射成网络驱动器,后面Source Insight访问的就是一个稳定盘符了。

要注意一点:WSL2每次启动IP可能都会变,所以每次重启WSL后需要重新查IP。如果觉得麻烦,那就用6.1里的\\wsl$方案,虽然性能上不如Samba共享稳定,但胜在不用重新查IP。

6.3 工程索引提速:文件过滤器、编码与同步细节

Source Insight 4默认会使用自己的一套文件过滤器,但AOSP源码里除了常规的.c、.h、.cpp、.java之外,还有大量.mk、.bp、.go文件。构建逻辑就藏在这些文件里,所以我建议在Options > Preferences > Files里把文件扩展名范围调整好,把*.mk、*.bp也纳入。

编码设置上,AOSP源码大部分是UTF-8编码,注释里偶尔会有中文或特殊字符,默认编码如果不对容易显示成乱码。把Default File Encoding设置为UTF-8基本可以覆盖所有文件。

同步环节有一个容易忽略的点:工程创建后第一次同步会扫描全部加入的文件,如果后续在Linux侧用命令行改了源码,Source Insight里的符号库不会自动更新,需要手动执行Project > Synchronize Files重新同步。我习惯在改动代码后、使用跳转之前按一次Ctrl+S,保证索引和实际内容一致。

索引完成后做个验证,比如在frameworks/base/core/java/android/app/Activity.java里找到一个onCreate方法,右键跳转到定义。如果Source Insight能正确带你跳到真正的实现,说明索引链路已经完全打通。

7. 收尾:几个零散但关键的实操体会

整个流程跑下来,有几个体会想留给大家。

第一,WSL2里编译Android源码,重点不是“能不能”,而是“配好了再动手”。内存、磁盘、Python解释器、JDK版本、源码目录位置,这五项任何一项没提前检查好,后续都会在编译的某个深夜变成玄学问题。我自己就是因为一开始没配.wslconfig,直接把make的并行数开到最大,白白浪费了一个晚上的编译时间。

第二,Source Insight读源码,不要一股脑把所有源码目录都加进去。AOSP的全量目录包含大量工具链、测试代码和预编译库,全部索引会让Source Insight变得非常沉重,跳转反而变慢。精准勾选你想读的模块,比追求全量更实用。

第三,如果你只是读源码,不需要每次开机都走完整编译流程。源码同步完成后,平时直接打开Source Insight浏览,WSL2里的源码文件就在那里,不需要额外启动任何服务。只有在你改了源码并想重新验证构建时才需要回到终端执行make。

按这套流程走下来,你手里就多了一套随时可以翻看的Android 10源码树,以及一个跑通了全链路的开发环境。后面无论是读某个系统服务的实现,还是想自己改点东西再编译验证,起点都已经铺好了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 17:29:54

大模型从预测到创造:核心原理与落地经验

如果你让一个AI大模型写一首关于夏天的诗&#xff0c;或者让它给一份技术报告拟个摘要&#xff0c;它给你的结果往往“很像个创作”&#xff1b;可一旦你追问它“刚才是怎么做到的”&#xff0c;它会告诉你&#xff0c;它其实只是根据前面的文字&#xff0c;一个一个地预测下一…

作者头像 李华
网站建设 2026/10/21 17:27:35

两级式三相光伏逆变并网系统Simulink建模与调试全解析

做光伏并网仿真的人&#xff0c;十有八九都被同一个问题卡过&#xff1a;搜到的“两级式三相光伏逆变并网系统”模型&#xff0c;下载下来一跑就报错&#xff0c;要么是版本不兼容&#xff0c;要么控制环根本不收敛&#xff1b;想自己搭一个&#xff0c;又不知道从拓扑、控制到…

作者头像 李华
网站建设 2026/10/21 17:26:47

VDA Agile Collaboration:汽车供应链跨组织敏捷协作指南

简介&#xff1a;本资源是德国汽车工业协会&#xff08;VDA&#xff09;于2021年1月发布的《Agile Collaboration》英文版官方指南&#xff0c;属非约束性行业推荐文件&#xff0c;专为ZF Friedrichshafen AG内部敏捷转型实践而定制&#xff0c;面向汽车制造业研发、项目管理及…

作者头像 李华
网站建设 2026/10/21 17:25:34

AI视频工具选型:隐私安全与硬件兼容的决策顺序

1. 先搞清楚这个问题的本质&#xff1a;不是二选一&#xff0c;而是有先后顺序很多人第一次接触AI视频工具的时候&#xff0c;脑子里冒出来的第一个问题往往是“我这台机器跑不跑得动”&#xff0c;紧接着第二个问题才是“这东西会不会把我的素材传出去”。这两个问题看起来是并…

作者头像 李华
网站建设 2026/10/21 17:24:40

C++高性能MPSC无锁队列设计与实现:从内存序到节点回收

1. 先拆场景&#xff1a;"单停多发送"到底是什么并发模型1.1 从名字到模型&#xff1a;单停是消费端&#xff0c;多发送是生产端看到这个标题&#xff0c;我第一反应是通信协议里的"一个停机指令、多个发送线程"。但放到 C 无锁编程的语境里&#xff0c;更…

作者头像 李华