Short answer
You can do it with help of the new version of the CMake (any version cmake-3.3.20150721-g9cd2f-win32-x86.exe or higher).
Currently it’s in the dev branch.
Later, the feature will be added in the release version of the cmake-3.4.
Link to the cmake dev:
cmake_dev
Link to an article which describe the technic:
Create dlls on Windows without declspec() using new CMake export all feature
Link to an example project:
cmake_windows_export_all_symbols
Long answer
Caution:
All information below is related to the MSVC compiler or Visual Studio.
If you use other compilers like gcc on Linux or MinGW gcc compiler on Windows you don’t have linking errors due to not exported symbols, because gcc compiler export all symbols in a dynamic library (dll) by default instead of MSVC or Intel windows compilers.
In windows you have to explicitly export symbol from a dll.
More info about this is provided by links:
Exporting from a DLL
HowTo: Export C++ classes from a DLL
So if you want to export all symbols from dll with MSVC (Visual Studio compiler) you have two options:
- Use the keyword __declspec(dllexport) in the class/function’s definition.
- Create a module definition (.def) file and use the .def file when building the DLL.
1. Use the keyword __declspec(dllexport) in the class/function’s definition
1.1. Add «__declspec(dllexport) / __declspec(dllimport)» macros to a class or method you want to use. So if you want to export all classes you should add this macros to all of them
More info about this is provided by link:
Exporting from a DLL Using __declspec(dllexport)
Example of usage (replace «Project» by real project name):
// ProjectExport.h
#ifndef __PROJECT_EXPORT_H
#define __PROJECT_EXPORT_H
#ifdef USEPROJECTLIBRARY
#ifdef PROJECTLIBRARY_EXPORTS
#define PROJECTAPI __declspec(dllexport)
#else
#define PROJECTAPI __declspec(dllimport)
#endif
#else
#define PROJECTAPI
#endif
#endif
Then add «PROJECTAPI» to all classes.
Define «USEPROJECTLIBRARY» only if you want export/import symbols from dll.
Define «PROJECTLIBRARY_EXPORTS» for the dll.
Example of class export:
#include "ProjectExport.h"
namespace hello {
class PROJECTAPI Hello {}
}
Example of function export:
#include "ProjectExport.h"
PROJECTAPI void HelloWorld();
Caution: don’t forget to include «ProjectExport.h» file.
1.2. Export as C functions.
If you use C++ compiler for compilation code is written on C, you could add extern «C» in front of a function to eliminate name mangling
More info about C++ name mangling is provided by link:
Name Decoration
Example of usage:
extern "C" __declspec(dllexport) void HelloWorld();
More info about this is provided by link:
Exporting C++ Functions for Use in C-Language Executables
2. Create a module definition (.def) file and use the .def file when building the DLL
More info about this is provided by link:
Exporting from a DLL Using DEF Files
Further I describe three approach about how to create .def file.
2.1. Export C functions
In this case you could simple add function declarations in the .def file by hand.
Example of usage:
extern "C" void HelloWorld();
Example of .def file (__cdecl naming convention):
EXPORTS
_HelloWorld
2.2. Export symbols from static library
I tried approach suggested by «user72260».
He said:
- Firstly, you could create static library.
- Then use «dumpbin /LINKERMEMBER» to export all symbols from static library.
- Parse the output.
- Put all results in a .def file.
- Create dll with the .def file.
I used this approach, but it’s not very convinient to always create two builds (one as a static and the other as a dynamic library). However, I have to admit, this approach really works.
2.3. Export symbols from .obj files or with help of the CMake
2.3.1. With CMake usage
Important notice: You don’t need any export macros to a classes or functions!
Important notice: You can’t use /GL (Whole Program Optimization) when use this approach!
- Create CMake project based on the «CMakeLists.txt» file.
- Add the following line to the «CMakeLists.txt» file:
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) - Then create Visual Studio project with help of «CMake (cmake-gui)».
- Compile the project.
Example of usage:
Root folder
CMakeLists.txt (Root folder)
cmake_minimum_required(VERSION 2.6)
project(cmake_export_all)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(dir ${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${dir}/bin")
set(SOURCE_EXE main.cpp)
include_directories(foo)
add_executable(main ${SOURCE_EXE})
add_subdirectory(foo)
target_link_libraries(main foo)
main.cpp (Root folder)
#include "foo.h"
int main() {
HelloWorld();
return 0;
}
Foo folder (Root folder / Foo folder)
CMakeLists.txt (Foo folder)
project(foo)
set(SOURCE_LIB foo.cpp)
add_library(foo SHARED ${SOURCE_LIB})
foo.h (Foo folder)
void HelloWorld();
foo.cpp (Foo folder)
#include <iostream>
void HelloWorld() {
std::cout << "Hello World!" << std::endl;
}
Link to the example project again:
cmake_windows_export_all_symbols
CMake uses the different from «2.2. Export symbols from static library» approach.
It does the following:
1) Create «objects.txt» file in the build directory with information of .obj files are used in a dll.
2) Compile the dll, that is create .obj files.
3) Based on «objects.txt» file information extract all symbols from .obj file.
Example of usage:
DUMPBIN /SYMBOLS example.obj > log.txt
More info about this is provided by link:
/SYMBOLS
4) Parse extracted from .obj file information.
In my opinion I would use calling convection, for example «__cdecl/__fastcall», «SECTx/UNDEF» symbol field (the third column), «External/Static» symbol field (the fifth column), «??», «?» information for parsing an .obj files.
I don’t know how exactly CMake parse an .obj file.
However, CMake is open source, so you could find out if it’s interested for you.
Link to the CMake project:
CMake_github
5) Put all exported symbols in a .def file.
6) Link a dll with usage of a .def created file.
Steps 4)-5), that is parse .obj files and create a .def file before linking and using the .def file CMake does with help of «Pre-Link event».
While «Pre-Link event» fires you could call any program you want.
So in case of «CMake usage» «Pre-Link event» call the CMake with the following information about where to put the .def file and where the «objects.txt» file and with argument «-E __create_def».
You could check this information by creating CMake Visusal Studio project with «set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)» and then check the «.vcxproj» project file for dll.
If you try to compile a project without «set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)» or with «set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF)» you will get linking errors, due to the fact that symbols are not exported from a dll.
More info about this is provided by link:
Understanding Custom Build Steps and Build Events
2.3.2. Without CMake usage
You simple could create a small program for parsing .obj file by youself without CMake usege. Hovewer, I have to admit that CMake is very usefull program especially for cross-platform development.
Short answer
You can do it with help of the new version of the CMake (any version cmake-3.3.20150721-g9cd2f-win32-x86.exe or higher).
Currently it’s in the dev branch.
Later, the feature will be added in the release version of the cmake-3.4.
Link to the cmake dev:
cmake_dev
Link to an article which describe the technic:
Create dlls on Windows without declspec() using new CMake export all feature
Link to an example project:
cmake_windows_export_all_symbols
Long answer
Caution:
All information below is related to the MSVC compiler or Visual Studio.
If you use other compilers like gcc on Linux or MinGW gcc compiler on Windows you don’t have linking errors due to not exported symbols, because gcc compiler export all symbols in a dynamic library (dll) by default instead of MSVC or Intel windows compilers.
In windows you have to explicitly export symbol from a dll.
More info about this is provided by links:
Exporting from a DLL
HowTo: Export C++ classes from a DLL
So if you want to export all symbols from dll with MSVC (Visual Studio compiler) you have two options:
- Use the keyword __declspec(dllexport) in the class/function’s definition.
- Create a module definition (.def) file and use the .def file when building the DLL.
1. Use the keyword __declspec(dllexport) in the class/function’s definition
1.1. Add «__declspec(dllexport) / __declspec(dllimport)» macros to a class or method you want to use. So if you want to export all classes you should add this macros to all of them
More info about this is provided by link:
Exporting from a DLL Using __declspec(dllexport)
Example of usage (replace «Project» by real project name):
// ProjectExport.h
#ifndef __PROJECT_EXPORT_H
#define __PROJECT_EXPORT_H
#ifdef USEPROJECTLIBRARY
#ifdef PROJECTLIBRARY_EXPORTS
#define PROJECTAPI __declspec(dllexport)
#else
#define PROJECTAPI __declspec(dllimport)
#endif
#else
#define PROJECTAPI
#endif
#endif
Then add «PROJECTAPI» to all classes.
Define «USEPROJECTLIBRARY» only if you want export/import symbols from dll.
Define «PROJECTLIBRARY_EXPORTS» for the dll.
Example of class export:
#include "ProjectExport.h"
namespace hello {
class PROJECTAPI Hello {}
}
Example of function export:
#include "ProjectExport.h"
PROJECTAPI void HelloWorld();
Caution: don’t forget to include «ProjectExport.h» file.
1.2. Export as C functions.
If you use C++ compiler for compilation code is written on C, you could add extern «C» in front of a function to eliminate name mangling
More info about C++ name mangling is provided by link:
Name Decoration
Example of usage:
extern "C" __declspec(dllexport) void HelloWorld();
More info about this is provided by link:
Exporting C++ Functions for Use in C-Language Executables
2. Create a module definition (.def) file and use the .def file when building the DLL
More info about this is provided by link:
Exporting from a DLL Using DEF Files
Further I describe three approach about how to create .def file.
2.1. Export C functions
In this case you could simple add function declarations in the .def file by hand.
Example of usage:
extern "C" void HelloWorld();
Example of .def file (__cdecl naming convention):
EXPORTS
_HelloWorld
2.2. Export symbols from static library
I tried approach suggested by «user72260».
He said:
- Firstly, you could create static library.
- Then use «dumpbin /LINKERMEMBER» to export all symbols from static library.
- Parse the output.
- Put all results in a .def file.
- Create dll with the .def file.
I used this approach, but it’s not very convinient to always create two builds (one as a static and the other as a dynamic library). However, I have to admit, this approach really works.
2.3. Export symbols from .obj files or with help of the CMake
2.3.1. With CMake usage
Important notice: You don’t need any export macros to a classes or functions!
Important notice: You can’t use /GL (Whole Program Optimization) when use this approach!
- Create CMake project based on the «CMakeLists.txt» file.
- Add the following line to the «CMakeLists.txt» file:
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) - Then create Visual Studio project with help of «CMake (cmake-gui)».
- Compile the project.
Example of usage:
Root folder
CMakeLists.txt (Root folder)
cmake_minimum_required(VERSION 2.6)
project(cmake_export_all)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(dir ${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${dir}/bin")
set(SOURCE_EXE main.cpp)
include_directories(foo)
add_executable(main ${SOURCE_EXE})
add_subdirectory(foo)
target_link_libraries(main foo)
main.cpp (Root folder)
#include "foo.h"
int main() {
HelloWorld();
return 0;
}
Foo folder (Root folder / Foo folder)
CMakeLists.txt (Foo folder)
project(foo)
set(SOURCE_LIB foo.cpp)
add_library(foo SHARED ${SOURCE_LIB})
foo.h (Foo folder)
void HelloWorld();
foo.cpp (Foo folder)
#include <iostream>
void HelloWorld() {
std::cout << "Hello World!" << std::endl;
}
Link to the example project again:
cmake_windows_export_all_symbols
CMake uses the different from «2.2. Export symbols from static library» approach.
It does the following:
1) Create «objects.txt» file in the build directory with information of .obj files are used in a dll.
2) Compile the dll, that is create .obj files.
3) Based on «objects.txt» file information extract all symbols from .obj file.
Example of usage:
DUMPBIN /SYMBOLS example.obj > log.txt
More info about this is provided by link:
/SYMBOLS
4) Parse extracted from .obj file information.
In my opinion I would use calling convection, for example «__cdecl/__fastcall», «SECTx/UNDEF» symbol field (the third column), «External/Static» symbol field (the fifth column), «??», «?» information for parsing an .obj files.
I don’t know how exactly CMake parse an .obj file.
However, CMake is open source, so you could find out if it’s interested for you.
Link to the CMake project:
CMake_github
5) Put all exported symbols in a .def file.
6) Link a dll with usage of a .def created file.
Steps 4)-5), that is parse .obj files and create a .def file before linking and using the .def file CMake does with help of «Pre-Link event».
While «Pre-Link event» fires you could call any program you want.
So in case of «CMake usage» «Pre-Link event» call the CMake with the following information about where to put the .def file and where the «objects.txt» file and with argument «-E __create_def».
You could check this information by creating CMake Visusal Studio project with «set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)» and then check the «.vcxproj» project file for dll.
If you try to compile a project without «set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)» or with «set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF)» you will get linking errors, due to the fact that symbols are not exported from a dll.
More info about this is provided by link:
Understanding Custom Build Steps and Build Events
2.3.2. Without CMake usage
You simple could create a small program for parsing .obj file by youself without CMake usege. Hovewer, I have to admit that CMake is very usefull program especially for cross-platform development.
Время на прочтение
2 мин
Количество просмотров 36K
Введение
CMake (от англ. cross platform make) — это кроссплатформенная система автоматизации сборки программного обеспечения из исходного кода.
CMake не занимается непосредственно сборкой, a лишь генерирует файлы управления сборкой из файлов CMakeLists.txt.
Динамические библиотеки. Теория
Создание динамических библиотек со статической линковкой в ОС Windows отличается от ОС GNU/Linux.
На ОС Windows для этого требуется связка .dll (dynamic link library) + .lib (library) файлов.
На ОС GNU/Linux для этого нужен всего лишь один .so (shared object) файл.
Динамические библиотеки. Практика
На практике хочется писать удобный, одинаковый код на обеих ОС.
В каждом проекте (или на несколько проектов одна) присутствовала условная компиляция:
#ifndef __linux__
#if defined( <target_name>_EXPORTS )
#define DLL_<target_name>_EXPORT __declspec(dllexport)
#else // !BUILDING_DLL
#define DLL_<target_name>_EXPORT __declspec(dllimport)
#endif // BUILDING_DLL
#else
#define DLL_<target_name>_EXPORT
#endif // __linux__
Соответственно, для каждого экспортируемого класса из библиотеки необходимо прописать данный макрос:
class DLL_<target_name>_EXPORT <class_name>
В данном случае, на ОС Windows экспортируются все классы/методы, которые помечены данным макросом, а на ОС GNU/Linux, по умолчанию, всё экспортируется, т.к. нет макроса для скрытия классов/методов.
С выходом CMake версии 3.4.0, стало возможным создание библиотек с классами, которые экспортируются по умолчанию. Для этого в каждой цели (target), которые объявлены как SHARED (динамическая библиотека), необходимо включить свойство:
set ( CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
Пример небольшой библиотеки:
# Проверка версии CMake
cmake_minimum_required( VERSION 3.4.0 )
# Если версия установленой программы ниже, то ошибка выполнения
# Название проекта и проверка доступности компиляторя с++
project( shared_lib CXX )
# Установка переменной со списком исходников
set( SOURCE_LIB example.cpp )
# Включение экспорта всех символов для создания динамической библиотеки
set ( CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON )
# Создание динамической библиотеки с именем example
add_library( example SHARED ${SOURCE_LIB} )
И убрать определение и использование макросов из кода:
DLL_<target_name>_EXPORT
Данное свойство автоматически создает module definition (.def) со всеми глобальными символами из .obj файла для динамической библиотеки на ОС Windows.
Далее данный файл (.def) передается компоновщику для создания .lib файла. На выходе на ОС Windows получается связка .lib + .dll
Итоги
Код стал более читабельным, нет лишних строчек. И вероятность появления ошибки во время неправильного написания блока условной компиляции и определения макроса среди разработчиков сведена к нулю. В CMakeLists файле всего одна дополнительная строчка.
Search code, repositories, users, issues, pull requests…
Provide feedback
Saved searches
Use saved searches to filter your results more quickly
Sign up
CMake 3.4 will have a new feature to simplify porting C and C++ software using shared libraries from Linux/UNIX to Windows. Linux/UNIX developers are often surprised to learn that creating a shared library on Windows known as a DLL (dynamic linked library) requires changes to the source code or an explicit listing of all the symbols that the dll will export. The compilers on Linux/UNIX have the ability to export all symbols in a shared library automatically. On Windows, you must either use compiler directives __declspec(import) and __declspec(export) to declare which symbols are exported/imported from a shared library, or you must create a module definition text file (.def) with a list of all the symbols you want to export and pass that file to the linker.
With C libraries, creating a .def file by hand or automatically is not very difficult since you just have to list the names of all the functions in the library. However, with C++ code, name mangling and the sheer number of functions make crafting a .def file by hand nearly impossible. The standard workaround uses the preprocessor to conditionally insert __declspec(import) and __declspec(export) into the code. However, with a large existing C++ code base, it can be difficult and time consuming to edit all of the source code. CMake now has a feature which allows it to query .obj files that will make up a DLL and create a .def file automatically, in most cases without needing to modify the original source code.
The feature is implemented in CMake via a new target property, WINDOWS_EXPORT_ALL_SYMBOLS. When enabled, this property causes CMake to automatically create a .def file with all symbols found in the input .obj files for a SHARED library on Windows. The .def 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. The symbol is exported from the DLL correctly and automatically, but the compiler needs to know that it is being imported from a DLL at compile time. 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 CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS variable when a target is created.
To try this out on an existing project, run
cmake -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=TRUE -DBUILD_SHARED_LIBS=TRUE
on a CMake project. This should turn all add_library calls that do not explicitly specify build type into shared builds. If there are no global data variables in the project, all libraries will be built as DLLs with no errors. If you run into undefined symbols, check for global data like static data members of classes. The easiest way to handle the global data is to use the CMake GenerateExportHeader module like this:
add_library(mylibrary ${mylibrary_SOURCES}) # add these lines include(GenerateExportHeader) generate_export_header(mylibrary)
// edit the source like this:
#include <mylibary_export.h> class myclass { static mylibrary_EXPORT int GlobalCounter; …
Note, if you use GenerateExportHeader and still want static builds to work, you will need to add a -Dmylibrary_STATIC during static builds. See the generated mylibary_export.h for more details.
Real World Use Cases
After adding this feature to CMake, I tried it on two relatively large software projects VXL ( (http://vxl.sourceforge.net/) and ITK (www.itk.org). Although, it did take some time, it was much easier than adding dll markup to every source file. In the end the changes required fell into the following repeated issues and fixes:
-
Handling class static data members
-
Add dll import/export macros to the static data.
-
Eliminate the static data –
-
This can be done by making a static getter function with a local static variable.
-
-
-
Static const members
-
These are not exported by the new feature. In some cases to support older compilers, these are put in .cxx files and compiled into the library and are not just inlined in the .h files. If you have this case, you will need to use dllimport and dllexport markup and not just dllimport mark up as the auto export will not work.
-
-
Inherit from std::string or other templated classes with dllimport/dllexport mark up.
class mylibrary_EXPORT myclass public std::string { ..
-
This can cause duplicate symbol errors in the parent templated class to show up in some cases. The fix for this is to remove the markup and let the auto-export do its thing.
-
-
Not having fully linked libraries. On many Unix systems the linker will allow for shared libraries to have unresolved symbols at create time, that are expected to be resolved at the link time of the executable. Windows dlls must resolve all symbols, so your library must explicitly link to everything it depends on. One way to find out where symbols exist in a big system if you get an undefined symbol at link to time is to look for the symbol in the .def files that are created. From a bash (cygwin or git) shell you could run something like this:
find . -name "*.def" | xargs grep symbol
-
Static data members of templated classes. It is best to just avoid these, or only use explicit instantiation or specialization for these. I was not able to get these to work at all.
Thanks
I would like to give a special thanks to Valeri Fine (code author) and Bertrand Bellenot and the rest of the ROOT team at CERN for contributing the .obj file symbols parsing code, and for testing the feature on their project. Enjoy!
Links
Nightly CMake with new feature: http://www.cmake.org/files/dev/cmake-3.3.20150721-g9cd2f-win32-x86.exe
Nightly Docs for feature:
http://www.cmake.org/cmake/help/git-master/prop_tgt/WINDOWS_EXPORT_ALL_SYMBOLS.html
ITK changes for this feature: http://review.source.kitware.com/#/c/20020
VXL changes for this feature: https://github.com/vxl/vxl/pull/4
Valeri Fine presented this approach and introduced the original code at Computing in High Energy Physics Berlin, Lichtenberger Congress Center April 7-11, 2977:
http://www.ifh.de/CHEP97/abstract/g340.htm
http://www.ifh.de/CHEP97/paper/340.ps.