Explorar o código

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 <[email protected]>
Brad King %!s(int64=10) %!d(string=hai) anos
pai
achega
e134e53b47

+ 7 - 0
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.

+ 2 - 2
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 "<CMAKE_COMMAND> -E vs_link_dll --intdir=<OBJECT_DIR> ")
-    set(_CMAKE_VS_LINK_EXE "<CMAKE_COMMAND> -E vs_link_exe --intdir=<OBJECT_DIR> ")
+    set(_CMAKE_VS_LINK_DLL "<CMAKE_COMMAND> -E vs_link_dll --intdir=<OBJECT_DIR> --manifests <MANIFESTS> -- ")
+    set(_CMAKE_VS_LINK_EXE "<CMAKE_COMMAND> -E vs_link_exe --intdir=<OBJECT_DIR> --manifests <MANIFESTS> -- ")
   endif()
   set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
     "${_CMAKE_VS_LINK_DLL}<CMAKE_LINKER> ${CMAKE_CL_NOLOGO} <OBJECTS> ${CMAKE_START_TEMP_FILE} /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /dll /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PLATFORM_LINK_FLAGS} <LINK_FLAGS> <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}")

+ 17 - 0
Source/cmCommonTargetGenerator.cxx

@@ -412,3 +412,20 @@ cmCommonTargetGenerator::GetLinkedTargetDirectories() const
     }
   return dirs;
 }
+
+std::string cmCommonTargetGenerator::GetManifests()
+{
+  std::vector<cmSourceFile const*> manifest_srcs;
+  this->GeneratorTarget->GetManifests(manifest_srcs, this->ConfigName);
+
+  std::vector<std::string> manifests;
+  for (std::vector<cmSourceFile const*>::iterator mi = manifest_srcs.begin();
+       mi != manifest_srcs.end(); ++mi)
+    {
+    manifests.push_back(this->Convert((*mi)->GetFullPath(),
+                                      this->WorkingDirectory,
+                                      cmOutputConverter::SHELL));
+    }
+
+  return cmJoin(manifests, " ");
+}

+ 1 - 0
Source/cmCommonTargetGenerator.h

@@ -88,6 +88,7 @@ protected:
   ByLanguageMap DefinesByLanguage;
   std::string GetIncludes(std::string const& l);
   ByLanguageMap IncludesByLanguage;
+  std::string GetManifests();
 
   std::vector<std::string> GetLinkedTargetDirectories() const;
 };

+ 14 - 0
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<IsSameTag<Tag, AppManifestTag>::Result>::Do(this->Data, sf);
       }
+    else if (ext == "manifest")
+      {
+      DoAccept<IsSameTag<Tag, ManifestsTag>::Result>::Do(this->Data, sf);
+      }
     else if (ext == "pfx")
       {
       DoAccept<IsSameTag<Tag, CertificatesTag>::Result>::Do(this->Data, sf);
@@ -623,6 +628,15 @@ cmGeneratorTarget
   IMPLEMENT_VISIT(AppManifest);
 }
 
+//----------------------------------------------------------------------------
+void
+cmGeneratorTarget
+::GetManifests(std::vector<cmSourceFile const*>& data,
+               const std::string& config) const
+{
+  IMPLEMENT_VISIT(Manifests);
+}
+
 //----------------------------------------------------------------------------
 void
 cmGeneratorTarget

+ 2 - 0
Source/cmGeneratorTarget.h

@@ -71,6 +71,8 @@ public:
                               const std::string& config) const;
   void GetAppManifest(std::vector<cmSourceFile const*>&,
                       const std::string& config) const;
