Browse Source

Merge topic 'ms-manifest-files'

e134e53b Add support for *.manifest source files with MSVC tools
da00be63 MSVC: Rewrite manifest file handling with Makefile and Ninja
d488b5c9 Ninja: Always add OBJECT_DIR variable to link rules
6d620f5a VS: Add manifest tool settings to VS 8 and 9 project files
Brad King 10 years ago
parent
commit
309026147a

+ 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)
 macro(__windows_compiler_msvc lang)
   if(NOT MSVC_VERSION LESS 1400)
   if(NOT MSVC_VERSION LESS 1400)
     # for 2005 make sure the manifest is put in the dll with mt
     # for 2005 make sure the manifest is put in the dll with mt
-    set(_CMAKE_VS_LINK_DLL "<CMAKE_COMMAND> -E vs_link_dll ")
-    set(_CMAKE_VS_LINK_EXE "<CMAKE_COMMAND> -E vs_link_exe ")
+    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()
   endif()
   set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
   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}")
     "${_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;
   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;
   ByLanguageMap DefinesByLanguage;
   std::string GetIncludes(std::string const& l);
   std::string GetIncludes(std::string const& l);
   ByLanguageMap IncludesByLanguage;
   ByLanguageMap IncludesByLanguage;
+  std::string GetManifests();
 
 
   std::vector<std::string> GetLinkedTargetDirectories() const;
   std::vector<std::string> GetLinkedTargetDirectories() const;
 };
 };

+ 14 - 0
Source/cmGeneratorTarget.cxx

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

+ 2 - 0
Source/cmGeneratorTarget.h

@@ -71,6 +71,8 @@ public:
                               const std::string& config) const;
                               const std::string& config) const;
   void GetAppManifest(std::vector<cmSourceFile const*>&,
   void GetAppManifest(std::vector<cmSourceFile const*>&,
                       const std::string& config) const;
                       const std::string& config) const;
+  void GetManifests(std::vector<cmSourceFile const*>&,
+                    const std::string& config) const;
   void GetCertificates(std::vector<cmSourceFile const*>&,
   void GetCertificates(std::vector<cmSourceFile const*>&,
                        const std::string& config) const;
                        const std::string& config) const;
   void GetXamlSources(std::vector<cmSourceFile 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;
       return replaceValues.LinkFlags;
       }
       }
     }
     }
