一、Makefile初识
执行 make
命令时,它会去当前目录下查找名为Makefile
的文件,并根
据它的指示去执行操作,生成第一个目标
。
二、第一个Makefile(效率很低)
同一目录下存在 main.c
、sub.c
和 sub.h
这三个文件。
make 将 main.c
和 sub.c
这两个源代码文件编译成一个名为 test
的可执行文件
# 编译并链接main.c和sub.c生成可执行文件test
test : main.c sub.c sub.h
gcc -o test main.c sub.c
main.c
:这是主程序的源代码文件。sub.c
:这是辅助函数的源代码文件。sub.h
:这是辅助函数的头文件。
指令 gcc -o test main.c sub.c
使用 GCC 编译器来将 main.c
和 sub.c
这两个源代码文件编译成一个名为 test
的可执行文件。 -o
选项用于指定输出的可执行文件名。
编译完成后,你可以运行 ./test
命令来执行生成的可执行文件 test
。请确保在同一目录下存在 main.c
、sub.c
和 sub.h
这三个文件,不然编译过程会出错。
三、第二个Makefile(效率高、规则太多、不支持自动检测头文件)
test : main.o sub.o # 定义test目标,依赖于main.o和sub.o两个目标文件
gcc -o test main.o sub.o # 使用gcc编译器将main.o和sub.o目标文件链接为可执行文件test
main.o : main.c # 定义main.o目标,依赖于main.c源文件
gcc -c -o main.o main.c # 使用gcc编译器将main.c源文件编译为main.o目标文件
sub.o : sub.c # 定义sub.o目标,依赖于sub.c源文件
gcc -c -o sub.o sub.c # 使用gcc编译器将sub.c源文件编译为sub.o目标文件
clean: # 定义clean目标,用于清理生成的目标文件和可执行文件
rm *.o test -f # 删除所有的目标文件和可执行文件,-f选项表示强制删除而不提示
test : main.o sub.o
:test
目标依赖于main.o
和sub.o
文件。如果它们有任何更新,将会重新构建test
可执行文件。gcc -o test main.o sub.o
:该规则描述了如何构建test
目标。它使用gcc
编译器将main.o
和sub.o
链接为一个可执行文件test
。main.o : main.c
:main.o
目标依赖于main.c
文件。如果main.c
有任何更新,将会重新构建main.o
。gcc -c -o main.o main.c
:该规则描述了如何构建main.o
目标。它使用gcc
编译器将main.c
编译为一个目标文件main.o
。sub.o : sub.c
:sub.o
目标依赖于sub.c
文件。如果sub.c
有任何更新,将会重新构建sub.o
。gcc -c -o sub.o sub.c
:该规则描述了如何构建sub.o
目标。它使用gcc
编译器将sub.c
编译为一个目标文件sub.o
。clean: rm *.o test -f
:clean
目标用于删除生成的目标文件和可执行文件。rm
命令用于删除文件,*.o
表示匹配所有以.o
结尾的文件,test
表示删除名为test
的文件,-f
选项用于强制删除而不提示。
四、第三个Makefile(效率高、不支持头文件检测)
test : main.o sub.o # 定义test目标,依赖于main.o和sub.o两个目标文件
gcc -o test main.o sub.o # 使用gcc编译器将main.o和sub.o目标文件链接为可执行文件test
%.o : %.c # 定义模式规则,表示以.o结尾的目标文件依赖于同名的.c文件
gcc -c -o $@ $< # 使用gcc编译器将.c文件编译为.o目标文件
clean: # 定义clean目标,用于清理生成的目标文件和可执行文件
rm *.o test -f # 删除所有的目标文件和可执行文件,-f选项表示强制删除而不提示
这段代码是一个使用变量和模式规则的Makefile脚本。它定义了以下几个规则:
test: main.o sub.o
:test
目标依赖于main.o
和sub.o
文件。如果它们有任何更新,将会重新构建test
可执行文件。gcc -o test main.o sub.o
:该规则描述了如何构建test
目标。它使用gcc
编译器将main.o
和sub.o
链接为一个可执行文件test
。%.o : %.c
:这个模式规则表示任何以.o
结尾的目标都依赖于同名的.c
文件。如果.c
文件有任何更新,将会重新构建对应的.o
目标文件。gcc -c -o $@ $<
:该规则描述了如何构建.o
目标文件。$@
表示当前目标的文件名,$<
表示当前目标的第一个依赖文件。使用gcc
编译器将.c
文件编译为.o
目标文件。clean: rm *.o test -f
:clean
目标用于删除生成的目标文件和可执行文件。rm
命令用于删除文件,*.o
表示匹配所有以.o
结尾的文件,test
表示删除名为test
的文件,-f
选项用于强制删除而不提示。
与之前相比,这段代码使用了模式规则来简化目标与依赖的定义。通过使用%.o : %.c
规则,可以在不同的.o
文件之间共享相同的规则,并自动匹配对应的.c
文件进行编译。这样可以减少代码的重复以及添加新的源文件时的维护工作。
五、第四个Makefile(效率高、精简,但是要手动添加头文件匹配规则)
test : main.o sub.o # test目标依赖于main.o和sub.o目标文件
gcc -o test main.o sub.o # 使用gcc编译器将main.o和sub.o目标文件链接为可执行文件test
%.o : %.c # 模式规则,表示以.o结尾的目标文件依赖于同名的.c文件
gcc -c -o $@ $< # 使用gcc编译器将.c文件编译为.o目标文件
sub.o : sub.h # sub.o目标文件依赖于sub.h头文件
clean: # clean目标,用于删除生成的目标文件和可执行文件
rm *.o test -f # 删除所有的目标文件和test可执行文件
这段代码是一个使用变量和模式规则的Makefile脚本,与之前相比进行了一些修改。它定义了以下几个规则:
test: main.o sub.o
:test
目标依赖于main.o
和sub.o
文件。如果它们有任何更新,将会重新构建test
可执行文件。gcc -o test main.o sub.o
:该规则描述了如何构建test
目标。它使用gcc
编译器将main.o
和sub.o
链接为一个可执行文件test
。%.o : %.c
:这个模式规则表示任何以.o
结尾的目标都依赖于同名的.c
文件。如果.c
文件有任何更新,将会重新构建对应的.o
目标文件。gcc -c -o $@ $<
:该规则描述了如何构建.o
目标文件。$@
表示当前目标的文件名,$<
表示当前目标的第一个依赖文件。使用gcc
编译器将.c
文件编译为.o
目标文件。sub.o : sub.h
:sub.o
目标依赖于sub.h
头文件。如果头文件有任何更新,将会重新构建sub.o
目标文件。clean: rm *.o test -f
:clean
目标用于删除生成的目标文件和可执行文件。rm
命令用于删除文件,*.o
表示匹配所有以.o
结尾的文件,test
表示删除名为test
的文件,-f
选项用于强制删除而不提示。
通过添加sub.o : sub.h
规则,当头文件sub.h
有任何更新时,会重新构建sub.o
目标文件。
同样,运行make
命令将会自动根据依赖关系构建所需的目标文件,make clean
命令则会删除生成的目标文件和可执行文件。
六、第五个Makefile(高效、精简,自动检测头文件)
以下是对代码的注释:
objs := main.o sub.o # 定义目标文件列表
test : $(objs) # test目标依赖于objs列表中的目标文件
gcc -o test $^ # 使用gcc编译器将目标文件链接为可执行文件test
# 判断是否存在依赖文件
# .main.o.d .sub.o.d
dep_files := $(foreach f, $(objs), .$(f).d)
dep_files := $(wildcard $(dep_files))
# 包含依赖文件
ifneq ($(dep_files),)
include $(dep_files)
endif
%.o : %.c # 模式规则,表示以.o结尾的目标文件依赖于同名的.c文件
gcc -Wp,-MD,.$@.d -c -o $@ $< # 使用gcc编译器将.c文件编译为.o目标文件,并生成依赖文件
clean: # clean目标,用于删除生成的目标文件和可执行文件
rm *.o test -f # 删除所有的目标文件和test可执行文件
distclean: # distclean目标,更彻底地清理,包括删除依赖文件
rm $(dep_files) *.o test -f # 删除依赖文件、所有目标文件和test可执行文件
这段代码是一个使用变量和模式规则的Makefile脚本,相对于之前的版本进行了一些修改。它定义了以下几个规则:
objs := main.o sub.o
:定义了一个变量objs
,包含需要构建的目标文件列表。test : $(objs)
:test
目标依赖于objs
列表中的目标文件。如果它们有任何更新,将会重新构建test
可执行文件。gcc -o test $^
:该规则描述了如何构建test
目标。$^
表示所有依赖文件的列表。使用gcc
编译器将依赖的目标文件链接为一个可执行文件test
。dep_files := $(foreach f, $(objs), .$(f).d)
:定义了一个变量dep_files
,用于存储生成的依赖文件列表。每个目标文件都对应一个以.d
结尾的依赖文件。dep_files := $(wildcard $(dep_files))
:使用wildcard
函数扩展dep_files
变量,将其中的通配符表示的依赖文件替换为实际的文件名。ifneq ($(dep_files),)
:条件判断,检查是否存在依赖文件。如果存在依赖文件,则执行下面的操作。include $(dep_files)
:将依赖文件包含进来,确保Makefile能够感知到依赖关系。%.o : %.c
:这个模式规则表示任何以.o
结尾的目标都依赖于同名的.c
文件。如果.c
文件有任何更新,将会重新构建对应的.o
目标文件。同时,-Wp,-MD,.$@.d
选项用于生成带有依赖关系的.d
文件。clean: rm *.o test -f
:clean
目标用于删除生成的目标文件和可执行文件。rm
命令用于删除文件,*.o
表示匹配所有以.o
结尾的文件,test
表示删除名为test
的文件,-f
选项用于强制删除而不提示。distclean: rm $(dep_files) *.o test -f
:distclean
目标用于执行更彻底的清理,不仅删除生成的目标文件和可执行文件,还会删除依赖文件。$(dep_files)
表示需要删除的依赖文件列表。
通过添加依赖文件的处理,可以更准确地跟踪源文件的更新并重新构建相关的目标文件。运行make
命令将会自动根据依赖关系构建所需的目标文件,make clean
命令将会删除生成的目标文件和可执行文件,make distclean
命令则会更彻底地清理,并删除依赖文件。