Reputation: 23
I have a C++ library that is built with cmake. The library is tested by unit tests which I would like to run as part of the build. I want the tests to run when the library or unit test source changes.
I found some help in past posts on the cmake mailing list:
http://www.cmake.org/pipermail/cmake/2010-January/034419.html
This approach, using add_custom_command() on the unit test program, works on Linux when the library is shared or static and on Windows when the libary is static. The problem is building the library shared on Windows. In that case, the test runs in the initial build, but doesn't run when the library changes.
Here is an example that illustrates the problem:
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(xyz)
SET(BUILD_SHARED_LIBS ON)
#----------- build library
if(${BUILD_SHARED_LIBS})
add_definitions(-DXYZ_SHARED)
endif(${BUILD_SHARED_LIBS})
add_library(${PROJECT_NAME} xyz.cpp xyz.h)
#----------- build unit tests program
add_executable(${PROJECT_NAME}_unit_tests unit_tests.cpp)
target_link_libraries(${PROJECT_NAME}_unit_tests ${PROJECT_NAME})
#----------- run unit tests program after it is built
add_custom_command(TARGET ${PROJECT_NAME}_unit_tests
POST_BUILD COMMAND ${PROJECT_NAME}_unit_tests)
xyz.h
#pragma once
#ifdef XYZ_SHARED
#ifdef _WIN32
#ifdef xyz_EXPORTS
#define XYZ_EXPORT __declspec(dllexport)
#else
#define XYZ_EXPORT __declspec(dllimport)
#endif
#else //_WIN32
#define XYZ_EXPORT
#endif //_WIN32
#else //XYZ_SHARED
#define XYZ_EXPORT
#endif //XYZ_SHARED
XYZ_EXPORT bool xyz_return_true();
xyz.cpp
#include "xyz.h"
XYZ_EXPORT bool xyz_return_true()
{
return true;
}
unit_tests.cpp
#include <iostream>
#include "xyz.h"
int main(int argc, char *argv[])
{
using namespace std;
cout << "running unit tests: ";
if (xyz_return_true()) {
//no error
cout << "pass" << endl;
return 0;
}
//error
cout << "fail" << endl;
return 1;
}
If I build on Windows, then change xyz.cpp's function to return false and build again, the tests aren't run. What can I do to make the tests run in this case?
I think the tests aren't being run because at build time xyz_unit_tests.exe only depends on the import library (xyz.lib). The import library doesn't change if the library interface doesn't change.
I'm using Visual Studio 10 and cmake 2.8.11.2.
Upvotes: 2
Views: 1449
Reputation: 78290
You could trigger a test rebuild by deleting the test executable as a post-build event of the library. So, for example:
if(WIN32 AND BUILD_SHARED_LIBS)
#----------- trigger rebuild of tests if lib has changed
get_target_property(TestPath ${PROJECT_NAME}_unit_tests LOCATION)
add_custom_command(TARGET ${PROJECT_NAME}
POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove ${TestPath}
COMMENT "Removing ${TestPath}")
endif()
You have to use the LOCATION
target property rather than a generator expression in the COMMAND
argument since that doesn't introduce a cyclic dependency in CMake's dependency graph. Ideally, we'd have the COMMAND
argument as:
COMMAND ${CMAKE_COMMAND} -E remove $<TARGET_FILE:${PROJECT_NAME}_unit_tests>
but this would cause the library to depend on the executable, and clearly the target_link_libraries
call causes the exe to depend on the lib.
There's probably a better way to make the test executable go out-of-date rather than just deleting it; it seems a bit brute-force to me. But it should work.
Upvotes: 1