+  if(replaceValues.Manifests)
+    {
+    if(variable == "MANIFESTS")
+      {
+      return replaceValues.Manifests;
+      }
+    }
   if(replaceValues.Flags)
   if(replaceValues.Flags)
     {
     {
     if(variable == "FLAGS")
     if(variable == "FLAGS")

+ 1 - 0
Source/cmLocalGenerator.h

@@ -219,6 +219,7 @@ public:
     const char* TargetSOName;
     const char* TargetSOName;
     const char* TargetInstallNameDir;
     const char* TargetInstallNameDir;
     const char* LinkFlags;
     const char* LinkFlags;
+    const char* Manifests;
     const char* LanguageCompileFlags;
     const char* LanguageCompileFlags;
     const char* Defines;
     const char* Defines;
     const char* Includes;
     const char* Includes;

+ 28 - 10
Source/cmLocalVisualStudio7Generator.cxx

@@ -972,25 +972,43 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(std::ostream& fout,
   fout << "\t\t\t\tProxyFileName=\"$(InputName)_p.c\"/>\n";
   fout << "\t\t\t\tProxyFileName=\"$(InputName)_p.c\"/>\n";
   // end of <Tool Name=VCMIDLTool
   // end of <Tool Name=VCMIDLTool
 
 
-  // Check if we need the FAT32 workaround.
+  // Add manifest tool settings.
   if(targetBuilds && this->GetVersion() >= cmGlobalVisualStudioGenerator::VS8)
   if(targetBuilds && this->GetVersion() >= cmGlobalVisualStudioGenerator::VS8)
     {
     {
+    const char* manifestTool  = "VCManifestTool";
+    if (this->FortranProject)
+      {
+      manifestTool = "VFManifestTool";
+      }
+    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.
     // Check the filesystem type where the target will be written.
-    if(cmLVS6G_IsFAT(target.GetDirectory(configName).c_str()))
+    if (cmLVS6G_IsFAT(target.GetDirectory(configName).c_str()))
       {
       {
       // Add a flag telling the manifest tool to use a workaround
       // Add a flag telling the manifest tool to use a workaround
       // for FAT32 file systems, which can cause an empty manifest
       // for FAT32 file systems, which can cause an empty manifest
       // to be embedded into the resulting executable.  See CMake
       // to be embedded into the resulting executable.  See CMake
       // bug #2617.
       // bug #2617.
-      const char* manifestTool  = "VCManifestTool";
-      if(this->FortranProject)
-        {
-        manifestTool = "VFManifestTool";
-        }
-      fout << "\t\t\t<Tool\n\t\t\t\tName=\"" << manifestTool << "\"\n"
-           << "\t\t\t\tUseFAT32Workaround=\"true\"\n"
-           << "\t\t\t/>\n";
+      fout << "\n\t\t\t\tUseFAT32Workaround=\"true\"";
       }
       }
+    fout << "/>\n";
     }
     }
 
 
   this->OutputTargetRules(fout, configName, target, libName);
   this->OutputTargetRules(fout, configName, target, libName);

+ 4 - 0
Source/cmMakefileExecutableTargetGenerator.cxx

@@ -353,6 +353,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
                           useResponseFileForObjects, buildObjs, depends,
                           useResponseFileForObjects, buildObjs, depends,
                           useWatcomQuote);
                           useWatcomQuote);
 
 
+  std::string manifests = this->GetManifests();
+
   cmLocalGenerator::RuleVariables vars;
   cmLocalGenerator::RuleVariables vars;
   vars.RuleLauncher = "RULE_LAUNCH_LINK";
   vars.RuleLauncher = "RULE_LAUNCH_LINK";
   vars.CMTarget = this->Target;
   vars.CMTarget = this->Target;
@@ -391,6 +393,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
   vars.LinkLibraries = linkLibs.c_str();
   vars.LinkLibraries = linkLibs.c_str();
   vars.Flags = flags.c_str();
   vars.Flags = flags.c_str();
   vars.LinkFlags = linkFlags.c_str();
   vars.LinkFlags = linkFlags.c_str();
+  vars.Manifests = manifests.c_str();
+
   // Expand placeholders in the commands.
   // Expand placeholders in the commands.
   this->LocalGenerator->TargetImplib = targetOutPathImport;
   this->LocalGenerator->TargetImplib = targetOutPathImport;
   for(std::vector<std::string>::iterator i = real_link_commands.begin();
   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;
   cmLocalGenerator::RuleVariables vars;
   vars.TargetPDB = targetOutPathPDB.c_str();
   vars.TargetPDB = targetOutPathPDB.c_str();
 
 
@@ -660,6 +662,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules
     }
     }
   vars.LinkFlags = linkFlags.c_str();
   vars.LinkFlags = linkFlags.c_str();
 
 
+  vars.Manifests = manifests.c_str();
+
   // Compute the directory portion of the install_name setting.
   // Compute the directory portion of the install_name setting.
   std::string install_name_dir;
   std::string install_name_dir;
   if(this->Target->GetType() == cmTarget::SHARED_LIBRARY)
   if(this->Target->GetType() == cmTarget::SHARED_LIBRARY)

+ 9 - 0
Source/cmMakefileTargetGenerator.cxx

@@ -1493,6 +1493,15 @@ void cmMakefileTargetGenerator
     depends.push_back(this->ModuleDefinitionFile);
     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.
   // Add user-specified dependencies.
   if(const char* linkDepends =
   if(const char* linkDepends =
      this->Target->GetProperty("LINK_DEPENDS"))
      this->Target->GetProperty("LINK_DEPENDS"))

+ 7 - 3
Source/cmNinjaNormalTargetGenerator.cxx

@@ -237,6 +237,7 @@ cmNinjaNormalTargetGenerator
 
 
     vars.Flags = "$FLAGS";
     vars.Flags = "$FLAGS";
     vars.LinkFlags = "$LINK_FLAGS";
     vars.LinkFlags = "$LINK_FLAGS";
+    vars.Manifests = "$MANIFESTS";
 
 
     std::string langFlags;
     std::string langFlags;
     if (targetType != cmTarget::EXECUTABLE)
     if (targetType != cmTarget::EXECUTABLE)