+  void GetManifests(std::vector<cmSourceFile const*>&,
+                    const std::string& config) const;
   void GetCertificates(std::vector<cmSourceFile const*>&,
                        const std::string& config) const;
   void GetXamlSources(std::vector<cmSourceFile const*>&,

+ 7 - 0
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")

+ 1 - 0
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;

+ 14 - 0
Source/cmLocalVisualStudio7Generator.cxx

@@ -984,6 +984,20 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(std::ostream& fout,
       "\t\t\t<Tool\n"
       "\t\t\t\tName=\"" << manifestTool << "\"";
 
+    std::vector<cmSourceFile const*> manifest_srcs;
+    gt->GetManifests(manifest_srcs, configName);
+    if (!manifest_srcs.empty())
+      {
+      fout << "\n\t\t\t\tAdditionalManifestFiles=\"";
+      for (std::vector<cmSourceFile const*>::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()))

+ 4 - 0
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<std::string>::iterator i = real_link_commands.begin();

+ 4 - 0
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)

+ 9 - 0
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<cmSourceFile const*> manifest_srcs;
+  this->GeneratorTarget->GetManifests(manifest_srcs, this->ConfigName);
+  for (std::vector<cmSourceFile const*>::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"))

+ 3 - 0
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

+ 9 - 0
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<cmSourceFile const*> manifest_srcs;
+  this->GeneratorTarget->GetManifests(manifest_srcs, this->ConfigName);
+  for (std::vector<cmSourceFile const*>::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"))
     {

+ 29 - 0
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<cmSourceFile const*> manifest_srcs;
+  this->GeneratorTarget->GetManifests(manifest_srcs, config);
+  if (!manifest_srcs.empty())
+    {
+    this->WriteString("<Manifest>\n", 2);
+    this->WriteString("<AdditionalManifestFiles>", 3);
+    for (std::vector<cmSourceFile const*>::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) << "</AdditionalManifestFiles>\n";
+    this->WriteString("</Manifest>\n", 2);
+    }
+}
 
 //----------------------------------------------------------------------------
 void cmVisualStudio10TargetGenerator::WriteAntBuildOptions(
@@ -2740,6 +2767,8 @@ void cmVisualStudio10TargetGenerator::WriteItemDefinitionGroups()
     this->WriteLinkOptions(*i);
     //    output lib flags       <Lib></Lib>
     this->WriteLibOptions(*i);
+    //    output manifest flags  <Manifest></Manifest>
+    this->WriteManifestOptions(*i);
     if(this->NsightTegra &&
        this->Target->GetType() == cmTarget::EXECUTABLE &&
        this->Target->GetPropertyAsBool("ANDROID_GUI"))

+ 1 - 0
Source/cmVisualStudio10TargetGenerator.h

@@ -111,6 +111,7 @@ private:
   void AddLibraries(cmComputeLinkInformation& cli,
                     std::vector<std::string>& 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<cmCustomCommand> const& commands,

+ 16 - 5
Source/cmcmd.cxx

@@ -1362,6 +1362,7 @@ class cmVSLink
   bool Incremental;
   bool LinkGeneratesManifest;
   std::vector<std::string> LinkCommand;
+  std::vector<std::string> UserManifests;
   std::string LinkerManifestFile;
   std::string ManifestFile;
   std::string ManifestFileRC;
@@ -1480,6 +1481,13 @@ bool cmVSLink::Parse(std::vector<std::string>::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<std::string>::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<std::string>::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)
     {

+ 1 - 0
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)

+ 5 - 0
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)

+ 9 - 0
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=$<TARGET_FILE:MSManifest>
+            -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake
+    )
+endif()

+ 6 - 0
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()

+ 1 - 0
Tests/MSManifest/Subdir/main.c

@@ -0,0 +1 @@
+int main(void) { return 0; }

+ 4 - 0
Tests/MSManifest/Subdir/test.manifest.in

@@ -0,0 +1,4 @@
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity type="win32" version="1.0.0.0"
+                    name="Kitware.CMake.MSManifestTest"/>
+</assembly>

+ 19 - 0
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 "$<TARGET_FILE:main>" 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-$<LOWER_CASE:$<CONFIG>>.cmake CONTENT "
+set(check_pairs
+  \"$<TARGET_FILE:main>|${CMAKE_CURRENT_BINARY_DIR}/test.manifest\"
+  )
+${EXTRA_CHECK}
+")

+ 6 - 0
Tests/RunCMake/BuildDepends/C-Exe-Manifest.step1.cmake

@@ -0,0 +1,6 @@
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/test.manifest" [[
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity type="win32" version="1.0.0.0"
+                    name="Kitware.CMake.C-Exe-Manifest-step1"/>
+</assembly>
+]])

+ 6 - 0
Tests/RunCMake/BuildDepends/C-Exe-Manifest.step2.cmake

@@ -0,0 +1,6 @@
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/test.manifest" [[
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity type="win32" version="1.0.0.0"
+                    name="Kitware.CMake.C-Exe-Manifest-step2"/>
+</assembly>
+]])

+ 11 - 0
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()

+ 3 - 0
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})

+ 1 - 0
Tests/RunCMake/BuildDepends/main.c

@@ -0,0 +1 @@
+int main(void) { return 0; }