From 8f86407cfd4331dc1f2eb67f4f179ed8fe9dea06 Mon Sep 17 00:00:00 2001 From: Bill Hoffman Date: Fri, 19 Jun 2015 16:12:43 -0400 Subject: [PATCH] Windows: Optionally generate DLL module definition files automatically Create target property WINDOWS_EXPORT_ALL_SYMBOLS to automatically generate a module definition file from MS-compatible .obj files and give it to the linker in order to export all symbols from the .dll part of a SHARED library. --- Help/manual/cmake-properties.7.rst | 1 + Help/manual/cmake-variables.7.rst | 1 + Help/prop_tgt/WINDOWS_EXPORT_ALL_SYMBOLS.rst | 18 +++++ Help/release/dev/auto_export_dll_symbols.rst | 6 ++ .../CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS.rst | 6 ++ Modules/Platform/Windows-MSVC.cmake | 2 + Source/cmGlobalVisualStudioGenerator.cxx | 70 +++++++++++++++++++ Source/cmGlobalVisualStudioGenerator.h | 4 ++ Source/cmLocalVisualStudio7Generator.cxx | 31 +++++++- Source/cmMakefileLibraryTargetGenerator.cxx | 53 ++++++++++++++ Source/cmNinjaNormalTargetGenerator.cxx | 54 ++++++++++++++ Source/cmTarget.cxx | 5 ++ Source/cmVisualStudio10TargetGenerator.cxx | 30 +++++++- Source/cmcmd.cxx | 39 +++++++++++ Tests/RunCMake/AutoExportDll/AutoExport.cmake | 7 ++ .../AutoExportDll/AutoExportBuild-stderr.txt | 1 + Tests/RunCMake/AutoExportDll/CMakeLists.txt | 3 + .../RunCMake/AutoExportDll/RunCMakeTest.cmake | 26 +++++++ Tests/RunCMake/AutoExportDll/foo.c | 15 ++++ Tests/RunCMake/AutoExportDll/hello.cxx | 13 ++++ Tests/RunCMake/AutoExportDll/hello.h | 18 +++++ Tests/RunCMake/AutoExportDll/say.cxx | 37 ++++++++++ .../RunCMake/AutoExportDll/sub/CMakeLists.txt | 5 ++ Tests/RunCMake/AutoExportDll/sub/sub.cxx | 4 ++ Tests/RunCMake/AutoExportDll/world.cxx | 6 ++ Tests/RunCMake/CMakeLists.txt | 3 + 26 files changed, 455 insertions(+), 3 deletions(-) create mode 100644 Help/prop_tgt/WINDOWS_EXPORT_ALL_SYMBOLS.rst create mode 100644 Help/release/dev/auto_export_dll_symbols.rst create mode 100644 Help/variable/CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS.rst create mode 100644 Tests/RunCMake/AutoExportDll/AutoExport.cmake create mode 100644 Tests/RunCMake/AutoExportDll/AutoExportBuild-stderr.txt create mode 100644 Tests/RunCMake/AutoExportDll/CMakeLists.txt create mode 100644 Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/AutoExportDll/foo.c create mode 100644 Tests/RunCMake/AutoExportDll/hello.cxx create mode 100644 Tests/RunCMake/AutoExportDll/hello.h create mode 100644 Tests/RunCMake/AutoExportDll/say.cxx create mode 100644 Tests/RunCMake/AutoExportDll/sub/CMakeLists.txt create mode 100644 Tests/RunCMake/AutoExportDll/sub/sub.cxx create mode 100644 Tests/RunCMake/AutoExportDll/world.cxx diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 9a60a103c..b767ed624 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -251,6 +251,7 @@ Properties on Targets /prop_tgt/VS_WINRT_EXTENSIONS /prop_tgt/VS_WINRT_REFERENCES /prop_tgt/WIN32_EXECUTABLE + /prop_tgt/WINDOWS_EXPORT_ALL_SYMBOLS /prop_tgt/XCODE_ATTRIBUTE_an-attribute /prop_tgt/XCTEST diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 0e6222f25..7c7db5e92 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -275,6 +275,7 @@ Variables that Control the Build /variable/CMAKE_USE_RELATIVE_PATHS /variable/CMAKE_VISIBILITY_INLINES_HIDDEN /variable/CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD + /variable/CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS /variable/CMAKE_WIN32_EXECUTABLE /variable/CMAKE_XCODE_ATTRIBUTE_an-attribute /variable/EXECUTABLE_OUTPUT_PATH diff --git a/Help/prop_tgt/WINDOWS_EXPORT_ALL_SYMBOLS.rst b/Help/prop_tgt/WINDOWS_EXPORT_ALL_SYMBOLS.rst new file mode 100644 index 000000000..3f48af88c --- /dev/null +++ b/Help/prop_tgt/WINDOWS_EXPORT_ALL_SYMBOLS.rst @@ -0,0 +1,18 @@ +WINDOWS_EXPORT_ALL_SYMBOLS +-------------------------- + +This property is implemented only for MS-compatible tools on Windows. + +Enable this boolean property to automatically create a module definition +(``.def``) file with all global symbols found in the input ``.obj`` files +for a ``SHARED`` library on Windows. The module definition file will be +passed to the linker causing all symbols to be exported from the ``.dll``. +For global *data* symbols, ``__declspec(dllimport)`` must still be used when +compiling against the code in the ``.dll``. All other function symbols will +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. + +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. diff --git a/Help/release/dev/auto_export_dll_symbols.rst b/Help/release/dev/auto_export_dll_symbols.rst new file mode 100644 index 000000000..9db2b5eab --- /dev/null +++ b/Help/release/dev/auto_export_dll_symbols.rst @@ -0,0 +1,6 @@ +auto_export_dll_symbols +----------------------- + +* On Windows with MS-compatible tools, CMake learned to optionally + generate a module definition (``.def``) file for ``SHARED`` libraries. + See the :prop_tgt:`WINDOWS_EXPORT_ALL_SYMBOLS` target property. diff --git a/Help/variable/CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS.rst b/Help/variable/CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS.rst new file mode 100644 index 000000000..1636842d9 --- /dev/null +++ b/Help/variable/CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS.rst @@ -0,0 +1,6 @@ +CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS +-------------------------------- + +Default value for :prop_tgt:`WINDOWS_EXPORT_ALL_SYMBOLS` target property. +This variable is used to initialize the property on each target as it is +created. diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake index 13fe8bc9c..aeaa2bdab 100644 --- a/Modules/Platform/Windows-MSVC.cmake +++ b/Modules/Platform/Windows-MSVC.cmake @@ -46,8 +46,10 @@ else() set(_PLATFORM_LINK_FLAGS "") endif() +set(CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS 1) if(CMAKE_GENERATOR MATCHES "Visual Studio 6") set (CMAKE_NO_BUILD_TYPE 1) + set(CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS 0) # not implemented for VS6 endif() if(NOT CMAKE_NO_BUILD_TYPE AND CMAKE_GENERATOR MATCHES "Visual Studio") set (CMAKE_NO_BUILD_TYPE 1) diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index 585d19a81..bc134e1de 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -13,12 +13,14 @@ #include "cmGlobalVisualStudioGenerator.h" #include "cmCallVisualStudioMacro.h" +#include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmLocalVisualStudioGenerator.h" #include "cmMakefile.h" #include "cmSourceFile.h" #include "cmTarget.h" #include +#include "cmAlgorithms.h" //---------------------------------------------------------------------------- cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator(cmake* cm) @@ -887,3 +889,71 @@ std::string cmGlobalVisualStudioGenerator::ExpandCFGIntDir( } return tmp; } + +void cmGlobalVisualStudioGenerator::AddSymbolExportCommand( + cmGeneratorTarget* gt, std::vector& commands, + std::string const& configName) +{ + std::vector outputs; + std::string deffile = gt->ObjectDirectory; + deffile += "/exportall.def"; + outputs.push_back(deffile); + std::vector empty; + std::vector objectSources; + gt->GetObjectSources(objectSources, configName); + std::map mapping; + for(std::vector::const_iterator it + = objectSources.begin(); it != objectSources.end(); ++it) + { + mapping[*it]; + } + gt->LocalGenerator-> + ComputeObjectFilenames(mapping, gt); + std::string obj_dir = gt->ObjectDirectory; + std::string cmakeCommand = cmSystemTools::GetCMakeCommand(); + cmSystemTools::ConvertToWindowsExtendedPath(cmakeCommand); + cmCustomCommandLine cmdl; + cmdl.push_back(cmakeCommand); + cmdl.push_back("-E"); + cmdl.push_back("__create_def"); + cmdl.push_back(deffile); + 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"; + cmdl.push_back(objs_file); + cmGeneratedFileStream fout(objs_file.c_str()); + if(!fout) + { + cmSystemTools::Error("could not open ", objs_file.c_str()); + return; + } + for(std::vector::const_iterator it + = objectSources.begin(); it != objectSources.end(); ++it) + { + // Find the object file name corresponding to this source file. + std::map::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; + // replace $(ConfigurationName) in the object names + cmSystemTools::ReplaceString(objFile, this->GetCMakeCFGIntDir(), + configName.c_str()); + if(cmHasLiteralSuffix(objFile, ".obj")) + { + fout << objFile << "\n"; + } + } + cmCustomCommandLines commandLines; + commandLines.push_back(cmdl); + cmCustomCommand command(gt->Target->GetMakefile(), + outputs, empty, empty, + commandLines, + "Auto build dll exports", + "."); + commands.push_back(command); +} diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index 69b4564c5..022e19005 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -102,6 +102,10 @@ public: const std::string& config) const; void ComputeTargetObjectDirectory(cmGeneratorTarget* gt) const; + + void AddSymbolExportCommand( + cmGeneratorTarget*, std::vector& commands, + std::string const& configName); protected: virtual void Generate(); diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index dc3a16d4c..a0e9e4da0 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -1081,6 +1081,14 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(std::ostream& fout, this->ConvertToOutputFormat(this->ModuleDefinitionFile, SHELL); linkOptions.AddFlag("ModuleDefinitionFile", defFile.c_str()); } + if (target.GetType() == cmTarget::SHARED_LIBRARY && + this->Makefile->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS")) + { + if (target.GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS")) + { + linkOptions.AddFlag("ModuleDefinitionFile", "$(IntDir)/exportall.def"); + } + } switch(target.GetType()) { case cmTarget::UNKNOWN_LIBRARY: @@ -2015,7 +2023,28 @@ void cmLocalVisualStudio7Generator // Add pre-link event. tool = this->FortranProject? "VFPreLinkEventTool":"VCPreLinkEventTool"; event.Start(tool); - event.Write(target.GetPreLinkCommands()); + bool addedPrelink = false; + if (target.GetType() == cmTarget::SHARED_LIBRARY && + this->Makefile->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS")) + { + if (target.GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS")) + { + addedPrelink = true; + std::vector commands = + target.GetPreLinkCommands(); + cmGlobalVisualStudioGenerator* gg + = static_cast(this->GlobalGenerator); + cmGeneratorTarget* gt = + this->GlobalGenerator->GetGeneratorTarget(&target); + gg->AddSymbolExportCommand( + gt, commands, configName); + event.Write(commands); + } + } + if (!addedPrelink) + { + event.Write(target.GetPreLinkCommands()); + } cmsys::auto_ptr pcc( this->MaybeCreateImplibDir(target, configName, this->FortranProject)); if(pcc.get()) diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index 660027c96..696dcc4f5 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -18,6 +18,7 @@ #include "cmSourceFile.h" #include "cmTarget.h" #include "cmake.h" +#include "cmAlgorithms.h" //---------------------------------------------------------------------------- cmMakefileLibraryTargetGenerator @@ -563,6 +564,58 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules useResponseFileForObjects, buildObjs, depends, useWatcomQuote); + // maybe create .def file from list of objects + if (this->Target->GetType() == cmTarget::SHARED_LIBRARY && + this->Makefile->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS")) + { + if(this->Target->GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS")) + { + std::string name_of_def_file = + this->Target->GetSupportDirectory(); + name_of_def_file += std::string("/") + + this->Target->GetName(); + name_of_def_file += ".def"; + std::string cmd = cmSystemTools::GetCMakeCommand(); + cmd = this->Convert(cmd, cmLocalGenerator::NONE, + cmLocalGenerator::SHELL); + cmd += " -E __create_def "; + cmd += this->Convert(name_of_def_file, + cmLocalGenerator::START_OUTPUT, + cmLocalGenerator::SHELL); + cmd += " "; + std::string objlist_file = name_of_def_file; + objlist_file += ".objs"; + cmd += this->Convert(objlist_file, + cmLocalGenerator::START_OUTPUT, + cmLocalGenerator::SHELL); + real_link_commands.push_back(cmd); + // create a list of obj files for the -E __create_def to read + cmGeneratedFileStream fout(objlist_file.c_str()); + for(std::vector::const_iterator i = this->Objects.begin(); + i != this->Objects.end(); ++i) + { + if(cmHasLiteralSuffix(*i, ".obj")) + { + fout << *i << "\n"; + } + } + for(std::vector::const_iterator i = + this->ExternalObjects.begin(); + i != this->ExternalObjects.end(); ++i) + { + fout << *i << "\n"; + } + // now add the def file link flag + linkFlags += " "; + linkFlags += + this->Makefile->GetSafeDefinition("CMAKE_LINK_DEF_FILE_FLAG"); + linkFlags += this->Convert(name_of_def_file, + cmLocalGenerator::START_OUTPUT, + cmLocalGenerator::SHELL); + linkFlags += " "; + } + } + cmLocalGenerator::RuleVariables vars; vars.TargetPDB = targetOutPathPDB.c_str(); diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 2fe53bfff..88da09bab 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -486,6 +486,22 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() linkPath, &genTarget, useWatcomQuote); + if(this->GetMakefile()->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS") + && target.GetType() == cmTarget::SHARED_LIBRARY) + { + if(target.GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS")) + { + std::string dllname = targetOutput; + std::string name_of_def_file + = target.GetSupportDirectory(); + name_of_def_file += "/" + target.GetName(); + name_of_def_file += ".def "; + vars["LINK_FLAGS"] += " /DEF:"; + vars["LINK_FLAGS"] += this->GetLocalGenerator() + ->ConvertToOutputFormat(name_of_def_file.c_str(), + cmLocalGenerator::SHELL); + } + } this->addPoolNinjaVariable("JOB_POOL_LINK", &target, vars); @@ -600,6 +616,44 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() } } + // maybe create .def file from list of objects + if (target.GetType() == cmTarget::SHARED_LIBRARY && + this->GetMakefile()->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS")) + { + if(target.GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS")) + { + std::string cmakeCommand = + this->GetLocalGenerator()->ConvertToOutputFormat( + cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL); + std::string dllname = targetOutput; + std::string name_of_def_file + = target.GetSupportDirectory(); + name_of_def_file += "/" + target.GetName(); + name_of_def_file += ".def"; + std::string cmd = cmakeCommand; + cmd += " -E __create_def "; + cmd += this->GetLocalGenerator() + ->ConvertToOutputFormat(name_of_def_file.c_str(), + cmLocalGenerator::SHELL); + cmd += " "; + cmNinjaDeps objs = this->GetObjects(); + std::string obj_list_file = name_of_def_file; + obj_list_file += ".objs"; + cmd += this->GetLocalGenerator() + ->ConvertToOutputFormat(obj_list_file.c_str(), + cmLocalGenerator::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 we have any PRE_LINK commands, we need to go back to HOME_OUTPUT for // the link commands. if (!preLinkCmdLines.empty()) diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index c7a13bcd7..844843108 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -429,6 +429,11 @@ void cmTarget::SetMakefile(cmMakefile* mf) { this->SetProperty("POSITION_INDEPENDENT_CODE", "True"); } + if(this->TargetTypeValue == cmTarget::SHARED_LIBRARY) + { + this->SetPropertyDefault("WINDOWS_EXPORT_ALL_SYMBOLS", 0); + } + if (this->GetType() != INTERFACE_LIBRARY && this->GetType() != UTILITY) { this->SetPropertyDefault("POSITION_INDEPENDENT_CODE", 0); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 12a1e42c0..57ec21234 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -2466,6 +2466,15 @@ cmVisualStudio10TargetGenerator::ComputeLinkOptions(std::string const& config) "%(IgnoreSpecificDefaultLibraries)"); } + if (this->Target->GetType() == cmTarget::SHARED_LIBRARY && + this->Makefile->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS")) + { + if (this->Target->GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS")) + { + linkOptions.AddFlag("ModuleDefinitionFile", "$(IntDir)exportall.def"); + } + } + this->LinkOptions[config] = pOptions.release(); return true; } @@ -2613,8 +2622,25 @@ void cmVisualStudio10TargetGenerator::WriteItemDefinitionGroups() void cmVisualStudio10TargetGenerator::WriteEvents(std::string const& configName) { - this->WriteEvent("PreLinkEvent", - this->Target->GetPreLinkCommands(), configName); + bool addedPrelink = false; + if (this->Target->GetType() == cmTarget::SHARED_LIBRARY && + this->Makefile->IsOn("CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS")) + { + if (this->Target->GetPropertyAsBool("WINDOWS_EXPORT_ALL_SYMBOLS")) + { + addedPrelink = true; + std::vector commands = + this->Target->GetPreLinkCommands(); + this->GlobalGenerator->AddSymbolExportCommand( + this->GeneratorTarget, commands, configName); + this->WriteEvent("PreLinkEvent", commands, configName); + } + } + if (!addedPrelink) + { + this->WriteEvent("PreLinkEvent", + this->Target->GetPreLinkCommands(), configName); + } this->WriteEvent("PreBuildEvent", this->Target->GetPreBuildCommands(), configName); this->WriteEvent("PostBuildEvent", diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 3ea21866e..63838b49b 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -34,6 +34,10 @@ #include #include // required for atoi +#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE) +// defined in binexplib.cxx +bool DumpFile(const char* filename, FILE *fout); +#endif void CMakeCommandUsage(const char* program) { @@ -211,6 +215,41 @@ int cmcmd::ExecuteCMakeCommand(std::vector& args) return 0; } +#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE) + else if(args[1] == "__create_def") + { + if(args.size() < 4) + { + std::cerr << + "__create_def Usage: -E __create_def outfile.def objlistfile\n"; + return 1; + } + FILE* fout = cmsys::SystemTools::Fopen(args[2].c_str(), "w+"); + if(!fout) + { + std::cerr << "could not open output .def file: " << args[2].c_str() + << "\n"; + return 1; + } + cmsys::ifstream fin(args[3].c_str(), + std::ios::in | std::ios::binary); + if(!fin) + { + std::cerr << "could not open object list file: " << args[3].c_str() + << "\n"; + return 1; + } + std::string objfile; + while(cmSystemTools::GetLineFromStream(fin, objfile)) + { + if (!DumpFile(objfile.c_str(), fout)) + { + return 1; + } + } + return 0; + } +#endif // run include what you use command and then run the compile // command. This is an internal undocumented option and should // only be used by CMake itself when running iwyu. diff --git a/Tests/RunCMake/AutoExportDll/AutoExport.cmake b/Tests/RunCMake/AutoExportDll/AutoExport.cmake new file mode 100644 index 000000000..3b2b2c5b4 --- /dev/null +++ b/Tests/RunCMake/AutoExportDll/AutoExport.cmake @@ -0,0 +1,7 @@ +project(autoexport) +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${autoexport_BINARY_DIR}/bin) +add_subdirectory(sub) +add_library(autoexport SHARED hello.cxx world.cxx foo.c) +add_executable(say say.cxx) +target_link_libraries(say autoexport autoexport2) diff --git a/Tests/RunCMake/AutoExportDll/AutoExportBuild-stderr.txt b/Tests/RunCMake/AutoExportDll/AutoExportBuild-stderr.txt new file mode 100644 index 000000000..d483c2ce1 --- /dev/null +++ b/Tests/RunCMake/AutoExportDll/AutoExportBuild-stderr.txt @@ -0,0 +1 @@ +^.*$ diff --git a/Tests/RunCMake/AutoExportDll/CMakeLists.txt b/Tests/RunCMake/AutoExportDll/CMakeLists.txt new file mode 100644 index 000000000..18dfd2686 --- /dev/null +++ b/Tests/RunCMake/AutoExportDll/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.2) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake b/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake new file mode 100644 index 000000000..3784a6a14 --- /dev/null +++ b/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake @@ -0,0 +1,26 @@ +include(RunCMake) +set(RunCMake_TEST_NO_CLEAN TRUE) +set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/AutoExport-build") +# start by cleaning up because we don't clean up along the way +file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") +# configure the AutoExport test +run_cmake(AutoExport) +unset(RunCMake_TEST_OPTIONS) +# don't run this test on VS 6 as it is not supported +if("${RunCMake_GENERATOR}" MATCHES "Visual Studio 6|Watcom WMake|Borland Makefiles") + return() +endif() +# we build debug so the say.exe will be found in Debug/say.exe for +# Visual Studio generators +if("${RunCMake_GENERATOR}" MATCHES "Visual Studio|Xcode") + set(INTDIR "Debug/") +endif() +# build AutoExport +run_cmake_command(AutoExportBuild ${CMAKE_COMMAND} --build + ${RunCMake_TEST_BINARY_DIR} --config Debug --clean-first) +# run the executable that uses symbols from the dll +if(WIN32) + set(EXE_EXT ".exe") +endif() +run_cmake_command(AutoExportRun + ${RunCMake_BINARY_DIR}/AutoExport-build/bin/${INTDIR}say${EXE_EXT}) diff --git a/Tests/RunCMake/AutoExportDll/foo.c b/Tests/RunCMake/AutoExportDll/foo.c new file mode 100644 index 000000000..4b1318b9e --- /dev/null +++ b/Tests/RunCMake/AutoExportDll/foo.c @@ -0,0 +1,15 @@ +#ifdef _MSC_VER +#include "windows.h" +#else +#define WINAPI +#endif + +int WINAPI foo() +{ + return 10; +} + +int bar() +{ + return 5; +} diff --git a/Tests/RunCMake/AutoExportDll/hello.cxx b/Tests/RunCMake/AutoExportDll/hello.cxx new file mode 100644 index 000000000..3933fc114 --- /dev/null +++ b/Tests/RunCMake/AutoExportDll/hello.cxx @@ -0,0 +1,13 @@ +#include +#include "hello.h" +int Hello::Data = 0; +void Hello::real() +{ + return; +} +void hello() +{ + printf("hello"); +} +void Hello::operator delete[](void*) {}; +void Hello::operator delete(void*) {}; diff --git a/Tests/RunCMake/AutoExportDll/hello.h b/Tests/RunCMake/AutoExportDll/hello.h new file mode 100644 index 000000000..3749b976f --- /dev/null +++ b/Tests/RunCMake/AutoExportDll/hello.h @@ -0,0 +1,18 @@ +#ifndef _MSC_VER +#define winexport +#else +#ifdef autoexport_EXPORTS +#define winexport +#else +#define winexport __declspec(dllimport) +#endif +#endif + +class Hello +{ +public: + static winexport int Data; + void real(); + static void operator delete[](void*); + static void operator delete(void*); +}; diff --git a/Tests/RunCMake/AutoExportDll/say.cxx b/Tests/RunCMake/AutoExportDll/say.cxx new file mode 100644 index 000000000..655b3c21a --- /dev/null +++ b/Tests/RunCMake/AutoExportDll/say.cxx @@ -0,0 +1,37 @@ +#include +#include "hello.h" +#ifdef _MSC_VER +#include "windows.h" +#else +#define WINAPI +#endif + +extern "C" +{ +// test __cdecl stuff + int WINAPI foo(); +// test regular C + int bar(); +} + +// test c++ functions +// forward declare hello and world +void hello(); +void world(); + +int main() +{ + // test static data (needs declspec to work) + Hello::Data = 120; + Hello h; + h.real(); + hello(); + printf(" "); + world(); + printf("\n"); + foo(); + printf("\n"); + bar(); + printf("\n"); + return 0; +} diff --git a/Tests/RunCMake/AutoExportDll/sub/CMakeLists.txt b/Tests/RunCMake/AutoExportDll/sub/CMakeLists.txt new file mode 100644 index 000000000..8b70e7dfa --- /dev/null +++ b/Tests/RunCMake/AutoExportDll/sub/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(autoexport2 SHARED sub.cxx) +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # Try msvc "big" object format. + target_compile_options(autoexport2 PRIVATE /bigobj) +endif() diff --git a/Tests/RunCMake/AutoExportDll/sub/sub.cxx b/Tests/RunCMake/AutoExportDll/sub/sub.cxx new file mode 100644 index 000000000..9766b41ec --- /dev/null +++ b/Tests/RunCMake/AutoExportDll/sub/sub.cxx @@ -0,0 +1,4 @@ +int sub() +{ + return 10; +} diff --git a/Tests/RunCMake/AutoExportDll/world.cxx b/Tests/RunCMake/AutoExportDll/world.cxx new file mode 100644 index 000000000..3a54df315 --- /dev/null +++ b/Tests/RunCMake/AutoExportDll/world.cxx @@ -0,0 +1,6 @@ +#include "stdio.h" + +void world() +{ + printf("world"); +} diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index bc706d312..743ef4be1 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -266,3 +266,6 @@ if("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") endif() add_RunCMake_test_group(CPack "DEB;RPM") +# add a test to make sure symbols are exported from a shared library +# for MSVC compilers CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS property is used +add_RunCMake_test(AutoExportDll)