This is because the shared library hello_library does not exports any symbols. If a shared library does not export any symbols, Visual Studio will not create a *.lib file which is required by hello_binary. On Windows, when building a shared library, you need to have __declspec(dllexport) before every functions or classes that you want to export. When another project is using the compiled library, you need to have __declspec(dllimport).
CMake can be solve this problem in two different ways:
1. Use CMake's CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS
CMake 3.4 added the variable CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS. When set to true, it creates a pre-link event in Visual Studio to call cmake.exe which generates an exports.def file that Visual Studio can use to exports most symbols.
To set this variable, you can add set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) to your CMakeLists.txt file or you can set the variable on the command line with -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=TRUE.
This solution is not perfect. This flag does not translate to something in Visual Studio solution/project files. It allows you to export most of your code without changes. However, it fails to export global and static variables. You can read more about this in this blog post.
This option was also partially discussed in #52.
2. Generate an export header file
On Windows, this is the usual/expected way for creating a shared library.
If you add the following to CMakeLists.txt, you can generate an export file header:
include (GenerateExportHeader)
generate_export_header(hello_library)
This will generate the file called ${PROJECT_BINARY_DIR}/hello_library_export.h which will declare the macro HELLO_LIBRARY_EXPORT that resolves to __declspec(dllexport) or __declspec(dllimport) as appropriate. With CMake 3.22, the content will look like this: hello_library_export.h.txt.
All files that export symbols must include this generated header. For example, the file include/shared/Hello.h must be modified as the following:
#include "hello_library_export.h"
class HELLO_LIBRARY_EXPORT Hello
Note the new include directive and the use of the HELLO_LIBRARY_EXPORT macro before the class declaration.
To find this include file, the target hello_library must be modified in CMakeLists.txt with the following:
target_include_directories(hello_library
PUBLIC
${PROJECT_SOURCE_DIR}/include
${PROJECT_BINARY_DIR} # for hello_library_export.h
)
This is the expected way for creating a shared library. I do not have enough experience on Linux to know if gcc (or other compilers) will be able to understand/ignore __declspec(dllexport) and __declspec(dllimport).
I am really happy to have found this tutorial. This is one of the most well made "step by step" guide I have seen. CMake lacks good and simple documentation and this repository really help. I am a huge fan of learning by examples. I also created a CMake guide myself.
Would you consider adding option 2 to the source code ? To clarify that this code is WINDOWS-ONLY, we could wrap the new code inside statements such as IF (WIN32) in CMakeLists.txt and #ifdef __WIN32 in c++ header files.
I realize this would add more complexity to the example but I would argue that this guide should cover both Linux and Windows.
Building example 01-basic/D-shared-library on Visual Studio fails with the following error:
This is because the shared library
hello_library
does not exports any symbols. If a shared library does not export any symbols, Visual Studio will not create a *.lib file which is required byhello_binary
. On Windows, when building a shared library, you need to have__declspec(dllexport)
before every functions or classes that you want to export. When another project is using the compiled library, you need to have__declspec(dllimport)
.CMake can be solve this problem in two different ways:
1. Use CMake's CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS
CMake 3.4 added the variable
CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS
. When set to true, it creates a pre-link event in Visual Studio to call cmake.exe which generates anexports.def
file that Visual Studio can use to exports most symbols.To set this variable, you can add
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
to your CMakeLists.txt file or you can set the variable on the command line with-DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=TRUE
.This solution is not perfect. This flag does not translate to something in Visual Studio solution/project files. It allows you to export most of your code without changes. However, it fails to export global and static variables. You can read more about this in this blog post.
This option was also partially discussed in #52.
2. Generate an export header file
On Windows, this is the usual/expected way for creating a shared library.
If you add the following to CMakeLists.txt, you can generate an export file header:
This will generate the file called
${PROJECT_BINARY_DIR}/hello_library_export.h
which will declare the macroHELLO_LIBRARY_EXPORT
that resolves to__declspec(dllexport)
or__declspec(dllimport)
as appropriate. With CMake 3.22, the content will look like this: hello_library_export.h.txt.All files that export symbols must include this generated header. For example, the file
include/shared/Hello.h
must be modified as the following:Note the new include directive and the use of the
HELLO_LIBRARY_EXPORT
macro before the class declaration.To find this include file, the target
hello_library
must be modified inCMakeLists.txt
with the following:This is the expected way for creating a shared library. I do not have enough experience on Linux to know if gcc (or other compilers) will be able to understand/ignore
__declspec(dllexport)
and__declspec(dllimport)
.I am really happy to have found this tutorial. This is one of the most well made "step by step" guide I have seen. CMake lacks good and simple documentation and this repository really help. I am a huge fan of learning by examples. I also created a CMake guide myself.
Would you consider adding option 2 to the source code ? To clarify that this code is WINDOWS-ONLY, we could wrap the new code inside statements such as
IF (WIN32)
in CMakeLists.txt and#ifdef __WIN32
in c++ header files.I realize this would add more complexity to the example but I would argue that this guide should cover both Linux and Windows.