10

I'm using CMake on Windows to build test suite based on Boost.Test. As I'm linking to Boost.Test dynamically, my executable needs to be able to find the DLL (which is under ../../../boost/boost_1_47/lib or something like that relative to the executable).

So I need to either copy the DLL into the folder where the executable is, or make it findable in some other way. What's the best way to achieve this with CMake?

-- Additional info --

My CMakeLists.txt has this Boost related configuration at the moment:

set(Boost_ADDITIONAL_VERSIONS "1.47" "1.47.0")
set(BOOST_ROOT "../boost")

find_package(Boost 1.47 COMPONENTS unit_test_framework REQUIRED)
include_directories(${Boost_INCLUDE_DIR})
link_directories(${Boost_LIBRARY_DIR})

add_executable(test-suite test-suite.cpp)
target_link_libraries(test-suite ${Boost_LIBRARIES})
Alex Korban
  • 14,916
  • 5
  • 44
  • 55

3 Answers3

6

Assuming you are running your tests by building the RUN_TESTS target in Visual Studio:

  1. I always add .../boost/boost_1_47/lib to my command PATH environment variable, so the boost unit_test_framework dlls can be found at run time. That's what I recommend.

  2. If for some reason changing your PATH is not possible, you could copy the files with cmake.

(untested)

get_filename_component(LIBNAME "${Boost_UNIT_TEST_FRAMEWORK_LIBRARY_RELEASE}" NAME)
add_custom_command(TARGET test-suite POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy "${Boost_UNIT_TEST_FRAMEWORK_LIBRARY_RELEASE}" "${CMAKE_CURRENT_BINARY_DIR}/${LIBNAME}"
)

3. If you are NOT only running the tests at build time (as I was assuming above), then you need a series of INSTALL commands, like Hans Passant suggested. In your snippet you don't have an INSTALL command for your executable; so even your executable won't end up "in the executable folder". First add a cmake INSTALL command to put your executable someplace in response to the cmake INSTALL target. Once you have that working, we can work on figuring out how to add another INSTALL command to put the boost unit_test_framework library into the same location. After that, if you want to make an installer using CPACK, the library will automatically be installed with the executable.

Christopher Bruns
  • 9,160
  • 7
  • 46
  • 61
4

I ended up using the install command to copy the Boost DLL over to the executable's folder:

get_filename_component(UTF_BASE_NAME ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY_RELEASE} NAME_WE)
get_filename_component(UTF_PATH ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY_RELEASE} PATH)
install(FILES ${UTF_PATH}/${UTF_BASE_NAME}.dll
  DESTINATION ../bin
  CONFIGURATIONS Release RelWithDebInfo
)

get_filename_component(UTF_BASE_NAME_DEBUG ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY_DEBUG} NAME_WE)
install(FILES ${UTF_PATH}/${UTF_BASE_NAME_DEBUG}.dll
  DESTINATION ../bin
  CONFIGURATIONS Debug
)
Alex Korban
  • 14,916
  • 5
  • 44
  • 55
2

I have a very similar problem, but the solution presented here is not really satisfactory. Like the original poster, I want to run unit tests based on boost::test.

I have multiple test projects, one for each mayor component of our product. Having to run the install target prior to every test means recompiling the whole thing just to run the tests belonging to a core component. That's what I want to avoid.

If I change something in a core component, I want to compile that core component and the associated tests. And then run the tests. When the tests succeed, only then do I want to compile and eventually install the rest of it.

For running the tests in the debugger, I found some very useful cmake scripts at : https://github.com/rpavlik/cmake-modules

With this, I can specify all the directories of the required dlls, and the PATH environment variable is set for the new process:

# for debugging
INCLUDE(CreateLaunchers)

create_target_launcher(PLCoreTests 
    ARGS  "--run-test=Core1"
    RUNTIME_LIBRARY_DIRS ${PL_RUNTIME_DIRS_DEBUG} ${PROJECT_BINARY_DIR}/bin/Debug 
    WORKING_DIRECTORY ${PL_MAIN_DIR}/App/PL/bin
)

Where ${PL_RUNTIME_DIRS_DEBUG} contains the directories where the dlls from boost and all the other libraries can be found.

Now I'm looking for how I can achieve something similar with ADD_CUSTOM_COMMAND()

Update:

ADD_CUSTOM_COMMAND() can have multiple commands that cmake writes into a batch file. So, you can first set the path with all the runtime directories, and then execute the test executable. To be able to easily execute the tests manually, I let cmake create an additional batch file in the build directory:

MACRO(RunUnitTest TestTargetName)
    IF(RUN_UNIT_TESTS)
        SET(TEMP_RUNTIME_DIR ${PROJECT_BINARY_DIR}/bin/Debug)
        FOREACH(TmpRuntimeDir ${PL_RUNTIME_DIRS_DEBUG})
            SET(TEMP_RUNTIME_DIR  ${TEMP_RUNTIME_DIR}  ${TmpRuntimeDir})
        ENDFOREACH(TmpRuntimeDir)

        ADD_CUSTOM_COMMAND(TARGET ${TestTargetName} POST_BUILD 
            COMMAND echo "PATH=${TEMP_RUNTIME_DIR};%PATH%" > ${TestTargetName}_script.bat
            COMMAND echo ${TestTargetName}.exe --result_code=no --report_level=no >> ${TestTargetName}_script.bat
            COMMAND ${TestTargetName}_script.bat
            WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Debug
        )
    ENDIF(RUN_UNIT_TESTS)
ENDMACRO()

With this, the unit tests catch the errors as soon as possible, without having to compile the whole lot first.

ЯegDwight
  • 24,821
  • 10
  • 45
  • 52
Richard
  • 21
  • 2