4.makefile自动推导目标代码配置和伪目标clean清理
4.makefile自动推导目标代码配置和伪目标clean清理
4.1 make和makefile
- makefile文件主要包含了5部分内容:
- 显式规则。说明了如何生成一个或多个目标文件。由makefile文件的创作者指出,包括要生成的文件、文件的依赖文件、生成的命令。
- 隐式规则。由于make有自动推导的功能,所以隐式的规则可以比较粗糙地简略书写makefile文件,这是由make所支持的。
- 变量定义。在makefile文件中要定义一系列的变量,变量一般都是字符串,这与C语言中的宏有些类似。当makefile文件执行时,其中的变量都会扩展到相应的引用位置上。
- 文件指示。其包括3个部分,一个是在一个makefile文件中引用另一个makefile文件;另一个是指根据某些情况指定makefile文件中的有效部分;还有就是定义一个多行的命令。
- 注释。makefile文件中只有行注释,其注释用“#”字符。如果要在makefile文件中使用“#”字符,可以用反斜框进行转义,如:“#”。
make 是一个功能强大的构建工具,虽然 make 需要根据 makefile 中指定的规则来完成源文件的编译。我们编写 makefile 的时候难免写的不是那么严谨从而漏写一些构建规则,但是会发现程序还是会被编译成功。这是因为 make 有自动推导的能力,不会完全依据makefile。
4.2 自动推导
如果说某个依赖项是由它能推导出来,它自己可以生成编译代码,自动推导涉及.o文件
# first_make
# $^ 依赖 不重复
# $@ 目标
# @ 不显示命令执行 -失败不停止
TARGET=first_make
LIBS=-lpthread
$(TARGET):first_make.o xdata.o
@#-rm test
@echo "begin build $(TARGET)"
$(CXX) $^ -o $@ $(LIBS)
@echo "$(TARGET) build success!"
root@cmt-virtual-machine:/home/cmt/src/first_make# ls
first_make.cpp makefile xdata.cpp xdata.h
root@cmt-virtual-machine:/home/cmt/src/first_make# make
g++ -c -o first_make.o first_make.cpp
g++ -c -o xdata.o xdata.cpp
begin build first_make
g++ first_make.o xdata.o -o first_make -lpthread
first_make build success!
root@cmt-virtual-machine:/home/cmt/src/first_make#
g++针对.o文件生成,这里涉及了自动推导。
但是自动推导这里有一个问题,如果说自动推导涉及头文件的引用,
//first_make.cpp
#include <iostream>
#include <thread>
#include "xdata.h"
#include "test.h"
using namespace std;
void ThreadMain()
{
cout << "Thread Main" << endl;
}
int main(int argc, char *argv[])
{
thread th(ThreadMain);
cout << "test make" << endl;
th.join();
XData d;
return 0;
}
test.h
//../test_gcc
#define CONF_PATH "/user/local/xcj/"
root@cmt-virtual-machine:/home/cmt/src/first_make# ls
first_make first_make.cpp first_make.o makefile xdata.cpp xdata.h xdata.o
root@cmt-virtual-machine:/home/cmt/src/first_make# make
g++ -c -o first_make.o first_make.cpp
first_make.cpp:5:10: fatal error: test.h: 没有那个文件或目录
5 | #include "test.h"
| ^~~~~~~~
compilation terminated.
make: *** [<内置>:first_make.o] 错误 1
root@cmt-virtual-machine:/home/cmt/src/first_make#
编译时找不头文件
手动做
root@cmt-virtual-machine:/home/cmt/src/first_make# g++ -c -o first_make.o first_make.cpp -I../test_gcc
自动推导如何加这个命令?
# first_make
# $^ 依赖 不重复
# $@ 目标
# @ 不显示命令执行 -失败不停止
TARGET=first_make
LIBS=-lpthread
OBJS=first_make.o xdata.o
CXXFLAGS=-I../test_gcc
$(TARGET):$(OBJS)
@#-rm test
@echo "begin build $(TARGET)"
$(CXX) $^ -o $@ $(LIBS)
@echo "$(TARGET) build success!"
root@cmt-virtual-machine:/home/cmt/src/first_make# make
begin build first_make
g++ first_make.o xdata.o -o first_make -lpthread
first_make build success!
在 Makefile
中添加一个清理目标(clean
target)是一个常见的做法,用于自动清理编译过程中生成的中间文件或可执行文件。这样可以避免手动清理的麻烦。
清理和编译目标有一个区别,所谓的目标项都有一个对应的目标式文件,添加清理,没有目标也没有依赖项
(1)伪目标
伪目标(Phony Targets)是在 Makefile
中一种特殊的目标,它们并不对应于实际的文件,而是用于执行特定的命令。使用伪目标的主要目的是避免文件名和目标名冲突,并确保目标总是被执行。
①伪目标的定义
伪目标通常通过在 Makefile
中使用 .PHONY
来声明。下面是一个例子:
.PHONY: clean
在这个例子中,clean
被定义为一个伪目标。无论当前目录下是否存在名为 clean
的文件,make clean
命令都会执行对应的命令(如删除文件),而不会试图将其视为一个文件目标。
②为什么使用伪目标?
-
避免冲突:
如果你有一个目标名与文件名相同,而你不希望make
把它当成一个文件目标(即不会尝试通过规则去生成这个文件),你可以将其定义为伪目标。例如:clean: rm -f *.o first_make .PHONY: clean
如果没有
.PHONY
,且当前目录中有一个名为clean
的文件,make
将认为clean
已经是最新的,并且不会执行rm -f *.o first_make
这条命令。 -
确保执行:
伪目标总是被认为需要执行,而不会受制于文件的时间戳。即使在不需要重新构建的情况下,你仍然希望某些命令被执行,如清理操作。 -
提高可读性:
伪目标通常用于表示一个动作或一组命令,而非生成某个文件。例如,clean
、install
、test
等动作就是常见的伪目标。定义伪目标可以使Makefile
更具可读性。
③多个伪目标
你可以在 .PHONY
中定义多个伪目标,用空格分隔:
.PHONY: all clean install
在这个例子中,all
、clean
和 install
都被定义为伪目标。
④使用 .PHONY
的例子
一个简单的 Makefile
示例:
.PHONY: all clean install
all: program
program: main.o lib.o
gcc -o program main.o lib.o
main.o: main.c
gcc -c main.c
lib.o: lib.c
gcc -c lib.c
clean:
rm -f *.o program
install:
cp program /usr/local/bin/
在这个例子中:
all
、clean
和install
都是伪目标。make clean
会执行rm -f *.o program
,无论当前目录中是否存在clean
文件。make install
会将program
复制到/usr/local/bin/
,无论当前目录中是否存在install
文件。
总结来说,伪目标是 Makefile
中一种重要的机制,可以确保某些命令总是被执行,同时避免与文件名的冲突,从而增强 Makefile
的灵活性和可靠性。
# first_make
# $^ 依赖 不重复
# $@ 目标
# @ 不显示命令执行 -失败不停止
TARGET=first_make
LIBS=-lpthread
OBJS=first_make.o xdata.o
CXXFLAGS=-I../test_gcc
$(TARGET):$(OBJS)
@#-rm test
@echo "begin build $(TARGET)"
$(CXX) $^ -o $@ $(LIBS)
@echo "$(TARGET) build success!"
clean:
$(RM) $(OBJS) $(TARGET)
.PHONY: clean *clean
root@cmt-virtual-machine:/home/cmt/src/first_make# ls
first_make first_make.cpp makefile xdata.cpp xdata.h
root@cmt-virtual-machine:/home/cmt/src/first_make# make
g++ -I../test_gcc -c -o first_make.o first_make.cpp
g++ -I../test_gcc -c -o xdata.o xdata.cpp
begin build first_make
g++ first_make.o xdata.o -o first_make -lpthread
first_make build success!
root@cmt-virtual-machine:/home/cmt/src/first_make# vi makefile
root@cmt-virtual-machine:/home/cmt/src/first_make# make clean
rm -f first_make.o xdata.o first_make
root@cmt-virtual-machine:/home/cmt/src/first_make# make clean
rm -f first_make.o xdata.o first_make
root@cmt-virtual-machine:/home/cmt/src/first_make# make
g++ -I../test_gcc -c -o first_make.o first_make.cpp
g++ -I../test_gcc -c -o xdata.o xdata.cpp
begin build first_make
g++ first_make.o xdata.o -o first_make -lpthread
first_make build success!
root@cmt-virtual-machine:/home/cmt/src/first_make#
每次想重新编译就进行一下清理
(2)为什么涉及到清理?
因为用了时间戳的问题可能涉及到大家代码的复制、从版本库上拿到代码时间上有时候会产生问题。一般会清理掉重新编译一下。
参考资料来源:夏曹俊