Makefile-例程讲解

我们已经在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虽然能够遍历所有目录,并最终生成可执行代码。但是看编译过程还是有问题,先记录下,再改进吧!

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值