@@ -509,6 +510,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
   vars["LINK_FLAGS"] = cmGlobalNinjaGenerator
   vars["LINK_FLAGS"] = cmGlobalNinjaGenerator
                         ::EncodeLiteral(vars["LINK_FLAGS"]);
                         ::EncodeLiteral(vars["LINK_FLAGS"]);
 
 
+  vars["MANIFESTS"] = this->GetManifests();
+
   vars["LINK_PATH"] = frameworkPath + linkPath;
   vars["LINK_PATH"] = frameworkPath + linkPath;
 
 
   // Compute architecture specific link flags.  Yes, these go into a different
   // Compute architecture specific link flags.  Yes, these go into a different
@@ -579,11 +582,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
     vars["TARGET_PDB"] = base + suffix + dbg_suffix;
     vars["TARGET_PDB"] = base + suffix + dbg_suffix;
     }
     }
 
 
+  const std::string objPath = GetTarget()->GetSupportDirectory();
+  vars["OBJECT_DIR"] = ConvertToNinjaPath(objPath);
+  EnsureDirectoryExists(objPath);
+
   if (this->GetGlobalGenerator()->IsGCCOnWindows())
   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)
     // ar.exe can't handle backslashes in rsp files (implicitly used by gcc)
     std::string& linkLibraries = vars["LINK_LIBRARIES"];
     std::string& linkLibraries = vars["LINK_LIBRARIES"];
     std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');
     std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');

+ 9 - 0
Source/cmNinjaTargetGenerator.cxx

@@ -209,6 +209,15 @@ cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const
     result.push_back(this->ConvertToNinjaPath(this->ModuleDefinitionFile));
     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.
   // Add user-specified dependencies.
   if (const char* linkDepends = this->Target->GetProperty("LINK_DEPENDS"))
   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(
 void cmVisualStudio10TargetGenerator::WriteAntBuildOptions(
@@ -2740,6 +2767,8 @@ void cmVisualStudio10TargetGenerator::WriteItemDefinitionGroups()
     this->WriteLinkOptions(*i);
     this->WriteLinkOptions(*i);
     //    output lib flags       <Lib></Lib>
     //    output lib flags       <Lib></Lib>
     this->WriteLibOptions(*i);
     this->WriteLibOptions(*i);
+    //    output manifest flags  <Manifest></Manifest>
+    this->WriteManifestOptions(*i);
     if(this->NsightTegra &&
     if(this->NsightTegra &&
        this->Target->GetType() == cmTarget::EXECUTABLE &&
        this->Target->GetType() == cmTarget::EXECUTABLE &&
        this->Target->GetPropertyAsBool("ANDROID_GUI"))
        this->Target->GetPropertyAsBool("ANDROID_GUI"))

+ 1 - 0
Source/cmVisualStudio10TargetGenerator.h

@@ -111,6 +111,7 @@ private:
   void AddLibraries(cmComputeLinkInformation& cli,
   void AddLibraries(cmComputeLinkInformation& cli,
                     std::vector<std::string>& libVec);
                     std::vector<std::string>& libVec);
   void WriteLibOptions(std::string const& config);
   void WriteLibOptions(std::string const& config);
+  void WriteManifestOptions(std::string const& config);
   void WriteEvents(std::string const& configName);
   void WriteEvents(std::string const& configName);
   void WriteEvent(const char* name,
   void WriteEvent(const char* name,
                   std::vector<cmCustomCommand> const& commands,
                   std::vector<cmCustomCommand> const& commands,

+ 232 - 184
Source/cmcmd.cxx

@@ -1355,6 +1355,35 @@ int cmcmd::WindowsCEEnvironment(const char* version, const std::string& name)
   return -1;
   return -1;
 }
 }
 
 
+class cmVSLink
+{
+  int Type;
+  bool Verbose;
+  bool Incremental;
+  bool LinkGeneratesManifest;
+  std::vector<std::string> LinkCommand;
+  std::vector<std::string> UserManifests;
+  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<std::string>::const_iterator argBeg,
+             std::vector<std::string>::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
 // 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
 // exe and dll's.  This code does that in such a way that incremental linking
 // still works.
 // still works.
@@ -1364,11 +1393,7 @@ int cmcmd::VisualStudioLink(std::vector<std::string>& args, int type)
     {
     {
     return -1;
     return -1;
     }
     }
-  bool verbose = false;
-  if(cmSystemTools::GetEnv("VERBOSE"))
-    {
-    verbose = true;
-    }
+  bool verbose = cmSystemTools::GetEnv("VERBOSE")? true:false;
   std::vector<std::string> expandedArgs;
   std::vector<std::string> expandedArgs;
   for(std::vector<std::string>::iterator i = args.begin();
   for(std::vector<std::string>::iterator i = args.begin();
       i != args.end(); ++i)
       i != args.end(); ++i)
@@ -1389,79 +1414,19 @@ int cmcmd::VisualStudioLink(std::vector<std::string>& args, int type)
       expandedArgs.push_back(*i);
       expandedArgs.push_back(*i);
       }
       }
     }
     }
