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 などの情報を元に、実際にビルドするモジュールを決定し、次の段階に移行します。

あわせて読みたい