Selaa lähdekoodia

Merge topic 'module-def-and-WINDOWS_EXPORT_ALL_SYMBOLS'

075f6454 Support WINDOWS_EXPORT_ALL_SYMBOLS with `.def` files
21c4ec4f cmGlobalVisualStudioGenerator: Simplify __create_def command generation
24361a45 bindexplib: Add support for parsing and integrating `.def` files
845c4824 bindexplib: Add method for parsing and integrating `.def` files
4f90e793 bindexplib: Revise coding style of CMake-specific methods

Acked-by: Kitware Robot <[email protected]>
Merge-request: !581
Brad King 8 vuotta sitten
vanhempi
sitoutus
989484d51f

+ 7 - 0
Help/prop_tgt/WINDOWS_EXPORT_ALL_SYMBOLS.rst

@@ -14,6 +14,13 @@ be automatically exported and imported by callers.  This simplifies porting
 projects to Windows by reducing the need for explicit ``dllexport`` markup,
 even in ``C++`` classes.
 
+When this property is enabled, zero or more ``.def`` files may also be
+specified as source files of the target.  The exports named by these files
+will be merged with those detected from the object files to generate a
+single module definition file to be passed to the linker.  This can be
+used to export symbols from a ``.dll`` that are not in any of its object
+files but are added by the linker from dependencies (e.g. ``msvcrt.lib``).
+
 This property is initialized by the value of
 the :variable:`CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS` variable if it is set
 when a target is created.

+ 8 - 0
Help/release/dev/module-def-and-WINDOWS_EXPORT_ALL_SYMBOLS.rst

@@ -0,0 +1,8 @@
+module-def-and-WINDOWS_EXPORT_ALL_SYMBOLS
+-----------------------------------------
+
+* The :prop_tgt:`WINDOWS_EXPORT_ALL_SYMBOLS` target property may now
+  be used in combination with explicit ``.def`` files in order to
+  export all symbols from the object files within a target plus
+  an explicit list of symbols that the linker finds in dependencies
+  (e.g. ``msvcrt.lib``).

+ 36 - 14
Source/bindexplib.cxx

@@ -64,7 +64,7 @@
 #include "bindexplib.h"
 
 #include <cmsys/Encoding.hxx>
-#include <fstream>
+#include <cmsys/FStream.hxx>
 #include <iostream>
 #include <windows.h>
 