-  bool hasIncremental = false;
-  bool hasManifest = true;
-  for(std::vector<std::string>::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<std::string>& args,
-                                        std::vector<std::string>& command,
-                                        std::string& targetName)
-{
-  std::vector<std::string>::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 -1;
     }
     }
-  return 0;
+  return vsLink.Link();
 }
 }
 
 
-bool cmcmd::RunCommand(const char* comment,
+static bool RunCommand(const char* comment,
                        std::vector<std::string>& command,
                        std::vector<std::string>& command,
                        bool verbose,
                        bool verbose,
-                       int* retCodeOut)
+                       int* retCodeOut = 0)
 {
 {
   if(verbose)
   if(verbose)
     {
     {
@@ -1503,8 +1468,134 @@ bool cmcmd::RunCommand(const char* comment,
   return retCode == 0;
   return retCode == 0;
 }
 }
 
 
-int cmcmd::VisualStudioLinkIncremental(std::vector<std::string>& args,
-                                       int type, bool verbose)
+bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg,
+                     std::vector<std::string>::const_iterator argEnd)
+{
+  // Parse our own arguments.
+  std::string intDir;
+  std::vector<std::string>::const_iterator arg = argBeg;
+  while (arg != argEnd && cmHasLiteralPrefix(*arg, "-"))
+    {
+    if (*arg == "--")
+      {
+      ++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);
+      ++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 if (this->UserManifests.empty())
+    {
+    // 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;
+    }
+
+  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 || !this->UserManifests.empty()))
+    {
+    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:
   // This follows the steps listed here:
   // http://blogs.msdn.com/zakramer/archive/2006/05/22/603558.aspx
   // http://blogs.msdn.com/zakramer/archive/2006/05/22/603558.aspx
@@ -1528,161 +1619,118 @@ int cmcmd::VisualStudioLinkIncremental(std::vector<std::string>& args,
   //    7.  Finally, the Linker does another incremental link, but since the
   //    7.  Finally, the Linker does another incremental link, but since the
   //    only thing that has changed is the *.res file that contains the
   //    only thing that has changed is the *.res file that contains the
   //    manifest it is a short link.
   //    manifest it is a short link.
-  std::vector<std::string> linkCommand;
-  std::string targetName;
-  if(cmcmd::ParseVisualStudioLinkCommand(args, linkCommand, targetName) == -1)
-    {
-    return -1;
-    }
-  std::string manifestArg = "/MANIFESTFILE:";
-  std::vector<std::string> rcCommand;
-  rcCommand.push_back(cmSystemTools::FindProgram("rc.exe"));
-  std::vector<std::string> 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;
     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());
+    cmsys::ofstream foutTmp(this->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;
-    }
-  // Now run the link command to link and create manifest
-  if(!cmcmd::RunCommand("LINK Pass 1", linkCommand, verbose))
+
+  // Compile the resource file.
+  std::vector<std::string> 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;
     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;
     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;
     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;
     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 -1;
     }
     }
   return 0;
   return 0;
 }
 }
 
 
