Android源码本地编译实战

解决源码本地编译步骤,短时间编译出对应产物

准备工作:

权限申请:

确认自己是否有对应机型,对应 android 大版本,miui 大版本权限

https://project.pt.miui.com/#/v2/authority/requestlist

其他配置

  1. 进入 CloudDev 申请云服务器,如果已有云服务器,可跳过此步骤,如果使用本地编译也可以跳过这一步。
  2. 进入 权限管理平台 申请对应设备权限,本例采用 M2 设备作为例子。
  3. 确保当前设备上,小米在线刷机平台可用 , Win上无法连接设备可以参考 Win USB 驱动安装。另外某些数据线也可能导致无法连接设备,可以尝试更换数据线。

下载代码:

搜索对应机型,准备 repo init 环境

https://bbm.pt.miui.com/releases

以 O2 为例:

在您希望存放源码的目录中,使用 repo init 命令来初始化仓库,并通过 -m 参数指定您要使用的 manifest 文件,如果你想了解更多的Repo使用方法点击这里。例如:

// 在本地新建一个目录用于存放源码
mkdir o2_w_stable
cd o2_w_stable
// 进入目录后执行如上复制的repo init命令
repo init -u ssh://zhoubencheng@git.mioffice.cn:29418/platform/manifest.git -b release-25q2-3.0.0 -m release-25q2-qcom-xiaomi.xml --repo-url=ssh://zhoubencheng@git.mioffice.cn:29418/tools/repo.git --no-repo-verify --depth=1 --git-lfs
 

代码下载过程中可能发生 repo版本过老,具体报错如下,解决办法如

https://xiaomi.f.mioffice.cn/docs/dock4p0dybTjn253HRhQ85fFOkc

也可以参考 https://gitlab.tx2.898311.xyz/zbc/GeneralAndroid/-/blob/master/main/blog/%E6%97%A5%E5%B8%B8%E5%B7%A5%E4%BD%9C/Gerrit%E5%8D%87%E7%BA%A7%E6%8F%90%E2%BD%B0.md

建议:

在执行 repo sync 的时候,同时在 corgi 上打一个对应机型对应版本的 userdebug 刷机包

https://corgi.pt.miui.com/next/pangu/rom/list

刷机包打好后把这个包刷入手机,本地代码的修改都 push 进这个手机即可,可以保证本地代码与手机代码一致性更好,避免其他模块有依赖,导致 push 后手机起不来。

其中:

  • -u 参数指定了 manifest 仓库的 URL。

  • -m 参数指定了仓库中具体的 manifest 文件名(例如 my_manifest.xml)。如果不指定,默认使用 default.xml

  • -u:指定清单(manifest)仓库的 URL。该 URL 指向了一个 Git 仓库,其中包含了多个项目的配置文件。CSDN Blog+1Cnblogs+1

  • ssh://xiaoming@git.oppeoffice.cn:29418/platform/manifest.git

  • -b:指定要检出的分支名称。 比如这里的master-t分支

  • -m:指定清单仓库中的具体清单文件名。Android Open Source Project+5git-repo.info+5360doc+5 比如这里的 missi_t_qcom.xml

  • --repo-url:指定用于初始化 Repo 工具的 Git 仓库 URL。阿里云开发者社区-云计算社区-阿里云+1CSDN Blog+1 , 比如这里的ssh://xiaoming@git.oppeoffice.cn:29418/tools/repo.git

  • --no-repo-verify:在初始化 Repo 时跳过对 Repo 仓库的验证检查。

  • --depth=1:在克隆仓库时仅获取最新的提交记录,减少下载的数据量。

  • --git-lfs:启用 Git 大文件存储(Git LFS)支持,用于管理大型二进制文件。

用途

repo init 用于初始化一个新的 repo 工作区(workspace),相当于告诉系统:

我要从某个 manifest 仓库拉取一组项目配置,用它来管理多个 git 子仓库的同步。

Android 系统、嵌入式项目、小米 / 高通平台的源码同步,常用这种结构。


🧱 参数详细解释

