美文网首页Shell和命令
Makefile生成目录+多文件批量编译

Makefile生成目录+多文件批量编译

作者: 哈莉_奎茵 | 来源:发表于2018-03-08 17:48 被阅读21次

示例项目结构

~/project$ tree
.
├── include
│   └── foo.h
├── lib
│   └── foo.c
├── Makefile
└── src
    ├── Makefile
    ├── test1.cc
    ├── test2.cc
    └── test3.cc

3 directories, 7 files

lib/foo.c是C编写的库文件,为foo()函数的具体实现,include/foo.h是对应的头文件,包含foo()函数的声明,src目录下的3个文件则是main()函数中调用foo()函数的源文件。

需求及相应gcc编译命令

现在需求是,把foo.c生成的.o文件放到目录obj下(若不存在则新建),然后分别对src目录下的文件进行编译生成各自的可执行文件,把生成的可执行文件放到目录bin下(若不存在则新建)。

~/project$ mkdir -p build
~/project$ gcc -c lib/foo.c -o ./build/foo.o
~/project$ mkdir -p bin
~/project$ g++ -Wall -O2 -I./include src/test1.cc obj/foo.o -o ./bin/test1
~/project$ g++ -Wall -O2 -I./include src/test2.cc obj/foo.o -o ./bin/test2
~/project$ g++ -Wall -O2 -I./include src/test3.cc obj/foo.o -o ./bin/test3
~/project$ tree
.
├── bin
│   ├── test1
│   ├── test2
│   └── test3
├── include
│   └── foo.h
├── lib
│   └── foo.c
├── Makefile
├── obj
│   └── foo.o
└── src
    ├── Makefile
    ├── test1.cc
    ├── test2.cc
    └── test3.cc

6 directories, 12 files

mkdir命令的-p选项则是在目录已经不存在时不报错,把该目录当成已经创建的目录。
可以发现,编译testx.cc(x=1,2,3)的命令基本一致,都要和foo.o一起链接,都要把include目录作为包含目录。因此这些命令重复度较大,可以利用Makefile的进行简化。

Makefile的编写

特殊变量

  • $@ 目标文件
  • $^ 所有的依赖文件
  • $< 第一个依赖文件

也就是说对于下面的Makefile命令

foo.o: foo.c foo.h
    gcc -c foo.c -o foo.o

可以简化为

foo.o: foo.c foo.h
    gcc -c $< -o $@

这几个特殊变量即Makefile批量编译的基础支持,可以把这样的语句理解成类似C函数的概念,那么第二行就是函数体内容,$<代指第1个输入参数,$@代指输出参数。现在的问题是如何取得需要调用的列表。

函数

具体用法可以参考陈皓先生的跟我一起写Makefile
利用函数可以取得源文件列表和目标文件列表