-int cmcmd::VisualStudioLinkNonIncremental(std::vector<std::string>& args,
-                                          int type,
-                                          bool hasManifest,
-                                          bool verbose)
+int cmVSLink::LinkNonIncremental()
 {
 {
-  std::vector<std::string> linkCommand;
-  std::string targetName;
-  if(cmcmd::ParseVisualStudioLinkCommand(args, linkCommand, targetName) == -1)
-    {
-    return -1;
-    }
-  // Run the link command as given
-  if (hasManifest)
-    {
-    linkCommand.push_back("/MANIFEST");
-    }
-  if(!cmcmd::RunCommand("LINK", linkCommand, verbose))
+  // Run the link command (possibly generates intermediate manifest).
+  if (!RunCommand("LINK", this->LinkCommand, this->Verbose))
     {
     {
     return -1;
     return -1;
     }
     }
-  if(!hasManifest)
+
+  // If we have no manifest files we are done.
+  if (!this->LinkGeneratesManifest && this->UserManifests.empty())
     {
     {
     return 0;
     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<std::string> mtCommand;
   std::vector<std::string> mtCommand;
   mtCommand.push_back(cmSystemTools::FindProgram("mt.exe"));
   mtCommand.push_back(cmSystemTools::FindProgram("mt.exe"));
   mtCommand.push_back("/nologo");
   mtCommand.push_back("/nologo");
   mtCommand.push_back("/manifest");
   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.insert(mtCommand.end(),
+                   this->UserManifests.begin(), this->UserManifests.end());
+  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 -1;
     }
     }
-  return 0;
+  return mtRet;
 }
 }

+ 0 - 14
Source/cmcmd.h

@@ -35,20 +35,6 @@ protected:
   static int WindowsCEEnvironment(const char* version,
   static int WindowsCEEnvironment(const char* version,
                                   const std::string& name);
                                   const std::string& name);
   static int VisualStudioLink(std::vector<std::string>& args, int type);
   static int VisualStudioLink(std::vector<std::string>& args, int type);
-  static int VisualStudioLinkIncremental(std::vector<std::string>& args,
-                                         int type,
-                                         bool verbose);
-  static int VisualStudioLinkNonIncremental(std::vector<std::string>& args,
-                                            int type,
-                                            bool hasManifest,
-                                            bool verbose);
-  static int ParseVisualStudioLinkCommand(std::vector<std::string>& args,
-                                          std::vector<std::string>& command,
-                                          std::string& targetName);
-  static bool RunCommand(const char* comment,
-                         std::vector<std::string>& command,
-                         bool verbose,
-                         int* retCodeOut = 0);
 };
 };
 
 
 #endif
 #endif

+ 1 - 0
Tests/CMakeLists.txt

@@ -276,6 +276,7 @@ if(BUILD_TESTING)
   if(TEST_RESOURCES)
   if(TEST_RESOURCES)
     ADD_TEST_MACRO(VSResource VSResource)
     ADD_TEST_MACRO(VSResource VSResource)
   endif()
   endif()
+  ADD_TEST_MACRO(MSManifest MSManifest)
   ADD_TEST_MACRO(Simple Simple)
   ADD_TEST_MACRO(Simple Simple)
   ADD_TEST_MACRO(PreOrder PreOrder)
   ADD_TEST_MACRO(PreOrder PreOrder)
   ADD_TEST_MACRO(MissingSourceFile MissingSourceFile)
   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(RunCMake-check-file check.cmake)
   set(check_step 1)
   set(check_step 1)
   run_cmake_command(${CASE}-build1 ${CMAKE_COMMAND} --build . --config Debug)
   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
   execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1.125) # handle 1s resolution
   include(${RunCMake_SOURCE_DIR}/${CASE}.step2.cmake OPTIONAL)
   include(${RunCMake_SOURCE_DIR}/${CASE}.step2.cmake OPTIONAL)
   set(check_step 2)
   set(check_step 2)
@@ -21,3 +24,11 @@ function(run_BuildDepends CASE)
 endfunction()
 endfunction()
 
 
 run_BuildDepends(C-Exe)
 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)
 if(EXISTS ${RunCMake_TEST_BINARY_DIR}/check-debug.cmake)
   include(${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)
   foreach(exe IN LISTS check_exes)
     execute_process(COMMAND ${exe} RESULT_VARIABLE res)
     execute_process(COMMAND ${exe} RESULT_VARIABLE res)
     if(NOT res EQUAL ${check_step})
     if(NOT res EQUAL ${check_step})

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

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