@@ -426,24 +426,46 @@ DumpFile(const char* filename,
 
 bool bindexplib::AddObjectFile(const char* filename)
 {
-  if(!DumpFile(filename, this->Symbols, this->DataSymbols))
-    {
-    return false;
-    }
-  return true;
+  return DumpFile(filename, this->Symbols, this->DataSymbols);
+}
+
+bool bindexplib::AddDefinitionFile(const char* filename)
+{
+   cmsys::ifstream infile(filename);
+   if (!infile) {
+     fprintf(stderr, "Couldn't open definition file '%s'\n", filename);
+     return false;
+   }
+   std::string str;
+   while (std::getline(infile, str)) {
+     // skip the LIBRAY and EXPORTS lines (if any)
+     if ((str.compare(0,7,"LIBRARY") == 0) ||
+         (str.compare(0,7,"EXPORTS") == 0)) {
+       continue;
+     }
+     // remove leading tabs & spaces
+     str.erase(0, str.find_first_not_of(" \t"));
+     std::size_t found = str.find(" \t DATA");
+     if (found != std::string::npos) {
+       str.erase (found, std::string::npos);
+       this->DataSymbols.insert(str);
+     } else {
+       this->Symbols.insert(str);
+     }
+   }
+   infile.close();
+   return true;
 }
 
 void bindexplib::WriteFile(FILE* file)
 {
   fprintf(file,"EXPORTS \n");
-  for(std::set<std::string>::const_iterator i = this->DataSymbols.begin();
-      i!= this->DataSymbols.end(); ++i)
-    {
+  for (std::set<std::string>::const_iterator i = this->DataSymbols.begin();
+       i != this->DataSymbols.end(); ++i) {
     fprintf(file, "\t%s \t DATA\n", i->c_str());
-    }
-  for(std::set<std::string>::const_iterator i = this->Symbols.begin();
-      i!= this->Symbols.end(); ++i)
-    {
+  }
+  for (std::set<std::string>::const_iterator i = this->Symbols.begin();
+       i != this->Symbols.end(); ++i) {
     fprintf(file, "\t%s\n", i->c_str());
-    }
+  }
 }

+ 1 - 0
Source/bindexplib.h

@@ -13,6 +13,7 @@ class bindexplib
 {
 public:
   bindexplib() {}
+  bool AddDefinitionFile(const char* filename);
   bool AddObjectFile(const char* filename);
   void WriteFile(FILE* file);
 private:

+ 6 - 5
Source/cmGeneratorTarget.cxx

@@ -1973,15 +1973,16 @@ cmGeneratorTarget::GetModuleDefinitionInfo(std::string const& config) const
 void cmGeneratorTarget::ComputeModuleDefinitionInfo(
   std::string const& config, ModuleDefinitionInfo& info) const
 {
-  std::vector<cmSourceFile const*> sources;
-  this->GetModuleDefinitionSources(sources, config);
+  this->GetModuleDefinitionSources(info.Sources, config);
   info.WindowsExportAllSymbols =
     this->Makefile->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS") &&
     this->GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS");
-  if (info.WindowsExportAllSymbols) {
+  info.DefFileGenerated =
+    info.WindowsExportAllSymbols || info.Sources.size() > 1;
+  if (info.DefFileGenerated) {
     info.DefFile = this->ObjectDirectory /* has slash */ + "exports.def";
-  } else if (!sources.empty()) {
-    info.DefFile = sources.front()->GetFullPath();
+  } else if (!info.Sources.empty()) {
+    info.DefFile = info.Sources.front()->GetFullPath();
   }
 }
 

+ 2 - 0
Source/cmGeneratorTarget.h

@@ -238,7 +238,9 @@ public:
   struct ModuleDefinitionInfo
   {
     std::string DefFile;
+    bool DefFileGenerated;
     bool WindowsExportAllSymbols;
+    std::vector<cmSourceFile const*> Sources;
   };
   ModuleDefinitionInfo const* GetModuleDefinitionInfo(
     std::string const& config) const;

+ 41 - 32
Source/cmGlobalVisualStudioGenerator.cxx

@@ -815,7 +815,7 @@ void cmGlobalVisualStudioGenerator::AddSymbolExportCommand(
 {
   cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
     gt->GetModuleDefinitionInfo(configName);
-  if (!mdi || !mdi->WindowsExportAllSymbols) {
+  if (!mdi || !mdi->DefFileGenerated) {
     return;
   }
 
@@ -842,46 +842,55 @@ void cmGlobalVisualStudioGenerator::AddSymbolExportCommand(
   std::string obj_dir_expanded = obj_dir;
   cmSystemTools::ReplaceString(obj_dir_expanded, this->GetCMakeCFGIntDir(),
                                configName.c_str());
-  std::string objs_file = obj_dir_expanded;
-  cmSystemTools::MakeDirectory(objs_file.c_str());
-  objs_file += "/objects.txt";
+  cmSystemTools::MakeDirectory(obj_dir_expanded);
+  std::string const objs_file = obj_dir_expanded + "/objects.txt";
   cmdl.push_back(objs_file);
   cmGeneratedFileStream fout(objs_file.c_str());
   if (!fout) {
     cmSystemTools::Error("could not open ", objs_file.c_str());
     return;
   }
-  std::vector<std::string> objs;
-  for (std::vector<cmSourceFile const*>::const_iterator it =
-         objectSources.begin();
-       it != objectSources.end(); ++it) {
-    // Find the object file name corresponding to this source file.
-    std::map<cmSourceFile const*, std::string>::const_iterator map_it =
-      mapping.find(*it);
-    // It must exist because we populated the mapping just above.
-    assert(!map_it->second.empty());
-    std::string objFile = obj_dir + map_it->second;
-    objs.push_back(objFile);
-  }
-  std::vector<cmSourceFile const*> externalObjectSources;
-  gt->GetExternalObjects(externalObjectSources, configName);
-  for (std::vector<cmSourceFile const*>::const_iterator it =
-         externalObjectSources.begin();
-       it != externalObjectSources.end(); ++it) {
-    objs.push_back((*it)->GetFullPath());
-  }
 
-  gt->UseObjectLibraries(objs, configName);
-  for (std::vector<std::string>::iterator it = objs.begin(); it != objs.end();
-       ++it) {
-    std::string objFile = *it;
-    // replace $(ConfigurationName) in the object names
-    cmSystemTools::ReplaceString(objFile, this->GetCMakeCFGIntDir(),
-                                 configName.c_str());
-    if (cmHasLiteralSuffix(objFile, ".obj")) {
-      fout << objFile << "\n";
+  if (mdi->WindowsExportAllSymbols) {
+    std::vector<std::string> objs;
+    for (std::vector<cmSourceFile const*>::const_iterator it =
+           objectSources.begin();
+         it != objectSources.end(); ++it) {
+      // Find the object file name corresponding to this source file.
+      std::map<cmSourceFile const*, std::string>::const_iterator map_it =
+        mapping.find(*it);
+      // It must exist because we populated the mapping just above.
+      assert(!map_it->second.empty());
+      std::string objFile = obj_dir + map_it->second;
+      objs.push_back(objFile);
+    }
+    std::vector<cmSourceFile const*> externalObjectSources;
+    gt->GetExternalObjects(externalObjectSources, configName);
+    for (std::vector<cmSourceFile const*>::const_iterator it =
+           externalObjectSources.begin();
+         it != externalObjectSources.end(); ++it) {
+      objs.push_back((*it)->GetFullPath());
+    }
+
+    gt->UseObjectLibraries(objs, configName);
+    for (std::vector<std::string>::iterator it = objs.begin();
+         it != objs.end(); ++it) {
+      std::string objFile = *it;
+      // replace $(ConfigurationName) in the object names
+      cmSystemTools::ReplaceString(objFile, this->GetCMakeCFGIntDir(),
+                                   configName.c_str());
+      if (cmHasLiteralSuffix(objFile, ".obj")) {
+        fout << objFile << "\n";
+      }
     }
   }
+
+  for (std::vector<cmSourceFile const*>::const_iterator i =
+         mdi->Sources.begin();
+       i != mdi->Sources.end(); ++i) {
+    fout << (*i)->GetFullPath() << "\n";
+  }
+
   cmCustomCommandLines commandLines;
   commandLines.push_back(cmdl);
   cmCustomCommand command(gt->Target->GetMakefile(), outputs, empty, empty,

+ 1 - 1
Source/cmLocalVisualStudio7Generator.cxx

@@ -1819,7 +1819,7 @@ void cmLocalVisualStudio7Generator::OutputTargetRules(
   bool addedPrelink = false;
   cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
     target->GetModuleDefinitionInfo(configName);
-  if (mdi && mdi->WindowsExportAllSymbols) {
+  if (mdi && mdi->DefFileGenerated) {
     addedPrelink = true;
     std::vector<cmCustomCommand> commands = target->GetPreLinkCommands();
     cmGlobalVisualStudioGenerator* gg =

+ 25 - 12
Source/cmMakefileTargetGenerator.cxx

@@ -1414,10 +1414,14 @@ void cmMakefileTargetGenerator::AppendLinkDepends(
   this->AppendTargetDepends(depends);
 
   // Add a dependency on the link definitions file, if any.
-  cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
-    this->GeneratorTarget->GetModuleDefinitionInfo(this->GetConfigName());
-  if (mdi && !mdi->WindowsExportAllSymbols && !mdi->DefFile.empty()) {
-    depends.push_back(mdi->DefFile);
+  if (cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
+        this->GeneratorTarget->GetModuleDefinitionInfo(
+          this->GetConfigName())) {
+    for (std::vector<cmSourceFile const*>::const_iterator i =
+           mdi->Sources.begin();
+         i != mdi->Sources.end(); ++i) {
+      depends.push_back((*i)->GetFullPath());
+    }
   }
 
   // Add a dependency on user-specified manifest files, if any.
@@ -1724,7 +1728,7 @@ void cmMakefileTargetGenerator::GenDefFile(
 {
   cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
     this->GeneratorTarget->GetModuleDefinitionInfo(this->GetConfigName());
-  if (!mdi || !mdi->WindowsExportAllSymbols) {
+  if (!mdi || !mdi->DefFileGenerated) {
     return;
   }
   std::string cmd = cmSystemTools::GetCMakeCommand();
@@ -1744,15 +1748,24 @@ void cmMakefileTargetGenerator::GenDefFile(
   real_link_commands.insert(real_link_commands.begin(), cmd);
   // create a list of obj files for the -E __create_def to read
   cmGeneratedFileStream fout(objlist_file.c_str());
-  for (std::vector<std::string>::const_iterator i = this->Objects.begin();
-       i != this->Objects.end(); ++i) {
-    if (cmHasLiteralSuffix(*i, ".obj")) {
+
+  if (mdi->WindowsExportAllSymbols) {
+    for (std::vector<std::string>::const_iterator i = this->Objects.begin();
+         i != this->Objects.end(); ++i) {
+      if (cmHasLiteralSuffix(*i, ".obj")) {
+        fout << *i << "\n";
+      }
+    }
+    for (std::vector<std::string>::const_iterator i =
+           this->ExternalObjects.begin();
+         i != this->ExternalObjects.end(); ++i) {
       fout << *i << "\n";
     }
   }
-  for (std::vector<std::string>::const_iterator i =
-         this->ExternalObjects.begin();
-       i != this->ExternalObjects.end(); ++i) {
-    fout << *i << "\n";
+
+  for (std::vector<cmSourceFile const*>::const_iterator i =
+         mdi->Sources.begin();
+       i != mdi->Sources.end(); ++i) {
+    fout << (*i)->GetFullPath() << "\n";
   }
 }

+ 15 - 5
Source/cmNinjaNormalTargetGenerator.cxx

@@ -978,7 +978,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
   // maybe create .def file from list of objects
   cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
     gt.GetModuleDefinitionInfo(this->GetConfigName());
-  if (mdi && mdi->WindowsExportAllSymbols) {
+  if (mdi && mdi->DefFileGenerated) {
     std::string cmakeCommand =
       this->GetLocalGenerator()->ConvertToOutputFormat(
         cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
@@ -987,18 +987,28 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
     cmd += this->GetLocalGenerator()->ConvertToOutputFormat(
       mdi->DefFile, cmOutputConverter::SHELL);
     cmd += " ";
-    cmNinjaDeps objs = this->GetObjects();
     std::string obj_list_file = mdi->DefFile + ".objs";
     cmd += this->GetLocalGenerator()->ConvertToOutputFormat(
       obj_list_file, cmOutputConverter::SHELL);
     preLinkCmdLines.push_back(cmd);
+
     // create a list of obj files for the -E __create_def to read
     cmGeneratedFileStream fout(obj_list_file.c_str());
-    for (cmNinjaDeps::iterator i = objs.begin(); i != objs.end(); ++i) {
-      if (cmHasLiteralSuffix(*i, ".obj")) {
-        fout << *i << "\n";
+
+    if (mdi->WindowsExportAllSymbols) {
+      cmNinjaDeps objs = this->GetObjects();
+      for (cmNinjaDeps::iterator i = objs.begin(); i != objs.end(); ++i) {
+        if (cmHasLiteralSuffix(*i, ".obj")) {
+          fout << *i << "\n";
+        }
       }
     }
+
+    for (std::vector<cmSourceFile const*>::const_iterator i =
+           mdi->Sources.begin();
+         i != mdi->Sources.end(); ++i) {
+      fout << (*i)->GetFullPath() << "\n";
+    }
   }
   // If we have any PRE_LINK commands, we need to go back to CMAKE_BINARY_DIR
   // for

+ 8 - 4
Source/cmNinjaTargetGenerator.cxx

@@ -212,10 +212,14 @@ cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const
   std::transform(deps.begin(), deps.end(), result.begin(), MapToNinjaPath());
 
   // Add a dependency on the link definitions file, if any.
-  cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
-    this->GeneratorTarget->GetModuleDefinitionInfo(this->GetConfigName());
-  if (mdi && !mdi->WindowsExportAllSymbols && !mdi->DefFile.empty()) {
-    result.push_back(this->ConvertToNinjaPath(mdi->DefFile));
+  if (cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
+        this->GeneratorTarget->GetModuleDefinitionInfo(
+          this->GetConfigName())) {
+    for (std::vector<cmSourceFile const*>::const_iterator i =
+           mdi->Sources.begin();
+         i != mdi->Sources.end(); ++i) {
+      result.push_back(this->ConvertToNinjaPath((*i)->GetFullPath()));
+    }
   }
 
   // Add a dependency on user-specified manifest files, if any.

+ 1 - 1
Source/cmVisualStudio10TargetGenerator.cxx

@@ -3300,7 +3300,7 @@ void cmVisualStudio10TargetGenerator::WriteEvents(
   bool addedPrelink = false;
   cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
     this->GeneratorTarget->GetModuleDefinitionInfo(configName);
-  if (mdi && mdi->WindowsExportAllSymbols) {
+  if (mdi && mdi->DefFileGenerated) {
     addedPrelink = true;
     std::vector<cmCustomCommand> commands =
       this->GeneratorTarget->GetPreLinkCommands();

+ 11 - 4
Source/cmcmd.cxx

@@ -258,11 +258,18 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args)
                   << "\n";
         return 1;
       }
-      std::string objfile;
+      std::string file;
       bindexplib deffile;
-      while (cmSystemTools::GetLineFromStream(fin, objfile)) {
-        if (!deffile.AddObjectFile(objfile.c_str())) {
-          return 1;
+      while (cmSystemTools::GetLineFromStream(fin, file)) {
+        std::string const& ext = cmSystemTools::GetFilenameLastExtension(file);
+        if (cmSystemTools::LowerCase(ext) == ".def") {
+          if (!deffile.AddDefinitionFile(file.c_str())) {
+            return 1;
+          }
+        } else {
+          if (!deffile.AddObjectFile(file.c_str())) {
+            return 1;
+          }
         }
       }
       deffile.WriteFile(fout);

+ 8 - 1
Tests/ModuleDefinition/CMakeLists.txt

@@ -4,6 +4,8 @@ project(ModuleDefinition C)
 # Test .def file source recognition for DLLs.
 add_library(example_dll SHARED example_dll.c example_dll.def)
 
+add_library(split_dll SHARED split_dll.c split_dll_1.def split_dll_2.def)
+
 # Test generated .def file.
 add_custom_command(OUTPUT example_dll_gen.def
   DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/example_dll_gen.def.in
@@ -24,7 +26,12 @@ endif()
 # Test .def file source recognition for EXEs.
 add_executable(example_exe example_exe.c example_exe.def)
 set_property(TARGET example_exe PROPERTY ENABLE_EXPORTS 1)
-target_link_libraries(example_exe example_dll example_dll_gen ${example_dll_2})
+target_link_libraries(example_exe
+  example_dll
+  example_dll_gen
+  ${example_dll_2}
+  split_dll
+  )
 
 # Test linking to the executable.
 add_library(example_mod_1 MODULE example_mod_1.c)

+ 5 - 1
Tests/ModuleDefinition/example_exe.c

@@ -3,15 +3,19 @@ extern int __declspec(dllimport) example_dll_gen_function(void);
 #ifdef EXAMPLE_DLL_2
 extern int __declspec(dllimport) example_dll_2_function(void);
 #endif
+extern int __declspec(dllimport) split_dll_1(void);
+extern int __declspec(dllimport) split_dll_2(void);
+
 int example_exe_function(void)
 {
   return 0;
 }
+
 int main(void)
 {
   return example_dll_function() + example_dll_gen_function() +
 #ifdef EXAMPLE_DLL_2
     example_dll_2_function() +
 #endif
-    example_exe_function();
+    split_dll_1() + split_dll_2() + example_exe_function();
 }

+ 9 - 0
Tests/ModuleDefinition/split_dll.c

@@ -0,0 +1,9 @@
+int split_dll_1(void)
+{
+  return 0;
+}
+
+int split_dll_2(void)
+{
+  return 0;
+}

+ 2 - 0
Tests/ModuleDefinition/split_dll_1.def

@@ -0,0 +1,2 @@
+EXPORTS
+split_dll_1

+ 2 - 0
Tests/ModuleDefinition/split_dll_2.def

@@ -0,0 +1,2 @@
+EXPORTS
+split_dll_2