Android 构建系统的组成
Android构建系统由python
,gn
,ninja
,makefile
这几部分组成。
-
**
python
**∶做好编译前的准备工作和为gn
收集命令参数。胶水语言,最擅长的是对参数,环境变量,文件操作。 -
gn
∶类似构建界的高级语言,与它对标的是cmake。
gn和ninja的关系有点像C和汇编语言的关系它的作用是生成.ninja文件。
不用gn
直接用ninja
,也行,但更麻烦。就跟全用汇编写 系统一样,理论上可行,可谁会这么去干呢.
ninja
∶类似构建界的汇编语言,与它对标的是make,由它完成对编译器clang,链接器Id的使用。makefile
∶ 有些模块用的还是make编译,听说后面会统一使用ninja,目前是还有大量的make存在.
环境准备
虚拟机准备
Ubuntu 20.04.6 LTS (Focal Fossa)
64-bit PC (AMD64) desktop image (ubuntu.com)
库&工具
一键安装
sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig python python3
配置 git
git config --global user.email "zpengyi659@163.com"
git config --global user.name "pyzhang"
mkdir ~/aosp
cd ~/aosp
repo
下载 repo 工具
mkdir ~/bin
curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo -o ~/bin/repo
# curl https://storage.googleapis.com/git-repo-downloads/repo-1 > ~/bin/repo
chmod +x ~/bin/repo
repo 的运行过程中会尝试访问官方的 git 源更新自己,如果想使用 tuna 的镜像源进行更新:
# 修改 ~/.bashrc
vi ~/.bashrc
# 在最后添加
export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'
PATH=~/bin:$PATH
# 保存后更新一下
source ~/.bashrc
ssh
配置 Ubuntu ssh 服务
# 安装 openssh-server
sudo apt install openssh-server
# 开机自启动
sudo systemctl enable ssh
# 重启 ssh 服务
sudo systemctl restart ssh
配置固定 IP 地址
sudo apt install net-tools -y
cd /etc/netplan
# 备份旧的配置文件
sudo cp 00-installer-config.yaml 00-installer-config.yaml_before
# 查看 ip
ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.0.105 netmask 255.255.255.0 broadcast 192.168.0.255
inet6 fe80::1cb9:c003:c412:eabc prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:41:82:38 txqueuelen 1000 (Ethernet)
RX packets 328753 bytes 473816577 (473.8 MB)
RX errors 0 dropped 123 overruns 0 frame 0
TX packets 125740 bytes 10414123 (10.4 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 471 bytes 48645 (48.6 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 471 bytes 48645 (48.6 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
# 修改配置文件:
sudo vim 00-installer-config.yaml
network:
version: 2
renderer: NetworkManager
ethernets:
enp0s5: # 网卡名称
dhcp4: no # 关闭dhcp
dhcp6: no
addresses: [10.0.0.89/24] # 静态ip,根据自己网络情况配置
gateway4: 10.0.0.1 # 网关,根据自己网络情况配置
nameservers:
addresses: [10.0.0.1, 114.114.114.114] #dns,根据自己网络情况配置
使配置生效:
sudo netplan apply
至此,固定 IP 就配置好了。
开发工具
VScode免密
打开的过程中,需要我们输入 Ubuntu 的登录密码。我们也可以通过配置,实现免密登录的效果:
打开 win 上的 PowerShell:
# 生成公钥
ssh-keygen -t rsa
cat ~/.ssh/id_rsa.pub
将公钥文件的内容拷贝到 ubuntu 的 ~/.ssh/authorized_keys 中
ch
vi ~/.ssh/authorized_keys
粘贴
ubuntu 重启 ssh 服务
sudo systemctl restart ssh
免密登录如果失败,大概率是权限问题:
- .ssh 目录的权限必须是 700
- authorized_keys 文件权限必须是 600 或者 644
- 家目录文件权限必须是 700
Samba 服务器搭建
Samba 是一款数据共享的软件,可用于 Ubuntu 与 Windows 之间共享源代码,传输文件。
Ubuntu 安装 samba 服务端:
sudo apt install samba
配置 samba,修改 /etc/samba/smb.conf,添加如下内容:
vi /etc/samba/smb.conf
[Project]
comment = project
path = /home/pyzhang/Project #你自己需要共享的目录
browseable = yes
writable = yes
后续操作:
chmod 777 /home/pyzhang/Project
#这里是当前用户名
sudo smbpasswd -a pyzhang
#samba 开机自启动
sudo systemctl enable smbd
#重启 samba
sudo systemctl restart smbd
windows 访问 Ubuntu 共享的文件键:
win 键加 R 键,跳出运行窗口,输入 \\10.0.0.89
,其中 10.0.0.89
是我的 ubuntu 的 IP 地址,你需要根据你自己的情况修改为自己的 Ubuntu 的 IP 地址。
如果 win 不能访问,大概是权限问题,可以尝试以下操作:
sudo chmod 777 /home/username
sudo chmod 777 /home/username/yourshare
Idegen + Android Studio 查看源码
source build/envsetup.sh
lunch aosp_x86_64-eng
make idegen -j16
mmm development/tools/idegen
development/tools/idegen/idegen.sh
完成上面的操作后,就会在源码下生成 android.ipr 和 android.iml 文件
打开 Android Studio,File->open,选择 android.ipr 文件。
AIDEGen + Android Studio
6.1 准备工作
AIDEGen,我叫它 “爱得跟”,大概是 Android10 中,google 推出的一个工具,用于方便开发者使用 IDE 来查看和修改系统源码。
使用 “爱得跟” 之前我们需要做一些准备工作,编译 sdk:
source build/envsetup.sh
lunch sdk-eng
# or
#lunch sdk-userdebug
# or
#lunch sdk-user
make sdk
接着我们需要去 google 官方下载 Android Studio,这里推荐3.6.3版本,并将其放到 /opt 目录下。
接下来,选择我们的目标 Product:
source build/envsetup.sh
lunch aosp_x86_64-eng
做好以上准备工作后,我们就可以打开系统模块了,这里我们以 Settings 为例:
# Settings 更换为其他模块名或是模块存在的路径即可打开其他模块
aidegen Settings -i s # -i 表示使用ide,s 表示 Android Studio
Android Studio 配置
稍作等待,Android Studio 就打开了,不过现在还不能直接使用,我们还需要做一些基本的配置:
6.2.1 添加源码中的 jdk 和 sdk
Android Studio 打开后,右下角会出现一个提示框(如果没有出现,点击 file -> Invalidate Caches -> Invalidate and Restart):
接着配置 jdk 和 sdk,点击 file -> Project Structure:
接着点击 + 号:
点击 Add JDK
:
选择源码下的 prebuilts/jdk/jdk9/linux-x86
,然后点击 ok:
接着将 name 修改为 aosp10-jdk9,然后点击右下角 apply:
接着点击 + 号,添加 Android SDK:
选择系统源码目录下的 out/host/linux-x86/sdk/sdk/android-sdk_eng.pyzhang_linux-x86
(最后一个文件夹的名字和你的用户名相关,你的和我的可能不一样),然后点击 ok:
接着 Java SDK 选择刚刚添加的 aosp10-jdk9,点击 ok:
接着,修改 skd 的名字为 aosp10-android-sdk29:
点击 ok 即可。
配置项目的 sdk
接着我们需要配置项目的 sdk,点击 file -> Project Structure,点击左侧 Project,右侧 SDK 选项选择 aosp10-android-sdk:
点击左侧 Modules,中间选择 Settings,右侧 Module SDK 选择 Project SDK:
接着,中间选择 dependencies-Settings,右侧 Module SDK 选择 Project SDK:
最后点击 apply,项目开始重新索引,项目内绝大部分类方法变量均可正确跳转。
至此,配置完成。
下载编译源码
自己动手编译Android源码(超详细)_Android
android源码编译的四个流程:1.源码下载;2.构建编译环境;3.编译源码;4运行。本节涉及前两个环节。
下载源码命令
10
初始化仓库并同步远程代码
# # 选用了 android-10.0.0_r41 版本用于学习
# 初始化仓库,-b 指示分支,这里使用 android10
repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-10.0.0_r41
repo sync
# 可能遇到 SyntaxError: invalid syntax` `‘python’: No such file or directory` 等错误
# 这是 repo 和 Python 版本不一致导致的,使用 python3 执行 repo
python3 ~/bin/repo xxx
-b 后面的值参考源代码标记和 build (opens new window)
还有一种比较简单的方法,直接建立软链接:
# 安装 python3
sudo apt install python3
sudo apt install python-is-python3
# # 建立软链接
# sudo ln -s /usr/bin/python3.6 /usr/bin/python
编译源码
source build/envsetup.sh
# 如果是 Android13
# lunch sdk_phone_x86_64
lunch aosp_x86_64-eng
make -j16
运行模拟器
emulator -verbose -cores 4 -show-kernel
14
# # 类似的 Android14 初始化仓库如下:
repo init https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-14.0.0_r15
# 同步远程代码
repo sync
# 编译
source build/envsetup.sh
lunch aosp_cf_x86_64_phone-eng
m
Cuttlefish 安装与启动:
# 安装 CuttleFish 模拟器所需的依赖
sudo apt install -y git devscripts config-package-dev debhelper-compat golang curl
git clone https://github.com/google/android-cuttlefish
cd android-cuttlefish
for dir in base frontend; do
cd $dir
debuild -i -us -uc -b -d
cd ..
done
sudo dpkg -i ./cuttlefish-base_*_*64.deb || sudo apt-get install -f
sudo dpkg -i ./cuttlefish-user_*_*64.deb || sudo apt-get install -f
sudo usermod -aG kvm,cvdnetwork,render $USER
sudo reboot
# CuttleFish 模拟器启动
launch_cvd --start_webrtc=true
浏览器中输入 https://localhost:8443
即可使用虚拟机了
repo介绍
https://blog.csdn.net/guyongqiangx/article/details/113526601)
概念
Google提供的,专门用于下载Android系统源码的,python脚本
- repo由一系列python脚本组成,可以一次下载多个Git 仓库的内容,避免多次git clone,并能进行更好的管理和使用。
- 通过封装Git命令实现对AOSP项目的管理(Git原本是不支持断点续传)
repo的manifest.xml文件全解
Repo_洛奇看世界的博客-CSDN博客
清单文件 xml
说明文档位于.repo/repo/docs/manifest-format.md
-
列举了清单文件中所有11种节点类型
最常见也最重要的是前3种(remote, default, project)。
-
展示清单文件的format
remote
<remote fetch=".." name="pxlw" review="http://gerritreview:8080" />
remote节点:远程仓库信息
-
name
属性: 命名${remote.name}
远程manifest仓库名称,一般默认为origin
-
fetch
属性: fetch操作的地址${remote.fetch}
仓库url。如果设置为"."(注意是一个点), 就表示在repo init -u url中配置。
-
review
属性: 评审系统网址。如果提交代码,就会进入这个评审系统。
default
default节点:project节点的默认属性
如果project的某个属性不存在,就从default节点取相应的属性来补充
<default remote="pxlw" revision="refs/tags/LA.UM.9.14.R1.11.00.00.651.133" sync-c="true" sync-tags="false" />
project
需要clone的单独git
<project path="trusted-firmware-a"
name="TF-A/trusted-firmware-a.git"
revision="refs/tags/v2.2"
clone-depth="1"
remote="tfo"/>
project节点:需要下载的代码库 在远程仓库的名字和本地存储的路径
-
name
属性: 远程仓库中的名字 服务器端git仓库名称${project.name}
远端git库的位置由
project的name属性
指定(TF-A/trusted-firmware-a.git
)远端git库
${project.name}
-
path
属性: 本地存储的路径${project.path}
客户端同步到本地后的路径名称
(包括git库的路径和工作区目录的路径)
-
revision
属性: 工作区检出的branch分支 -
group
属性:列出project所属的组,以空格或者逗号分隔多个组名。所有的project都自动属于"all"组。 -
clone-depth
属性:从远程仓库下载project的设置 (不重要) -
列出project所属的组,以空格或者逗号分隔多个组名。所有的project都自动属于"all"组。每一个project自动属于
拷贝 链接
linkfile: 软链接文件,src和dest表示源和目的。
copyfile: 文件拷贝,src和dest表示源和目的。
<!-- linkfile: 建立一个软链接Android.bp指向build/soong/root.bp -->
<linkfile src="root.bp" dest="Android.bp" />
<copyfile dest="Makefile" src="core/root.mk" />
<linkfile dest="build/CleanSpec.mk" src="CleanSpec.mk" />
添加删除
include
,remove-project
include
name
属性: 另一个需要导入的manifest文件名字- 通过name属性可以引入另外一个manifest文件(路径相对与当前的manifest.xml 的路径)
remove-project
- 从内部的manifest表中删除指定的project。
- 经常用于本地的manifest文件,用户可以替换一个project的定义
git库下载路径
git库的下载路径(project)由以下两点决定
- 远程仓库的url
remote.fetch
属性 - 远端git库的位置
project.name
属性
${remote_fetch}.git # 远程仓库url
${project_name}.git # 远端git库
project
元素如果有 remote
属性。
- 根据
project
元素的remote
属性,查找对应的remote
元素。 - 再根据找到的
remote
元素的fetch
属性——获取远程仓库url地址。获取方法${remote.fetch}
project
元素如果没有 remote
属性
- 根据
default
元素的remote
属性,查找对应的remote
元素。 - 再根据找到的
remote
元素的fetch
属性——获取远程仓库url地址。获取方法${remote.fetch}
<default remote="pxlw" revision="refs/tags/LA.UM.9.14.R1.11.00.00.651.133" sync-c="true" sync-tags="false" />
# 被默认指定
<remote fetch=".." name="pxlw" review="http://gerritreview:8080" />
# 需要project特殊指定
<remote name="aosp" fetch="https://android.googlesource.com" review="https://10.10.10.29" />
若fetch
属性给出的是url地址:那么repo就会从对应地址下载.git
若fetch
属性给出的是 ".“或者”…“或者”./"等相对路径,就和manifests.git有关系了,:
<remote fetch=".." name="pxlw" review="http://gerritreview:8080"/>
<default remote="pxlw" revision="refs/tags/xxxxx" sync-c="true" sync-tags="false"/>
# 比如manifests.git的地址是
https://android.googlesource.com/platform/manifests.git
# fetch="."或者"./"就相当于
fetch="https://android.googlesource.com/platform"
# fetch=".."或者"../"就相当于
fetch="https://android.googlesource.com"
repo仓库的目录结构
repo在组织时也分为仓库区(.repo)和工作区
跟git仓库分为仓库区(.git)和工作区一样
- 以镜像(–mirror选项指定)方式下载,则只有仓库区(.repo),没有工作区。
- 以默认方式下载,先同步代码到仓库区(.repo),再检出代码到工作区。
以默认方式下载,同步代码到仓库(.repo)时,会涉及到objdir,gitdir,worktree三个概念:
objdir
用于同步远程git仓库数据,相当于是服务端仓库的本地镜像。
# objdir 对应的目录
.repo/project-objects
# 远程git仓库的本地镜像
.repo/project-objects + ${project_name} + .git
# # 即objdir
# # ${project_name}来自清单文件manifest.xml中project节点的name属性。
# # 在name属性的后面追加.git作为本地目录的名字。
如果name属性中包含了目录层次,则会按照相应的目录层次存放,确保跟服务器端的存放一致。
<project path="optee_client" name="OP-TEE/optee_client.git" revision="refs/tags/3.9.0" clone-depth="1" />
repo会将远程仓库https://github.com/OP-TEE/optee_client.git
同步到本地的.repo/project-objects/OP-TEE/optee_client.git.git中。
gitdir
对应checkout 检出的代码。
# gitdir 对应的目录
.repo/projects
# 对应checkout 检出的代码
.repo/projects + ${project.path} + .git
# # 即 gitdir
# # ${project_path}来自清单文件manifest.xml中project节点的path属性。
# # 在path属性的后面追加.git作为本地目录的名字。
如果path属性中包含了目录层次,则会按照相应的目录层次存放。
<project path="optee_client" name="OP-TEE/optee_client.git" revision="refs/tags/3.9.0" clone-depth="1" />
repo会将远程仓库https://github.com/OP-TEE/optee_client.git
同步到本地的.repo/project-objects/OP-TEE/optee_client.git.git中
# 再将这个仓库映射到.repo/projects/optee_client目录中
worktree工作区
上述同步完成后,本地执行checkout操作,将gitdir下面的项目,按照project的path属性检出到工作区。如果path中包含了目录层次,也会按照相应的目录层次存放。
为了在工作区执行git操作,工作区的每一个项目下也存在一个.git目录。实际上.git 统统会放在 .repo 里,外面的.git都只是link。每个project都有三个.git与之对应。为了减少磁盘空间的占用,工作区公共的git数据会链接到gitdir下,同时gitdir下公共的git数据会链接到objdir下。
match python
这里可能会遇到类型 SyntaxError: invalid syntax
‘python’: No such file or directory
等错误,这是 repo 和 Python 版本不一致导致的,我们可以用如下方式解决:
# 安装 python3
sudo apt install python3
# 下载最新的 repo
curl https://storage.googleapis.com/git-repo-downloads/repo-1 > ~/bin/repo
chmod a+x ~/bin/repo
# 使用 python3 执行 repo
python3 ~/bin/repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-10.0.0_r41
还有一种比较简单的方法,直接建立软链接:
# 安装 python3
sudo apt install python3
sudo apt install python-is-python3
# # 建立软链接
# sudo ln -s /usr/bin/python3.6 /usr/bin/python
repo命令
repo init
初始化仓库
# 创建源码文件夹
mkdir source && cd source
# 现在初始化这个仓库
# 两者实现的效果一致,仅仅只是协议不同
# # 1. 初始化仓库 方式1
repo init -u https://aosp.tuna.tsinghua.edu.cn/platform/manifest
# # 初始化仓库 方式2
repo init -u git://aosp.tuna.tsinghua.edu.cn/aosp/platform/manifest
通过-b参数指定获取某个特定的android版本
repo init -u https://aosp.tuna.tsinghua.edu.cn/platform/manifest -b android-4.0.1_r1
repo init 报错
如果执行该命令的过程中,如果提示 无法连接到 gerrit.googlesource.com
,
原因:repo 的运行过程中会尝试访问官方的 git 源更新自己,如果想使用 tuna 的镜像源进行更新:
# 修改 ~/.bashrc
vi ~/.bashrc
# 在最后添加
export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'
PATH=~/bin:$PATH
# 保存后更新一下
source ~/.bashrc
# 重新执行上述 repo init 命令
repo mirror 服务搭建
当一个项目的代码量非常大,发现使用repo sync从远程服务器端拉取的速度非常慢,这个时候制作一个repo mirror镜像仓库就非常有用
刚才介绍过,repo在组织时也分为仓库区(.repo)和工作区,就跟git仓库分为仓库区(.git)和工作区一样
- 以镜像(–mirror选项指定)方式下载,则只有仓库区(.repo),没有工作区。
- 以默认方式下载,先同步代码到仓库区(.repo),再检出代码到工作区。
1、从代码服务器端通过mirror的方式下载整套源代码,一般把mirror放在服务器的根目录下面
cd /mnt
mkdir mirror
cd mirror
repo init -u Gerrit_URL -b branch -m manifest.xml --mirror
repo sync
2、mirror镜像下载完成后,在服务器的其它目录通过mirror的方式初始化工程目录
repo init -u Gerrit_URL -b branch -m manifest.xml --reference=/mnt/mirror
# 例如 repo init -u ssh://$USER@gerritreview:xxx/xxx/manifest.git -m xxx_u_dev_vendor_combo.xml --repo-url=ssh://$USER@gerritreview:xxxx/aosp/tools/repo.git --reference=/mirror
这种方式对于多个工程师工作在同一个服务器上面的时候非常有用,大大的缩短了拉取整套代码的时间,减轻Gerrit服务器的压力。
repo sync
初始化仓库repo init
之后,就可以开始正式同步代码到本地了
注意:repo sync 是不会更新 .repo/repo这个仓的!那需要repo init更新manifest仓库。
repo sync # 默认同步所有仓
repo sync [PROJECT1] [PROJECT2]... # 可以指定project
# 如果是第一次运行 repo sync ,则相当于
git clone # 会把server所有内容都拷贝到本地。根据manifests中的xml文件中git的commit进行同步,这个xml文件在repo init的时候指定;
# 如果不是第一次运行 repo sync ,则相当于
git remote update ; git rebase origin/branch # 将server上的code与本地合并;如果在rebase 的过程中出现冲突,这需要手动解决冲突,再 git rebase --continue
在同步过程中,如果因为网络原因中断也没关系,支持断点续传。
参数:
参数 | 意义 |
---|---|
-j | 开启多线程同步操作 |
-c ,–current-branch | 只同步指定的远程分支。否则默认情况下,sync会同步所有的远程分支。 |
-f ,–force-broken | 当有git库sync失败了,不中断整个同步操作,继续同步其他的git库。 |
-d ,-detach | 脱离当前的本地分支,将HEAD强制切换到manifest.xml中设定的分支,忽略本地的改动且不会覆盖掉本地修改。 |
--force-sync | 强制同步,可能丢失本地改动。(实测无效) |
--no-tags | don’t fetch tags |
–no-clone-bundle | 原本在向服务器发起请求时,为了做到尽快的响应速度,会用到内容分发网络(CDN, Content Delivery Network)。同步操作也会通过CDN与就近的服务器建立连接, 使用HTTP/HTTPS的$URL/clone.bundle来初始化本地的git库,clone.bundle实际上是远程git库的镜像,通过HTTP直接下载,这会更好的利用网络带宽,加快下载速度。 |