qtcreator版本_Qt Creator 源码学习 03:qtcreator.pro

当我们准备好 Qt Creator 的源代码之后,首先进入到它的目录,来看一下它的源代码目录有什么奥秘。 这里一共有 9 个文件夹和 9 个文件。我们来一一看看它们都是干什么用的。
  • .git: 版本控制 git 的隐藏目录,这与 Qt Creator 代码没有关系。
  • bin: 生成 Linux 平台 shell 脚本。
  • dist: 安装文件配置信息和版本更新记录。
  • doc: 生成 doxygen 文档的配置文件。
  • qbs: QBS 配置文件。QBS,即 Qt Build Suite,是一种跨平台的编译工具,目的是将高层的项目描述(使用类似 QML 的语言)转换成底层的编译描述(供 make 等工具使用的信息)。它可以简化多平台的编译过程。QBS 与 qmake 类似,区别在于前者适用于任意项目,而后者一般仅供 Qt 项目使用。我们在阅读代码时将关注 qmake,不会深入研究 QBS 的使用。
  • scripts: Qt Creator 使用的 perl 以及 python 等脚本。
  • share: 源代码中所需要的一些非代码共享文件,例如代码模板等。
  • src: Qt Creator 源代码文件。
  • tests: Qt Creator 测试代码。
  • .gitignore: git 忽略文件配置。
  • .gitmodules: git 子模块配置。
  • HACKING: Qt Creator 编码规范。
  • LICENSE.GPL3-EXCEPT: GPLv3 协议。
  • qtcreator.pri: Qt Creator 项目需要使用的通用配置,该文件一般会被 include 到大部分 pro 文件。
  • qtcreator.pro: Qt Creator 的 qmake 项目文件。
  • qtcreator.qbs: Qt Creator 的 QBS 项目文件。
  • qtcreatordata.pri: Qt Creator 数据相关的配置。
  • README.md: 有关如何编译 Qt Creator 等相关事宜的一些说明。
