1.简介
在 Qt 项目里,.pro
文件是项目文件,其作用是对项目的构建进行配置。实现对Qt工程的全生命周期控制,从编译优化到部署策略均可精准把控,是构建企业级Qt应用的必备核心技能。建议结合Qt Creator的.pro解析器源码深入研究,可达到"人剑合一"的工程配置境界。
2.基础配置信息
- TEMPLATE:指定项目的模板类型,常用的类型有:
app
:用于创建可执行应用程序,默认。lib
:用于创建库文件,可细分为静态库(staticlib
)和动态库(sharedlib
)。subdirs
:用于管理多个子项目(生成makefile文件编译subdirs指定的子文件夹)
subdirs模板告诉qmake生成一个makefile,它可以进入到特定子目录并为这个目录中的项目文件生成makefile并且为它调用make。
在这个模板中只有一个系统变量SUBDIRS可以被识别。这个变量中包含了所要处理的含有项目文件的子目录的列表。这个项目文件的名称是和子目录同名的,这样qmake就可以发现它。
例如,如果子目里是“myapp”,那么在这个目录中的项目文件应该被叫做myapp.pro。
//指定子工程目录
TEMPLATE = subdirs
SUBDIRS += \
project1 \
project2
//或//指定子工程的工程文件
TEMPLATE = subdirs
SUBDIRS += \
project1/myProject1.pro \
project2
//指定子工程的构建顺序
CONFIG += ordered
- TARGET:指定生成的目标文件的名称。
- DESTDIR: 指定构建目录(避免污染源码)
- OBJECTS_DIR: 指定输出中间文件目录,如:OBJECTS_DIR = $$DESTDIR/obj
- MOC_DIR: 指定输出moc文件目录,如:MOC_DIR= $$DESTDIR/moc
- RCC_DIR: 指定输出rcc文件目录,如:RCC_DIR= $$DESTDIR/rcc
- UI_DIR: 指定输出ui文件目录,如:UI_DIR= $$DESTDIR/ui
- VERSION: 生成版本信息,如:VERSION = 1.0.0
示例如下:配置成静态库
# 设置项目模板为静态库
TEMPLATE = staticlib
# 指定生成的静态库名称
TARGET = MyStaticLibrary
#指定目标目录
DESTDIR = ../bin
#或
# TEMPLATE = lib
# config = staticlib
# 源文件列表
SOURCES += \
myclass.cpp
# 头文件列表
HEADERS += \
myclass.h
# 若有需要,可以添加包含路径和库链接
# INCLUDEPATH += /path/to/include
# LIBS += -L/path/to/lib -lexternal_library
在这个示例里,TEMPLATE
被设定成 staticlib
,这表明项目会构建成静态库。TARGET
定义了生成的静态库的名称,SOURCES
和 HEADERS
分别列出了源文件和头文件。
配置成动态库
# 设置项目模板为动态库
TEMPLATE = shared
# 指定生成的动态库名称
TARGET = MyDynamicLibrary
#指定目标目录
DESTDIR = ../bin
#或
# TEMPLATE = lib
# config = shared
# 源文件列表
SOURCES += \
myclass.cpp
# 头文件列表
HEADERS += \
myclass.h
# 若有需要,可以添加包含路径和库链接
# INCLUDEPATH += /path/to/include
# LIBS += -L/path/to/lib -lexternal_library
# 导出符号定义(在 Windows 上可能需要)
DEFINES += MYDYNAMICLIBRARY_LIBRARY
在这个示例中,TEMPLATE
被设置为 shared
,意味着项目会构建成动态库。同样,TARGET
定义了生成的动态库的名称,SOURCES
和 HEADERS
分别列出了源文件和头文件。DEFINES
中的 MYDYNAMICLIBRARY_LIBRARY
是在 Windows 平台上导出符号时可能需要的定义。
3.源码文件和资源文件
1) SOURCES :列出项目中的源文件(.cpp
文件)。
SOURCES += main.cpp \
mainwindow.cpp
2) HEADERS: 列出项目中的头文件(.h
文件)
3) FORMS: 列出项目中的 UI 文件(.ui
文件)
FORMS += myform.ui
4) RESOURCES : 列出项目中的Qt资源文件(.qrc)
RESOURCES += icons.qrc
5)QML_SOURCES: 列出项目中的qml文件
QML_SOURCES += main.qml
6)INCLUDEPATH
:指定编译器在查找头文件时的搜索路径
TEMPLATE = app
TARGET = MyApp
# 源文件和头文件
SOURCES += main.cpp
HEADERS += myclass.h
# 添加头文件搜索路径
INCLUDEPATH += /path/to/include1 \
/path/to/include2 \
../include
# 根据操作系统添加不同的路径
win32 {
INCLUDEPATH += C:/Windows/Include
}
unix {
INCLUDEPATH += /usr/local/include
}
# 使用环境变量
INCLUDEPATH += $$(MY_INCLUDE_PATH)
7)DEPENDPATH
: 用于指定项目依赖文件的搜索路径。依赖文件通常指那些在项目构建过程中,编译器或构建工具需要检查其修改状态以确定是否需要重新编译某些文件的文件,比如头文件等。当这些依赖文件发生变化时,Qt 的构建系统会根据这些变化来决定是否重新编译受影响的源文件。
TEMPLATE = app
TARGET = MyApp
# 源文件和头文件
SOURCES += main.cpp
HEADERS += myclass.h
# 添加依赖文件搜索路径
DEPENDPATH += /path/to/dependencies1 \
/path/to/dependencies2 \
../dependencies
# 根据操作系统添加不同的路径
win32 {
DEPENDPATH += C:/Windows/Dependencies
}
unix {
DEPENDPATH += /usr/local/dependencies
}
# 使用环境变量
DEPENDPATH += $$(MY_DEPENDENCY_PATH)
DEPENDPATH
与 INCLUDEPATH
的区别
INCLUDEPATH
主要是给编译器指明头文件的搜索路径,以便在编译源文件时能找到所需的头文件;而 DEPENDPATH
主要是让构建系统知道在哪里查找依赖文件,从而判断是否需要重新编译。虽然二者在很多情况下可能指向相同的路径,但它们的用途是不同的。
8) LIBS: 指定链接的库文件。
LIBS += -L/path/to/lib -lmylib
# 库路径与链接库
LIBS += -L$$PWD/lib -lmylib
# Windows特定库
win32:LIBS += -luser32
# Unix特定库
unix:LIBS += -lpthread
其中,-L
用于指定库文件的搜索路径,-l
用于指定要链接的库名。
9) DEF_DEF: 只有Windows需要:应用程序所要连接的.def文件
10) RC_DEF : 只有Windows需要:应用程序的资源文件
11) RS_DEF : 只有Windows需要:应用程序所要连接的资源文件。
4.编译器和链接器选项
1) QMAKE_CXXFLAGS: 指定 C++ 编译器的选项。
QMAKE_CXXFLAGS += -std=c++11
2) QMAKE_LFLAGS:指定链接器的选项。
QMAKE_LFLAGS += -Wl,-rpath=/path/to/lib
3) DEFINES : 用来定义编译选项
DEFINES += enable_use_xxx
5.Qt模块配置
# 添加核心模块(默认包含,可省略)
QT += core gui
# 添加其他模块(如widgets、network等)
QT += widgets network printsupport multimedia
6.条件编译
可以使用 CONFIG
变量来设置编译配置,并且可以使用条件语句进行条件编译。配置变量CONFIG 指定了编译器所要使用的选项和所需要被连接的库。配置变量中可以添加任何东西,但只有下面这些选项可以被qmake识别。
1)控制编译器
下面这些选项控制着使用哪些编译器标志:
-
release - 应用程序将以release模式连编。如果“debug”被指定,它将被忽略。
-
debug - 应用程序将以debug模式连编(与release互斥)。
-
debug_and_release - 工程同时用调试和发布模式编译。
-
build_all - 如果指定是debug_and_release模式,工程默认是同时用调试和发布模式编译。
-
ordered - 使用subdirs模板时,本选项指定了子目录应该按照给出的顺序编译。
-
warn_on - 编译器会输出尽可能多的警告信息。如果“warn_off”被指定,它将被忽略。
-
warn_off - 编译器会输出尽可能少的警告信息(与warn_on互斥)。
CONFIG += debug_and_release
contains(CONFIG, debug) {
DEFINES += DEBUG_MODE
}
2)连编类型
下面这些选项定义了所要连编的库/应用程序的类型:
-
qt - 应用程序是一个Qt应用程序,并且Qt库将会被链接。
-
c++11
,c++14
,c++17
,c++20
: 指定使用的C++标准版本。 -
thread - 应用程序是一个多线程应用程序。
-
x11 - 应用程序是一个X11应用程序或库。
-
windows - 只用于“app”模板:应用程序是一个Windows下的窗口应用程序。
-
console - 只用于“app”模板:应用程序是一个Windows下的控制台应用程序。
-
dll - 只用于“lib”模板:库是一个共享库(dll)。
-
staticlib - 只用于“lib”模板:库是一个静态库。
-
plugin - 只用于“lib”模板:库是一个插件,这将会使dll选项生效。
-
exceptions
,rtti
: 分别控制异常处理和运行时期类型识别(RTTI)的支持。 -
qml_debug
: 启用QML调试支持。
例如,如果你的应用程序使用Qt库,并且你想把它连编为一个可调试的使用了C++11标准,启用了Qt模块、警告显示、多线程支持的应用程序,你的项目文件应该会有下面这行:
CONFIG += qt debug
CONFIG += c++11 qt warn_on thread
注意,你必须使用“+=”,不要使用“=”,否则qmake就不能正确使用连编Qt的设置了,比如没法获得所编译的Qt库的类型了。
3) 声明QT库模块
如果CONFIG变量值中包含了qt这个值,qmake支持了qt的程序(因为qmake也可以用在非qt程序的编译)这就要调整一些你程序中使用的qt的模块,而QT变量,正是达到这个目的的。QT是用来声明使用到的一些额外的模块,例如:通过下面的方法使得xml和网络模块有效:
CONFIG += qt
QT += network xml
注意:默认情况下,qt包含了core 和 gui 模块,所以上面的声明仅仅是添加xml和网络模块到默认的列表中。
比如,下面的语句就是忽略了默认模块,当编译程序源码时候会导致错误:
QT = network xml
#这种方法将删除core 和 gui 两个模块
假如你想编译一个不需要gui模块的工程,你需要用“-=”操作符来去除包含。
例如:下面的语句就是小型的Qt工程会被编译
QT -= gui
下面的罗列显示了QT变量可以使用的选项,并解释了相应的特点:
-
core (included by default) QtCore module 核心模块
-
gui (included by default) QtGui module 界面模块
-
network QtNetwork module 支持网络模块
-
opengl QtOpenGL module 支持opengl图像编程
-
sql QtSql module 支持sql数据库驱动
-
svg QtSvg module 支持svg矢量图形
-
xml QtXml module 支持xml模块
-
qt3support Qt3Support module 支持qt3类
注意:添加opengl到QT变量里面,等价于往CONFIG变量里面添加。
所以对于Qt应用程序来说,没必要同时往QT变量和CONFIG变量里面添加opengl选项。
4) 关于 CONFIG(debug, debug|release)语法
CONFIG变量可以同时定义 debug 和 release,但只有一个处于active(当两个互斥的值都出现时,最后设置的处于active状态)
7.平台条件判断
# 根据平台添加不同文件
win32 {
SOURCES += windows_utils.cpp
}
unix:!macx {
SOURCES += linux_utils.cpp
}
macx {
SOURCES += mac_utils.cpp
}
8.contain关键字
contain
关键字用于检查某个字符串是否包含在另一个字符串中。如果包含,则执行接下来的代码块。
contains(VARIABLE, VALUE) {
# 如果VARIABLE包含VALUE,则执行这里的代码
}
示例:检查Qt模块是否启用
contains(QT, gui) {
message("Qt GUI module is enabled")
# 如果启用了Qt GUI模块,则执行这里的代码
} else {
message("Qt GUI module is not enabled")
}
通过使用contains
关键字和相关的条件判断,你可以灵活地控制项目的编译过程,根据不同的条件包含或排除特定的代码块,从而优化项目配置和编译过程。这在处理跨平台开发或者需要根据项目配置动态调整构建设置时特别有用。
9.目标安装路径INSTALLS
qnx: target.path = /tmp/$${TARGET}/bin
else: unix: !android: target.path = /opt/$${TARGET}/bin
!isEmpty( target.path) : INSTALLS += target
qnx: target.path = /tmp/$${TARGET}/bin: 这一行意味着如果当前目标平台是 QNX(一个实时操作系统),那么目标文件将会被安装到 /tmp/$${TARGET}/bin 目录中。其中,$${TARGET} 会被替换为你在.pro文件中指定的目标名称。
else: unix:!android: target.path = /opt/$${TARGET}/bin: 如果目标平台不是 QNX(因为上面的条件中包括了qnx),并且是一个 Unix-like 系统但不是 Android,那么目标文件将会被安装到 /opt/$${TARGET}/bin 目录中。
!isEmpty(target.path): INSTALLS += target: 这行的作用是检查 target.path 是否被设置,如果已经设置,就将目标文件添加到安装列表中。也就是说,只有当前目标路径 target.path 不为空时,才会将目标文件添加到安装列表中。
总的来说,这些行的作用是根据当前的构建平台设置了不同的目标安装路径。在不同平台上,可能会有不同的目录结构和标准,因此可以使用条件语句来根据需要进行设置。
10.exists关键字
如果你想检查某个文件或目录是否存在,可以使用 exists()
函数。这在配置项目时非常有用,比如当你想根据某些外部文件的可用性来决定是否包含某些源文件或库时。
CONFIG(debug, debug|release) {
exists(path/to/debug_feature.h) {
HEADERS += path/to/debug_feature.h
SOURCES += path/to/debug_feature.cpp
} else {
message("Debug feature header not found.")
}
} else {
exists(path/to/release_feature.h) {
HEADERS += path/to/release_feature.h
SOURCES += path/to/release_feature.cpp
} else {
message("Release feature header not found.")
}
}
通过这种方式,你可以灵活地根据不同的条件来配置你的Qt项目。