makefile 多级目录 嵌套调用 实例

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

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

        首先贴目录结构图:

        makefile 多级目录 嵌套调用 实例_第1张图片

        这个目录里面,源文件的目录有两个:分别是 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
                }
            ]
        }
    ]
}

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

 

你可能感兴趣的:(c++)