From c9406bcffe63f1ce232ce91b00be294e0135282a Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Wed, 15 Aug 2012 12:18:49 +0200 Subject: qmake: support incremental linking when embedding manifests When embedding manifests we modified the EXE/DLL after linking using the manifest tool. This breaks the incremental linking feature of MSVC. The MS way to embed a manifest without breaking incremental linking is: - let the linker create the manifest file, - create a resource that contains the manifest file, - invoke the linker again to embed the resource. The embed_manifest_{exe|dll}.prf files have been removed. All manifest logic is now in qmake's nmake makefile generator. With QMAKE_MANIFEST one can specify a custom manifest file that gets embedded without disturbing incremental linking. Task-number: QTBUG-22718 Change-Id: Idb9d2644a0577b2002cbdd2d62b695b9171b1bd5 Reviewed-by: Oswald Buddenhagen --- qmake/generators/win32/msvc_nmake.cpp | 76 ++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 5 deletions(-) (limited to 'qmake/generators/win32/msvc_nmake.cpp') diff --git a/qmake/generators/win32/msvc_nmake.cpp b/qmake/generators/win32/msvc_nmake.cpp index ff73ef7e79..a5c215a653 100644 --- a/qmake/generators/win32/msvc_nmake.cpp +++ b/qmake/generators/win32/msvc_nmake.cpp @@ -357,7 +357,8 @@ void NmakeMakefileGenerator::writeImplicitRulesPart(QTextStream &t) void NmakeMakefileGenerator::writeBuildRulesPart(QTextStream &t) { - if (project->first("TEMPLATE") == "aux") { + const QString templateName = project->first("TEMPLATE"); + if (templateName == "aux") { t << "first:" << endl; t << "all:" << endl; return; @@ -371,12 +372,65 @@ void NmakeMakefileGenerator::writeBuildRulesPart(QTextStream &t) t << "\n\t" <isActiveConfig("staticlib")) { t << "\n\t" << "$(LIBAPP) $(LIBFLAGS) /OUT:$(DESTDIR_TARGET) @<<" << "\n\t " - << "$(OBJECTS)"; + << "$(OBJECTS)" + << "\n<<"; } else { - t << "\n\t" << "$(LINK) $(LFLAGS) /OUT:$(DESTDIR_TARGET) @<< " << "\n\t " - << "$(OBJECTS) $(LIBS)"; + const bool embedManifest = ((templateName == "app" && project->isActiveConfig("embed_manifest_exe")) + || (templateName == "lib" && project->isActiveConfig("embed_manifest_dll") + && !(project->isActiveConfig("plugin") && project->isActiveConfig("no_plugin_manifest")) + )); + if (embedManifest) { + bool generateManifest = false; + const QString target = var("DEST_TARGET"); + QString manifest = project->first("QMAKE_MANIFEST"); + QString extraLFlags; + if (manifest.isEmpty()) { + generateManifest = true; + manifest = escapeFilePath(target + ".embed.manifest"); + extraLFlags = "/MANIFEST /MANIFESTFILE:" + manifest; + project->values("QMAKE_CLEAN") << manifest; + } + + const bool incrementalLinking = project->values("QMAKE_LFLAGS").filter(QRegExp("(/|-)INCREMENTAL:NO")).isEmpty(); + if (incrementalLinking) { + // Link a resource that contains the manifest without modifying the exe/dll after linking. + + QString manifest_rc = escapeFilePath(target + "_manifest.rc"); + QString manifest_res = escapeFilePath(target + "_manifest.res"); + QString manifest_bak = escapeFilePath(target + "_manifest.bak"); + project->values("QMAKE_CLEAN") << manifest_rc << manifest_res; + + t << "\n\t" << "@if not exist " << manifest_rc << " echo 1 /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 24 /* RT_MANIFEST */ " << manifest + << ">" << manifest_rc; + + if (generateManifest) { + t << "\n\tif not exist $(DESTDIR_TARGET) del " << manifest << ">NUL 2>&1"; + t << "\n\tif exist " << manifest << " copy /Y " << manifest << ' ' << manifest_bak; + const QString extraInlineFileContent = "\n!IF EXIST(" + manifest_res + ")\n" + manifest_res + "\n!ENDIF"; + t << "\n\t"; + writeLinkCommand(t, extraLFlags, extraInlineFileContent); + const QString check_manifest_bak_existence = "\n\tif exist " + manifest_bak + ' '; + t << check_manifest_bak_existence << "fc " << manifest << ' ' << manifest_bak << " && del " << manifest_bak; + t << check_manifest_bak_existence << "rc.exe /fo" << manifest_res << ' ' << manifest_rc; + t << check_manifest_bak_existence; + writeLinkCommand(t, extraLFlags, manifest_res); + t << check_manifest_bak_existence << "del " << manifest_bak; + } else { + t << "\n\t" << "rc.exe /fo" << manifest_res << " " << manifest_rc; + t << "\n\t"; + writeLinkCommand(t, extraLFlags, manifest_res); + } + } else { + // directly embed the manifest in the executable after linking + t << "\n\t"; + writeLinkCommand(t, extraLFlags); + t << "\n\t" << "mt.exe /nologo /manifest " << manifest << " /outputresource:$(DESTDIR_TARGET);1"; + } + } else { + t << "\n\t"; + writeLinkCommand(t); + } } - t << endl << "<<"; QString signature = !project->isEmpty("SIGNATURE_FILE") ? var("SIGNATURE_FILE") : var("DEFAULT_SIGNATURE"); bool useSignature = !signature.isEmpty() && !project->isActiveConfig("staticlib") && !project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH"); @@ -389,4 +443,16 @@ void NmakeMakefileGenerator::writeBuildRulesPart(QTextStream &t) t << endl; } +void NmakeMakefileGenerator::writeLinkCommand(QTextStream &t, const QString &extraFlags, const QString &extraInlineFileContent) +{ + t << "$(LINK) $(LFLAGS)"; + if (!extraFlags.isEmpty()) + t << ' ' << extraFlags; + t << " /OUT:$(DESTDIR_TARGET) @<<\n" + << "$(OBJECTS) $(LIBS)"; + if (!extraInlineFileContent.isEmpty()) + t << ' ' << extraInlineFileContent; + t << "\n<<"; +} + QT_END_NAMESPACE -- cgit v1.2.3