阅读源代码,一般可以从 main() 着手。但是阅读 Qt 项目的源代码,我们也可以从 pro 文件开始。pro 文件是 Qt 项目组织结构,规定了我们希望该项目如何编译、编译之后要做什么操作等。 下面我们从根目录的 qtcreator.pro 开始。使用 Qt Creator 或者任意文本编辑器打开 qtcreator.pro,开始真正的代码阅读。
include(qtcreator.pri)
第一行是 include qtcreator.pri 。前面我们提到过,qtcreator.pri 中定义了很多函数和适用于各个模块的通用操作。pri 文件可以理解为 pro 文件片段,可以使用 include 操作符将其引入一个 pro 文件。qmake 会自动处理引用操作,类似于将 pri 文件的全部内容复制到 include 语句处。这与 C++ 的 #include 指令类似。这里的处理是线性的,也就是 qmake 会从上向下进行解析。因此,如果你在 pri 中定义了一个函数,那么必须在 include 语句之后才能正常使用该函数。这是在使用时需要注意的。有关 qtcreator.pri 文件的内容,会在以后的文章中详细介绍。如果你使用 Qt Creator 打开, include 语句会在左侧的项目树中显示一个节点。这种节点不需要物理上的文件夹隔离,只需要 include 不同的 pri 文件即可。这样,即便你的所有文件都在同一个目录下,你也可以使用 pri 文件创建出来多个虚拟目录节点。这样的项目结构看起来会清晰很多。
#version check qt
!minQtVersion(5, 6, 0) {
message("Cannot build Qt Creator with Qt version ${QT_VERSION}.")
error("Use at least Qt 5.6.0.")
}
接下来的几行用于判断 Qt 的版本。 minQtVersion() 是在 qtcreator.pri 中定义的函数。没错!pro 也可以定义自己的函数!这正是 pro 的强大之处。我们会在后面详细介绍如何定义函数。顾名思义,这个函数函数用于判断 Qt 的版本。前面的 ! 即取非运算符,这与 C++ 一致。当 Qt 的版本低于 5.6.0 时,执行块中的操作。 message() 是 qmake 预定义的函数,类似于 qDebug() ,可以在控制台输出一段文本。这里我们输出的是“Cannot build Qt Creator with Qt version $${QT_VERSION}.”。字符串最后的 $${QT_VERSION} 是占位符,会使用 QT_VERSION 变量的内容进行替换。这一操作被称为变量展开(variable expansion)。有关 $$ 以及相关运算符的使用相当重要。 $$ 运算符通常用于展开变量的内容,展开的内容可以用于变量的赋值,也可以用于函数的传参。例如:
EVERYTHING = $SOURCES $HEADERS
message("The project contains the following files:")
message($EVERYTHING)
上面的代码中,第一行将 SOURCESHEADERS 的内容赋值给 EVERYTHING ;第三行则将 EVERYTHING 作为函数参数赋值给 message() 函数。如果没有 $$ 运算符,将只会输出 EVERYTHING 字符串。 变量可以保存环境变量。这些变量可以在 qmake 执行时计算出,或者直接包含在 Makefile 中以便构建时使用。如果需要在 qmake 运行时获取环境变量的值,使用 $$()$${} 运算符。例如:
DESTDIR = $(PWD)
message(The project will be installed in $DESTDIR)
在上面代码中, PWD 是 qmake 内置的一个环境变量,用于表示当前正在处理的文件所在文件夹的绝对路径。使用 $$()${} 运算符,会在 qmake 运行时将值赋给 DESTDIR 。如果需要在生成 Makefile 时获取环境变量的值,则需要使用 $() 运算符。例如:
DESTDIR = $(PWD)
message(The project will be installed in $DESTDIR)
DESTDIR = $(PWD)
message(The project will be installed in the value of PWD)
message(when the Makefile is processed.)
在上面的语句中, PWD 的值在 qmake 处理是就已经获取到了,但是 $(PWD) 则会在生成的 Makefile 中赋值给 DESTDIR 。这能够保证在处理 Makefile 时环境变量是正确的。 通过上面的解释,我们知道, $${QT_VERSION} 会在 qmake 运行时进行变量展开。 下面再来看另外的代码:
TEMPLATE = subdirs
CONFIG += ordered
这是 qmake 典型的配置。 TEMPLATE 即代码模板,将告诉 qmake 我们要怎么生成最后的文件。它的可选值分别是:
  • app:创建用于构建可执行文件的 Makefile。
  • lib:创建用于构建库的 Makefile。
  • subdirs:创建依次构建子目录中文件的 Makefile。子目录使用SUBDIRS变量指定。
  • aux:创建不构建任何东西的 Makefile。如果构建目标不需要编译器,就可以使用这个模板。例如,你的项目使用的是解释型语言,就可以这么做。注意,此时生成的 Makefile 仅适用于基于 Makefile 的生成器,不一定能供 vcxproj 或 Xcode 使用。
  • vcapp:仅适用于 Windows 平台,用于生成 VS 应用程序项目。
  • vclib:仅适用于 Windows 平台,用于生成 VS 库项目。
