将 qt 构建为静态库
接以前的博客
之前失败了,现在成功了。
静态链接 qt 失败
配置
使用 ps1 脚本进行配置和构建
$env:CC = "clang"
$env:CXX = "clang++"$skiped_modules = @("qttools""qtdoc""qttranslations""qtlanguageserver""qtdeclarative""qtquicktimeline""qtquick3d""qtgraphs""qtlocation""qtlottie""qtmqtt""qtopcua""qtquick3dphysics""qtquickeffectmaker""qtvirtualkeyboard""qtwebengine""qtwebview"
)Invoke-Expression "../configure.bat -static -skip $($skiped_modules -join ",") -prefix ${install_path}"
if ($LASTEXITCODE)
{throw "$source_path 配置失败"
}cmake -G "Ninja" $source_path `-DCMAKE_C_COMPILER="clang" `-DCMAKE_CXX_COMPILER="clang++" `-DCMAKE_C_STANDARD=17 `-DCMAKE_CXX_STANDARD=20 `-DCMAKE_BUILD_TYPE=Release `-DCMAKE_INSTALL_PREFIX="${install_path}"if ($LASTEXITCODE)
{throw "$source_path 配置失败"
}ninja -j12
if ($LASTEXITCODE)
{throw "$source_path 编译失败"
}ninja install
- 因为构建老是失败,所以我跳过了一些模块。
- 构建为静态库地要点是配置时添加
-static
选项。
手动链接
静态链接 qt 时需要手动额外链接一些库,否则会链接错误。下面是我链接的库
target_link_libraries(${target_name} PUBLICsetupapizsynchronizationpcre2-16double-conversionntdllversionharfbuzzb2zstdnetapi32authzws2_32winmmuserenvdwritepngmd4cd3d12dxgid3d11wtsapi32imm32freetypeshcoredwmapid3d9shlwapiruntimeobjectuxtheme
)
此外,还要链接 qt5/plugins/platforms/libqwindows.a
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
最终的可执行文件项目要添加如下代码
#include "QtPlugin"Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
这个宏展开后是:
extern ::QStaticPlugin const qt_static_plugin_QWindowsIntegrationPlugin();namespace
{struct StaticQWindowsIntegrationPluginPluginInstance{StaticQWindowsIntegrationPluginPluginInstance(){qRegisterStaticPluginFunction(qt_static_plugin_QWindowsIntegrationPlugin());}};StaticQWindowsIntegrationPluginPluginInstance staticQWindowsIntegrationPluginInstance;
} // namespace
首先声明了一个函数,然后在匿名命名空间中定义了一个 StaticQWindowsIntegrationPluginPluginInstance
类,在构造函数中会调用
qRegisterStaticPluginFunction(qt_static_plugin_QWindowsIntegrationPlugin());
然后在匿名命名空间中定义了 StaticQWindowsIntegrationPluginPluginInstance
类的对象。
也就是说任何包含了下面这段代码
#include "QtPlugin"Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
的翻译单元都会声明一次函数,然后创建一个匿名命名空间中的 StaticQWindowsIntegrationPluginPluginInstance
类的对象,利用构造函数去注册所声明的函数。
如果多个翻译单元都包含了这段代码,就会有多个匿名命名空间中的 StaticQWindowsIntegrationPluginPluginInstance
类和它们的对象,造成不必要的代码膨胀。所以这段代码只在一个翻译单元中包含一次就够了,但是不能是在静态库的翻译单元中包含这段代码以期望能够注册 qt 模块,因为静态库中的对象的构造函数不会执行,这个匿名命名空间中的 StaticQWindowsIntegrationPluginPluginInstance
类和它的对象会被链接器丢弃。
所以,最好是在最终的可执行文件项目中的其中一个翻译单元包含一次这段代码。例如可以放在 main.cpp
中。
进行了上述操作后,就可以静态链接 qt 库了,此时已经不需要 qwindows.dll
这个动态库插件了。