# 构建单元说明

## 定义

* 片段文件: 用来指导某个具体的文件夹下源码应如何获取和编译以及链接的makefile文本片段, 基本只包含变量的赋值
* 默认的片段文件是名为`iot.mk`的文本文件, 可以在工程顶层目录`project.mk`里, 用`MAKE_SEGMENT := <new>`更改片段文件名
* 从工程顶层目录以下, 每一个含有`iot.mk`的子目录, 都被构建系统认为是一个构建单元

> 举例来说:

> 假设在/path/to/project/project.mk文件中有
> 
>     MAKE_SEGEMENT := build.mk
 
> 那么, 如下的目录排布
> 
>     /path/to/project/bar/build.mk
>     /path/to/project/foo/sub1/build.mk
>     /path/to/project/foo/sub2/build.mk
>     /path/to/project/mmm/nnn/ppp/qqq/build.mk
 
> 会导致
> 
>     bar
>     foo/sub1
>     foo/sub2
>     mmm/nnn/ppp/qqq
 
> 都被认为是构建单元, 可以用类似`make bar`, `make foo/bar1`这样的命令单独编译, 也可以调试完成后通过修改顶层`makefile`集成到工程中

## 工作过程

对整个工程进行构建的过程基本是遍历每个构建单元逐个构建, 而对每个构建单元的构建过程是:

* 构造编译的临时目录, 例如 `.O/bar` 目录, 对应源码 `bar` 构建单元
* 在编译的临时目录中动态生成或更新`makefile`和源代码
* 切换到临时目录中, 编译源代码, 产生目标代码

## 开发过程

增加一个新的构建单元可以按如下步骤进行:

* 创建新单元对应的目录, 比如

        mkdir foo/bar

* 创建该单元的片段文件, 比如

        vi foo/bar/iot.mk
        1 LIBA_TARGET := libfoobar.a

* 为该单元添加一些源码文件, 比如

        cp .../*.c foo/bar/

* 这时, 在工程文件中运行`make reconfig`, 告诉构建系统新增了一个单元, 这个单元就可以被独立编译了

        make reconfig
        make foo/bar

* 单独的编译和运行没问题之后, 修改工程的`makefile`, 使这个新单元也加入到工程中

        vi makefile
        ...
        ...
        SUBDIRS += foo/bar
        ...
        ...

## 编写片段文件

**简化编译的设计思想就是使每一个模块的开发者, 基本只需要编写片段文件就能构建自己的功能模块**

**而片段文件之所以能够起到简化的效果, 是因为它基本上只是一些"变量赋值"语句的集合, 编写它, 几乎可以不需要学习任何语言的任何语法**

---
以下是在片段文件中可以使用的变量名:

### 输出相关

| 变量名            | 用处                                                                                          |
|-------------------|-----------------------------------------------------------------------------------------------|
| ORIGIN            | 如果你的模块希望按自己独特的方式编译, 比如`./configure ...; make; ...`, 需要设置此变量为1 |
| LIBSO_TARGET      | 设置本单元被编译出的`Linux`动态库文件名, 对它赋值导致系统帮助你产生`*.so`共享库文件 | 
| LIBA_TARGET       | 设置本单元被编译出的静态库文件名, 对它赋值导致系统帮助你产生`*.a`静态库文件 |
| TARGET            | 设置本单元被编译出的可执行程序文件名, 对它赋值导致系统帮助你产生`Linux`下的可执行程序 |
| KMOD_TARGET       | 设置本单元被编译出的内核模块文件名, 对它赋值导致系统帮助你产生`Linux`下的`*.ko`内核模块 |

**以上变量至少要有一个被设置, 这样构建系统才知道构建的输出是什么, 至多则可以同时被设置, 例如同时产生静态库文件和可执行程序, 甚至内核模块等**

### 输入相关

| 变量名            | 用处                                                                                          |
|-------------------|-----------------------------------------------------------------------------------------------|
| LIB_SRCS          | 可选, 列出哪些`*.c`文件被编译成`*.a`或者`*.so` |
| SRCS              | 可选, 列出哪些`*.c`文件被编译成可执行程序 |
| EXTRA_SRCS        | 可选, 列出哪些源文件需要在开始编译之前被复制到临时的编译目录 |
| CFLAGS            | 可选, 列出需要特别应用在本模块上的编译选项 |
| LDFLAGS           | 可选, 列出需要特别应用在本模块上的链接选项 |

### 组合相关

| 变量名            | 用处                                                                                          |
|-------------------|-----------------------------------------------------------------------------------------------|
| HDR_REFS          | 可选, 如果你的模块引用了其它模块的头文件, 那么需要设置这个变量, 列出被引用模块在工程里的相对路径 |
| DEPENDS           | 可选, 如果你的模块需要在其它模块构建完成后才能编译, 那么需要设置这个变量, 列出被依赖模块在工程里的相对路径 |

### 高级用法

| 变量名            | 用处                                                                                          |
|-------------------|-----------------------------------------------------------------------------------------------|
| PKG_SOURCE        | 可选, 表示源码来自`*.c`之外的形式, 例如压缩包文件, 或者独立的git仓库等 |
| PKG_BRANCH        | 可选, 仅在`PKG_SOURCE`是git仓库的时候才有意义, 指定使用该仓库的哪个分支作为源码输入 |
| PKG_REVISION      | 可选, 仅在`PKG_SOURCE`是git仓库的时候才有意义, 指定使用该仓库的哪个分支上的哪个`commit`或者`tag` |
| PKG_UPSTREAM      | 可选, 仅在`PKG_SOURCE`是git仓库的时候才有意义, 指定使用哪个云端的git仓库地址来更新本地git仓库 |


### 其它

#### 产生多个可执行程序

* 如果你的模块希望产生单个的可执行程序, 那么对变量`TARGET`和`SRCS`赋值就可以了, 但如果希望产生多个可执行程序, 比如

        TARGET = prog1 prog2 prog3

* 那么需要用`SRCS_<name>`变量对这多个可执行程序分别设定他们的源文件, 比如

        SRCS_prog1 = prog1.c
        SRCS_prog2 = prog2.c library_A.c
        SRCS_prog3 = prog3.c library_A.c library_B.c
