参考
NMAKE参考之五——Makefile中的命令_nmake在指定目录下生成_XanaduT的博客-CSDN博客
NMAKE Reference | Microsoft Learn
目录
一、Compiler, tools and options 变量定义
序、makefile规则简介
在 Makefile 中,规则是指一条定义了目标文件、依赖文件以及生成命令的语句。下面是 Makefile 中规则的一般格式:
target: dependencies
command
其中,target
表示要生成的目标文件,可以是可执行程序、静态库、动态库等;dependencies
表示生成 target
文件所依赖的文件列表;command
表示生成 target
文件的命令,可以是编译命令、链接命令等。
例如,下面这个简单的 Makefile 包含了一个规则:
hello: hello.c
gcc -o hello hello.c
这个规则表示要生成可执行文件 hello
,它依赖于源文件 hello.c
,生成可执行文件的命令是执行 gcc
编译器将 hello.c
编译成 hello
可执行文件。
上面是单条规则,实际应用下,依赖的文件本身可能又会依赖另一个文件,make就这样一层又一层地去找文件的依赖关系,最终都是为了生成最根的那个文件(可能是一个exe等,这个过程可参考最后一节)。
在 Makefile 中还可以使用变量、函数、条件语句等高级语法,以实现更复杂的自动化编译任务。
下面以一个最简单的窗口程序为例,来看看生成的makefile文件
环境:msvc+qt5
makefile位置:项目生成目录
一、Compiler, tools and options 变量定义
CC = cl
CXX = cl
DEFINES = -DUNICODE -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DQT_DEPRECATED_WARNINGS -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB
CFLAGS = -nologo -Zc:wchar_t -FS -Zc:strictStrings -Zi -MDd -W3 -w44456 -w44457 -w44458 /Fddebug\untitled.vc.pdb $(DEFINES)
CXXFLAGS = -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -EHsc /Fddebug\untitled.vc.pdb $(DEFINES)
INCPATH = -I..\untitled -I. -I..\..\..\..\qt\qt5install\include -I..\..\..\..\qt\qt5install\include\QtWidgets -I..\..\..\..\qt\qt5install\include\QtGui -I..\..\..\..\qt\qt5install\include\QtANGLE -I..\..\..\..\qt\qt5install\include\QtCore -Idebug -I. -I..\..\..\..\qt\qt5install\mkspecs\win32-msvc
LINKER = link
LFLAGS = /NOLOGO /DYNAMICBASE /NXCOMPAT /DEBUG /SUBSYSTEM:WINDOWS "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'"
LIBS = C:\qt\qt5install\lib\Qt5Widgetsd.lib C:\qt\qt5install\lib\Qt5Guid.lib C:\qt\qt5install\lib\Qt5Cored.lib C:\qt\qt5install\lib\qtmaind.lib shell32.lib
QMAKE = C:\qt\qt5install\bin\qmake.exe
DEL_FILE = del
CHK_DIR_EXISTS= if not exist
MKDIR = mkdir
COPY = copy /y
COPY_FILE = copy /y
COPY_DIR = xcopy /s /q /y /i
INSTALL_FILE = copy /y
INSTALL_PROGRAM = copy /y
INSTALL_DIR = xcopy /s /q /y /i
QINSTALL = C:\qt\qt5install\bin\qmake.exe -install qinstall
QINSTALL_PROGRAM = C:\qt\qt5install\bin\qmake.exe -install qinstall -exe
DEL_FILE = del
SYMLINK = $(QMAKE) -install ln -f -s
DEL_DIR = rmdir
MOVE = move
IDC = idc
IDL = midl
ZIP = zip -r -9
DEF_FILE =
RES_FILE =
SED = $(QMAKE) -install sed
MOVE = move
- cc、cxx 定义了编译器命令 cl,即msvc的编译器cl.exe
- DEFINES 定义了给编译器传参的一些预处理定义 -D开头,相当于代码中 #define的作用,如 -DWIN32,即定义了 WIN32
-
CFLAGS、CXXFLAGS 定义了给编译器命令行的一些传参,通过 $(DEFINES) 又引用了上面的传参
-
LINKER 链接器 link,即msvc的链接器link.exe
说明:
变量定义是makefile的语法,可以在后面自定义使用,上面的变量名不一定是固定的,当然有些变量名是用于一些惯用的隐式规则的,如CXXFLAGS。
要和编译器等选项区分开,只是有些变量刚好传给命令行了。
二、Files 变量定义
SOURCES = ..\untitled\main.cpp \
..\untitled\mainwindow.cpp debug\moc_mainwindow.cpp
OBJECTS = debug\main.obj \
debug\mainwindow.obj \
debug\moc_mainwindow.obj
DIST = ..\untitled\mainwindow.h ..\untitled\main.cpp \
..\untitled\mainwindow.cpp
QMAKE_TARGET = untitled
DESTDIR = debug\ #avoid trailing-slash linebreak
TARGET = untitled.exe
DESTDIR_TARGET = debug\untitled.exe
- SOURCES 源文件相关
-
OBJECTS 目标文件
-
最后生成的TARGET文件名路径等
三、Implicit rules 隐式规则
.SUFFIXES: .c .cpp .cc .cxx
{debug}.cpp{debug\}.obj::
$(CXX) -c $(CXXFLAGS) $(INCPATH) -Fodebug\ @<<
$<
<<
{..\untitled}.cpp{debug\}.obj::
$(CXX) -c $(CXXFLAGS) $(INCPATH) -Fodebug\ @<<
$<
<<
{.}.cpp{debug\}.obj::
$(CXX) -c $(CXXFLAGS) $(INCPATH) -Fodebug\ @<<
$<
<<
到这里开始迷惑了 ,makefile语法较多,且有些是nmake下特有,建议参考下下面这些:
3.1 推理规则
首先看看什么是隐式规则,nmake下应该叫推理规则:如果没有明确指定某个目标的生成命令,nmake 会尝试使用推理规则(Inference Rules)自动推导出如何生成该目标文件,这样处理是为了避免重复的的一些生成命令定义,推理规则定义形如:
{from_path}.from_ext{to_path}.to_ext:
commands
- .from_ext 目标后缀
- .to_ext 依赖后缀
- 两个path是搜索路径
- commands,执行命令
如:{debug}.cpp{debug\}.obj,就是匹配指定目录.cpp到.obj的推理规则,运行commands
3.2 具体qt生成的规则介绍
- .SUFFIXES 把指定后缀加入推理规则的匹配列表
- 一条推理规则:
{..\untitled}.cpp{debug\}.obj::
$(CXX) -c $(CXXFLAGS) $(INCPATH) -Fodebug\ @<<
$<
<<
匹配:可匹配规则如 debug\main.obj: ..\untitled\main.cpp
命令:匹配上则使用定义的生成命令,命令又用到前面定义的变量$(CXX)、$(CXXFLAGS)等,命令中有一些编译器选项及makefile的语法如下:
-Fo | MSVC编译选项,它用于指定编译器输出目标文件的路径和名称 |
@ | 是一个特殊的编译选项,会将@后面的文件读入作为命令行参数 |
<< | 用于创建一个临时内联文件,即将到后面的第二个 <<之间的内容以文件形式临时保存 |
@<< …… << | 将中间的内容以文件传给编译器,这样处理是为了避免命令行参数过长 |
$< | 推理规则中,表示时间戳晚于当前目标的依赖文件 |
总之,匹配规则并生成obj文件。
四、构建规则 Build rules
此处只摘取部分,从最终目标exe往前回溯。
1、从依赖关系来看,这里最终生成目标exe,目标exe又依赖目标文件obj,所以先要生成obj
first: all
all: Makefile.Debug debug\untitled.exe
debug\untitled.exe: C:\qt\qt5install\lib\Qt5Widgetsd.lib C:\qt\qt5install\lib\Qt5Guid.lib C:\qt\qt5install\lib\Qt5Cored.lib C:\qt\qt5install\lib\qtmaind.lib ui_mainwindow.h $(OBJECTS)
$(LINKER) $(LFLAGS) /MANIFEST:embed /OUT:$(DESTDIR_TARGET) @<<
debug\main.obj debug\mainwindow.obj debug\moc_mainwindow.obj
$(LIBS)
<<
2、 目标obj生成依赖,这里没写具体生成命令,而是匹配前面的推理规则命令
debug\main.obj: ..\untitled\main.cpp ..\untitled\mainwindow.h \
debug\mainwindow.obj: ..\untitled\mainwindow.cpp ..\untitled\mainwindow.h \
ui_mainwindow.h
debug\moc_mainwindow.obj: debug\moc_mainwindow.cpp
3、 这些依赖有两个文件不在源代码中,ui_mainwindow.h及moc_mainwindow.cpp,肯定需要先生成它们,那么它们的依赖及命令是啥呢
debug\moc_mainwindow.cpp: ..\untitled\mainwindow.h \
debug\moc_predefs.h \
..\..\..\..\qt\qt5install\bin\moc.exe
C:\qt\qt5install\bin\moc.exe $(DEFINES) --compiler-flavor=msvc --include C:/Users/zd/Documents/build-untitled-unknown-Debug/debug/moc_predefs.h -IC:/qt/qt5install/mkspecs/win32-msvc -IC:/Users/zd/Documents/untitled -IC:/qt/qt5install/include -IC:/qt/qt5install/include/QtWidgets -IC:/qt/qt5install/include/QtGui -IC:/qt/qt5install/include/QtANGLE -IC:/qt/qt5install/include/QtCore -I. -I"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.34.31933\include" -I"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.34.31933\ATLMFC\include" -I"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\VS\include" -I"C:\Program Files (x86)\Windows Kits\10\include\10.0.22000.0\ucrt" -I"C:\Program Files (x86)\Windows Kits\10\\include\10.0.22000.0\\um" -I"C:\Program Files (x86)\Windows Kits\10\\include\10.0.22000.0\\shared" -I"C:\Program Files (x86)\Windows Kits\10\\include\10.0.22000.0\\winrt" -I"C:\Program Files (x86)\Windows Kits\10\\include\10.0.22000.0\\cppwinrt" ..\untitled\mainwindow.h -o debug\moc_mainwindow.cpp
ui_mainwindow.h: ..\untitled\mainwindow.ui \
..\..\..\..\qt\qt5install\bin\uic.exe
C:\qt\qt5install\bin\uic.exe ..\untitled\mainwindow.ui -o ui_mainwindow.h
可见这两个文件是qt通过uic.exe及moc.exe生成的
- uic.exe:将ui文件转成头文件
- moc.exe:生成moc文件,qt信号槽机制的支撑
不断往上寻找生成依赖,然后就可以生成最后的exe了