Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cmake - How to copy files with input data to build output folder

I'm using CLion and CMake for project building. I've created Google Test build configuration and my project tree looks like:

project tree

The tests for tokenizer are simple: tokenizer should open source files and output tokens.

This is my CMakeLists.txt file for tokenizer_test:

include_directories(${gtest_SOURCE_DIRS}/include ${gtest_SOURCE_DIRS})
add_subdirectory(test_src)
add_executable(run_tokenizer_tests
    tokenizer_test.cpp ${CMAKE_SOURCE_DIR}/includes/tokenizer.h
    ${CMAKE_SOURCE_DIR}/src/tokenizer.cpp
)

target_link_libraries(run_tokenizer_tests gtest gtest_main) 

Am I able to place test sources (like 0.cpp on the picture) nearby executable or should I write my own testing scripts? How can I do it?

like image 890
Дмитрий Терехов Avatar asked Nov 01 '25 10:11

Дмитрий Терехов


1 Answers

You can call file(COPY source DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

See https://cmake.org/Wiki/CMake_Useful_Variables for other variables.

Here is the snippet from our project that does this and some more.

Overall, this snippet allows you to avoid dependencies on specific file locations on a file system, provided your build directory is alive, because file paths will be hardcoded in your unit-tests.

It simplifies automated builds and checking - no need to track where to cd before calling test runner.

It also improves readability of code in unit-tests.

CMakeLists.txt:

# list all test images

set(test_data

    orig_5_15Fps_3_27.png
    orig_5_15Fps_5_34.png
    ....
)

# Loop over all items in the "test_data" list
# Copy PNG, JPEG and BMP images to the directory, where test binaries are created
# And generate full paths to them for hardcoding in the include file test_config.h

foreach(df ${test_data})

    # copy images to build dir

    if(${df} MATCHES "((jp|pn)g|bmp)$")
        file(COPY ${df} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})   
        set(df_file_path ${CMAKE_CURRENT_BINARY_DIR}/${df})
    else()
        set(df_file_path ${CMAKE_CURRENT_SOURCE_DIR}/${df}) 
    endif()

    # generate some C++ code in CMake variables IMAGE_PATHS and IMAGE_IDS
    # (see below)

    if (NOT IMAGE_PATHS)
        set(IMAGE_PATHS "    \"${df_file_path}\"")
    else()
        set(IMAGE_PATHS "${IMAGE_PATHS},\n    \"${df_file_path}\"")
    endif()

    string(REGEX REPLACE "[^a-zA-Z0-9]" "_" df_id ${df})

    if (NOT IMAGE_IDS)
        set(IMAGE_IDS "    img_${df_id}")
    else()
        set(IMAGE_IDS "${IMAGE_IDS},\n    img_${df_id}")
    endif()
endforeach()

set(TEST_PATH \"${CMAKE_CURRENT_BINARY_DIR}\")

configure_file(test_config.h.in test_config.h @ONLY)  # see below for test_config.h.in 

...
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )   # make test_config.h visible for compiler
add_executable (some_unit_test some_unit_test.cpp)
add_test(NAME some_unit_test COMMAND some_unit_test WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
 ... 
add_executable ( ... )
add_test(  ... )
...

File test_config.h.in.

/* DO NOT EDIT THIS FILE, IT IS AUTOGENERATED!
   All your changes will be overwritten.

   If you want to add new test data files, 
   add them to the `test_data` list in file CMakeLists.txt
*/ 

#ifndef __TEST_CONFIG_H__
#define __TEST_CONFIG_H__

const char* test_path = @TEST_PATH@; //!< full path to test data, without trailing slash

//! full paths to all test images
const char* image_paths[] = {
    @IMAGE_PATHS@
};

enum image_ids { //!< test file names, converted to enum constants
    @IMAGE_IDS@
};

#endif

Call to configure_file replaces @TEST_PATH@, @IMAGE_PATHS@ and @IMAGE_IDS@ with their values.

Here is how configured file test_config.h in the build directory looks like.

/* DO NOT EDIT THIS FILE, IT IS AUTOGENERATED!
   All your changes will be overwritten.

   If you want to add new test data files, 
   add them to the `test_data` list in file CMakeLists.txt
*/ 

#ifndef __TEST_CONFIG_H__
#define __TEST_CONFIG_H__

const char* test_path = "F:/projects/project/build64/test"; //!< full path to test data, without trailing slash


//! full paths to all test images
const char* image_paths[] = {
  "F:/projects/project/build64/test/orig_5_15Fps_3_27.png",
  "F:/projects/project/build64/test/orig_5_15Fps_5_34.png",
   ... 
};

enum image_ids { //!< test file names, converted to enum constants
    img_orig_5_15Fps_3_27_png,
    img_orig_5_15Fps_5_34_png,
...
};

#endif

Using in tests:

#include "test_config.h"
....     
img0 = cv::imread(image_paths[img_orig_5_15Fps_3_27_png], cv::IMREAD_GRAYSCALE);

enum image_ids is used for readable indexing in the image_paths array. IMHO, it is much better than image_paths[0], as it clearly shows which image is read.

like image 111
wl2776 Avatar answered Nov 04 '25 00:11

wl2776