Qt Lite の仕組み: qmake 編 (1)
この記事は、configure 編 の続きになります。
configure が qmake を呼び出した際に実際に実行されるのは qt.pro になります。
TEMPLATE = subdirs
となっているので、サブディレクトリ型のプロジェクトファイルになります。
それ以降は、qmake の Test関数 や Replace関数 を駆使したコードになっているため、慣れるまでは追うのが結構大変です。
モジュールの情報を構築する
# Extract submodules from .gitmodules.
lines = $$cat(.gitmodules, lines)
for (line, lines) {
mod = $$replace(line, "^\\[submodule \"([^\"]+)\"\\]$", \\1)
!equals(mod, $$line) {
module = $$mod
modules += $$mod
} else {
prop = $$replace(line, "^$$escape_expand(\\t)([^ =]+) *=.*$", \\1)
!equals(prop, $$line) {
val = $$replace(line, "^[^=]+= *", )
module.$${module}.$$prop = $$split(val)
} else {
error("Malformed line in .gitmodules: $$line")
}
}
}
Qt 5 は、.gitmodules という git のサブモジュール管理用のファイルを利用(悪用?)して、Qt のリポジトリ間の依存関係などを定義しています。上記のコードでは、そのファイルを読み込んで、パラメーターを qmake の変数に変換しています。
ビルドしないモジュールの取得
QT_SKIP_MODULES =
# This is a bit hacky, but a proper implementation is not worth it.
args = $$QMAKE_EXTRA_ARGS
contains(args, -redo): \
args += $$cat($$OUT_PWD/config.opt, lines)
for (ever) {
isEmpty(args): break()
a = $$take_first(args)
equals(a, -skip) {
isEmpty(args): break()
m = $$take_first(args)
contains(m, -.*): next()
m ~= s/^(qt)?/qt/
!contains(modules, $$m): \
error("-skip command line argument used with non-existent module '$$m'.")
QT_SKIP_MODULES += $$m
}
}
-skip モジュール名 形式のオプションを走査し、明示的にビルド対象から外すものの一覧を構築しています。
モジュールの依存関係の解決
modules = $$sort_depends(modules, module., .depends .recommends .serialize)
modules = $$reverse(modules)
for (mod, modules) {
project = $$eval(module.$${mod}.project)
equals(project, -): \
next()
deps = $$eval(module.$${mod}.depends)
recs = $$eval(module.$${mod}.recommends) $$eval(module.$${mod}.serialize)
for (d, $$list($$deps $$recs)): \
!contains(modules, $$d): \
error("'$$mod' depends on undeclared '$$d'.")
contains(QT_SKIP_MODULES, $$mod): \
next()
!isEmpty(QT_BUILD_MODULES):!contains(QT_BUILD_MODULES, $$mod): \
next()
isEmpty(project) {
!exists($$mod/$${mod}.pro): \
next()
$${mod}.subdir = $$mod
} else {
!exists($$mod/$$project): \
next()
$${mod}.file = $$mod/$$project
$${mod}.makefile = Makefile
}
$${mod}.target = module-$$mod
for (d, deps) {
!contains(SUBDIRS, $$d) {
$${mod}.target =
break()
}
$${mod}.depends += $$d
}
isEmpty($${mod}.target): \
next()
for (d, recs) {
contains(SUBDIRS, $$d): \
$${mod}.depends += $$d
}
SUBDIRS += $$mod
}
qmake の内部で実装されている sort_depends という専用関数を利用してモジュールの整理をし(逆順でソートし)た後、各モジュールがビルド可能かをチェックしています。
依存関係が解決できなかったり、プロジェクトファイルが存在しなかったり、前段でスキップ対象に指定されていたりしたものは除外します。
ビルド対象のものは、最終的に依存関係の情報が含まれた状態で SUBDIRS に追加されます。
qmake による configure
load(qt_configure)
qt_configure.prf をロードし実行します。
続きについては別途記事を書きますので、お楽しみに!
まとめ
qmake が実行されると、qt.pro が .gitmodule などの情報を元に、実際にビルドするモジュールを決定し、次の段階に移行します。