mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-05 21:31:08 -06:00
Merge topic 'module-def-and-WINDOWS_EXPORT_ALL_SYMBOLS'
075f6454Support WINDOWS_EXPORT_ALL_SYMBOLS with `.def` files21c4ec4fcmGlobalVisualStudioGenerator: Simplify __create_def command generation24361a45bindexplib: Add support for parsing and integrating `.def` files845c4824bindexplib: Add method for parsing and integrating `.def` files4f90e793bindexplib: Revise coding style of CMake-specific methods Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !581
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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``).
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ class bindexplib
|
||||
{
|
||||
public:
|
||||
bindexplib() {}
|
||||
bool AddDefinitionFile(const char* filename);
|
||||
bool AddObjectFile(const char* filename);
|
||||
void WriteFile(FILE* file);
|
||||
private:
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
Tests/ModuleDefinition/split_dll.c
Normal file
9
Tests/ModuleDefinition/split_dll.c
Normal file
@@ -0,0 +1,9 @@
|
||||
int split_dll_1(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int split_dll_2(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
2
Tests/ModuleDefinition/split_dll_1.def
Normal file
2
Tests/ModuleDefinition/split_dll_1.def
Normal file
@@ -0,0 +1,2 @@
|
||||
EXPORTS
|
||||
split_dll_1
|
||||
2
Tests/ModuleDefinition/split_dll_2.def
Normal file
2
Tests/ModuleDefinition/split_dll_2.def
Normal file
@@ -0,0 +1,2 @@
|
||||
EXPORTS
|
||||
split_dll_2
|
||||
Reference in New Issue
Block a user