From 6d620f5ad7fc9767a7232659cde1d7c9abf78285 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 15 Sep 2015 15:36:07 -0400 Subject: VS: Add manifest tool settings to VS 8 and 9 project files Always generate a VCManifestTool element in targets that compile. --- Source/cmLocalVisualStudio7Generator.cxx | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index cf67251..191f739 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -972,25 +972,29 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(std::ostream& fout, fout << "\t\t\t\tProxyFileName=\"$(InputName)_p.c\"/>\n"; // end of GetVersion() >= cmGlobalVisualStudioGenerator::VS8) { + const char* manifestTool = "VCManifestTool"; + if (this->FortranProject) + { + manifestTool = "VFManifestTool"; + } + fout << + "\t\t\tFortranProject) - { - manifestTool = "VFManifestTool"; - } - fout << "\t\t\t\n"; + fout << "\n\t\t\t\tUseFAT32Workaround=\"true\""; } + fout << "/>\n"; } this->OutputTargetRules(fout, configName, target, libName); -- cgit v0.12 From d488b5c9764805222d33d50cc070fd4f71aab262 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 15 Sep 2015 15:57:19 -0400 Subject: Ninja: Always add OBJECT_DIR variable to link rules The placeholder is always available in Makefile generators so make it available from the Ninja generator too. --- Source/cmNinjaNormalTargetGenerator.cxx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index b855bea..f62f8ad 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -579,11 +579,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() vars["TARGET_PDB"] = base + suffix + dbg_suffix; } + const std::string objPath = GetTarget()->GetSupportDirectory(); + vars["OBJECT_DIR"] = ConvertToNinjaPath(objPath); + EnsureDirectoryExists(objPath); + if (this->GetGlobalGenerator()->IsGCCOnWindows()) { - const std::string objPath = GetTarget()->GetSupportDirectory(); - vars["OBJECT_DIR"] = ConvertToNinjaPath(objPath); - EnsureDirectoryExists(objPath); // ar.exe can't handle backslashes in rsp files (implicitly used by gcc) std::string& linkLibraries = vars["LINK_LIBRARIES"]; std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/'); -- cgit v0.12 From da00be6359055ffdb2067a9ec1e817eb782ad145 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 15 Sep 2015 15:51:19 -0400 Subject: MSVC: Rewrite manifest file handling with Makefile and Ninja Add a helper class private to "cmcmd.cxx" to contain the implementation. Update the link logic to use the intermediate files directory for each target to hold manifest and resource files before embedding into the binary. Preserve the old behavior of placing the .manifest file next to the binary when not linking incrementally even though it will be embedded. --- Modules/Platform/Windows-MSVC.cmake | 4 +- Source/cmcmd.cxx | 405 ++++++++++++++++++++---------------- Source/cmcmd.h | 14 -- 3 files changed, 223 insertions(+), 200 deletions(-) diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake index 94470c3..d4b1cd8 100644 --- a/Modules/Platform/Windows-MSVC.cmake +++ b/Modules/Platform/Windows-MSVC.cmake @@ -274,8 +274,8 @@ set (CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL_INIT ${CMAKE_EXE_LINKER_FLAGS_MINSIZER macro(__windows_compiler_msvc lang) if(NOT MSVC_VERSION LESS 1400) # for 2005 make sure the manifest is put in the dll with mt - set(_CMAKE_VS_LINK_DLL " -E vs_link_dll ") - set(_CMAKE_VS_LINK_EXE " -E vs_link_exe ") + set(_CMAKE_VS_LINK_DLL " -E vs_link_dll --intdir= ") + set(_CMAKE_VS_LINK_EXE " -E vs_link_exe --intdir= ") endif() set(CMAKE_${lang}_CREATE_SHARED_LIBRARY "${_CMAKE_VS_LINK_DLL} ${CMAKE_CL_NOLOGO} ${CMAKE_START_TEMP_FILE} /out: /implib: /pdb: /dll /version:.${_PLATFORM_LINK_FLAGS} ${CMAKE_END_TEMP_FILE}") diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index aa70aa0..307e78b 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -1355,6 +1355,34 @@ int cmcmd::WindowsCEEnvironment(const char* version, const std::string& name) return -1; } +class cmVSLink +{ + int Type; + bool Verbose; + bool Incremental; + bool LinkGeneratesManifest; + std::vector LinkCommand; + std::string LinkerManifestFile; + std::string ManifestFile; + std::string ManifestFileRC; + std::string ManifestFileRes; + std::string TargetFile; +public: + cmVSLink(int type, bool verbose) + : Type(type) + , Verbose(verbose) + , Incremental(false) + , LinkGeneratesManifest(true) + {} + bool Parse(std::vector::const_iterator argBeg, + std::vector::const_iterator argEnd); + int Link(); +private: + int LinkIncremental(); + int LinkNonIncremental(); + int RunMT(std::string const& out, bool notify); +}; + // For visual studio 2005 and newer manifest files need to be embedded into // exe and dll's. This code does that in such a way that incremental linking // still works. @@ -1364,11 +1392,7 @@ int cmcmd::VisualStudioLink(std::vector& args, int type) { return -1; } - bool verbose = false; - if(cmSystemTools::GetEnv("VERBOSE")) - { - verbose = true; - } + bool verbose = cmSystemTools::GetEnv("VERBOSE")? true:false; std::vector expandedArgs; for(std::vector::iterator i = args.begin(); i != args.end(); ++i) @@ -1389,79 +1413,19 @@ int cmcmd::VisualStudioLink(std::vector& args, int type) expandedArgs.push_back(*i); } } - bool hasIncremental = false; - bool hasManifest = true; - for(std::vector::iterator i = expandedArgs.begin(); - i != expandedArgs.end(); ++i) - { - if(cmSystemTools::Strucmp(i->c_str(), "/INCREMENTAL:YES") == 0) - { - hasIncremental = true; - } - if(cmSystemTools::Strucmp(i->c_str(), "/INCREMENTAL") == 0) - { - hasIncremental = true; - } - if(cmSystemTools::Strucmp(i->c_str(), "/MANIFEST:NO") == 0) - { - hasManifest = false; - } - } - if(hasIncremental && hasManifest) - { - if(verbose) - { - std::cout << "Visual Studio Incremental Link with embedded manifests\n"; - } - return cmcmd::VisualStudioLinkIncremental(expandedArgs, type, verbose); - } - if(verbose) - { - if(!hasIncremental) - { - std::cout << "Visual Studio Non-Incremental Link\n"; - } - else - { - std::cout << "Visual Studio Incremental Link without manifests\n"; - } - } - return cmcmd::VisualStudioLinkNonIncremental(expandedArgs, - type, hasManifest, verbose); -} -int cmcmd::ParseVisualStudioLinkCommand(std::vector& args, - std::vector& command, - std::string& targetName) -{ - std::vector::iterator i = args.begin(); - i++; // skip -E - i++; // skip vs_link_dll or vs_link_exe - command.push_back(*i); - i++; // move past link command - for(; i != args.end(); ++i) - { - command.push_back(*i); - if(i->find("/Fe") == 0) - { - targetName = i->substr(3); - } - if(i->find("/out:") == 0) - { - targetName = i->substr(5); - } - } - if(targetName.empty() || command.empty()) + cmVSLink vsLink(type, verbose); + if (!vsLink.Parse(expandedArgs.begin()+2, expandedArgs.end())) { return -1; } - return 0; + return vsLink.Link(); } -bool cmcmd::RunCommand(const char* comment, +static bool RunCommand(const char* comment, std::vector& command, bool verbose, - int* retCodeOut) + int* retCodeOut = 0) { if(verbose) { @@ -1503,8 +1467,126 @@ bool cmcmd::RunCommand(const char* comment, return retCode == 0; } -int cmcmd::VisualStudioLinkIncremental(std::vector& args, - int type, bool verbose) +bool cmVSLink::Parse(std::vector::const_iterator argBeg, + std::vector::const_iterator argEnd) +{ + // Parse our own arguments. + std::string intDir; + std::vector::const_iterator arg = argBeg; + while (arg != argEnd && cmHasLiteralPrefix(*arg, "-")) + { + if (*arg == "--") + { + ++arg; + break; + } + else if (cmHasLiteralPrefix(*arg, "--intdir=")) + { + intDir = arg->substr(9); + ++arg; + } + else + { + std::cerr << "unknown argument '" << *arg << "'\n"; + return false; + } + } + if (intDir.empty()) + { + return false; + } + + // The rest of the arguments form the link command. + if (arg == argEnd) + { + return false; + } + this->LinkCommand.insert(this->LinkCommand.begin(), arg, argEnd); + + // Parse the link command to extract information we need. + for (; arg != argEnd; ++arg) + { + if (cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL:YES") == 0) + { + this->Incremental = true; + } + else if (cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0) + { + this->Incremental = true; + } + else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0) + { + this->LinkGeneratesManifest = false; + } + else if (cmHasLiteralPrefix(*arg, "/Fe")) + { + this->TargetFile = arg->substr(3); + } + else if (cmHasLiteralPrefix(*arg, "/out:")) + { + this->TargetFile = arg->substr(5); + } + } + + if (this->TargetFile.empty()) + { + return false; + } + + this->ManifestFile = intDir + "/embed.manifest"; + this->LinkerManifestFile = intDir + "/intermediate.manifest"; + + if (this->Incremental) + { + // We will compile a resource containing the manifest and + // pass it to the link command. + this->ManifestFileRC = intDir + "/manifest.rc"; + this->ManifestFileRes = intDir + "/manifest.res"; + this->LinkCommand.push_back(this->ManifestFileRes); + } + else + { + // CMake places the linker-generated manifest next to the binary (as if it + // were not to be embedded) when not linking incrementally. + this->ManifestFile = this->TargetFile + ".manifest"; + this->LinkerManifestFile = this->ManifestFile; + } + + if (this->LinkGeneratesManifest) + { + this->LinkCommand.push_back("/MANIFEST"); + this->LinkCommand.push_back("/MANIFESTFILE:" + this->LinkerManifestFile); + } + + return true; +} + +int cmVSLink::Link() +{ + if (this->Incremental && + this->LinkGeneratesManifest) + { + if (this->Verbose) + { + std::cout << "Visual Studio Incremental Link with embedded manifests\n"; + } + return LinkIncremental(); + } + if (this->Verbose) + { + if (!this->Incremental) + { + std::cout << "Visual Studio Non-Incremental Link\n"; + } + else + { + std::cout << "Visual Studio Incremental Link without manifests\n"; + } + } + return LinkNonIncremental(); +} + +int cmVSLink::LinkIncremental() { // This follows the steps listed here: // http://blogs.msdn.com/zakramer/archive/2006/05/22/603558.aspx @@ -1528,161 +1610,116 @@ int cmcmd::VisualStudioLinkIncremental(std::vector& args, // 7. Finally, the Linker does another incremental link, but since the // only thing that has changed is the *.res file that contains the // manifest it is a short link. - std::vector linkCommand; - std::string targetName; - if(cmcmd::ParseVisualStudioLinkCommand(args, linkCommand, targetName) == -1) - { - return -1; - } - std::string manifestArg = "/MANIFESTFILE:"; - std::vector rcCommand; - rcCommand.push_back(cmSystemTools::FindProgram("rc.exe")); - std::vector mtCommand; - mtCommand.push_back(cmSystemTools::FindProgram("mt.exe")); - std::string tempManifest; - tempManifest = targetName; - tempManifest += ".intermediate.manifest"; - std::string resourceInputFile = targetName; - resourceInputFile += ".resource.txt"; - if(verbose) + + // Create a resource file referencing the manifest. + std::string absManifestFile = + cmSystemTools::CollapseFullPath(this->ManifestFile); + if (this->Verbose) { - std::cout << "Create " << resourceInputFile << "\n"; + std::cout << "Create " << this->ManifestFileRC << "\n"; } - // Create input file for rc command - cmsys::ofstream fout(resourceInputFile.c_str()); - if(!fout) + { + cmsys::ofstream fout(this->ManifestFileRC.c_str()); + if (!fout) { return -1; } - std::string manifestFile = targetName; - manifestFile += ".embed.manifest"; - std::string fullPath= cmSystemTools::CollapseFullPath(manifestFile); - fout << type << " /* CREATEPROCESS_MANIFEST_RESOURCE_ID " - "*/ 24 /* RT_MANIFEST */ " << "\"" << fullPath << "\""; - fout.close(); - manifestArg += tempManifest; - // add the manifest arg to the linkCommand - linkCommand.push_back("/MANIFEST"); - linkCommand.push_back(manifestArg); - // if manifestFile is not yet created, create an - // empty one - if(!cmSystemTools::FileExists(manifestFile.c_str())) + fout << this->Type << " /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ " + "24 /* RT_MANIFEST */ \"" << absManifestFile << "\""; + } + + // If we have not previously generated a manifest file, + // generate an empty one so the resource compiler succeeds. + if (!cmSystemTools::FileExists(this->ManifestFile)) { - if(verbose) + if (this->Verbose) { - std::cout << "Create empty: " << manifestFile << "\n"; + std::cout << "Create empty: " << this->ManifestFile << "\n"; } - cmsys::ofstream foutTmp(manifestFile.c_str()); - } - std::string resourceFile = manifestFile; - resourceFile += ".res"; - // add the resource file to the end of the link command - linkCommand.push_back(resourceFile); - std::string outputOpt = "/fo"; - outputOpt += resourceFile; - rcCommand.push_back(outputOpt); - rcCommand.push_back(resourceInputFile); - // Run rc command to create resource - if(!cmcmd::RunCommand("RC Pass 1", rcCommand, verbose)) - { - return -1; + cmsys::ofstream foutTmp(this->ManifestFile.c_str()); } - // Now run the link command to link and create manifest - if(!cmcmd::RunCommand("LINK Pass 1", linkCommand, verbose)) + + // Compile the resource file. + std::vector rcCommand; + rcCommand.push_back(cmSystemTools::FindProgram("rc.exe")); + rcCommand.push_back("/fo" + this->ManifestFileRes); + rcCommand.push_back(this->ManifestFileRC); + if (!RunCommand("RC Pass 1", rcCommand, this->Verbose)) { return -1; } - // create mt command - std::string outArg("/out:"); - outArg+= manifestFile; - mtCommand.push_back("/nologo"); - mtCommand.push_back(outArg); - mtCommand.push_back("/notify_update"); - mtCommand.push_back("/manifest"); - mtCommand.push_back(tempManifest); - // now run mt.exe to create the final manifest file - int mtRet =0; - if(!cmcmd::RunCommand("MT", mtCommand, verbose, &mtRet)) + + // Run the link command (possibly generates intermediate manifest). + if (!RunCommand("LINK Pass 1", this->LinkCommand, this->Verbose)) { return -1; } - // if mt returns 0, then the manifest was not changed and - // we do not need to do another link step - if(mtRet == 0) - { - return 0; - } - // check for magic mt return value if mt returns the magic number - // 1090650113 then it means that it updated the manifest file and we need - // to do the final link. If mt has any value other than 0 or 1090650113 - // then there was some problem with the command itself and there was an - // error so return the error code back out of cmake so make can report it. - // (when hosted on a posix system the value is 187) - if(mtRet != 1090650113 && mtRet != 187) + + // Run the manifest tool to create the final manifest. + int mtRet = this->RunMT("/out:" + this->ManifestFile, true); + + // If mt returns 1090650113 (or 187 on a posix host) then it updated the + // manifest file so we need to embed it again. Otherwise we are done. + if (mtRet != 1090650113 && mtRet != 187) { return mtRet; } - // update the resource file with the new manifest from the mt command. - if(!cmcmd::RunCommand("RC Pass 2", rcCommand, verbose)) + + // Compile the resource file again. + if (!RunCommand("RC Pass 2", rcCommand, this->Verbose)) { return -1; } - // Run the final incremental link that will put the new manifest resource - // into the file incrementally. - if(!cmcmd::RunCommand("FINAL LINK", linkCommand, verbose)) + + // Link incrementally again to use the updated resource. + if (!RunCommand("FINAL LINK", this->LinkCommand, this->Verbose)) { return -1; } return 0; } -int cmcmd::VisualStudioLinkNonIncremental(std::vector& args, - int type, - bool hasManifest, - bool verbose) +int cmVSLink::LinkNonIncremental() { - std::vector linkCommand; - std::string targetName; - if(cmcmd::ParseVisualStudioLinkCommand(args, linkCommand, targetName) == -1) + // Run the link command (possibly generates intermediate manifest). + if (!RunCommand("LINK", this->LinkCommand, this->Verbose)) { return -1; } - // Run the link command as given - if (hasManifest) - { - linkCommand.push_back("/MANIFEST"); - } - if(!cmcmd::RunCommand("LINK", linkCommand, verbose)) - { - return -1; - } - if(!hasManifest) + + // If we have no manifest files we are done. + if (!this->LinkGeneratesManifest) { return 0; } + + // Run the manifest tool to embed the final manifest in the binary. + std::string mtOut = + "/outputresource:" + this->TargetFile + (this->Type == 1? ";#1" : ";#2"); + return this->RunMT(mtOut, false); +} + +int cmVSLink::RunMT(std::string const& out, bool notify) +{ std::vector mtCommand; mtCommand.push_back(cmSystemTools::FindProgram("mt.exe")); mtCommand.push_back("/nologo"); mtCommand.push_back("/manifest"); - std::string manifestFile = targetName; - manifestFile += ".manifest"; - mtCommand.push_back(manifestFile); - std::string outresource = "/outputresource:"; - outresource += targetName; - outresource += ";#"; - if(type == 1) + if (this->LinkGeneratesManifest) { - outresource += "1"; + mtCommand.push_back(this->LinkerManifestFile); } - else if(type == 2) + mtCommand.push_back(out); + if (notify) { - outresource += "2"; + // Add an undocumented option that enables a special return + // code to notify us when the manifest is modified. + mtCommand.push_back("/notify_update"); } - mtCommand.push_back(outresource); - // Now use the mt tool to embed the manifest into the exe or dll - if(!cmcmd::RunCommand("MT", mtCommand, verbose)) + int mtRet = 0; + if (!RunCommand("MT", mtCommand, this->Verbose, &mtRet)) { return -1; } - return 0; + return mtRet; } diff --git a/Source/cmcmd.h b/Source/cmcmd.h index 2bfbae7..64b2406 100644 --- a/Source/cmcmd.h +++ b/Source/cmcmd.h @@ -35,20 +35,6 @@ protected: static int WindowsCEEnvironment(const char* version, const std::string& name); static int VisualStudioLink(std::vector& args, int type); - static int VisualStudioLinkIncremental(std::vector& args, - int type, - bool verbose); - static int VisualStudioLinkNonIncremental(std::vector& args, - int type, - bool hasManifest, - bool verbose); - static int ParseVisualStudioLinkCommand(std::vector& args, - std::vector& command, - std::string& targetName); - static bool RunCommand(const char* comment, - std::vector& command, - bool verbose, - int* retCodeOut = 0); }; #endif -- cgit v0.12 From e134e53b47fc9f0337529ce2b6851cec6319a8af Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 16 Sep 2015 10:24:16 -0400 Subject: Add support for *.manifest source files with MSVC tools Classify .manifest sources separately, add dependencies on them, and pass them to the MS manifest tool to merge with linker-generated manifest files. Inspired-by: Gilles Khouzam --- Help/release/dev/ms-manifest-files.rst | 7 ++++++ Modules/Platform/Windows-MSVC.cmake | 4 +-- Source/cmCommonTargetGenerator.cxx | 17 +++++++++++++ Source/cmCommonTargetGenerator.h | 1 + Source/cmGeneratorTarget.cxx | 14 +++++++++++ Source/cmGeneratorTarget.h | 2 ++ Source/cmLocalGenerator.cxx | 7 ++++++ Source/cmLocalGenerator.h | 1 + Source/cmLocalVisualStudio7Generator.cxx | 14 +++++++++++ Source/cmMakefileExecutableTargetGenerator.cxx | 4 +++ Source/cmMakefileLibraryTargetGenerator.cxx | 4 +++ Source/cmMakefileTargetGenerator.cxx | 9 +++++++ Source/cmNinjaNormalTargetGenerator.cxx | 3 +++ Source/cmNinjaTargetGenerator.cxx | 9 +++++++ Source/cmVisualStudio10TargetGenerator.cxx | 29 ++++++++++++++++++++++ Source/cmVisualStudio10TargetGenerator.h | 1 + Source/cmcmd.cxx | 21 ++++++++++++---- Tests/CMakeLists.txt | 1 + Tests/MSManifest/CMakeLists.txt | 5 ++++ Tests/MSManifest/Subdir/CMakeLists.txt | 9 +++++++ Tests/MSManifest/Subdir/check.cmake | 6 +++++ Tests/MSManifest/Subdir/main.c | 1 + Tests/MSManifest/Subdir/test.manifest.in | 4 +++ Tests/RunCMake/BuildDepends/C-Exe-Manifest.cmake | 19 ++++++++++++++ .../BuildDepends/C-Exe-Manifest.step1.cmake | 6 +++++ .../BuildDepends/C-Exe-Manifest.step2.cmake | 6 +++++ Tests/RunCMake/BuildDepends/RunCMakeTest.cmake | 11 ++++++++ Tests/RunCMake/BuildDepends/check.cmake | 3 +++ Tests/RunCMake/BuildDepends/main.c | 1 + 29 files changed, 212 insertions(+), 7 deletions(-) create mode 100644 Help/release/dev/ms-manifest-files.rst create mode 100644 Tests/MSManifest/CMakeLists.txt create mode 100644 Tests/MSManifest/Subdir/CMakeLists.txt create mode 100644 Tests/MSManifest/Subdir/check.cmake create mode 100644 Tests/MSManifest/Subdir/main.c create mode 100644 Tests/MSManifest/Subdir/test.manifest.in create mode 100644 Tests/RunCMake/BuildDepends/C-Exe-Manifest.cmake create mode 100644 Tests/RunCMake/BuildDepends/C-Exe-Manifest.step1.cmake create mode 100644 Tests/RunCMake/BuildDepends/C-Exe-Manifest.step2.cmake create mode 100644 Tests/RunCMake/BuildDepends/main.c diff --git a/Help/release/dev/ms-manifest-files.rst b/Help/release/dev/ms-manifest-files.rst new file mode 100644 index 0000000..94fbe83 --- /dev/null +++ b/Help/release/dev/ms-manifest-files.rst @@ -0,0 +1,7 @@ +ms-manifest-files +----------------- + +* CMake learned to honor ``*.manifest`` source files with MSVC tools. + Manifest files named as sources of ``.exe`` and ``.dll`` targets + will be merged with linker-generated manifests and embedded in the + binary. diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake index d4b1cd8..8594596 100644 --- a/Modules/Platform/Windows-MSVC.cmake +++ b/Modules/Platform/Windows-MSVC.cmake @@ -274,8 +274,8 @@ set (CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL_INIT ${CMAKE_EXE_LINKER_FLAGS_MINSIZER macro(__windows_compiler_msvc lang) if(NOT MSVC_VERSION LESS 1400) # for 2005 make sure the manifest is put in the dll with mt - set(_CMAKE_VS_LINK_DLL " -E vs_link_dll --intdir= ") - set(_CMAKE_VS_LINK_EXE " -E vs_link_exe --intdir= ") + set(_CMAKE_VS_LINK_DLL " -E vs_link_dll --intdir= --manifests -- ") + set(_CMAKE_VS_LINK_EXE " -E vs_link_exe --intdir= --manifests -- ") endif() set(CMAKE_${lang}_CREATE_SHARED_LIBRARY "${_CMAKE_VS_LINK_DLL} ${CMAKE_CL_NOLOGO} ${CMAKE_START_TEMP_FILE} /out: /implib: /pdb: /dll /version:.${_PLATFORM_LINK_FLAGS} ${CMAKE_END_TEMP_FILE}") diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx index 4840e89..252e231 100644 --- a/Source/cmCommonTargetGenerator.cxx +++ b/Source/cmCommonTargetGenerator.cxx @@ -412,3 +412,20 @@ cmCommonTargetGenerator::GetLinkedTargetDirectories() const } return dirs; } + +std::string cmCommonTargetGenerator::GetManifests() +{ + std::vector manifest_srcs; + this->GeneratorTarget->GetManifests(manifest_srcs, this->ConfigName); + + std::vector manifests; + for (std::vector::iterator mi = manifest_srcs.begin(); + mi != manifest_srcs.end(); ++mi) + { + manifests.push_back(this->Convert((*mi)->GetFullPath(), + this->WorkingDirectory, + cmOutputConverter::SHELL)); + } + + return cmJoin(manifests, " "); +} diff --git a/Source/cmCommonTargetGenerator.h b/Source/cmCommonTargetGenerator.h index 0a49e12..a4b2c10 100644 --- a/Source/cmCommonTargetGenerator.h +++ b/Source/cmCommonTargetGenerator.h @@ -88,6 +88,7 @@ protected: ByLanguageMap DefinesByLanguage; std::string GetIncludes(std::string const& l); ByLanguageMap IncludesByLanguage; + std::string GetManifests(); std::vector GetLinkedTargetDirectories() const; }; diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 09387b7..fb5805b 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -75,6 +75,7 @@ struct IDLSourcesTag {}; struct ResxTag {}; struct ModuleDefinitionFileTag {}; struct AppManifestTag{}; +struct ManifestsTag{}; struct CertificatesTag{}; struct XamlTag{}; @@ -216,6 +217,10 @@ struct TagVisitor { DoAccept::Result>::Do(this->Data, sf); } + else if (ext == "manifest") + { + DoAccept::Result>::Do(this->Data, sf); + } else if (ext == "pfx") { DoAccept::Result>::Do(this->Data, sf); @@ -626,6 +631,15 @@ cmGeneratorTarget //---------------------------------------------------------------------------- void cmGeneratorTarget +::GetManifests(std::vector& data, + const std::string& config) const +{ + IMPLEMENT_VISIT(Manifests); +} + +//---------------------------------------------------------------------------- +void +cmGeneratorTarget ::GetCertificates(std::vector& data, const std::string& config) const { diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 06d9a1f..916f281 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -71,6 +71,8 @@ public: const std::string& config) const; void GetAppManifest(std::vector&, const std::string& config) const; + void GetManifests(std::vector&, + const std::string& config) const; void GetCertificates(std::vector&, const std::string& config) const; void GetXamlSources(std::vector&, diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 46d5cd8..97a9f1e 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -525,6 +525,13 @@ cmLocalGenerator::ExpandRuleVariable(std::string const& variable, return replaceValues.LinkFlags; } } + if(replaceValues.Manifests) + { + if(variable == "MANIFESTS") + { + return replaceValues.Manifests; + } + } if(replaceValues.Flags) { if(variable == "FLAGS") diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index b051e5d..771131f 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -219,6 +219,7 @@ public: const char* TargetSOName; const char* TargetInstallNameDir; const char* LinkFlags; + const char* Manifests; const char* LanguageCompileFlags; const char* Defines; const char* Includes; diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 191f739..a4bce8a 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -984,6 +984,20 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(std::ostream& fout, "\t\t\t manifest_srcs; + gt->GetManifests(manifest_srcs, configName); + if (!manifest_srcs.empty()) + { + fout << "\n\t\t\t\tAdditionalManifestFiles=\""; + for (std::vector::const_iterator + mi = manifest_srcs.begin(); mi != manifest_srcs.end(); ++mi) + { + std::string m = (*mi)->GetFullPath(); + fout << this->ConvertToXMLOutputPath(m.c_str()) << ";"; + } + fout << "\""; + } + // Check if we need the FAT32 workaround. // Check the filesystem type where the target will be written. if (cmLVS6G_IsFAT(target.GetDirectory(configName).c_str())) diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index ccb0974..90f679e 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -353,6 +353,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) useResponseFileForObjects, buildObjs, depends, useWatcomQuote); + std::string manifests = this->GetManifests(); + cmLocalGenerator::RuleVariables vars; vars.RuleLauncher = "RULE_LAUNCH_LINK"; vars.CMTarget = this->Target; @@ -391,6 +393,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) vars.LinkLibraries = linkLibs.c_str(); vars.Flags = flags.c_str(); vars.LinkFlags = linkFlags.c_str(); + vars.Manifests = manifests.c_str(); + // Expand placeholders in the commands. this->LocalGenerator->TargetImplib = targetOutPathImport; for(std::vector::iterator i = real_link_commands.begin(); diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index 2f995e8..cd387a0 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -616,6 +616,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules } } + std::string manifests = this->GetManifests(); + cmLocalGenerator::RuleVariables vars; vars.TargetPDB = targetOutPathPDB.c_str(); @@ -660,6 +662,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules } vars.LinkFlags = linkFlags.c_str(); + vars.Manifests = manifests.c_str(); + // Compute the directory portion of the install_name setting. std::string install_name_dir; if(this->Target->GetType() == cmTarget::SHARED_LIBRARY) diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index cf88a74..b278087 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -1493,6 +1493,15 @@ void cmMakefileTargetGenerator depends.push_back(this->ModuleDefinitionFile); } + // Add a dependency on user-specified manifest files, if any. + std::vector manifest_srcs; + this->GeneratorTarget->GetManifests(manifest_srcs, this->ConfigName); + for (std::vector::iterator mi = manifest_srcs.begin(); + mi != manifest_srcs.end(); ++mi) + { + depends.push_back((*mi)->GetFullPath()); + } + // Add user-specified dependencies. if(const char* linkDepends = this->Target->GetProperty("LINK_DEPENDS")) diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index f62f8ad..7e7e600 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -237,6 +237,7 @@ cmNinjaNormalTargetGenerator vars.Flags = "$FLAGS"; vars.LinkFlags = "$LINK_FLAGS"; + vars.Manifests = "$MANIFESTS"; std::string langFlags; if (targetType != cmTarget::EXECUTABLE) @@ -509,6 +510,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() vars["LINK_FLAGS"] = cmGlobalNinjaGenerator ::EncodeLiteral(vars["LINK_FLAGS"]); + vars["MANIFESTS"] = this->GetManifests(); + vars["LINK_PATH"] = frameworkPath + linkPath; // Compute architecture specific link flags. Yes, these go into a different diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 81fdde2..752c8a7 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -209,6 +209,15 @@ cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const result.push_back(this->ConvertToNinjaPath(this->ModuleDefinitionFile)); } + // Add a dependency on user-specified manifest files, if any. + std::vector manifest_srcs; + this->GeneratorTarget->GetManifests(manifest_srcs, this->ConfigName); + for (std::vector::iterator mi = manifest_srcs.begin(); + mi != manifest_srcs.end(); ++mi) + { + result.push_back(this->ConvertToNinjaPath((*mi)->GetFullPath())); + } + // Add user-specified dependencies. if (const char* linkDepends = this->Target->GetProperty("LINK_DEPENDS")) { diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 4c380f7..cb5048d 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -2203,6 +2203,33 @@ cmVisualStudio10TargetGenerator::WriteLibOptions(std::string const& config) } } +void cmVisualStudio10TargetGenerator::WriteManifestOptions( + std::string const& config) +{ + if (this->Target->GetType() != cmTarget::EXECUTABLE && + this->Target->GetType() != cmTarget::SHARED_LIBRARY && + this->Target->GetType() != cmTarget::MODULE_LIBRARY) + { + return; + } + + std::vector manifest_srcs; + this->GeneratorTarget->GetManifests(manifest_srcs, config); + if (!manifest_srcs.empty()) + { + this->WriteString("\n", 2); + this->WriteString("", 3); + for (std::vector::const_iterator + mi = manifest_srcs.begin(); mi != manifest_srcs.end(); ++mi) + { + std::string m = this->ConvertPath((*mi)->GetFullPath(), false); + this->ConvertToWindowsSlash(m); + (*this->BuildFileStream) << m << ";"; + } + (*this->BuildFileStream) << "\n"; + this->WriteString("\n", 2); + } +} //---------------------------------------------------------------------------- void cmVisualStudio10TargetGenerator::WriteAntBuildOptions( @@ -2740,6 +2767,8 @@ void cmVisualStudio10TargetGenerator::WriteItemDefinitionGroups() this->WriteLinkOptions(*i); // output lib flags this->WriteLibOptions(*i); + // output manifest flags + this->WriteManifestOptions(*i); if(this->NsightTegra && this->Target->GetType() == cmTarget::EXECUTABLE && this->Target->GetPropertyAsBool("ANDROID_GUI")) diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 451f8b2..5fadb60 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -111,6 +111,7 @@ private: void AddLibraries(cmComputeLinkInformation& cli, std::vector& libVec); void WriteLibOptions(std::string const& config); + void WriteManifestOptions(std::string const& config); void WriteEvents(std::string const& configName); void WriteEvent(const char* name, std::vector const& commands, diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 307e78b..f44c77d 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -1362,6 +1362,7 @@ class cmVSLink bool Incremental; bool LinkGeneratesManifest; std::vector LinkCommand; + std::vector UserManifests; std::string LinkerManifestFile; std::string ManifestFile; std::string ManifestFileRC; @@ -1480,6 +1481,13 @@ bool cmVSLink::Parse(std::vector::const_iterator argBeg, ++arg; break; } + else if (*arg == "--manifests") + { + for (++arg; arg != argEnd && !cmHasLiteralPrefix(*arg, "-"); ++arg) + { + this->UserManifests.push_back(*arg); + } + } else if (cmHasLiteralPrefix(*arg, "--intdir=")) { intDir = arg->substr(9); @@ -1544,10 +1552,11 @@ bool cmVSLink::Parse(std::vector::const_iterator argBeg, this->ManifestFileRes = intDir + "/manifest.res"; this->LinkCommand.push_back(this->ManifestFileRes); } - else + else if (this->UserManifests.empty()) { - // CMake places the linker-generated manifest next to the binary (as if it - // were not to be embedded) when not linking incrementally. + // Prior to support for user-specified manifests CMake placed the + // linker-generated manifest next to the binary (as if it were not to be + // embedded) when not linking incrementally. Preserve this behavior. this->ManifestFile = this->TargetFile + ".manifest"; this->LinkerManifestFile = this->ManifestFile; } @@ -1564,7 +1573,7 @@ bool cmVSLink::Parse(std::vector::const_iterator argBeg, int cmVSLink::Link() { if (this->Incremental && - this->LinkGeneratesManifest) + (this->LinkGeneratesManifest || !this->UserManifests.empty())) { if (this->Verbose) { @@ -1688,7 +1697,7 @@ int cmVSLink::LinkNonIncremental() } // If we have no manifest files we are done. - if (!this->LinkGeneratesManifest) + if (!this->LinkGeneratesManifest && this->UserManifests.empty()) { return 0; } @@ -1709,6 +1718,8 @@ int cmVSLink::RunMT(std::string const& out, bool notify) { mtCommand.push_back(this->LinkerManifestFile); } + mtCommand.insert(mtCommand.end(), + this->UserManifests.begin(), this->UserManifests.end()); mtCommand.push_back(out); if (notify) { diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 2c6a42c..fff04ce 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -276,6 +276,7 @@ if(BUILD_TESTING) if(TEST_RESOURCES) ADD_TEST_MACRO(VSResource VSResource) endif() + ADD_TEST_MACRO(MSManifest MSManifest) ADD_TEST_MACRO(Simple Simple) ADD_TEST_MACRO(PreOrder PreOrder) ADD_TEST_MACRO(MissingSourceFile MissingSourceFile) diff --git a/Tests/MSManifest/CMakeLists.txt b/Tests/MSManifest/CMakeLists.txt new file mode 100644 index 0000000..300cfa6 --- /dev/null +++ b/Tests/MSManifest/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.3) +project(MSManifest C) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) +add_subdirectory(Subdir) diff --git a/Tests/MSManifest/Subdir/CMakeLists.txt b/Tests/MSManifest/Subdir/CMakeLists.txt new file mode 100644 index 0000000..a47cf00 --- /dev/null +++ b/Tests/MSManifest/Subdir/CMakeLists.txt @@ -0,0 +1,9 @@ +configure_file(test.manifest.in test.manifest) +add_executable(MSManifest main.c ${CMAKE_CURRENT_BINARY_DIR}/test.manifest) + +if(MSVC AND NOT MSVC_VERSION LESS 1400) + add_custom_command(TARGET MSManifest POST_BUILD VERBATIM + COMMAND ${CMAKE_COMMAND} -Dexe=$ + -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake + ) +endif() diff --git a/Tests/MSManifest/Subdir/check.cmake b/Tests/MSManifest/Subdir/check.cmake new file mode 100644 index 0000000..b7b6841 --- /dev/null +++ b/Tests/MSManifest/Subdir/check.cmake @@ -0,0 +1,6 @@ +file(STRINGS "${exe}" content REGEX "name=\"Kitware.CMake.MSManifestTest\"") +if(content) + message(STATUS "Expected manifest content found:\n ${content}") +else() + message(FATAL_ERROR "Expected manifest content not found in\n ${exe}") +endif() diff --git a/Tests/MSManifest/Subdir/main.c b/Tests/MSManifest/Subdir/main.c new file mode 100644 index 0000000..78f2de1 --- /dev/null +++ b/Tests/MSManifest/Subdir/main.c @@ -0,0 +1 @@ +int main(void) { return 0; } diff --git a/Tests/MSManifest/Subdir/test.manifest.in b/Tests/MSManifest/Subdir/test.manifest.in new file mode 100644 index 0000000..540961a --- /dev/null +++ b/Tests/MSManifest/Subdir/test.manifest.in @@ -0,0 +1,4 @@ + + + diff --git a/Tests/RunCMake/BuildDepends/C-Exe-Manifest.cmake b/Tests/RunCMake/BuildDepends/C-Exe-Manifest.cmake new file mode 100644 index 0000000..ef33012 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/C-Exe-Manifest.cmake @@ -0,0 +1,19 @@ +enable_language(C) + +add_executable(main main.c ${CMAKE_CURRENT_BINARY_DIR}/test.manifest) + +if(MSVC AND NOT MSVC_VERSION LESS 1400) + set(EXTRA_CHECK [[ +file(STRINGS "$" content REGEX "name=\"Kitware.CMake.C-Exe-Manifest-step[0-9]\"") +if(NOT "${content}" MATCHES "name=\"Kitware.CMake.C-Exe-Manifest-step${check_step}\"") + set(RunCMake_TEST_FAILED "Binary has no manifest with name=\"Kitware.CMake.C-Exe-Manifest-step${check_step}\":\n ${content}") +endif() +]]) +endif() + +file(GENERATE OUTPUT check-$>.cmake CONTENT " +set(check_pairs + \"$|${CMAKE_CURRENT_BINARY_DIR}/test.manifest\" + ) +${EXTRA_CHECK} +") diff --git a/Tests/RunCMake/BuildDepends/C-Exe-Manifest.step1.cmake b/Tests/RunCMake/BuildDepends/C-Exe-Manifest.step1.cmake new file mode 100644 index 0000000..c0b939d --- /dev/null +++ b/Tests/RunCMake/BuildDepends/C-Exe-Manifest.step1.cmake @@ -0,0 +1,6 @@ +file(WRITE "${RunCMake_TEST_BINARY_DIR}/test.manifest" [[ + + + +]]) diff --git a/Tests/RunCMake/BuildDepends/C-Exe-Manifest.step2.cmake b/Tests/RunCMake/BuildDepends/C-Exe-Manifest.step2.cmake new file mode 100644 index 0000000..a75bf21 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/C-Exe-Manifest.step2.cmake @@ -0,0 +1,6 @@ +file(WRITE "${RunCMake_TEST_BINARY_DIR}/test.manifest" [[ + + + +]]) diff --git a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake index 98132cc..8782ba9 100644 --- a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake +++ b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake @@ -14,6 +14,9 @@ function(run_BuildDepends CASE) set(RunCMake-check-file check.cmake) set(check_step 1) run_cmake_command(${CASE}-build1 ${CMAKE_COMMAND} --build . --config Debug) + if(run_BuildDepends_skip_step_2) + return() + endif() execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1.125) # handle 1s resolution include(${RunCMake_SOURCE_DIR}/${CASE}.step2.cmake OPTIONAL) set(check_step 2) @@ -21,3 +24,11 @@ function(run_BuildDepends CASE) endfunction() run_BuildDepends(C-Exe) +if(NOT RunCMake_GENERATOR MATCHES "Visual Studio [67]|Xcode") + if(RunCMake_GENERATOR MATCHES "Visual Studio 10") + # VS 10 forgets to re-link when a manifest changes + set(run_BuildDepends_skip_step_2 1) + endif() + run_BuildDepends(C-Exe-Manifest) + unset(run_BuildDepends_skip_step_2) +endif() diff --git a/Tests/RunCMake/BuildDepends/check.cmake b/Tests/RunCMake/BuildDepends/check.cmake index be6debc..26a9eb6 100644 --- a/Tests/RunCMake/BuildDepends/check.cmake +++ b/Tests/RunCMake/BuildDepends/check.cmake @@ -1,5 +1,8 @@ if(EXISTS ${RunCMake_TEST_BINARY_DIR}/check-debug.cmake) include(${RunCMake_TEST_BINARY_DIR}/check-debug.cmake) + if(RunCMake_TEST_FAILED) + return() + endif() foreach(exe IN LISTS check_exes) execute_process(COMMAND ${exe} RESULT_VARIABLE res) if(NOT res EQUAL ${check_step}) diff --git a/Tests/RunCMake/BuildDepends/main.c b/Tests/RunCMake/BuildDepends/main.c new file mode 100644 index 0000000..78f2de1 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/main.c @@ -0,0 +1 @@ +int main(void) { return 0; } -- cgit v0.12