How to set working folder when running ctest - cmake

I want to set correct the working folder for my test. It should be the same folder as where my test executable is generated. The problem is setting the correct build config to the working folder.
This is the cmake code that adds my test:
gtest_discover_tests(${TEST_TARGET}
DISCOVERY_MODE PRE_TEST
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
The result test_PROJECT_include-Debug.cmake file looks something like this:
if(EXISTS "D:/work/PROJECT/test/Debug/test_PROJECT.exe")
if("D:/work/PROJECT/test/Debug/test_PROJECT.exe" IS_NEWER_THAN "D:/work/hfts/PROJECT/test/test_PROJECT[1]_tests-Debug.cmake")
include("C:/Program Files (x86)/Microsoft Visual Studio/2019/Professional/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/share/cmake-3.20/Modules/GoogleTestAddTests.cmake")
gtest_discover_tests_impl(
TEST_EXECUTABLE [==[D:/work/PROJECT/test/Debug/test_PROJECT.exe]==]
TEST_EXECUTOR [==[]==]
TEST_WORKING_DIR [==[D:/work/PROJECT/test]==]
TEST_EXTRA_ARGS [==[]==]
TEST_PROPERTIES [==[]==]
TEST_PREFIX [==[]==]
TEST_SUFFIX [==[]==]
NO_PRETTY_TYPES [==[FALSE]==]
NO_PRETTY_VALUES [==[FALSE]==]
TEST_LIST [==[test_PROJECT_TESTS]==]
CTEST_FILE [==[D:/work/PROJECT/test/test_PROJECT[1]_tests-Debug.cmake]==]
TEST_DISCOVERY_TIMEOUT [==[5]==]
TEST_XML_OUTPUT_DIR [==[]==]
)
endif()
include("D:/work/PROJECT/test/test_PROJECT[1]_tests-Debug.cmake")
else()
add_test(test_PROJECT_NOT_BUILT test_PROJECT_NOT_BUILT)
endif()
I have tried adding CMAKE_CONFIGURATION_TYPES to the gtest_discover_tests expression, but that only worked for Debug build. The release build also referred to the debug folder.
I also tried to manually add ${CTEST_CONFIGURATION_TYPE} to the result test_PROJECT_include-Debug.cmake just for testing, but that did not work either.
Any tips on how to set the correct build config for my working folder?

The variable CMAKE_CURRENT_BINARY_DIR denotes "binary directory currently being processed" by CMake. Usually, this directory and its subdirectories contains build artifacts, like executables, libraries or other generated files.
What are you looking for is CMAKE_RUNTIME_OUTPUT_DIRECTORY.
Multiconfiguration build tools, like Visual Studio and Xcode, create subdirectory (named as configuration itself) under CMAKE_RUNTIME_OUTPUT_DIRECTORY for each specific configuration Debug, Release... For just determine directory with executable, use $<TARGET_FILE_DIR:${TEST_TARGET}> generator expression.
gtest_discover_tests(${TEST_TARGET}
DISCOVERY_MODE PRE_TEST
WORKING_DIRECTORY $<TARGET_FILE_DIR:${TEST_TARGET}>)

Related

Cmake library outdir

I cant output my lib to my lib folder , I tried this , and it outputs to lib/debug
but I need output mylib.lib exactly to ${CMAKE_CURRENT_SOURCE_DIR}/lib
add_library(mylib STATIC ${SOURCES} ${HEADERS})
set_target_properties(mylib PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib
)
In CMake, Visual Studio is a multiconfiguration generator. Such generators have a specific when interpret ARCHIVE_OUTPUT_DIRECTORY property for the target:
Multi-configuration generators (Visual Studio, Xcode, Ninja Multi-Config) append a per-configuration subdirectory to the specified directory unless a generator expression is used.
If you want to use ${CMAKE_CURRENT_BINARY_DIR}/lib as output directory for the library only for Debug build, and Release build is not interesting for you, then set ARCHIVE_OUTPUT_DIRECTORY_DEBUG property instead:
set_target_properties(mylib PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}/lib
)
If you want to specify different output directories for Debug and Release builds, then set both properties:
set_target_properties(mylib PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}/lib
ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}/lib-release
)
Setting the same directory for both Debug and Release builds is discouraged: That way Release build will overwrite library created in Debug and vise versa.
Placing build artifacts (e.g. libraries and executables created by the project) in the source directory, as implied by ${CMAKE_CURRENT_SOURCE_DIR}/lib output directory, is discouraged too.
Usually, build artifacts are placed in the build directory and its descendant directories. So, deleting build directory completely removes the build.
If you want to store some build artifacts for use even when build directory is remove, then install those artifacts. CMake provides the functionality for installing libraries, executables and other files.
The documentation of ARCHIVE_OUTPUT_DIRECTORY presents you with a possible solution (emphasis mine):
Multi-configuration generators (Visual Studio, Xcode, Ninja Multi-Config) append a per-configuration subdirectory to the specified directory unless a generator expression is used.
You can use a generator expression not changing anything about the value to prevent the config based suffix. You should modify the archive name accordingly to allow for output files of different configurations to coexist in the same directory.
set_target_properties(mylib PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib$<0:>"
OUTPUT_NAME "mylib-$<CONFIG>"
)
Try this code:
set_target_properties(mylib PROPERTIES CMAKE_ARCHIVE_OUTPUT_DIRECTORY " ${CMAKE__BINARY_DIR}/lib")

