0

I use CMake to create a DLL Library. This is my Project Structure:

|---CMakeLists.txt
|
├───include
│   └───FileInput
│           export.hpp (Autogenerated by CMAKE, containing the Export Macros)
│           my_lib.h
├───src
│       my_lib.cpp
│       StringHelper.cpp
│       StringHelper.h
└───tests
        CMakeLists.txt
        my_libTest.cpp
        StringHelperTest.cpp
        TestRunner.cpp

And this is my Base CMakeLists.txt:

cmake_minimum_required(VERSION 3.14)

if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
    message(FATAL_ERROR "Do not build in-source. Please remove CMakeCache.txt and the CMakeFiles/ directory. Then build out-of-source.")
endif()

set(LIBRARY_NAME "FileInput")

project("${LIBRARY_NAME}" VERSION 1.0.0 LANGUAGES CXX)

set_property(GLOBAL PROPERTY USE_FOLDERS ON)

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

message(STATUS "Created target ${LIBRARY_NAME} for export ${PROJECT_NAME}.")

set(${LIBRARY_NAME}_SRC
    src/my_lib.cpp
    src/StringHelper.cpp
)

set(${LIBRARY_NAME}_PRIVATE_HEADER
    src/StringHelper.h
)

set(${LIBRARY_NAME}_HDR
    include/${LIBRARY_NAME}/export.hpp
    include/${LIBRARY_NAME}/my_lib.h
)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

option(BUILD_SHARED_LIBS "Build shared (dynamic) libraries." ON)
option(ENABLE_TESTING "Enable a Unit Testing build." ON)

add_library(${LIBRARY_NAME} ${${LIBRARY_NAME}_SRC} ${${LIBRARY_NAME}_HDR} ${${LIBRARY_NAME}_PRIVATE_HEADER})

add_library("ExampleProject::FileInput" ALIAS ${LIBRARY_NAME})

set_target_properties(${LIBRARY_NAME} PROPERTIES VERSION ${${PROJECT_NAME}_VERSION}
                                                        PUBLIC_HEADER "${${LIBRARY_NAME}_HDR}")
if(MSVC)
    set_target_properties(${LIBRARY_NAME} PROPERTIES DEBUG_POSTFIX "d")
endif()

target_include_directories(${LIBRARY_NAME} PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
                                                         "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>"
                                            PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>")   
                                            
if(NOT BUILD_SHARED_LIBS)
    string (TOUPPER ${LIBRARY_NAME} LIBRARY_NAME_UPPER)
    set_target_properties(${LIBRARY_NAME} PROPERTIES COMPILE_FLAGS -D${LIBRARY_NAME_UPPER}_STATIC)
endif()

include(GenerateExportHeader)
string (TOUPPER ${LIBRARY_NAME} LIBRARY_NAME_UPPER)
generate_export_header(${LIBRARY_NAME}
    EXPORT_FILE_NAME ${CMAKE_CURRENT_SOURCE_DIR}/include/${LIBRARY_NAME}/export.hpp
    EXPORT_MACRO_NAME ${LIBRARY_NAME_UPPER}_EXPORT
    STATIC_DEFINE ${LIBRARY_NAME_UPPER}_STATIC
)


if (ENABLE_TESTING)
    message(STATUS "Building Library as Static Version for testing purpose")
    add_library(${LIBRARY_NAME}_static STATIC ${${LIBRARY_NAME}_HDR} ${${LIBRARY_NAME}_SRC} ${${LIBRARY_NAME}_PRIVATE_HEADER})
    target_include_directories(${LIBRARY_NAME}_static  PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
                                "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>"
                                PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>") 

    set_target_properties(${LIBRARY_NAME}_static PROPERTIES VERSION ${${PROJECT_NAME}_VERSION}
                            PUBLIC_HEADER "${${LIBRARY_NAME}_HDR}")

    string (TOUPPER ${LIBRARY_NAME} LIBRARY_NAME_UPPER)
    set_target_properties(${LIBRARY_NAME}_static PROPERTIES COMPILE_FLAGS -D${LIBRARY_NAME_UPPER}_STATIC)    

    message(STATUS "Running Tests for the Library ${LIBRARY_TARGET_NAME}")
    include(CTest)
    add_subdirectory(tests)
endif()

For dependency Management i use Conan which works quite well.

And this is the CMakeLists.txt File in the test Folder:

set(TEST_TARGET_NAME ${PROJECT_NAME}_Test)

set(${TEST_TARGET_NAME}_SRC
        my_libTest.cpp
        StringHelperTest.cpp
        TestRunner.cpp
        )

add_executable(${TEST_TARGET_NAME} ${${TEST_TARGET_NAME}_SRC})

target_link_libraries(${TEST_TARGET_NAME} PRIVATE ${CONAN_LIBS_GTEST} FileInput_static)

add_test(NAME ${TEST_TARGET_NAME}
         COMMAND ${TEST_TARGET_NAME}
         WORKING_DIRECTORY $<TARGET_FILE_DIR:${TEST_TARGET_NAME}>)

target_include_directories(${TEST_TARGET_NAME} PRIVATE $<TARGET_PROPERTY:FileInput_static,INCLUDE_DIRECTORIES>)

Know my question is how to i test the Private Parts of my Library (e.g. the StringHelper.cpp/.h contains Code which will not be exported.

My current solution is to add a second target and build the whole library as a Static Library. And then in the Test Target link against it.

Is there maybe a better way on how to do such a task? In Generall i would also be happy if someone could tell me if there are improvements to the CMakeLists.txt files because i'm relatively new to CMake.

Kevin
  • 785
  • 2
  • 10
  • 32
  • "My current solution is to add a second target and build the whole library as a Static Library. And then in the Test Target link against it." That's what I would do. Then the DLL target also links against it and additionally handles the symbols visibility. – csguth Jun 16 '21 at 12:21
  • @csguth so you would not build the dll from the source files instead just link it against the static library? – Kevin Jun 16 '21 at 12:25
  • Yep. You build your static lib from the sources and then link both your test and your dll against it. You might also want to add a test for the DLL itself, to simulate the final consumer of such DLL. – csguth Jun 16 '21 at 12:28
  • @csguth do i also have to add `target_include_directories` for the dll target? And what kind of sources do i pass to the `add_library` call? – Kevin Jun 17 '21 at 05:02
  • `target_include_directories` should inherit any `PUBLIC` directories from the linked targets. regarding `add_library`, you might need to add some wrapper.cpp to just pull the symbols from the static lib. In the other hand you can have a single target for your library, then you omit `SHARED` (or `STATIC`) from `add_library` and make your test target conditional to `BUILD_SHARED_LIBS`. Another alternative would be to have the list of sources in a separate variable and reuse it for both tests and dynamic lib. – csguth Jun 17 '21 at 11:59
  • @csguth i have posted another question with another problem when trying to link to the static library. Maybe you can put a answer there? [Question](https://stackoverflow.com/questions/68018948/cmake-add-additional-dll-target-and-link-it-to-the-static-target) – Kevin Jun 17 '21 at 12:28

0 Answers0