一个简单的Makefile编译框架
最近在做一个项目时对makefile有了一个简单的认识,于是自己写了一个简单的编译框架,留待后用。下边是一个demo示例,附上所有的源文件,移植工程时搬过来应该就可以用了。一、demo的文件目录树
- ├── app
- │ ├── api
- │ │ ├── api.c
- │ │ └── Makefile
- │ ├── include
- │ │ └── app.h
- │ ├── Makefile
- │ └── src
- │ ├── app.c
- │ └── Makefile
- ├── main
- │ ├── include
- │ │ └── main.h
- │ ├── Makefile
- │ └── src
- │ ├── main.c
- │ └── Makefile
- ├── Makefile
- ├── make.rules
- │ ├── config.in
- │ ├── make.obj
- │ ├── make.src
- │ └── rules.mk
- └── sdk
- ├── Makefile
- ├── sdk1
- │ ├── include
- │ │ └── sdk1.h
- │ ├── Makefile
- │ └── src
- │ ├── Makefile
- │ ├── sdk1_1.cpp
- │ └── sdk1_2.c
- └── sdk2
- ├── Makefile
- ├── sdk2_1
- │ ├── include
- │ │ └── sdk2_1.h
- │ ├── Makefile
- │ └── src
- │ ├── Makefile
- │ └── sdk2_1.c
- └── sdk2_2
- ├── include
- │ └── sdk2_2.h
- ├── Makefile
- └── src
- ├── Makefile
- └── sdk2_2.c
二、使用说明
1.编译帮助,在主目录中输入make help,可以得到编译的帮助信息。
2.user_debug目标是留给用户打印变量使用的。只要在该目标下调用$(call PRINT,变量),就能在执行makefile时打印出指定的变量的值。编译时需要指明打印等级,0-打印所有变量,1-打印内部变量,2-打印用户定义的变量。只打印用户变量使用make all DEBUG_LEVEL=2。
3.user_clean目标是留给用户做清理工作的,在make clean之前会调用到。
4.user目标是留给用户编译时使用的。比如可以在编译完成之后自动清理掉生成的不需要的.o文件,可以直接在user后边加上一个依赖clean。
5.主目录中编译整个工程直接使用make all,只清理.o和可执行文件使用make clean,清理掉所有生成的文件使用make distclean。也可以指定单个目录进行编译,比如make –C app就会只编译app目录,make –C app clean就会单独清理掉app目录。
6.非主目录编译需要先在主目录中成功make all一次,只有生成了配置文件rules.conf才能找到编译时需要依赖的规则文件。所以要在非主目录中编译,不能使用make distclean命令。在非主目录中编译直接使用make 就可以了,清理使用make clean,打印变量使用make DEBUG_LEVEL=2。
三、编译框架说明
1. 整个编译过程用到了三种makefile文件。
(1)第一种是将各个子目录中的.o文件链接成一个.o目标文件。如下,./sdk/Makefile文件。
- # 需要编译的子目录
- obj-y = sdk1
- obj-y += sdk2
- # 编译所需参数
- # EXTRA_LDFLAGS:链接时需要的参数
- EXTRA_LDFLAGS =
- # 加载make.obj文件
- ifdef MAKE_RULES
- include $(MAKE_RULES)/make.obj
- else
- include ./rules.conf
- ifdef MAKE_RULES
- include $(MAKE_RULES)/make.obj
- endif
- endif
- # 用户编译配置处
- # clean: 清除掉源文件生成的目标文件
- # distclean: 清除当前和子目录所有的目标文件
- # 编译时在这里可以做其它事情
- user:
- # 用户清理
- user_clean:
- # 打印变量
- # $(call PRINT,变量):调用该函数可以打印编译时变量的值,该函数定义在rules.mk文件中
- # DEBUG_LEVEL = 2时,才会有打印
- user_debug:
- $(call PRINT,MAKE_OBJ)
这个makefile几乎不用修改任何地方,除了必须指定需要编译的子目录obj-y。其中的所有链接工作是在./make.rules/make.obj文件中完成的。
(2)第二种是将子目录中的源文件编译成.o文件,然后将所有.o文件连接成一个目标.o文件。如下,./sdk/sdk1/Makefile文件。
- # 目标文件夹
- src-y = src
- # 目标文件类型
- source_type = .cpp .c
- # 编译所需参数
- # EXTRA_CFLAGS:c编译参数
- # EXTRA_CXXFLAGS:cpp编译参数
- # EXTRA_LDFLAGS:链接成目标.o时的参数
- EXTRA_CFLAGS += -I$(CURR_DIR)/include
- EXTRA_CXXFLAGS += -I$(CURR_DIR)/include
- EXTRA_LDFLAGS =
- # 加载make.src文件
- ifdef MAKE_RULES
- include $(MAKE_RULES)/make.src
- else
- include ./rules.conf
- ifdef MAKE_RULES
- include $(MAKE_RULES)/make.src
- endif
- endif
- # 用户编译配置处
- # clean: 清除掉源文件生成的目标文件
- # distclean: 清除当前和子目录所有的目标文件
- user:
- # 用户清理
- user_clean:
- # 打印变量
- # $(call PRINT,变量):调用该函数可以打印编译时变量的值,该函数定义在rules.mk文件中
- # DEBUG_LEVEL = 2时,才会有打印
- user_debug:
- $(call PRINT,CURR_DIR)
这个文件需要指定源文件目录src-y和需要编译的源文件类型source_type,还有编译的头文件目录。其主要编译规则在./make.rules/make.src中。
(3)第三种makefile是在源文件中,主要是用来进行清理工作的,比如./sdk/sdk1/src/Makefile,如下
- clean:
- rm -rf *.[od]
- distclean: clean
- .PHONY: clean distclean
2. 关于./make.rules目录中的规则或配置文件。
config.in:提供编译时需要的全局变量,比如make.rules的位置,环境变量,编译工具链,一些通用的编译参数。
rules.mk: 提供具体的编译规则,打印函数,打印等级,打印颜色。
make.obj: 将多个.o文件连接成一个.o文件。
make.src: 将源文件编译成.o文件,并链接成一个.o文件。
四、一些实现细节
1. 打印函数
- PRINT = @echo -e "$(BABYBLUECOLOR)$(1): $($(1)) $(ENDCOLOR)\n";
- $(call PRINT,变量)
使用call会将PRINT视为一个函数调用,而后边的变量则视为函数的