参数含义
repo init初始化 repo 工作区的命令。必须首先执行(之后才会用 repo sync 拉代码)。
-u ssh://zhoubencheng@git.mioffice.cn:29418/platform/manifest.gitmanifest 仓库地址。这是一个特殊的 Git 仓库,里面的 default.xml 或指定的 release-25q2-qcom-xiaomi.xml 文件描述了所有子项目的路径、分支、远程源等。这里使用了 SSH 协议,说明要通过个人帐号 zhoubencheng 的权限访问公司 Git 服务。
-b release-25q2-3.0.0指定 manifest 仓库的分支。相当于 git clone 后的 git checkout release-25q2-3.0.0。这个分支对应一个特定版本(这里是 Q2 2025 release 3.0.0)。
-m release-25q2-qcom-xiaomi.xml指定 manifest 文件名。默认是 default.xml,这里用的是 release-25q2-qcom-xiaomi.xml,表示针对小米定制的高通平台配置。这个 XML 文件定义了要同步的源码模块。
--repo-url=ssh://zhoubencheng@git.mioffice.cn:29418/tools/repo.git指定使用的 repo 工具仓库地址。通常 repo 默认从 Google 的 https://gerrit.googlesource.com/git-repo 下载,这里替换成了公司内部(小米)镜像。这样可以避免被墙或加快下载。
--no-repo-verify跳过 repo 工具签名验证。某些公司内部修改过 repo(非官方版本),加上这个参数可以防止验证失败。
--depth=1浅克隆(shallow clone),只拉取最新一层提交记录(不包含历史),可以显著减少下载时间和磁盘占用。适合只需要编译、不改底层代码的情况。
--git-lfs启用 Git Large File Storage (LFS) 支持,用于拉取大文件(如图片、固件、模型等)。确保代码同步时不会丢失大文件。

⚙️ 示例目录结构

执行完 repo init 后,你的项目结构大致如下:

编辑/
 ├── .repo/
    ├── manifests/                  # 存放 manifest 仓库内容
    ├── manifest.xml                # 当前使用的 XML 配置
    └── repo/                       # repo 工具脚本
 └── (子项目将在 repo sync 后出现)

