linux 下 make嵌套编译,makefile 多级目录 嵌套调用 实例

最近要搞 linux 下 c++ 开发,花了几天时间学习了一下 makefile. 总结了一点自己平时会用的比较多的东西。首先是多目录下嵌套调用 makefile. 我觉得这个很重要。很多时候自己写测试代码或者做一些预研的时候都要用到。

我这个演示例子采用了一个平时我用的比较多的目录结构,相信大家应该也常会用到与此类似的目录结构。今天写这篇文章希望能对正在查相关资料的同行有所帮助。我保证这篇文章里的所有代码都经过充分测试。

首先贴目录结构图:

d79ee642dcdea3617eb682626ef9cace.png

这个目录里面,源文件的目录有两个:分别是 my_str_utils 和 xtests.  my_str_utils 目录里面主要是写了一些函数的声明和实现,头文件都放在 my_str_utils/headers 里面,对应的源文件都放在 my_str_utils/source 里面,。而 xtests 里面放了个 main.cpp 用来测试 my_str_utils 中写的函数。我把目标文件单独放在 xdirObjs 中,这样目录会整洁很多。最后的可执行程序我单独放在了 xdirExecs 中。

在每个有源文件的目录里都写了一个 Makefile, 用于编译当前目录下的 Makefile. 在 xdirObjs 目录里写了一个 Makefile 用于链接目标文件并生成可执行文件。最后在工程的根目录里写了一个 Makefile 用于发出 make 指令,启动编译过程。

下面是各个目录中的 Makefile 文件。我就不用太多的文字来描述了。但注释会非常详细。

首先是项目根目录下的 Makefile 的代码:

.PHONY: all clean

# 可执行文件名, 这个根据具体程序会有变动。

EXEC_FINAL := tests.exe

# 子目录可以有多个, 以空格分开即可,这个会根据具体程序有所变动。

# 注意 xdirObjs 目录要和 DIR_OBJS 中定义的一致。

SUBDIRS := my_str_utils/source \

xtests \

xdirObjs

# 当前目录

DIR_TOP := $(shell pwd)

# 定义变量(有的书上也叫宏),指定 object 文件的文件夹和可执行文件的文件夹。

DIR_OBJS := $(DIR_TOP)/xdirObjs

DIR_EXEC := $(DIR_TOP)/xdirExecs

# 我这里由需要在 DIR_OBJS 目录中放一个 Makefile 文件,所以 DIR_OBJS 目录已经事

# 先创建好,这里就不再创建了。

DIRS := $(DIR_EXEC)

# 定义变量

CC = g++

# 定义命令起了个别名。

RM = rm

MKDIR = mkdir

MAKE = make

MAKECLEAN = make clean

# -Wall 表示输出警告信息。

CFLAGS = -Wall

# 使在这个目录里面定义的变量可以在被调用的下级 Makefile 中使用。

export EXEC_FINAL DIR_TOP DIR_OBJS DIR_EXEC CC RM MKDIR MAKE MAKECLEAN CFLAGS

# 给伪目标 all 添加两个依赖。这两个依赖在后面也被当作目标。

all: $(DIRS) $(SUBDIRS)

# 创建文件夹

$(DIRS):

$(MKDIR) -p $@

# 关键在于这部分

# 对每个 source 目录(或子项目)进行编译。

# 这种写法,后面一定要跟一个动作,这里是 ECHO, 也可以定义成其他名字。

# ECHO 可以什么都不做,但一定要存在。

# 这里有个隐含规则,由于 SUBDIRS 定义了多个文件,如果用了后面的 ECHO, 就会遍历每个

# 目录并对每个目录执行一次 $(MAKE) -C $@

# 如果不加 ECHO, 就会把 SUBDIRS 中的整个字符串当成一个路径,只执行一次。(非常神奇)

$(SUBDIRS): ECHO

# 注意这个 -C 一定需要大写。这句代码的意思据说与 cd $(SUBDIRS) && $(MAKE) 一样, 没有亲自测。

$(MAKE) -C $@

ECHO:

# 执行清理任务。

clean:

