c++cmakeshared-librariesdllimportdllexport

How can I define the switch macro that switches between my dllexport and dllimport attribute macros with CMake?


So I have a project (meant to be supported on MacOS, Linux, and Windows) where I am building a shared library and a set of executables linked to that library. In my root CMakeLists.txt I have added:

set(CMAKE_CXX_VISIBILITY_PRESET hidden)

So that all symbols are hidden by default regardless of on Windows (which does this by default) or Linux. This will require me to manually export symbols of the public API. To help facilitate this, I've written a defines.hpp header in my project's utils/ directory which contains the following:

#ifndef __DEFINES_H_
#define __DEFINES_H_

#if defined(linux) || defined(__linux__)
    #define EXPORT __attribute__((visibility("default"))) 
#elif defined(_WIN64) || defined(__WIN32__)
    #if defined(COMPILE_LIB)
        #define EXPORT __declspec(dllexport)
    #elif
        #define EXPORT __declspec(dllimport)
    #endif
#endif

#endif

The idea being then I would include this header into any header that contains declarations that need to be exported (and subsequently imported as well) via:

#include "utils/defines.hpp"

class EXPORT MyCoolClass();

EXPORT void MyCoolFunction();

(I have not tested this yet so if there are any glaring issues with what I've written please let me know!)

My question is, it is not entirely clear to me how to define the COMPILE_LIB macro, given that the library and exectuables are built/linked with the same CMake setup. How can I ensure that the library is built using dllexport and then linked to the executables using dllimport within the same CMake build? Or would I need to take a different approach?

The accepted answer in this previous SO post seems to indicate using a compiler argument and building each separately, which isn't exactly what I am looking for. A separate answer indicates you should use the CMake defined projectname as when compiling the DLL it will define the macro projectname_EXPORTS. I'm also not sure that this will work again my shared library and the executables are all built within the same CMake project. (Relevant CMakeLists.txt files):

CMakeLists.txt for executables:

add_executable(quick-render quick_render.cpp)
target_link_libraries(quick-render ${PROJECT_NAME})
INSTALL(TARGETS quick-render DESTINATION bin)
...

CMakeLists.txt for library:

...

add_library(${PROJECT_NAME} SHARED
   ...
)

target_link_libraries(${PROJECT_NAME} PUBLIC
   ...
)
...

Would this approach still work here, or is it my CMake setup bad for using the project in multiple ways like this?


Solution

  • As Tsyvarev said in the comments,

    Define the macro by using target_compile_definitions command with PRIVATE keyword. That way, the macro will be defined only when the library itself is compiled.

    Adding to what Tsyvarev said, if you just use the GenerateExportHeader module, that part will be done for you automatically. It will also take care of generating C++ attributes for the compiler you configured the buildsystem to use. See also Is there a way to avoid redefining boilerplate macros for shared library export attributes in every source file when using CMake?.