CTest, CMake & MinGW: Executables build, but fail to run, because fresh DLL is not found

The top-level CMakeLists.txt contains:
include(CTest)
add_subdirectory(lib)
add_subdirectory(demo)
add_subdirectory(test)
lib/CMakeLists.txt is essentially:
add_library(MyLib <sources>)
demo/CMakeLists.txt is essentially:
add_executable(Demo demo.c)
target_link_libraries(Demo MyLib)
test/CMakeLists.txt is just:
add_test(NAME Demo COMMAND Demo)
From a gitlab-runner, we execute:
cmake -G "Ninja" -DCMAKE_INSTALL_PREFIX=C:\opt\x64 -B. ..
cmake --build
ctest --output-on-failure
The first two steps succeed; the third one fails with:
Start 1: Demo
1/1 Test #1: Demo .......................Exit code 0xc0000135
***Exception: 0.03 sec
If I retry:
cmake --install
ctest
then the test succeeds. So the sole problem is that build/lib/mylib.dll is not found when running ctest. Whereas C:\opt\x64\lib is in PATH, and therefore the DLL is found after cmake --install. Which, however, is not what we want: ctest shall always use the fresh DLL from the current build, not the installed version.
Under Linux, everything works correctly. Why doesn't it for Windows and MinGW? Is this a bug in CMake? How can we work around this so that ctest executes correctly on all platforms?
Your issue seems to be the Windows DLL search procedure failing to find mylib.dll when your Demo executable is run by ctest. The Windows DLL search order is specified here:
The directory from which the application loaded.
The system directory. Use the GetSystemDirectory function to get the path of this directory.
The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
The current directory.
The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by
the App Paths registry key. The App Paths key is not used when
computing the DLL search path.
So, you could modify your PATH environment variable to also include the location of the fresh DLL from the current build.
A better, less error-prone solution might be to place the DLL in the same directory as the Demo executable. You can force CMake to use the same binary directory for both the DLL and executable by modifying your top-level CMake file:
include(CTest)
add_subdirectory(lib ${CMAKE_BINARY_DIR}/demo)
add_subdirectory(demo ${CMAKE_BINARY_DIR}/demo)
add_subdirectory(test)
Alternatively, as a less localized approach, you can place the DLL in the same directory as the executable by setting CMAKE_RUNTIME_OUTPUT_DIRECTORY:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
Yet another alternative:
add_test(NAME Demo COMMAND Demo WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

Setting CLion build and binary directory

I'm trying to build libwebsockets inside of my project in CLion. During build libwebsockets creates a header file that is required by other files and puts it in PROJECT_BINARY_DIR. CLion builds everything inside a random build directory it creates for the project and the header file ends up in that directory. I've tried:
Setting the websockets_BINARY_DIR variable
Setting the CMAKE_RUNTIME_OUTPUT_DIRECTORY variable
Setting CMAKE_LIBRARY_OUTPUT_DIRECTORY variable
Changing every variable in the CMake cache to point away from CLion's random directory
Changing the build output path setting in CLion's preferences
None of these work, Which leads me to my questions:
Is there a way to tell CLion where to build (not just where to put some of its output buthow to override the random directory it chooses).
If there isn't a way to tell CLion where to build, is there another variable that I should be setting?
I don't know how to set build dir.
Examples of how to set up bin directory with Clion below
set(dir ${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${dir}/bin")
or (separate for .exe, .dll, .lib):
set(dir ${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${dir}/lib")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${dir}/lib")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${dir}/bin")
Also I added link about how to set up build directory:
https://stackoverflow.com/a/28200869/3001953
But it soesn't work in my case (Clion 1.1).
You may also have a look at the Clion built-in path variables: https://www.jetbrains.com/help/clion/2019.2/absolute-path-variables.html

How to point cmake at specific directory for library?

I have a CMake project where I am using a library and now I want to test my code with a different version of that library. I can set INCLUDE_DIRECTORIES (and possibly later also linking) in the below example. But because I only want to do this temporarily, I'd like to manually set this path with ccmake/cmake-gui.
How do I do this?
project(min_example)
cmake_minimum_required(VERSION 2.8)
find_package(OpenCV REQUIRED)
# Without the following line please:
INCLUDE_DIRECTORIES("/home/me/src/opencv/install/include")
add_executable(min_example main.cpp)
target_link_libraries(min_example ${OpenCV_LIBS})
This should be possible by setting the CMAKE_PREFIX_PATH variable upon configuring your build. In your project directory generate a test_build directory and run:
mkdir test_build
cd test_build
cmake -DCMAKE_PREFIX_PATH=/home/me/src/opencv/install ..
Setting the CMAKE_PREFIX_PATH variable will make the find_package(OpenCV REQUIRED) command pick your OpenCV installation in /home/me/src/opencv and set the OpenCV_LIBS and OpenCV_INCLUDE_DIR variables accordingly.
Alternatively you can edit a CMakeCache.txt file of an existing build directory with the CMake GUI editor and add the CMAKE_PREFIX_PATH definition there. You have to re-configure your project then.
Using config in find_package will restrict search path to OpenCV_DIR. This will use the cmake config that opencv generates at build time to setup paths to include and libs
set(OpenCV_DIR "<cusompath>" CACHE PATH '' ${SHOULD_FORCE_CACHE})
find_package(OpenCV REQUIRED CONFIG)

CMake Compiling Generated Files

I have a list of files that get generated during the CMake build process. I want to compile these files using "add_library" afterward, but I won't know which files get generated until after they get generated. Is there anyway to build this into a CMake script?
Well, I think it is possible, so I'll share what I've done. My problem was that I had to compile several CORBA idls to use as part of a project's source and I didn't want to manually list every file. I thought it would be better to find the files. So I did it like this:
file(GLOB IDLS "idls/*.idl")
set(ACE_ROOT ${CMAKE_FIND_ROOT_PATH}/ace/ACE-${ACE_VERSION})
foreach(GENERATE_IDL ${IDLS})
get_filename_component(IDLNAME ${GENERATE_IDL} NAME_WE)
set(OUT_NAME ${CMAKE_CURRENT_SOURCE_DIR}/idls_out/${IDLNAME})
list(APPEND IDL_COMPILED_FILES ${OUT_NAME}C.h ${OUT_NAME}C.cpp ${OUT_NAME}S.h ${OUT_NAME}S.cpp)
add_custom_command(OUTPUT ${OUT_NAME}C.h ${OUT_NAME}C.cpp ${OUT_NAME}S.h ${OUT_NAME}S.cpp
COMMAND ${ACE_ROOT}/bin/tao_idl -g ${ACE_ROOT}/bin/ace_gperf -Sci -Ssi -Wb,export_macro=TAO_Export -Wb,export_include=${ACE_ROOT}/include/tao/TAO_Export.h -Wb,pre_include=${ACE_ROOT}/include/ace/pre.h -Wb,post_include=${ACE_ROOT}/include/ace/post.h -I${ACE_ROOT}/include/tao -I${CMAKE_CURRENT_SOURCE_DIR} ${GENERATE_IDL} -o ${CMAKE_CURRENT_SOURCE_DIR}/idls_out/
COMMENT "Compiling ${GENERATE_IDL}")
endforeach(GENERATE_IDL)
set_source_files_properties(${IDL_COMPILED_FILES}
PROPERTIES GENERATED TRUE)
set(TARGET_NAME ${PROJECT_NAME}${DEBUG_SUFFIX})
add_executable(
${TARGET_NAME}
${SOURCE}
${IDL_COMPILED_FILES}
)
The GENERATED properties is useful in case one of my idl compilation outputs (*C.cpp, *C.h, *S.cpp and *S.h) is not created, so that the build command doesn't complain that the file doesn't exist.
Well, it is possible to do so with CMake's CMAKE_CONFIGURE_DEPENDS directory property. This forces CMake to reconfigure if any of the given files changed.
Simple solution
The following code shows the approach for a single model file, that is used as input for the code generation:
set(MODEL_FILE your_model_file)
set_directory_properties(PROPERTIES CMAKE_CONFIGURE_DEPENDS ${MODEL_FILE})
set(GENERATED_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/${MODEL_FILE})
file(REMOVE_RECURSE ${GENERATED_SOURCE_DIR})
file(MAKE_DIRECTORY ${GENERATED_SOURCE_DIR})
execute_process(COMMAND your_code_generation_tool -o ${GENERATED_SOURCE_DIR} ${MODEL_FILE})
file(GLOB LIBGENERATED_FILES ${GENERATED_SOURCE_DIR}/*)
add_library(libgenerated ${LIBGENERATED_FILES})
target_include_directories(libgenerated ${GENERATED_SOURCE_DIR})
With the above approach, each time the model file has changed CMake will reconfigure which results in the model being regenerated.
Advanced solution
The problem with the simple solution is that even for the smallest possible change in the model the entire dependencies of the generated files have to be rebuilt.
The advanced approach uses CMake's copy_if_different feature to let only generated files that are affected by the model change to appear modified which results in better build times. To achieve that we use a staging directory as destination for the generator and sync the contents subsequently with the generator output of the previous compile run:
set(MODEL_FILE your_model_file)
set(GENERATOR_STAGING_DIR ${CMAKE_CURRENT_BINARY_DIR}/${MODEL_FILE}.staging)
set(GENERATOR_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${MODEL_FILE})
set_directory_properties(PROPERTIES CMAKE_CONFIGURE_DEPENDS ${MODEL_FILE})
# Create fresh staging/final output directory
file(REMOVE_RECURSE ${GENERATOR_STAGING_DIR})
file(MAKE_DIRECTORY ${GENERATOR_STAGING_DIR})
file(MAKE_DIRECTORY ${GENERATOR_OUTPUT_DIR})
# Run code generation
execute_process(COMMAND your_code_generation_tool -o ${GENERATOR_STAGING_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_FILE}")
# Remove stale files from final generator output directory
file(GLOB GENERATED_FILES RELATIVE "${GENERATOR_OUTPUT_DIR}/" "${GENERATOR_OUTPUT_DIR}/*")
foreach(FILE ${GENERATED_FILES})
if(NOT EXISTS "${GENERATOR_STAGING_DIR}/${FILE}")
file(REMOVE "${GENERATOR_OUTPUT_DIR}/${FILE}")
endif()
endforeach()
# Copy modified files from staging to final generator output directory
file(GLOB GENERATED_FILES RELATIVE "${GENERATOR_STAGING_DIR}/" "${GENERATOR_STAGING_DIR}/*")
foreach(FILE ${GENERATED_FILES})
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${GENERATOR_STAGING_DIR}/${FILE}" "${GENERATOR_OUTPUT_DIR}")
endforeach()
file(GLOB LIBGENERATED_FILES "${GENERATOR_OUTPUT_DIR}/*")
add_library(libgenerated ${LIBGENERATED_FILES})
target_include_directories(libgenerated PUBLIC ${GENERATOR_OUTPUT_DIR})
If you don't know the name of the files that will be generated, you can "glob" the folders where they reside.
file( GLOB_RECURSE MY_SRC dest_folder/*.cpp )
add_library( libname SHARED ${MY_SRC} )
Now I'm not sure what triggers the generation of these files. The "globbing" will happen only when you manually run cmake: it will not be able to detect automatically that new files are present.
Treat this as a non-answer, just more info:
I recently had to do something for one case where I had a .cpp file that was auto-generated, but I could not figure out how to get CMake to construct the Visual Studio project file that would then compile it. I had to resort to something quite stinky: I had to #include <the_generated.cpp> file from another file that resided under the ${CMAKE_CURRENT_SOURCE} directory. That won't help you much in your case because I suspect you have several .cpp files, so this approach is not scalable.
Also, I found that the GENERATED source file property, when added to the file, did not help at all.
I consider this condition either a bug in Visual Studio (in my case this was VS2008 SP1), or in how CMake generates the .vcproj files, or both.