### envsetup.sh

本文件的作用是初始化编译环境，并引入一些辅助命令。

- 定义hmm, 加载编译时使用的命令。

- 定义 printconfig, 显示当前构建的配置信息。

- 定义 add_lunch_combo, 用来给lunch命令添加编译选项。

  本函数的处理过程是，遍历数组LUNCH_MENU_CHOICES，判断要添加的编译选项是否在数组里，如在数组里则直接返回，如不在数组里则将其添加到数组里。


- 添加了6条默认的选项：aosp_*。
- 定义print_lunch_menu，用于列出所有的编译选项。
- 定义lunch，用于让用户指定编译选项。
- 定义_lunch，用于为lunch命令提供参数自动补齐的功能。
- 这是一条complete命令，指定由_lunch实现lunch命令里参数自动补齐的功能。
- 659~668, m, 在源码树的根目录执行make
- 687~723, mm, 构建当前目录下的模块
- 725～770， mmm, 构建指定目录下的模块
- 823～831，croot，切换到源码树的根的目录
- 1329~1332, jgrep, 在所有java文件上执行grep
- 1334~1337, cgrep, 在所有c/c++文件上执行grep
- 1339~1342, resgrep, 在所有res/*.xml文件上执行grep
- 1590~1632, godir, 转到包含某个文件的目录路径
- 在文件的末尾，查找vendorsetup.sh脚本并加载它。openthos的编译选项就是在这里被加载的。



### lunch

lunch命令定义在build/envsetup.sh里，用来让用户选择编译设备与编译类型。其执行步骤如下：

1. 定义变量answer(用以代表用户的选择)，如果命令里给了参数则直接使用给定的参数，否则列出所有的编译选项让用户选项。

2. 定义变量selection(用以代表变量answer所对应的字符串)，如果用户没选择则使用默认选项aosp_arm-eng，如果用户输入的是数字则使用该数字对应的字符串，如果用户输入的是字符串则直接使用该字符串。

3. 定义变量product，将selection里"-"的前面部分放在变量product里，如编译openthos则其值为"openthos"。

4. 定义变量variant，将selection里两个"-"之间的部分放在变量variant里，如编译openthos则其值为"x86_64"。

5. 定义变量version，将seleciton里"-"之后的部分放在version里，如编译openthos则其值可能是"user"、"userdebug"或"eng"。eng适用于开发阶段，userdebug适用于调试阶段，user适用于最终发布阶段。

6. 定义如下3个环境变量

   ```
   export TARGET_PRODUCT=$product
   export TARGET_BUILD_VARIANT=$variant
   export TARGET_BUILD_TYPE=release
   ```

7. set_stuff_for_environment函数，设置一些环境变量。

8. printconfig函数，打印一些主要的变量。

### make

- make命令定义在build/envsetup.sh里。

  ```
  function make()
  {
      _wrap_build $(get_make_command) "$@"
  }
  ```

- 依据`get_make_command`函数的定义，$(get_make_command)的值是"build/soong/soong_ui.bash --make-mode"。

- 在`_wrap_build`函数可见，第二行"$@"已经执行完所有的编译过程了，其它部分只不过是输出一些统计信息而已。

- build/soong/soong_ui.bash。首先执行build/soong/cmd/microfactory/microfactory.bash，定义了两个工具函数，`getoutdir`用来查找out目录，`build_go`用来编译所需的二进制(参数1是所要编译的二进制的名字，参数2是包名)；然后用build_go来编译soong_ui，最后执行的是`out/soong_ui $@`完成构建的过程。

- 函数build_go的执行逻辑：如果$mf_bin存在且$mf_version的值等于$mf_version_file里保存的值，说明$mf_bin存在且是最新的，此时它就是$mf_cmd；否则说明$mf_bin不存在或它的版本不是最新的，这时就需要构建新的$mf_cmd，它的值为`go run $mf_src/microfactory.go`。`$mf_cmd ...`那行是真正干活的，其中-s用来指示microfactory的源码目录(用于必要时重构microfactory)，-b用来指示microfactory二进制的位置，-pkg-path是从包前缀到文件路径的映射，-trimpath用于从记录的源文件路径中删除前缀，-o用于指定输出文件的名称。

- 程序microfactory源码在build/soong/cmd/microfactory/microfactory.go，首先main函数的`mainPackage.Compile`真正执行编译的过程，而`Compile`函数则调用的prebuild/go/linux-x86/pkg/tool/linux_amd64/compile命令执行编译。

- soong_ui代码在`build/soong/cmd/soong_ui/main.go`，真正干活的是main函数里的最后一句：

```
build.Build(buildCtx, config, build.BuildAll)
```

- build.Build在`ui/build/build.go`，从中可以看到执行soong的流程：
  1. `runMakeProductConfig`在ui/build/make.go，用来配置编译参数。在这里可以看到[第一段输出](# 第一段输出)
  2. `runSoongBootstrap`和`runSoong`在ui/build/soong.go，分别运行的是`./bootstrap.bash`和`soong`两个命令。在这里可以看到[第二段输出](# 第二段输出)和[第三段输出](# 第三段输出)
  3. `./bootstrap.bash`的最后一句会执行`build/blueprint/bootstrap.bash $@`，如不带参数则生成minibp，如带参数则生成一个基于Blueprint的构建系统。
  4. `runKati`在ui/build/kati.go，运行的是ckati命令。把所有的Android.mk文件生成ninja文件。
  5. `createCombinedBuildNinjaFile`在ui/build/build.go，在out/combined-openthos_x86_64.ninja文件里用include关键字把前两步生成的ninja文件包含进来。
  6. `runNinja`在ui/build/ninja.go，运行ninja命令执行构建过程，所使用的配置文件就是第四步生成的combined-openthos_x86_64.ninja。在这里看到[第四段输出](# 第四段输出)

#### 构建的过程

构建的起点在`build/core/main.mk`，可以看到整个文件的结构如下：

```
ifndef KATI
  ...
else	# KATI
  ...
endif	# KATI
```

由于没有定义KATI，所以走的是ifndef流程。MAKECMDGOALS是make执行时后面的参数，这意味着不管执行什么目标最终都执行的都是run_soong_ui这个目标。这样编译的控制权就交到了soong手上，后面的事就跟make没有关系了。

#### 第一段输出

输出的是关于编译环境的若干变量。

#### 第二段输出

在`build/soong/build.ninja.in`，编译生成`out/soong/.bootstrap`下的文件。源文件在build/blueprint。

#### 第三段输出

搜集所有的`Android.bp`文件生成build.ninja。

#### 第四段输出

详细的编译过程

##### 第四段之生成oto_img

在bootable/newinstaller/Android.mk

```
# 1. 从175行可见，OTO_IMAGE即oto_img
	oto_img: $(OTO_IMAGE)
# 2. OTO_IMAGE详见143~158行
	# OTO_IMAGE:	openthos_x86_64_oto.img
	# OTO_INITRD_RAMDISK:oto_initrd.img
	# OTO_BILT_IMG:	ramdisk.img install.img system.sfs kernel
	# DATA_IMG: 	data.img
	143：$LOCAL_PaTH是/bootable/newinstaller
	144:把$LOCAL_PATH/install/refind/efi目录压缩为$REFIND。$PRODUCT是out/target/product/openthos/，$REFIND是$PRODUCT/efi.tar.bz2
	145:把$PRODUCT_OUT/oto_initrd.img复制到$PRODUCT_OUT/initrd.img。$PRODUCT_OUT是out/target/product/openthos/
	146~149:统计依赖文件的大小
	150:把$REFIND的大小加到size上，且再增加8k大小
	151:把size调大了一点。size=size+62+(size/100)
	152:创建($OTO_IMAGE).fat，即openthos_x86_64_oto.img.fat，然后用mkdosfs命令为其创建文件系统，大小为size。
	153:把$LOCAL_PATH/install/refind/efi目录复制到openthos_x86_64_oto.img.fat镜像的顶层目录
	154:在openthos_x86_64_oto.img.fat里创建OpenThos目录
	155:把$LOCAL_PATH/install/refind/boto_linux.conf里的"BOOT_MODE="替换为$BOARD_KERNEL_CMDLINE，将输出重定向到out/target/product/openthos/boto_linux.conf
	156:把$PRODUcT_OUT目录下的ramdisk.img install.img system.sfs initrd.img kernel efi.tar.bz2 boto_linux.conf复制到镜像openthos_x86_64_oto.img.fat的OpenThos目录下
	157:创建openthos_x86_64_oto.img，然后使用edit_mbr命令，以bootable/newinstaller/editdisklbl/esp_layout.conf为layout配置文件，以openthos_x86_64_oto.img为要编辑的镜像,以openthos_x86_64_oto.img.fat为配置文件里的esp，创建启动镜像
	158:删除openthos_x86_64_oto.img.fat和initrd.img
# 3. oto_initrd.img的生成详见76~83行
# 4. install.img的生成详见86~91行
```

ramdisk.img是在build/core/Makefile里生成的

```
580~596行
```

system.sfs

kernel是对源码目录kernel的编译，编译规则可能在build/core/Makefile或kernel/Makefile。

### 对镜像的分析

#### 挂载openthos_x86_64.img

```
sudo mount -o loop,offset=1048576 openthos_x86_64.img /mnt
	# offset是使用fdisk -l命令计算出来的：起始扇区*每个扇区的大小
```

#### 解析initrd.img

通过对比可见initrd.img的内容来自于out/target/product/openthos/installer

```
file initrd.img	# 发现它是gzip格式
cp initrd.img ~/initrd.img.gz && cd ~
gunzip initrd.img.gz	# 生成initrd.img
file initrd.img	# 发现它是cpio格式
mkdir initrd && cd initrd
cpio -i -F ../initrd.img	# 把initrd.img的内容复制到initrd目录
```

#### 解析install.img

解压方法同initrd.img，通过对比可见其内容主要来自bootable/newinstaller/install

#### 解析ramdisk.img

解压方法同initrd.img，通过对比可见其内容主要来自out/target/product/openthos/root

#### 解析system.sfs

解压出system.img，然后挂载。通过对比可见其内容主要来自out/target/product/openthos/system

```
unsquashfs system.sfs
```



### 编译错误记录

>   DEPMOD  4.9.109-android-x86_64-01316-g45b3cc90-dirty
>
> /root/szx/kernel/Makefile:1350: recipe for target '_modinst_post' failed
> make[1]: *** [_modinst_post] Error 255
> make[1]: Leaving directory '/root/szx/out/target/product/openthos/obj/kernel'
> Makefile:152: recipe for target 'sub-make' failed
> make: *** [sub-make] Error 2
> make: Leaving directory '/root/szx/kernel'
> [ 99% 99776/99781] build out/target/product/openthos/install.img
> ninja: build stopped: subcommand failed.
> 02:44:29 ninja failed with: exit status 1

问题分析：我的情况是少了kmod，把它安装上即可。`sudo apt install kmod`。另，还应检查`syslinux  genisoimage   gettext   bc  dosfstools   mtools   kmod`是否都安装齐全。另：从这里是看不出来问题在哪的，要从往前翻，找到真正错误的地方。

### 参考资料

[理解安卓build系统](https://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/)

[Android.bp及其工具链](http://note.qidong.name/2017/08/android-blueprint/)

[Android Soong build系统介绍](https://www.jianshu.com/p/80013a768a45)