我们已经在Makefile语法和Makefile常用函数这两篇文章中讲解了makefile编写的一些基本语法和规则,下面就让我们实战一下吧。
今天我们主要讲一下makefile文件递归式写法。
我们先看一下我们将要用到的源码:
1.主文件test/main.c:
//path: ..src/main.c
#include <stdio.h>
#include "add.h"
#include "sub.h"
int main()
{
int a=10,b=12;
float x=1.23456,y=9.87654;
printf("int a+b IS:%d\n",add_int(a,b));
printf("int a-b IS:%d\n",sub_int(a,b));
printf("float x+y IS:%f\n",add_float(x,y));
printf("float x-y IS:%f\n",sub_float(x,y));
return 0;
}
2.减法的头文件
//path: ../inc/sub/sub.h
#ifndef _SUB_H_
#define _SUB_H_
extern float sub_float(float a, float b);
extern int sub_int(int a, int b);
#endif
3.浮点数减法的实现
//path: ../src/sub/sub_float.c
float sub_float(float a, float b)
{
return a-b;
}
4.实现int型的减法函数定义文件:
//path: ../src/sub/sub_int.c
int sub_int(int a, int b)
{
return a-b;
}
5.加法声明的头文件:
//path: ../inc/add/add.h
#ifndef _ADD_H_
#define _ADD_H_
extern int add_int(int a, int b);
extern float add_float(float a, float b);
#endif
6.浮点数加法的实现
//path: ../src/add/add_float.c
float add_float(float a, float b)
{
return a+b;
}
7.整数加法的实现
//path: ../src/add/add_int.c
int add_int(int a, int b)
{
return a+b;
}
我们的目录结构如下:
.
├── inc
│ ├── add
│ │ └── add.h
│ └── sub
│ └── sub.h
├── Makefile
└── src
├── add
│ ├── add_float.c
│ ├── add_int.c
│ └── Makefile
├── main.c
├── Makefile
└── sub
├── Makefile
├── sub_float.c
└── sub_int.c
我们可以看到顶层目录有两个文件夹inc和src,分别用来包含头文件和源文件;还有一个顶层Makefile。inc目录就不细说了。src子目录下有2个深层子目录用来分别存储不同模块的源代码,还有一个main.c和Makefile文件:main.c实现该工程的入口以及主要功能;Makefile是用来管理src/目录下编译的文件。两个子目录./sub
和./add分别实现加法和减法,也有自己独立的Makefile。
接下来看看怎么写Makefile文件。
递归式
为了实现每个目录下的代码具有独立的Makefile,且根目录下的Makefile可以一次执行子目录下的Makefile,就有了递归式的方法来写父目录下的Makefile了,该方法的实现是使用的$(MAKE)变量,使用格式如下:
dd:
cd add && $(MAKE)
也可以这样写:
add:
$(MAKE) -C add
这样就可以递归调用子目录add下面make命令来执行add目录下的Makefile文件,我们先把add和sub两个子目录下的makefile文件写好来,如下:
./add/Makefile
OBJS = add_int.o add_float.o
CFLAGS = -O2
all:$(OBJS)
$(OBJS):%.o:%.c
$(CC) -c $< -o $(OBJS_DIR)/$@ $(CFLAGS)
OBJS = sub_int.o sub_float.o
CFLAGS = -O2
all:$(OBJS)
$(OBJS):%.o:%.c
$(CC) -c $< -o $(OBJS_DIR)/$@ $(CFLAGS)
./src/Makefile内容:
# 取得当前子目录深度为1的所有目录名称
SRC_DIRS := $(shell find . -maxdepth 1 -type d)
SRC_DIRS := $(basename $(patsubst ./%,%,$(SRC_DIRS)))
SRC_DIRS := $(filter-out $(EXCLUDE_DIRS),$(SRC_DIRS))
.PHONY: $(SRC_DIRS)
# 子目录执行默认make target
$(SRC_DIRS): $(SRC_DIRS) main.o
$(MAKE) -C $@
main.o: %.o:%.c
$(CC) -c $< -o $(OBJS_DIR)/$@ $(CFLAGS) $(INCS_DIRS)
接下来就是写主目录makefile_common_project下面的Makefile文件了,使用前面递归调用格式,Makefile文件如下:
CC = gcc
CFLAGS = -O2
CXXFLAGS =
TARGET = outfile
BIN_DIRS := bin
INCS_DIRS := inc
# 需要排除的目录
EXCLUDE_DIRS := inc bin
# INCLUDE DIRECTORY
INCS_DIRS := $(shell find $(shell pwd)/$(INCS_DIRS) -maxdepth 1 -type d)
INCS_DIRS := $(basename $(patsubst ./%,%,$(INCS_DIRS)))
INCS_DIRS := $(foreach inc_tmp,$(INCS_DIRS),-I $(inc_tmp))
export INCS_DIRS
# 取得当前子目录深度为1的所有目录名称
ALL_DIRS := $(shell find . -maxdepth 1 -type d)
ALL_DIRS := $(basename $(patsubst ./%,%,$(ALL_DIRS)))
ALL_DIRS := $(filter-out $(EXCLUDE_DIRS),$(ALL_DIRS))
# 避免clean子目录操作同名,加上 _clean_ 前缀
SUB_DIRS := $(ALL_DIRS)
CLEAN_DIRS := $(addprefix _clean_,$(SUB_DIRS))
export OBJS_DIR = ${shell pwd}/.objs
.PHONY: $(TARGET) $(SUB_DIRS) clean
# 执行默认make target
$(SUB_DIRS):
mkdir -p $(OBJS_DIR)
$(MAKE) -C $@
$(CC) -o $(TARGET) $(OBJS_DIR)/*.o
$(TARGET): $(SUB_DIRS)
# 执行clean
#$(CLEAN_DIRS):
# $(MAKE) -C $(patsubst _clean_%,%,$@) clean
# @,为不回显的命令
#clean: $(CLEAN_DIRS)
# find ./
# /( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' /
# -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' /
# -o -name '*.symtypes' /) /
# -type f -print | xargs rm -f
#clean: $(CLEAN_DIRS)
clean:
-$(RM) -r $(OBJS_DIR) $(OBJS_DIR)/*.o
“
(MAKE)−C
@” 这句实现的就是在执行下面的命令前先遍历执行子目录下的makefile文件。
递归式的测试结果:
aaron@aaron-Inspiron-5520:~/tmp/makefile_test/makefile_common_project$ make
mkdir -p /home/aaron/tmp/makefile_test/makefile_common_project/.objs
make -C src
make[1]: 正在进入目录 `/home/aaron/tmp/makefile_test/makefile_common_project/src'
make[1]: 放弃循环依赖 sub <- sub 。
make[1]: 放弃循环依赖 add <- sub 。
make[1]: 放弃循环依赖 add <- add 。
cc -c main.c -o /home/aaron/tmp/makefile_test/makefile_common_project/.objs/main.o -I /home/aaron/tmp/makefile_test/makefile_common_project/inc -I /home/aaron/tmp/makefile_test/makefile_common_project/inc/sub -I /home/aaron/tmp/makefile_test/makefile_common_project/inc/add
make -C add
make[2]: 正在进入目录 `/home/aaron/tmp/makefile_test/makefile_common_project/src/add'
cc -c add_int.c -o /home/aaron/tmp/makefile_test/makefile_common_project/.objs/add_int.o
cc -c add_float.c -o /home/aaron/tmp/makefile_test/makefile_common_project/.objs/add_float.o
make[2]:正在离开目录 `/home/aaron/tmp/makefile_test/makefile_common_project/src/add'
make -C sub
make[2]: 正在进入目录 `/home/aaron/tmp/makefile_test/makefile_common_project/src/sub'
cc -c sub_int.c -o /home/aaron/tmp/makefile_test/makefile_common_project/.objs/sub_int.o
cc -c sub_float.c -o /home/aaron/tmp/makefile_test/makefile_common_project/.objs/sub_float.o
make[2]:正在离开目录 `/home/aaron/tmp/makefile_test/makefile_common_project/src/sub'
make[1]:正在离开目录 `/home/aaron/tmp/makefile_test/makefile_common_project/src'
gcc -o outfile /home/aaron/tmp/makefile_test/makefile_common_project/.objs/*.o
总结:
本人也是初学Makefile的编写,上面的Makefile虽然能够遍历所有目录,并最终生成可执行代码。但是看编译过程还是有问题,先记录下,再改进吧!