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 <kwrobot@kitware.com>
Merge-request: !581
This commit is contained in:
Brad King
2017-03-22 12:48:56 +00:00
committed by Kitware Robot
18 changed files with 188 additions and 80 deletions

View File

@@ -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.

View File

@@ -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``).

View File

@@ -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());
}
}
}

View File

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

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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,

View File

@@ -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 =

View File

@@ -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";
}
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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();

View File

@@ -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);

View File

@@ -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)

View File

@@ -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();
}

View File

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

View File

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

View File

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