mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-04 21:00:17 -06:00
Support WINDOWS_EXPORT_ALL_SYMBOLS with .def files
The `WINDOWS_EXPORT_ALL_SYMBOLS` target property exports all symbols found in object files explicitly given to the linker. However, the linker may also find additional symbols in dependencies and copy them into the linked binary (e.g. from `msvcrt.lib`). Provide a way to export an explicit list of such symbols by adding a `.def` file as a source file. Fixes: #16473
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``).
|
||||
@@ -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;
|
||||
|
||||
@@ -816,7 +816,7 @@ void cmGlobalVisualStudioGenerator::AddSymbolExportCommand(
|
||||
{
|
||||
cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
|
||||
gt->GetModuleDefinitionInfo(configName);
|
||||
if (!mdi || !mdi->WindowsExportAllSymbols) {
|
||||
if (!mdi || !mdi->DefFileGenerated) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -851,37 +851,47 @@ void cmGlobalVisualStudioGenerator::AddSymbolExportCommand(
|
||||
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,
|
||||
|
||||
@@ -1818,7 +1818,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.
|
||||
|
||||
@@ -3162,7 +3162,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();
|
||||
|
||||
@@ -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