注意,上述地址仓库需要在申请对应设备权限后,换成自己的仓库地址,具体地址可以参考BBM中对应机型的命令下载提示,但注意,不要直接使用BBM中的初始化命令,仅拷贝ssh开头的地址链接即可。另外,branch 可以替换成你需要的分支,如果不知道分支名,可以参考柯基编译平台 中对应的 system 编译日志(右侧操作下的齿轮图标,点选System日志,进入后左侧栏目右键点选View as plain text下载到本地,查找repo init字段,可以找到该次编译使用的--repo-branch对应的值。

指定下载的xml文件:

  1. 上面命令中<release-25q2-qcom-xiaomi.xml> 这个xml是可以替换成你自己的xml的,如果机器当前使用了不同的xml,并且你需要debug机器代码,那么这个时候,你可以下载机器代码对应manifest文件
repo init -u ssh://zhoubencheng@git.mioffice.cn:29418/platform/manifest.git -b release-25q2-3.0.0 -m release-25q2-qcom-xiaomi.xml --repo-url=ssh://zhoubencheng@git.mioffice.cn:29418/tools/repo.git --no-repo-verify --depth=1 --git-lfs
 

  1. 然后cp到.repo中对应 .repo/manifests目录下

  1. 打开对应manifest替换所有的 username_placeholder关键字为自己的邮箱

repo下载信息基参考这个命令就可以

但是如果需要确定的manifest和分支信息需要进一步下载对应机器的manifest文件

柯基编译平台

查找repo init字段,提取分支信息

当然最好还是参考corgi平台的编译思路

repo init -g all -u ssh://ci-autobuilder@git.mioffice.cn:29418/platform/manifest.git -b master --repo-url=ssh://ci-autobuilder@git.mioffice.cn:29418/tools/repo.git --repo-branch=stable-2.16.5-build --no-repo-verify --reference=/home/work/REPO_LOCAL_MIRROR --depth=1
 
 
》》》》》》Downloading manifest from git@c3dns.pt.miui.com:miui/platform/manifest.git
》》》》》》repo has been initialized in /home/work/mnt/miui_codes1/build_home_rom
 
cd /home/work/data/miui_codes/build_home_rom/.repo
rm -f manifest.xml
 
 
 
ln -s qcom-manifest-system-OS2.0.250410.1.VNUCNXM.STABLE-TEST-15.0-719ec08298.xml manifest.xml
 
// repo sync拉取整套代码即可
// 一般建议在执行repo sync的时候,同时在corgi上打一个对应机型对应版本的userdebug刷机包
// 使用8线程下载代码,且只下载manifest文件内默认的分支,且不下载仓库的tags
repo sync -cd --no-tags -j8

// 如果只想拉取某个仓库的代码,只需加上仓库位置即可,不写就默认全部下载
repo sync frameworks/base -cd --no-tags -j8

指定manifest再repo sync(非必须)

不同版本源代码、ROM所对应的manifest不同,因此我们可以使用manifest文件来保证即将下载的源代码和ROM版本完全一致。(若源代码和ROM版本不匹配,后续进行到push framework时可能会有重启卡在开机界面的Android Logo、无限重启等问题,需要刷ROM使手机恢复正常。而且,还需要重新下载源码、ROM使得版本匹配,这会耗费大量的时间。)

打开柯基编译平台,如下图所示按序操作(想要详细了解该平台可以参考柯基打包平台使用说明 - MiPhone系统软件部 - Formal)。

在序号3的红框处选择和获取init命令时同样的信息(q对应安卓Q版本即安卓10版本、r对应安卓R版本即安卓11版本;dev对应开发版、stable对应稳定版、native对应原生版、factory对应工厂版),作为版本号的日期选最新的(其实和下载ROM时保持一致就行)。

在序号4处点击复制链接,新建标签页粘贴链接,下载此版本的manifest文件到.repo目录下。

在命令行终端执行:

# 当前路径:/home/mi/sda/mymiui/picasso
 
cd .repo # 进入.repo目录
# ls -l # 查看当前软链接manifest.xml指向
# 令manifest.xml指向我们刚下载的manifest文件,注意替换刚下载的文件名称
ln -sf picasso_21.7.20_11.0_manifest_20210720.000515.xml manifest.xml
# ls -l # 确认软链接manifest.xml指向改变
cd .. # 返回上一级目录
 
# 使用4线程下载代码,且只下载manifest文件内默认的分支,且不下载仓库的tags
repo sync -j4 -d -c --no-tags

等待几个小时后,几百个G的代码下载完成,内容如图所示。

编译

环境配置

每次新打开一个终端,如果想要编译源码,都需要在源码根目录下执行如下操作:

source build/envsetup.sh
 
# w版本
# qcom
lunch missi-feature_phone_qcom_cn_only64-userdebug  # O2、o3
lunch missi-feature_pad_qcom_cn_only64-userdebug # O81 平板项目
# mtk
export OUT_DIR=out_sys && lunch missi-feature_phone_mtk_cn-userdebug # M12
# xring
lunch missi-feature_phone_xring_cn_only64-userdebug #O2S
 
# v版本
# qcom
lunch missi-feature_phone_qcom_cn_only64-userdebug
# mtk
lunch missi-feature_phone_mtk_cn-userdebug
# xring
lunch missi-feature_phone_xring_cn_only64-userdebug
 
# u版本
lunch missi_phone_cn-userdebug

可选输入如下命令来跳过 XX 功能,会快一点

export SKIP_DOWNLOAD_CUST_APPS=true不下载商业预装应用
export SKIP_DOWNLOAD_DECOUPLED_APPS=true不下载解耦应用 miui/prebuilts/decoupled_appsmiui/prebuilts/miui_decoupled_apps
export SKIP_DOWNLOAD_VENDOR_GOOGLE_APPS=true不下载 GMS 预装应用
export SKIP_DOWNLOAD_OPERATOR_APPS=true# 不下载运营商
export SKIP_SETTINGS_PROGUARD=true关闭混淆, 方便 debug
export ENABLE_MIUI_DEBUGGING=true允许调试
export WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY=true关闭 odex 优化
export XMS_BULDER_DISABLED=true关闭 xms(Xiaomi Mobile Services 类似与 GMS,包含一些 SDK 和应用等)

根据需要编译对应模块

framework 的修改主要涉及目录:

根据自己的代码所在位置,选择不同的模块进行编译,

找到对应代码所在目录下的 Android.bp 文件,即可确认需要编译的模块名。

例:

  1. 本地修改了 frameworks/native/services/surfaceflinger/Layer.cpp
  2. 查找该文件所在模块 bp 文件,frameworks/native/services/surfaceflinger/Android.bp,对应编译名字为 libsurfaceflinger,则模块名为 libsurfaceflinger。
  • 首次编译需要使用 make
  • 如果涉及到了文件的添加或删除,或 Android.bp 的修改,也需要 make
  • 常用模块名:
    • framework-minus-apex`` ``services`` ``libsurfaceflinger`` ``miui-framework`` ``miui-services`` ``libhwui`` ``libandroid_runtime
// 源码根目录下执行如下编译命令即可
make <模块> -j8

如需整编,可以执行make target-files-package -j10 ,整编根据个人机器性能,一般需要 3-4 小时,可参见:刷机 aosp 版本-刷机

后续再次编译时,可使用 ninja 加快编译速度

每套源码下载完成后,首次使用 ninja 前需要先配置 ninja

cp prebuilts/build-tools/linux-x86/bin/ninja out/host/linux-x86/bin/
ln -sf out/combined-missi.ninja build.ninja

之后即可使用 ninja 来加快编译速度

ninja <模块> -j8

编译结束,push 之前的需要执行的命令

在往 push 编译得到的 Jar 包或者是。so 文件前,都需要对手机进行以下命令

adb root
adb remount
adb disable-verity
adb reboot
 
//开机后需要重新 root和remount一下
adb root
adb remount

push 结束后如何生效

push framework 或 services 后,需要执行adb reboot 修改才能生效

push surfaceflinger,hwui,gui,jni 后,执行adb shell stop;adb shell start 后,修改即可生效

省事版 push 脚本

下载并执行脚本 pushframework.sh

下载并执行脚本 pushservices.sh

<object data=“file:///” type=“text/plain” width=“100%” height=“500px”>

执行完命令需要 adb reboot

下载并执行脚本 pushhwui.sh

下载并执行脚本 pushlibgui.sh

下载并执行脚本 pushsurfaceflinger.sh

下载并执行脚本 push_MiuiSystemUI.sh

执行完命令需要 adb shell stop;adb shell start

各模块详细编译及 push 命令

framework

文件范围:frameworks/base/core

make framework-minus-apex -j8
// 如果涉及到了miui/frameworks/base的修改,还需要编译
make miui-framework -j8

push 命令

adb root;adb remount
adb push $OUT/system/framework/framework.jar /product/pangu/system/framework/
adb push $OUT/system_ext/framework/miui-framework.jar /system_ext/framework/
adb reboot

services

文件范围:frameworks/base/services

make services -j8
// 如果涉及到了miui/frameworks/base的修改,还需要编译
make miui-services -j8

push 命令

adb root;adb remount
adb push $OUT/system/framework/services.jar /product/pangu/system/framework/
adb push $OUT/system_ext/framework/miui-services.jar /system_ext/framework/
adb reboot

surfaceflinger

文件范围:

frameworks/native/services/surfaceflinger

frameworks/native/libs/renderengine

W版本编译及push命令

make surfaceflinger -j80
adb push out/target/product/missi/system/bin/surfaceflinger system/bin/surfaceflinger
adb shell stop;adb shell start

V及以下编译命令

make libsurfaceflinger -j8

V 版本 push 命令

adb root;adb remount
adb push $OUT/system_ext/lib64/libsurfaceflinger.so  system_ext/lib64/libsurfaceflinger.so
adb shell stop;adb shell start

U 版本 push 命令

adb root;adb remount
adb push $OUT/system/lib/libsurfaceflinger.so system/lib/libsurfaceflinger.so
adb push $OUT/system/lib64/libsurfaceflinger.so system/lib64/libsurfaceflinger.so
adb shell stop;adb shell start

libmisurfaceflinger

文件范围:vendor/xiaomi/frameworks/native/services/surfaceflinger

make libmisurfaceflinger -j8

push 命令

adb root;adb remount
adb push $OUT/system_ext/lib/libmisurfaceflinger.so system_ext/lib/libmisurfaceflinger.so
adb push $OUT/system_ext/lib64/libmisurfaceflinger.so system_ext/lib64/libmisurfaceflinger.so
adb shell stop;adb shell start

hwui

文件范围:frameworks/base/libs/hwui

hwui 编译前,需要先打开 frameworks/base/libs/hwui/Android.bp

将对应文件中所有的 host_supported: true 都注释掉,才能开始本地编译,否则会报错

  • V版本bp修改可apply该diff

下载并应用diff文件 bp_diff_v

  • W版本bp修改可apply该diff

[下载并应用diff文件 bp_diff_w](./sh/bp_diff _w)

make libhwui -j8

push 命令

adb root;adb remount
adb push $OUT/system/lib/libhwui.so system/lib/libhwui.so
adb push $OUT/system/lib64/libhwui.so system/lib64/libhwui.so
adb shell stop;adb shell start

gui

文件范围:frameworks/native/libs/gui

make libgui -j8

push 命令

adb root;adb remount
adb push $OUT/system/lib/libgui.so system/lib/libgui.so
adb push $OUT/system/lib64/libgui.so system/lib64/libgui.so
adb shell stop;adb shell start

jni

文件范围:frameworks/base/core/jni

android_runtime 编译前,需要先打开 frameworks/base/core/jni/Android.bp

将对应文件中所有的 host_supported: true 都注释掉,才能开始本地编译,否则会报错,也可一并应用hwui中的diff

make libandroid_runtime -j8

push 命令

adb root;adb remount
adb push $OUT/system/lib/libandroid_runtime.so system/lib/libandroid_runtime.so
adb push $OUT/system/lib64/libandroid_runtime.so system/lib64/libandroid_runtime.so
adb shell stop;adb shell start

MiuiSystemUI

文件范围:

  • packages/apps/MiuiSystemUI
  • frameworks/base/libs/WindowManager/Shell
make MiuiSystemUI -j10

push 命令

adb root;adb remount
adb push $OUT/system_ext/priv-app/MiuiSystemUI/MiuiSystemUI.apk /system_ext/priv-app/MiuiSystemUI/MiuiSystemUI.apk
adb shell stop;adb shell start
# 如果不想重启手机,也可以直接kill掉原来的Systemui进程,等待Systemui重启后就可以生效
# 可以直接使用2.5的脚本

其它注意事项

adb 跳过开机引导

adb root;adb shell settings put secure user_setup_complete 1;adb shell settings put global device_provisioned 1;adb reboot

python 编译版本问题

print SyntaxError: invalid syntax 错误

Python 3 编译,print 函数出现语法错误(SyntaxError: invalid syntax)的问题

以上问题是因为 python 版本不对导致的语法错误,需要使用 python2 去编译

使用以下命令可以实现 python 版本切换

sudo update-alternatives --install /usr/bin/python python /usr/bin/python2 1
sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 2

参考链接:https://www.jianshu.com/p/3c844d965e5a

Android 中打印 C++代码中的调用堆栈

1. 首先在Android.bp中添加依赖库:libutilscallstack
cc_input_flinger_library_shared {
    name: "libinputflinger",
    ......
    shared_libs: [
        "libinputflinger_base",
        "libinputreporter",
        "libinputreader",
        "libutilscallstack"  // Here
    ],
}
2. 在对应的文件中,引入头文件:
#include <utils/CallStack.h>
3. android::CallStack stack("TAGXXX");
4. 添加完成之后,需要make一遍即可

具体操作见下:

  1. 添加链接库

surfaceflinger 加堆栈

frameworks/native/services/surfaceflinger/Android.bp

搜索 libsurfaceflinger_defaults

hwui 加堆栈

frameworks/base/libs/hwui/Android.bp

搜索 hwui_static_deps

libgui 中加堆栈

frameworks/native/libs/gui/Android.bp

搜索 libgui-defaults

http://minio.898311.xyz:8900/blogimg/17435765901455.png

skia 中加堆栈

external/skia/Android.bp

搜索 libpng

包含头文件

#include <utils/CallStack.h>

在需要打栈的地方加入

android::CallStack stack("TagXXX");

查看 log 时过滤”TagXXX”即可。

ninja 报错

error while loading shared libraries…No such file or directory

ninja: error while loading shared libraries: libc++.so: cannot open shared object file: No such file or directory

ninja: error while loading shared libraries: libjemalloc5.so: cannot open shared object file: No such file or directory

解决:

# 根据缺少的lib提示,执行对应的cp命令即可
sudo cp prebuilts/build-tools/linux-x86/lib64/libc++.so /usr/lib
 
sudo cp prebuilts/build-tools/linux-x86/lib64/libjemalloc5.so /usr/lib

// 配置 ninja 环境时有时候会遇到目录out/host/linux-x86/bin/不存在,这时候手动新建bin文件夹即可,如果还报少libc++库,就执行上面的命令

解决:

sudo apt remove ninja-build
sudo cp ./prebuilts/build-tools/linux-x86/bin/ninja /usr/bin
sudo cp ./prebuilts/build-tools/linux-x86/lib64/libjemalloc5.so /usr/lib
sudo cp ./prebuilts/build-tools/linux-x86/lib64/libc++.so /usr/lib/

编译报错

build/make/core/artifact_path_requirements.mk:40 error: Build failed

打开报错的文件,将报错部分注释掉,重新编译即可。

corgi编译报错解决

step1 找到日志

找到编译记录 - > 编译日志 - > system\product\vendor View as plain text

step2 搜索报错信息

搜索关键字 1 error generated.,进而可以定位到报错信息附近

1 error generated

如上图就是 frameworks/base/libs/hwui/pipeline/skia/RenderNodeDrawable.cppandroid::uirenderer::RenderProperties.getName报错导致BB,修复重新编译即可

frameworks/base/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp:504:101: error: no member named 'getName' in 'android::uirenderer::RenderProperties'
[2025-04-14T07:47:38.954Z]   504 |     trace_begin_formatStringCTM("isLayer[%s] clipFlags[%d] Alpha[%.1f] ignoreLayer[%s]", properties.getName(),clipFlags,properties.getAlpha(),ignoreLayer?"true":"false");
[2025-04-14T07:47:38.954Z]       |                                                                                          ~~~~~~~~~~ ^