$(RM) -rf $(DIR_OBJS)/*.o

$(RM) -rf $(DIR_EXEC)/$(EXEC_FINAL)

下面是 source 目录下的 Makefile:

# 需要使用 -I 选项指明本地头文件的目录

# 头文件路径根据项目目录结构不同,会有所变化

INC := -I../headers

# 定义变量 SRCS, 用于指代所有的 .cpp 文件。

SRCS = $(wildcard *.cpp)

# 根据 .cpp 推导出 .o

OBJS = $(SRCS:.cpp=.o)

# 给 OBJS 加路径前缀,使其变成完整路径的形式

OBJS := $(addprefix $(DIR_OBJS)/,$(OBJS))

.PHONY: taskslocal

# 此处必须加一个伪目标,因为 make 只能把没有匹配符的目标作为构造目标。

# 也就是说 Makefile 中至少要有一个没有匹配符的目标。否则会报没有 target 的错误。

taskslocal: $(OBJS)

# 用规则对 .o 文件个 .cpp 文件进行匹配。

$(DIR_OBJS)/%.o: %.cpp

$(CC) -g -c $(CFLAGS) $(INC) -o $@ $<

下面是 xtests 目录中的 Makefile:

# 需要使用 -I 选项指明本地头文件的目录

# 头文件路径根据项目目录结构不同,会有所变化

INC := -I../my_str_utils/headers

# 定义变量 SRCS, 用于指代所有的 .cpp 文件。

SRCS = $(wildcard *.cpp)

# 根据 .cpp 推导出 .o

OBJS = $(SRCS:.cpp=.o)

# 给 OBJS 加路径前缀,使其变成完整路径的形式

OBJS := $(addprefix $(DIR_OBJS)/,$(OBJS))

.PHONY: taskslocal

# 此处必须加一个伪目标,因为 make 只能把没有匹配符的目标作为构造目标。

# 也就是说 Makefile 中至少要有一个没有匹配符的目标。否则会报没有 target 的错误。

taskslocal: $(OBJS)

# 用规则对 .o 文件个 .cpp 文件进行匹配。

$(DIR_OBJS)/%.o: %.cpp

$(CC) -g -c $(CFLAGS) $(INC) -o $@ $<

最后是 xdirObjs 目录中的 Makefile:

# 定义变量,指明目标文件为当前目录下所有的 .o 文件。

OBJS_G = $(wildcard *.o)

# 链接目标文件,生成可执行文件。

$(DIR_EXEC)/$(EXEC_FINAL):$(OBJS_G)

$(CC) -g -o $@ $^

最后把编译成功时的打印信息也贴一下:

[karl@localhost ~/xcpps/005_utils]$ make clean

rm -rf /home/karl/xcpps/005_utils/xdirObjs/*.o

rm -rf /home/karl/xcpps/005_utils/xdirExecs/tests.exe

[karl@localhost ~/xcpps/005_utils]$ make

# 注意这个 -C 一定需要大写。

make -C my_str_utils/source

make[1]: Entering directory `/home/karl/xcpps/005_utils/my_str_utils/source'

# -c 表示只编译,不链接。

g++ -g -c -Wall -I../headers -o /home/karl/xcpps/005_utils/xdirObjs/my_str_utils.o my_str_utils.cpp

make[1]: Leaving directory `/home/karl/xcpps/005_utils/my_str_utils/source'

# 注意这个 -C 一定需要大写。

make -C xtests

make[1]: Entering directory `/home/karl/xcpps/005_utils/xtests'

# -c 表示只编译,不链接。

g++ -g -c -Wall -I../my_str_utils/headers -o /home/karl/xcpps/005_utils/xdirObjs/main.o main.cpp

make[1]: Leaving directory `/home/karl/xcpps/005_utils/xtests'

# 注意这个 -C 一定需要大写。

make -C xdirObjs

make[1]: Entering directory `/home/karl/xcpps/005_utils/xdirObjs'

g++ -g -o /home/karl/xcpps/005_utils/xdirExecs/tests.exe my_str_utils.o main.o

make[1]: Leaving directory `/home/karl/xcpps/005_utils/xdirObjs'

[karl@localhost ~/xcpps/005_utils]$

最后,我是在 vscode 里搭建的环境。理论上,对 Makefile 不会有任何影响。但我还是把 vscode 里面的项目配置贴出来,以免有网友需要看 vscode 项目设置。

c_cpp_properties.json:

{

"configurations": [

{

"name": "Linux",

"includePath": [

"${workspaceFolder}/**"

],

"defines": [],

"compilerPath": "/usr/bin/gcc",

"cStandard": "c11",

"cppStandard": "c++14",

"intelliSenseMode": "clang-x64"

}

],

"version": 4

}

tasks.json:

{

// See https://go.microsoft.com/fwlink/?LinkId=733558

// for the documentation about the tasks.json format

"version": "2.0.0",

"tasks": [

{

"label": "build",

"type": "shell",

"command": "make",

"args": [],

"group":{

"kind": "build",

"isDefault": true

}

}

]

}

launch.json:

{

// Use IntelliSense to learn about possible attributes.

// Hover to view descriptions of existing attributes.

// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387

"version": "0.2.0",

"configurations": [

{

"name": "(gdb) Launch",

"type": "cppdbg",

"request": "launch",

// "program": "enter program name, for example ${workspaceFolder}/a.out",

"program": "${workspaceFolder}/xdirExecs/tests.exe",

"args": [],

"stopAtEntry": false,

"cwd": "${workspaceFolder}",

"environment": [],

"externalConsole": false,

"MIMode": "gdb",

"setupCommands": [

{

"description": "Enable pretty-printing for gdb",

"text": "-enable-pretty-printing",

"ignoreFailures": true

}

]

}

]

}

写完了,希望对大家有所帮助。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值