MT6799 Android 7.0源码编译实操与避坑指南
简介面向MT6799开发板与Android系统定制工程师该文档系统整理了在Ubuntu 16.04虚拟机环境中下载并编译Android Nougat 7.0源码的完整实操经验同时兼顾Android 4.4版本。包体为1个docx文件约895KB但内容密度很高从修改阿里云软件源加速包管理、配置VMware共享文件夹到安装OpenJDK 8及Git/编译依赖、执行lunch与make等推荐命令每一步都给出可直接复制的指令和注意事项。文档还重点剖析了三个高频编译错误TAREGT_BRM_PLATFORM undefined、ninja subcommand failed、fork: Cannot allocate memory分别对应vendor mk包含缺失、依赖模块异常和内存不足并提供具体解决路径。已有633人学习下载适合需要从零搭建MT6799源码编译环境、排查编译异常的中高级开发人员也可作为MTK平台系统移植的入门参考。1. MT6799 编译 Android 7.0先把内存喂饱再来谈源码在 Android 源码编译这件事上环境往往比代码先出问题。MT6799 开发板的 Android Nougat 7.0 源码包动辄几十 GB解压后光源文件就占掉接近三个 300G 虚拟盘的一角编译过程中 ninja 并行任务一开内存、文件描述符、进程数全部撞在一起。这份资源并不是什么高级优化教程而是一份能直接照着走的实操笔记从 Ubuntu 16.04 虚拟机环境配置、软件源替换、工具链安装到完整编译指令再到三个典型报错的具体解法。适合第一次接触 MTK 平台 Android 7.0 源码编译的工程师也适合已经在编译中反复翻车、想找一份完整避坑清单的人。如果你手头正好有这块开发板又想自己编译 systemimage 或 bootimage这篇笔记能帮你少走至少两轮弯路。2. 虚拟机环境搭建换源、共享文件夹与硬件配比编译 Android 源码这件事虚拟机环境的完成度直接决定后面的每一步是否顺利。很多人在真实设备或双系统上编译时遇到的问题反而比在虚拟机里少原因很简单虚拟机多了一层资源配置约束内存、处理器、磁盘预留稍不匹配后期就会出现一系列“诡异”的报错。这一章先把环境基础打牢。2.1 软件源替换先备份再动 sources.listUbuntu 16.04 默认的软件源在国外下载依赖包的速度让人崩溃。换成阿里源是最常见的做法但有一个步骤顺序不能乱。sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup sudo gedit /etc/apt/sources.list第一行是备份当前的源列表若之后换源出了问题随时可以恢复。第二行用 gedit 打开源列表文件进行编辑。在文件末尾添加阿里云的 Xenial 源时需要注意一个细节deb-src行是源代码仓库Android 编译的大部分依赖工具都从deb对应的二进制仓库安装如果你不需要下载源码包deb-src行可以不加但文档里加上了因为这个环境下部分额外工具会直接从源码编译安装。sudo apt-get update sudo apt-get upgradeupdate这一步是换源后必须执行的它会重新建立软件包索引缓存。文档中建议紧接着做一次apt-get upgrade这里有一个容易踩的坑upgrade会更新所有已安装的软件包包括内核头文件。如果虚拟机的 VMware Tools 是在升级之前装的升级完内核后需要重新编译一次 VMware Tools 的内核模块否则/mnt/hgfs共享文件夹可能就消失了。我一般会在换源后先只做update等把全部依赖工具装完再做upgrade避免中途横生枝节。2.2 VMware Tools 安装压缩包必须在 Linux 环境解压共享文件夹是 Windows 宿主机和 Ubuntu 虚拟机之间传递大文件最方便的方式。文档中特别强调了一句话一定要把压缩文件从共享文件夹复制到 Linux 环境下解压不要直接在共享目录里解压。这是什么原因共享文件夹底层走的是 FUSE 文件系统对硬链接、软链接、文件权限的支持与 Linux 本地文件系统不同。Android 源码包里有大量符号链接和可执行权限位直接在/mnt/hgfs下解压会导致链接失效、权限丢失后续编译时各种奇怪报错都会冒出来。所以先把 tar 包复制到本地比如放到/home/develop/下再执行解压。VMware Tools 的安装命令是常规流程tar zxvf VMwareTools-10.1.15-6627299.tar.gz cd vmware-tools-distrib/ ./vmware-install.pl安装过程中会出现交互式提示文档里的处理方式是遇到YES的问题输入Y回车遇到路径问题直接回车接受默认值。如果你安装时发现vmware-install.pl提示权限不足或者出现找不到内核头文件的错误可以先执行sudo ./vmware-install.pl或者在gedit /etc/apt/sources.list后先完成一次apt-get install linux-headers-$(uname -r)再装 Tools。另外安装完成后建议重启一次虚拟机让共享文件夹正常挂载到/mnt/hgfs。2.3 虚拟机资源配置内存 16G 是底线磁盘 300G 是底气文档给出的虚拟机配置是硬盘 300G、内存 16G、处理器 2 核 2 线程。这套配置不是随手填的它对应着 Android 7.0 编译的实际资源消耗。先说内存。AOSP 7.0 编译时jack-server 要吃掉大量堆内存ninja 并行任务也会同时 fork 出几十个编译进程。8G 内存的虚拟机在编译过程中几乎必现Cannot allocate memory而 16G 内存只能算刚好进入舒适区。如果宿主机内存紧张可以先把编译任务数从make -j4降为make -j2但编译时长会翻倍。再说磁盘。源码解压后约 30-40G构建产物out/目录在编译过程中会膨胀到 100G 以上再加上编译缓存、临时文件300G 预留是比较合理的选择。文档提到把它装在 C 盘同时强调要安装在普通目录而不是系统保护目录否则虚拟机的磁盘文件无法使用碎片整理功能。这里我补充一个建议如果宿主机有机械硬盘尽量把虚拟机文件放到 SSD 上Android 编译是大量小文件随机读写的负载机械硬盘的寻道时间会让编译时间翻倍。提示虚拟机内存调整后需要关闭虚拟机再修改配置有些版本的 VMware 不支持在挂起状态下修改内存参数。3. 源码解压与依赖安装别跳过 md5也别迷信 md5源码包从宿主机进到虚拟机之后第一件事是校验完整性第二件事是解压第三件事是装依赖工具。这三步看起来简单里面都有值得说的门道尤其是文档里那句“md5 报错没关系”很多新手看到这里会困惑为什么校验失败还能继续编译这一章把逻辑拆开讲清楚。3.1 分包解压cat 管道合并 tar.bz2 的操作细节MTK 分发的源码包通常按分包压缩解压命令相当特殊cat MT6799_Android_Nougat_7.0.tar.bz2.* | tar jvxf -这条命令先把所有分包文件拼接成完整流再交给tar解压。j代表 bzip2 解压v是显示解压过程x是解压f指定从标准输入读取数据。注意最后的-它告诉 tar 从标准输入读取而不是从某个物理文件读取。文档里接下来做了一次 MD5 校验md5sum -c MT6799_Android_Nougat_7.0.tar.bz2.md5这里出现的现象是校验报错但编译正常。原因通常是分包的.md5校验文件里记录的是分包各自独立的校验和而后面的.md5文件记录的解压前整体包的校验方式与cat合并后的流不一致也可能是压缩包附带的分包文件在传输过程中个别字节发生变化但源码主体完整可用。文档给了一个重要判断标准只要后续编译能正常跑通、功能没问题这个报错就可以忽略。但你必须清楚一个底线——如果校验失败发生在源码主目录相关的几个分包上解压出来的代码可能缺文件这类缺文件问题不会在解压时报错而是会在编译中期某个模块上突然翻车。所以我的习惯是第一次解压务必md5sum -c一次如果报错至少确认报错的分包不是核心源码分卷之后再解压时可以跳过校验直接解压节省时间。源码解压完成后别忘了确认目录权限。如果解压出来的目录属主不是当前用户编译时会直接卡在文件写权限上提示各种Permission denied。处理方式很简单sudo chown -R $USER:$USER /home/develop/MT6799_Android_Nougat_7.0把源码目录的属主改成当前用户这一步是不少人在编译中期才踩到的隐藏雷区。3.2 依赖工具链Ubuntu 16.04 的固定组合AOSP 7.0 对宿主系统的要求是 Ubuntu 14.04 或 16.04不同版本上需要安装的依赖工具目录也不同。文档给出了一套基于 LTS 16.04 的依赖安装命令我把关键的组合列出来sudo apt-get install -y git-core gnupg flex bison gperf build-essential zip curl sudo apt-get install -y libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev sudo apt-get install -y gcc-multilib g-multilib lib32z-dev ccache sudo apt-get install -y libgl1-mesa-dev libxml2-utils xsltproc unzip m4 sudo apt-get install -y openjdk-8-jdk这里逐条解释一下选型理由。bison和flex是 Android 编译系统生成中间代码时必需的语法解析器gperf用于生成哈希函数lib32ncurses5-dev这类lib32系列库是为了在 64 位系统上编译 32 位目标代码Android 7.0 的启动引导和内核模块还有大量 32 位组件缺了这些库会报找不到curses.h或zlib.h。ccache是编译缓存工具第二次全量编译时可以省下大量时间但这需要你在编译前通过export USE_CCACHE1和ccache -M 50G指定缓存路径与容量上限。OpenJDK 8 的安装值得单独说。Ubuntu 16.04 自带 OpenJDK 8 仓库但文档里也给了 PPA 安装方式sudo add-apt-repository ppa:openjdk-r/ppa sudo apt-get update sudo apt-get install openjdk-8-jdk sudo update-alternatives --config javac sudo update-alternatives --config java如果系统里只有一个 JDK 版本运行update-alternatives --config时会提示没有可选的版本文档里说“不用管”就是这个原因。这里要注意的是Android 7.0 必须用 JDK 8不能用 JDK 9 或 JDK 11否则编译 luni 模块或 core 库时会出现字节码版本不兼容的错误。你可以在配置完成后用java -version和javac -version确认版本号是1.8.0再进入后续流程。4. 编译流程从 envsetup 到 systemimage 的完整路径依赖装完、源码解压完毕接下来是编译的核心流程。MTK 平台的 Android 编译和原生 AOSP 的区别在于它引入了大量 vendor 目录下的专有模块编译时对环境变量和依赖顺序的要求更高这也是为什么文档中特别强调“一定要按照 MT6799_Basic_v1.pdf 文档操作”。这一章把编译链路中的每一步讲透。4.1 make clobber 与 jack-server开局先清理编译的第一条命令不是source而是make clobber。make clobber这条命令会删除之前所有编译操作的已有输出相当于把out/目录清理干净。为什么必须做这一步因为 Android 编译系统会增量判断哪些模块需要重编如果旧的构建产物与当前代码的状态不一致尤其是修改过jack-admin配置或切换过 JDK 版本后旧产物会直接导致 ninja 解析出错误的依赖图。文档里特别强调“第一步很重要有时候提示添加了参数实际上没加进去没成功”说的就是这个现象——你以为配置生效了但旧的构建缓存还在。接下来是 jack-server 的启动./prebuilts/sdk/tools/jack-admin list-server ./prebuilts/sdk/tools/jack-admin start-server ./jack_install.shjack-server 是 Android 7.0 编译 Java 代码时的增量编译器服务。list-server查看当前正在运行的 server 实例start-server启动服务jack_install.sh是 MTK 工程里附带的本土化安装脚本负责把需要的 jack 组件放到正确位置。如果编译过程中出现jack server相关报错最彻底的修复方式是依次执行stop-server、make clobber、start-server这个组合在后面避坑章节还会用到。4.2 lunch 目标选择full_amt6799_evb_n-eng 的含义环境清理完毕后正式进入编译状态source build/envsetup.sh lunch full_amt6799_evb_n-engsource build/envsetup.sh将 Android 编译系统提供的一系列函数加载到当前 shell比如lunch、m、mm、mmm。lunch则是选择目标产品与构建变体。full_amt6799_evb_n-eng中amt6799_evb是设备代号对应device/vendor/amt6799_evb目录n代表 Android Nougat即 7.0eng是 engineering 编译类型会附带 root 权限、调试工具和adb root支持在开发板上调试时这是最实用的选择。如果之后要出正式版镜像可以把eng换成user或userdebug。执行lunch后系统会读取设备目录下的AndroidProducts.mk和device-vendor.mk设置编译环境变量。如果之前的源码解压不完整或 vendor 配置目录缺失这一步就会直接报错。正常的终端输出末尾会出现PLATFORM_VERSION和TARGET_PRODUCT两行摘要看到这两个值后再执行后面的编译命令才算进入安全区。4.3 模块化编译bootimage、systemimage、kernel 怎么选文档给出的编译指令是一组分模块指令这是 MTK 平台调试时最实用的编译方式export LC_ALLC make -j4 systemimage # make -j4 bootimage # make -j4 kernel # make -j4 userdataimage # make -j4逐条解释。export LC_ALLC是把当前 shell 的本地化设置强制为 C 语言环境避免 locale 编码问题导致编译脚本解析异常如果你之前编译时某一步出现invalid locale之类的错误多半是这个原因。make -j4指定 4 个并行任务这个数值不是越大越好它在文档对应的宿主机配置下刚好匹配 2 核 2 线程的 VM如果你把虚拟机处理器加到 4 核可以尝试-j8但以 16G 内存为上限并行任务再多就会撞上前文提过的内存墙。systemimage是 Android 系统分区的镜像包含整个/system目录内容是所有模块中编译耗时最长的bootimage是内核引导镜像只包含内核和 ramdisk调试内核修改时只需编译它速度快得多kernel单独编译 Linux kernel适合验证内核配置或驱动改动userdataimage是用户数据分区镜像平时调试不太用。如果你只需要产出一个可烧录的镜像直接make -j4执行全量编译即可整套流程跑完大约需要 2-3 小时具体时长取决于机器性能。注意如果你同时拿到了多个 MTK 平台工程比如 MT6799 和 MT6779 并存不要在一个源码树里混编两个工程这也对应了文档后面避坑章节里的 JDK 冲突案例。5. 避坑排查三大报错与两处隐藏雷区这一章是整份资源里最有价值的部分。文档作者在编译过程中反复踩坑最终把这几个报错的完整解法记录下来每一处的处理思路都值得细品。按「现象 → 原因 → 解决」的结构来看。5.1 TAREGT_BRM_PLATFORM is undefineddevice-vendor.mk 没有被正确引入编译刚开始就直接报错vendor/amt/libs/hwcomposer.mt6799.1.5.0/Android.mk:4: *** TAREGT_BRM_PLATFORM is undefined. Please make sure the device-vendor.mk is correctly included.注意这个报错里TAREGT是原文拼写实际变量名应为TARGET_BRM_PLATFORM。它出现的直接原因是device-vendor.mk没有被编译系统正确加载而它又是定义BRM_PLATFORM系列变量的入口。文档给出的处理方式是三步第一执行apt-get upgrade更新已安装的包这可以修复系统中不完整的依赖关系第二把jack_install.sh放到 MTK 工程根目录下并运行这一步看似无关实际作用是脚本会触发 vendor 目录下某些模块的配置加载第三安装g-multilib等 MTA 平台必需的交叉编译库。这三步处理完仍报同样的错基本可以确认是源码目录本身不完整或者是拷贝源码时没有保留可执行权限。检查源码根目录下的device/和vendor/目录是否存在并确认其内容完整。如果是从共享文件夹直接复制到 Linux 的优先回到2.2节提到的解压方式把符号链接和权限先修好。5.2 ninja: build stopped: subcommand failedjack-server 内存撞墙这个报错出现的频率最高也最容易让人抓狂ninja: build stopped: subcommand failed. build/core/ninja.mk:148: recipe for target ninja_wrapper failed make: *** [ninja_wrapper] Error 1报错信息完全没有告诉你是哪个模块编译失败subcommand failed只是说某个子任务退出码非 0。最典型的元凶是 jack-server 内存配置。Android 7.0 编译 Java 代码时jack-server 会在后台跑一个 JVM 进程默认内存限制为 2G 左右在编译大模块比如 framework、telephony时堆内存溢出子命令就直接挂掉。文档给出的修复方案是修改prebuilts/sdk/tools/jack-admin脚本vim prebuilts/sdk/tools/jack-admin在脚本中找到这一行JACK_SERVER_COMMANDjava -XX:MaxJavaStackTraceDepth-1 -Djava.io.tmpdir$TMPDIR$JACK_SERVER_VM_ARGUMENTS -cp $LAUNCHER_JAR $LAUNCHER_NAME把-Xmx4096m参数加进去同时修改变量JACK_SERVER_VM_ARGUMENTSJACK_SERVER_COMMANDjava -XX:MaxJavaStackTraceDepth-1 -Djava.io.tmpdir$TMPDIR$JACK_SERVER_VM_ARGUMENTS -Xmx4096m -cp $LAUNCHER_JAR $LAUNCHER_NAME JACK_SERVER_VM_ARGUMENTS${JACK_SERVER_VM_ARGUMENTS:-Dfile.encodingUTF-8 -XX:TieredCompilation -Xmx4096m }注意这里的-Xmx4096m是小写的m但文档作者提到一个很玄学的现象——之前用小写m编译正常某次突然报错改成大写-Xmx4096M再make clobber就恢复了。这个现象的本质是 JVM 在某些版本下对-Xmx后缀m和M的解析差异但具体触发条件难以复现所以文档建议改完参数后重启 jack-server 并强制清理一次make clobber确保新参数真正生效。make clobber ./prebuilts/sdk/tools/jack-admin stop-server ./prebuilts/sdk/tools/jack-admin start-server这三条命令的顺序不能变。make clobber会删除所有旧编译产物stop-server结束旧 JVM 进程start-server以新参数重新拉起服务之后再进行正常编译。如果你跳过make clobber旧进程可能仍在运行参数根本没有重新加载。5.3 fork: Cannot allocate memorypid_max 与 swapfile 双管齐下这个报错的文本特征非常明显fatal: fork: Cannot allocate memory ninja: fatal: fork: Cannot allocate memory很多人的第一反应是物理内存不够但文档里用sysctl kernel.pid_max查看后发现是系统最大进程数限制被撞穿了。编译时的 ninja 会并行 fork 大量子进程每个编译任务都会消耗一个 PID。解决办法分两步sysctl kernel.pid_max ps -eLf | wc -l echo 1000000 /proc/sys/kernel/pid_max echo kernel.pid_max1000000 /etc/sysctl.conf sysctl -p第一条命令查看当前的进程数上限第二条命令统计当前线程数正常情况下编译任务非常多时ps -eLf | wc -l的输出会逼近上限值。第三条命令临时把上限提升到 1000000第四、五条命令将该配置写入sysctl.conf使其永久生效。如果调完pid_max仍然报错文档还提供了一个兜底方案创建 swapfile 增加交换空间。sudo dd if/dev/zero of/swapfile bs2048 count524288 ls -lh /swapfile sudo mkswap /swapfile sudo swapon /swapfilebs2048表示块大小 2Mcount524288表示块数量相乘得到 1G 的 swap 文件。如果创建 512M将bs设为 1024。这里要提醒的是swapfile 是给编译进程临时换页用的不是内存替代品。当你把内存加到 16G 后swap 基本只起保险作用但如果虚拟机内存只有 6G只靠 swap 会让编译时间暴涨衔接上pid_max调整才有救。文档作者的最终做法是 16G 内存 pid_max swap 三管齐下才把编译稳定跑通。5.4 JDK 工具冲突多份源码共存同一工具的问题文档里还提到一个实战中很坑的场景在虚拟机里同时拷贝了两份源码MT6799 和 MT6779共用同一套工具链后面在编译过程中报 JDK 相关错误删掉其中一份源码后才恢复正常。这个现象的直接原因是jack-server是全局单例进程。当你在两台工程之间切换编译时jack-admin会在同一个$HOME目录下创建.jack-server状态文件两份源码的配置不统一时上一次编译产生的临时状态会污染下一次编译。更隐蔽的是bin目录下的一些工具会同时注册到系统的update-alternatives里两份工程对工具链版本的预期不一致导致某一方的编译脚本拿到错误的工具版本。解决方案其实简单同一台编译机上同时只保留一份正在编译的源码工程编译完一个平台后再引入另一个平台源码并重新执行make clobber。如果你必须同时维护多个工程建议为每个平台单独建一个虚拟机或者至少使用不同的 Linux 用户账户隔离$HOME/.jack-server目录。5.5 磁盘权限根因安装目录决定虚拟机权限最后一条踩坑记录发生在虚拟机安装阶段。文档作者的宿主机是 500G 磁盘为了节省空间把虚拟机文件直接放在 C 盘结果发现安装在部分系统保护目录下时虚拟机磁盘文件无法使用碎片整理功能同时编译产物生成时出现权限抖动。这个问题的根因是 Windows 的 UAC 机制拦截了虚拟机进程对系统目录的高权限写入请求。VMware 安装虚拟机的路径如果落在C:\Program Files这类受保护目录下非管理员模式下运行 VM 时磁盘文件的操作会频繁被权限检查打断表现为编译中途突然出现Cannot create file或Permission denied。文档的处理是改放到C:\Download这种普通目录避免权限模型干扰。提示如果你在编译中途收到的是ninja: error: writing to file ... No space left on device那对应的原因就不是权限而是虚拟磁盘真的满了可以执行df -h确认。6. 编译后的验证三个动作确认镜像真的可用编译结束时终端输出make completed successfully这不代表你的镜像就一定能烧录开机。最后的验证环节我会按下面三个动作依次走一遍确认产物真的可用。第一步检查镜像文件的生成时间和大小。进入输出目录看所有镜像的时间戳是否与编译结束时间一致避免拿到上一次残留的旧镜像。第二步执行一次镜像 MD5 校验把生成的文件与官方发布的 MD5 清单核对确保每个镜像都完整生成。第三步查看out/target/product/amt6799_evb/目录下的android-info.txt确认编译时选择的构建版本、平台版本和编译类型正确避免把eng版本当成正式版本烧录出去。ls -l out/target/product/amt6799_evb/*.img md5sum out/target/product/amt6799_evb/*.img cat out/target/product/amt6799_evb/android-info.txt其中第一条命令用于确认镜像存在及其大小第二条命令批量计算镜像文件的校验值第三条命令读取设备的构建信息。如果你对编译系统的中间产物有怀疑还可以执行make -j4 systemimage再次增量编译观察 ninja 是否提示No need to rebuild这个输出能直接证明当前源码与产物的一致性。排错时还有一个重要的手段重新执行source build/envsetup.sh和lunch然后执行make -j4做全量编译之前先单独编译一个改动最频繁的模块比如make -j4 framework确认单模块编译通过后再上整套编译链路这样定位问题范围会清晰得多。从那以后我每次编译完都会强制走一遍这三步并且在烧录前对镜像做一次 md5sum 对比直接把风险掐在源头。对于有问题的工程删掉源码、重解压、按文档里的三步处理重跑一遍远比硬着头皮反复编译省时间。这套方法不一定能让你一条命令编译成功但至少可以让你在迷茫时知道下一步该查什么。希望帮到你。本文还有配套的精品资源点击获取