# ~project/Makefile
SOURCE = $(wildcard src/*.cc)
PROGS = $(patsubst %.cc, bin/%, $(notdir $(SOURCE)))
  1. wildcard函数表示可以解释输入参数src/*.cc的通配符,就像ls命令一样。因此在上面的Makefile中,SOURCEsrc目录下的.cc文件列表src/test1.cc src/test2.cc src/test3.cc
  2. notdir函数表示找出输入参数中的非目录部分,比如对src/test1.cc会返回test1.cc,因此$(notdir $(SOURCE))test1.cc test2.cc test3.cc
  3. patsubst即字符串处理,参数3是批量处理的列表,参数1是模式,参数2是替换字符串。如果列表中的元素满足模式,则用参数2来替换。这里的%类似通配符,比如test1.cc满足模式%.cc,那么替换字符串的%则表示test1

也就是说,上述2句Makefile代码在我这个项目结构中等价于

SOURCE = src/test1.cc src/test2.cc src/test3.cc
PROGS = bin/test1 bin/test2 bin/test3

由于是动态生成的,假如src目录下多了文件test4.cc,上述变量会分别增加src/test4.ccsrc/test4

生成目录

参考GNU make下创建目录的问题
之前作者的做法类似于跟我一起学Makefile时的做法(.PHONY文件),把目录作为依赖项,然后目录下新建一个隐藏文件来作为目录的依赖项。因为在目录下创建新文件会导致目录的时间属性被更新,所以要用目录下的隐藏文件的时间属性来代替目录的时间属性。
在Make 3.80后的版本中,可以用order-only的依赖来解决。比如
../obj/foo.o: foo.c | ../obj
注意依赖参数../obj前面加了个|来表示该目录是order-only类型,因此在该目录下新建文件不会导致foo.o被重新生成。

最终Makefile文件

# src/Makefile 
CC = g++
FLAGS = -Wall
BIN = ../bin
LIB = ../lib
SRC = ../src
INC = ../include
OBJ = ../obj

FOO_LIB = $(OBJ)/foo.o

SOURCE = $(wildcard $(SRC)/*.cc)
PROGS = $(patsubst %.cc, $(BIN)/%, $(notdir $(SOURCE)))

all: $(PROGS) $(FOO_LIB)

$(BIN)/%: $(SRC)/%.cc $(FOO_LIB) $(INC)/foo.h | $(BIN)
    $(CC) $(FLAGS) $< $(FOO_LIB) -o $@ -I$(INC)

$(BIN):
    mkdir -p $@

$(FOO_LIB): $(LIB)/foo.c | $(OBJ)
    gcc -c $< -o $@

$(OBJ):
    mkdir -p $(OBJ)

clean:
    rm -f $(FOO_LIB)
    rm -f $(PROGS)
# Makefile 
DIRS = src
MAKE = make

all:
    for i in $(DIRS); do \
        (cd $$i && echo "making $$i" && $(MAKE)) || exit 1; \
    done

clean:
    for i in $(DIRS); do \
        (cd $$i && echo "cleaning $$i" && $(MAKE) clean) || exit 1; \
    done

分别是子目录和父目录的Makefile,这种做法是学习apue.3e的做法,即支持在子目录下单独make,也支持在根目录下批量调用子目录下的make。需要注意的一点是使用shell语法的for循环时,Makefile中要用$$i而非$i

相关文章

  • Makefile生成目录+多文件批量编译

    示例项目结构 lib/foo.c是C编写的库文件,为foo()函数的具体实现,include/foo.h是对应的头...

  • cmake

    CMake 什么是CMake 构建工具,Cmake 读取CMakeLists.txt 生成 makefile编译多...

  • 一、编译LAME

    编译脚本: 编译成功后: 这个armv7a是我新建的目录我们使用configure的方式来生成makefile文件...

  • CMKAE总结

    CMKAE总结 cmake:生成一个makefile文件。make:根据这个makefile文件的内容编译整个工程...

  • Android libpng-1.6.37交叉编译

    生成soname取消版本信息 编译脚本 应为libpng的编译是由makefile先生成libtool工具所需要l...

  • Mac 下 makefile编译使用

    依据makeFile文,编译包使用make命令,makefile文件所在目录下 文章:https://www.cn...

  • makefile编写(一)

    makefile或者Makefile 书写规则:命令就是用依赖来生成目标 目标:依赖 (tab) 命令 目录结构:...

  • grpc示例

    准备工作 目录结构 协议 服务器端 服务器端Makefile 客户端 客户端Makefile Makefile 编译运行

  • Makefile 的一些使用

    今天为了写编译原理的词法分析器,特地写了一下Makefile来简化编译过程,本来打算在子目录放置 Makefile...

  • 在macOS上通过openssl源码生成国密SM2密钥对

    生成流程 下载openssl源码: 解压源码包: 进入解压出的openssl目录: 配置生成makefile: 编...

网友评论

    本文标题:Makefile生成目录+多文件批量编译

    本文链接:https://www.haomeiwen.com/subject/ilpyfftx.html