我们最常用的是前三种设置。对于大型项目,一般会分成多个源代码文件夹,因此,Qt Creator 使用的是 subdirs。接下来一行, CONFIG += ordered 意思是,按照 SUBDIRS 书写顺序来编译。很多时候,我们虽然将源代码分为不同目录,但是这些目录之间是存在依赖关系的。比如,一个基础类库要被其它所有模块使用,在编译时,该类库应该首先被编译。这要求我们按照一定的顺序来添加 SUBDIRS 。有关这一点,Qt Creator 是这样做的:
SUBDIRS = src share
unix:!macx:!isEmpty(copydata):SUBDIRS += bin
!isEmpty(BUILD_TESTS):SUBDIRS += tests
首先, SUBDIRS 只有两个目录:src 和 share。按照顺序,应该是先编译 src,然后编译 share。后面则是一串复杂的判断:对于 Unix 平台( unix ),如果不是 Mac OS( !macx ),并且 copydata 不为空( !isEmpty(copydata) ),则需要再增加一个 bin 目录。最后再判断,如果 BUILD_TESTS 不为空( !isEmpty(BUILD_TESTS) ),则再增加一个 tests 目录。 += 运算符就像它所展示的那样,用于追加新的值。 copydataBUILD_TESTS 都是在 qtcreator.pri 中定义的宏。因为我们是在最前面 include 了 qtcreator.pri,所以我们可以自由使用在 qtcreator.pri 文件中定义的变量。类似 !isEmpty(BUILD_TESTS):SUBDIRS += tests 这样的写法是一种简写,完整的写法应该如下所示:
!isEmpty(BUILD_TESTS) {
SUBDIRS += tests
}
有关 isEmpty() 这样的函数,我们会在下面详细介绍。我们在看这段代码时,可以同 C++ 代码作类比,以便我们理解:
SUBDIRS = src share
if (unix) {
if (!macx) {
if (!isEmpty(copydata)) {
SUBDIRS += bin
}
}
}
if (!isEmpty(BUILD_TESTS)) {
SUBDIRS += tests
}
接下来我们遇到的是
DISTFILES += dist/copyright_template.txt \
README.md \
$files(dist/changes-*) \
...
DISTFILES 知道需要在最终的目标包括的文件。按照 qmake 的文档,这一特性只适用于 UnixMake。这里我们又遇到了熟悉的 $$file() ,只不过这里不是变量展开,而是函数调用。 qmake 提供了两类函数:替换函数(replace functions)和测试函数(test fucntion)。替换函数用于处理数据并将处理结果返回;测试函数的返回值只能是 bool 值,并且可以用于一些测试的情形。在使用时,替换函数需要添加 $$ 先导符而测试函数则不需要。 $$file() 正是一个替换函数,接受一个正则表达式作为参数,其返回值是所有符合这个正则表达式的文件名列表。因此, $$file(dist/changes-*) 返回的是在当前目录下的 dist 文件夹中,所有以 changes- 开头的文件,将它们全部添加到了 DISTFILES 。另外,这一函数还可以有第二个参数,是一个 bool 值,默认是 false ,表示是不是要递归寻找文件。 之后我们看到了
exists(src/shared/qbs/qbs.pro) {
...
}
exists() 则是一个测试函数,顾名思义,该函数用于测试其参数作为文件名,所代表的文件是否存在。注意测试函数的使用:它可以直接作为测试条件,后面跟着一对大括号,如果函数返回值为 true 则执行块中的语句。这里我们发现 src/shared/qbs/qbs.pro 并不存在,因此其中的语句并不会执行。 下面是语句
contains(QT_ARCH, i386): ARCHITECTURE = x86
else: ARCHITECTURE = $QT_ARCH
QT_VERSION 是 qmake 内置的一个变量,用于表示 Qt 的架构。很明显, contains() 是一个测试函数,其函数原型是 contains(variablename, value) ,当变量 variablename 中包含了 value 时,测试通过。那么,上面语句即是,如果 QT_ARCH 中有 i386 ,则将 ARCHITECTURE 赋值为 x86 ,否则就是 $$QT_ARCH 。注意在使用 contains 函数时, QT_ARCH 并没有使用 $$ 运算符。因为在使用该函数时,第一个参数是变量名,函数会自己取该变量名的实际值。
macx: PLATFORM = "mac"
else:win32: PLATFORM = "windows"
else:linux-*: PLATFORM = "linux-${ARCHITECTURE}"
else: PLATFORM = "unknown"
定义了一个新的宏 PLATFORM 。注意这里使用了前面刚刚定义的 ARCHITECTURE 宏。 接下来,
BASENAME = $(INSTALL_BASENAME)
isEmpty(BASENAME): BASENAME = qt-creator-${PLATFORM}$(INSTALL_EDITION)-${QTCREATOR_VERSION}$(INSTALL_POSTFIX)
是一种常见的写法。首先,我们定义了 BASENAME 宏为 $$(INSTALL_BASENAME) ;之后,如果 BASENAME 为空的话(使用了测试函数 isEmpty() 进行判断),则定义新的 BASENAME 的值。这种写法一方面允许我们在编译时通过传入自定义值改变默认设置(也就是说,如果之前定义了 INSTALL_BASENAME ,那么就会使用我们定义的值),否则就会生成一个默认值。以后我们会发现,Qt Creator 的 pro 文件中,很多地方都使用了类似的写法。 跳过部分代码,接下来是一大段:
macx {    APPBUNDLE = "$OUT_PWD/bin/Qt Creator.app"    BINDIST_SOURCE = "$OUT_PWD/bin/Qt Creator.app"    BINDIST_INSTALLER_SOURCE = $BINDIST_SOURCE    deployqt.commands = $PWD/scripts/deployqtHelper_mac.sh \"${APPBUNDLE}\" \"$[QT_INSTALL_TRANSLATIONS]\" \"$[QT_INSTALL_PLUGINS]\" \"$[QT_INSTALL_IMPORTS]\" \"$[QT_INSTALL_QML]\"    codesign.commands = codesign --deep -s \"$(SIGNING_IDENTITY)\" $(SIGNING_FLAGS) \"${APPBUNDLE}\"    dmg.commands = $PWD/scripts/makedmg.sh $OUT_PWD/bin ${BASENAME}.dmg    #dmg.depends = deployqt    QMAKE_EXTRA_TARGETS += codesign dmg} else {    BINDIST_SOURCE = "$(INSTALL_ROOT)$QTC_PREFIX"    BINDIST_INSTALLER_SOURCE = "$BINDIST_SOURCE/*"    deployqt.commands = python -u $PWD/scripts/deployqt.py -i \"$(INSTALL_ROOT)$QTC_PREFIX\" \"$(QMAKE)\"    deployqt.depends = install    win32 {        deployartifacts.depends = install        deployartifacts.commands = git clone "git://code.qt.io/qt-creator/binary-artifacts.git" -b $BINARY_ARTIFACTS_BRANCH&& xcopy /s /q /y /i "binary-artifacts\\win32" \"$(INSTALL_ROOT)$QTC_PREFIX\"&& rmdir /s /q binary-artifacts        QMAKE_EXTRA_TARGETS += deployartifacts    }}
这里使用 macx 分为两部分。很明显,如果系统是 macx ,则定义宏 APPBUNDLE 。我们需要详细解释的是,在定义新的变量时,Qt Creator 所用到的那些宏。首先, $$OUT_PWD 是 qmake 生成的 Makefile 所在的文件夹。 下面我们会看到一个新的语法: $$[] 。这是取 qmake 的属性。qmake 内置了很多属性值,例如:
message(Qt version: $[QT_VERSION])message(Qt is installed in $[QT_INSTALL_PREFIX])message(Qt resources can be found in the following locations:)message(Documentation: $[QT_INSTALL_DOCS])message(Header files: $[QT_INSTALL_HEADERS])message(Libraries: $[QT_INSTALL_LIBS])message(Binary files (executables): $[QT_INSTALL_BINS])message(Plugins: $[QT_INSTALL_PLUGINS])message(Data files: $[QT_INSTALL_DATA])message(Translation files: $[QT_INSTALL_TRANSLATIONS])message(Settings: $[QT_INSTALL_CONFIGURATION])message(Examples: $[QT_INSTALL_EXAMPLES])
在运行时,qmake 可以自定义属性:
qmake -set PROPERTY VALUE
然后,我们就可以用下面语句获取这个属性:
qmake -query PROPERTY
在 pro 文件中,则可以使用 $$[] 获取这些属性。可以查阅文档找到 qmake 内置了哪些属性。 语句
deployqt.commands = $PWD/scripts/deployqtHelper_mac.sh \"${APPBUNDLE}\" \"$[QT_INSTALL_TRANSLATIONS]\" \"$[QT_INSTALL_PLUGINS]\" \"$[QT_INSTALL_IMPORTS]\" \"$[QT_INSTALL_QML]\"
定义了一个目标 deployqt,这个目标的命令是 $$PWD/scripts/deployqtHelper_mac.sh \"$${APPBUNDLE}\" \"$$[QT_INSTALL_TRANSLATIONS]\" \"$$[QT_INSTALL_PLUGINS]\" \"$$[QT_INSTALL_IMPORTS]\" \"$$[QT_INSTALL_QML]\" 。我们可以使用 message() 函数输出这条命令。命令的具体实现暂不深究,感兴趣的话可以阅读 scripts/deployqtHelper_mac.sh 文件。接下来的语句是类似的。最后,这些定义的目标被添加到 QMAKE_EXTRA_TARGETS 。这才是真正重要的内容。 尽管 qmake 努力成为一个跨平台的构建工具,但是很多时候,我们不得不使用特定平台的语句。例如,一个常见的任务是,在编译完成之后,将预置的配置文件复制到特定目录。这种目标可以通过类似的语法进行定义,然后将定义好的目标添加到 QMAKE_EXTRA_TARGETS 。当 qmake 运行完毕后,会接着执行这些目标,直到编译成功。 例如,
mytarget.target = .buildfile
mytarget.commands = touch $mytarget.target
mytarget.depends = mytarget2
mytarget2.commands = @echo Building $mytarget.target
mytarget 是一个自定义目标; mytarget.target 是这个自定义目标的名字。之后生成的 Makefile 中将会使用这个名字作为 target。 mytarget.commands 定义了这个目标的命令:使用 touch 命令生成一个文件。 mytarget.depends 定义这个目标依赖于 mytarget2 ,尽管 mytarget2 是在后面定义的。最后,我们将这两个目标都添加到 QMAKE_EXTRA_TARGETS
QMAKE_EXTRA_TARGETS += codesign dmg
最后,我们来看
win32 {
deployqt.commands ~= s,/,\\\\,g
bindist.commands ~= s,/,\\\\,g
bindist_installer.commands ~= s,/,\\\\,g
installer.commands ~= s,/,\\\\,g
}
有是一个新的语法 ~=~= 运算符将符合正则表达式的内容替换为后面的部分。例如 DEFINES ~= s/QT_[DT].+/QT 会将以 QT_DQT_T 开头的文本替换为 QT 。后面 s,/,\\\\,g 是替换操作。三个逗号分为四个部分:第一个 s 表示输入字符串;第二个 / 表示 /;第三个 \\\\ 表示 \,之所以是四个,是因为 \ 需要转义;第四个 g 表示全局替换。合起来的意思就是,将输入字符串中的 / 全部替换为 \。这是适配命令路径中,Unix 的 / 和 Windows 的 \。这一个技巧在编写跨平台代码中非常有用,很多时候我们在 pro 中给出了 Unix 格式的路径,只需要使用简单的语句,例如
PWD_WIN = ${PWD}
PWD_WIN ~= s,/,\\,g
就可以转换为合法的 Windows 路径。 本章我们着重学习了 Qt Creator 的主项目文件 qtcreator.pro 的写法。下一节我们将详细介绍 qtcreator.pri 的写法。

qtcreator版本_Qt Creator 源码学习 03:qtcreator.pro_第1张图片

你可能感兴趣的:(qtcreator版本,qt打开项目只有pro文件)