How to best handle data files with CMake? - cmake

I've got a CMake project that contains code and a few data files (images to be precise).
My directory structure is like this:
src
data
src contains the source code, data the data files. CMake suggests out of source builds, so when I invoke make, I have the executable program, but not the data files, thus I cannot execute the program.
Of course, make install would copy my data files to the required location and make it work, therefore I develop like this right now:
cmake -DCMAKE_INSTALL_DIR=dist
<edit source code>
make install
dist/myprogram.exe
That's okay if I'm working with the command line and an editor, but I recently decided to move to Eclipse CDT. Generating an Eclipse project from CMake works great, but manually executing the install target from Eclipse is not so nice.
How do you people tackle this problem? Does your program have some clever algorithms to try and find its data directory even if it's not where the binary is? Or do you not use out of source builds?

configure_file should solve that problem.
I have a CMakeLists.txt file in my data directory which contains the following:
configure_file(data_file ${CMAKE_CURRENT_BINARY_DIR}/data_file COPYONLY)
This copies the specified file into the build directory when cmake is invoked, so it is available in the same location even in out of source builds.
configure_file does not support directories however while the file command does:
file(COPY assets DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

And if copying the files takes too much time (they are images...) you could make it even better by creating a "custom" data_header.h with configure_file which contains the paths to the data still in your source-directory.
This is what I do: I have a file "global_build_config.h.in" in my source, containing the following:
const char* const global_testdatapath = "#Test_Data_Path#";
and then use configure_file in CMake:
# Assume CMake knows a variable Test_Data_Path, it will be filled in automatically
# in the generated config/Global_Build_Config.h
configure_file( Global_Build_Config.h.in ${CMAKE_BINARY_DIR}/config/Global_Build_Config.h )
# The config directory should be added as a include-searchpath
include_directories( ${CMAKE_BINARY_DIR}/config/ )
I can then #include "Global_Build_Config.h" in my cpp files and refer to the fixed path.

Your question is a bit old, but in case you're still interested (or someone else), I have a similar scenario where I copy testdata for a unit-test target:
add_custom_command( TARGET ${UTEST_EXE_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying unit test data.."
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_HOME_DIRECTORY}/utest/testdata ${CMAKE_BINARY_DIR}
)
So the main idea is to use a post-build target, and it is executed after each build. For me, it's not much data, and the filesystem caches it, so I don't feel the copy process at all. You could probably enhance this by copying with copy_if_different. In that case, however, you have to create a list of your image files and write a loop, because the command is file based. With the GLOB command, this shouldn't be hard to do if you need to.

Related

How to use glib-compile-resources with CMake

As any GTK project grows, GTK applications tend to be bundled with gresources to separate out code and UI design. This is very useful because UI/UX designers don't need to know code in order to... well design and ultimately contribute their skills and effort to the project.
Not only designers but programmers too benefit a lot! Because code becomes heavily "logic or problem solving" instead of maintaining both UI and logic code together in one single file.
However, to compile our GResource we need glib-compile-resources utility tool. The command usually goes like this:
glib-compile-resources --generate-source --target=<output-file> <input-file>
But how do I create a build script that compiles our gresource files and link it with our target project? I'm still a newbie learning CMake and I've gotten far enough to know what a target is, how to set a variable, how to link a target, and also how to pull in the required GTK packages for linking. But I don't have any clue how to proceed ahead with solving this :(
A solution to this is using add_custom_command() to compile your gresources. But first here's a breakdown of what you need for your CMake script:
Pull in glib-compile-resources as executable program - find_program()
Define how to compile your gresource - add_custom_command()
Then define your custom target - add_custom_target()
Tell CMake that resource is a generated file - set_source_files_properties()
Finally, add your custom target to your project target as a dependency - add_dependencies()
Here's a sample CMake script:
cmake_minimum_required(VERSION 3.15)
project(dummy)
# Step 1:
find_program(GLIB_COMPILE_RESOURCES NAMES glib-compile-resources REQUIRED)
set(GRESOURCE_C test.gresource.c)
set(GRESOURCE_XML test.gresource.xml)
# Step 2:
add_custom_command(
OUTPUT ${GRESOURCE_C}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${GLIB_COMPILE_RESOURCES}
ARGS
--target=${CMAKE_CURRENT_BINARY_DIR}/${GRESOURCE_C}
${GRESOURCE_XML}
VERBATIM
MAIN_DEPENDENCY ${GRESOURCE_XML}
DEPENDS
for.glade
bar.glade
)
# Step 3:
add_custom_target(
dummy-resource
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${GRESOURCE_C}
)
# Step 4:
add_executable(${PROJECT_NAME} dummy.c ${CMAKE_CURRENT_BINARY_DIR}/${GRESOURCE_C})
set_source_files_properties(
${CMAKE_CURRENT_BINARY_DIR}/${GRESOURCE_C}
PROPERTIES GENERATED TRUE
)
# Step 5:
add_dependencies(${PROJECT_NAME} dummy-resource)
Brief explanation
add_custom_command()
OUTPUT - This is your generated resource file
WORKING_DIRECTORY - Where your XML and glade files are located
VERBATIM - Makes sure our COMMAND receives ARGS unchanged
MAIN_DEPENDENCY - for glib-compile-resources <input-file>
DEPENDS - Your glade file(s). If any of the file changes then your target build is triggered :)
add_custom_target()
dummy-resource - That's your custom target name
DEPENDS - The output your custom target needs in order to trigger your custom command
set_source_files_properties()
When you first generate your build files using cmake command, your resource file isn't generated yet. So CMake will run into error because it doesn't know where your resource file is or where it's coming from. We need to tell CMake "Don't fail, our resource file is generated later"
Use --generate-dependencies instead of hard-coding
Now you might notice we are duplicating our effort ie., when we add new glade files or remove existing ones (or any other resources such as icon, sounds, css files, etc) we have to edit both our XML and CMake script files. glib-compile-resources already provide dependency generation so we can use that in our CMake script and make it smart.
The trick is to change your .xml file to .xml.in as a configuration file. So when that configuration file changes, you call glib tool with --generate-dependencies, get new dependency output values, and send that to add_custom_command(... DEPENDS). Now we have an intelligent CMake :)
If you want to approach this method then the below post would be really helpful:
Use list as dependencies on add_custom_command
Good luck :)

Creating a library in CMake depending on source files not available when generating build files

I have a CMake configuration file building two libraries:
a third-party library (here called ThirdPartyLib) containing a real-time OS / board support package from a supplier. It is built outside CMake using the autotools toolchain.
an extended version of the former library (here called ExtendedThirdPartyLib)
Unfortunately, some source code that I need (various tools) are not built in the ordinary build script for (1). Since I don't want to mess with the suppliers build script I want to add another library (2), building the missing files and thus extending the library from the supplier.
I want to able to do something like this in CMakeFiles.txt:
cmake_minimum_required(VERSION 3.2)
project(bsp)
include(ExternalProject)
ExternalProject_Add(
ThirdPartyLib
URL <http://some.url/bsp.tar.bz2
BUILD_COMMAND make -C ../external/ThirdPartyLib/src
)
set_target_properties(ThirdPartyLib PROPERTIES EXCLUDE_FROM_ALL TRUE)
add_library(ExtendedThirdPartyLib
${CMAKE_CURRENT_BINARY_DIR}/some/path/missing_file1.c
${CMAKE_CURRENT_BINARY_DIR}/some/path/missing_file2.c
)
add_dependencies(ExtendedThirdPartyLib ThirdPartyLib)
target_include_directories(ExtendedThirdPartyLib PUBLIC
${CMAKE_CURRENT_BINARY_DIR}/some/path/include
)
target_link_libraries(ExtendedThirdPartyLib ThirdPartyLib)
The problem here is that the path to missing_file1.c and missing_file2.c are not valid when CMake is generating the build files (they are extracted from the tarball from the supplier). CMake exits with an error output saying: "Cannot find source file".
Is there a neat way to make this work? I.e. is it possible to convince CMake that certain non-existant input files will exist when building of the library begins? Or is there any other recommended way to solve this issue?
(I have temporary made local copies of the files I need to build from the suppliers tarball, but that is of course not a good solution. If those files are changed in future versions of the suppliers package and I forget to overwrite my local copies it could be a horrible mess...
Another "solution" would be to create a small makefile outside CMake and use another ExternalProject_Add in the CMakeFiles.txt somehow. But that's not a good solution either, e.g. if compile and linker flags are modified I need to remember to change the makefile too.)
Personally, I dislike the ExternalProject_Add command, because it does way too many things for my taste, but I've digressed.
What if you do something like this, where bar is simulating your ExtendedThirdPartyLib target, since it depends on generated files
cmake_minimum_required(VERSION 3.11)
project(lol C)
set(SOURCES lol.c) # only this file exists
add_library(lol ${SOURCES})
set(FOO_FILES "foo1.c" "foo2.c")
add_custom_command(OUTPUT ${FOO_FILES}
COMMAND ${CMAKE_COMMAND} -E touch ${FOO_FILES}
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Creating ${FOO_FILES}"
VERBATIM)
add_custom_target(foo DEPENDS ${FOO_FILES})
add_library(bar ${FOO_FILES})
add_dependencies(bar foo)
target_link_libraries(lol bar)
The whole approach hinges on the fact that the method, where produced/generated files are procured, is explicitly defined via the custom command and associated custom target.
You should modify the custom command to extract the required files (e.g. could even call some external script) from the tarball (which might require downloading with curl or something similar).

Add file dependencies to a custom target

I would like to set up CMake to build qresource files when the contents of files referenced in the .qrc file change. For example I have some qml files that are packaged into a qrc file and the qrc needs to be recompiled if the qml files are changed.
I have the following macro to run the resource compiler but it will only rebuild it if the resource file itself changes.
MACRO(PYQT5_WRAP_RC outfiles)
FOREACH(it ${ARGN})
GET_FILENAME_COMPONENT(outfile ${it} NAME_WE)
GET_FILENAME_COMPONENT(infile ${it} ABSOLUTE)
SET(outfile ${CMAKE_CURRENT_SOURCE_DIR}/${outfile}_rc.py)
ADD_CUSTOM_TARGET(${it} ALL
DEPENDS ${outfile}
)
ADD_CUSTOM_COMMAND(OUTPUT ${outfile}
COMMAND ${PYRCC5BINARY} ${infile} -o ${outfile}
MAIN_DEPENDENCY ${infile}
)
SET(${outfiles} ${${outfiles}} ${outfile})
ENDFOREACH(it)
ENDMACRO (PYQT5_WRAP_RC)
The macro is used like this:
PYQT5_WRAP_RC(rc_gen file1.qrc file2.qrc ...)
How can I make it so the qrc file gets recompiled if one of files that it refers to changes?
Do I need to do something convoluted like this?
It seems to me that the command you are looking for is add_custom_command:
This defines a command to generate specified OUTPUT file(s). A target
created in the same directory (CMakeLists.txt file) that specifies any
output of the custom command as a source file is given a rule to
generate the file using the command at build time.
Configuring the dependencies correctly you should be able to get the file recompiled only when it is really needed.
If I understand correctly your situation, the problem is that CMake does not know anything about what qrc file is and it does not know, that qrc file references some other files. This dependency is implicit for CMake (compare with C++ includes; but for those cmake have special mechanisms to detect dependencies).
So, as I understand, what you can do is to use add_custom_target (which is executed always) but not only add qrc file to its depends, but also write some script, which whould check that files referenced in qrc are also up to date and if they are not, then touch qrc file).
Of course you can use methods from the other answer, which you reference above, to touch qrc file, but you have to develop your own script to extract paths of files referenced in qrc files. Then you can use CMake scripting facilities to check if they are up to date and to touch qrc file.

CMake with regarding generated files

Good day everyone.
I have the following situation: I have a CMake file, which is supposed to compile my application, which consists of:
one or more cpp files
some template files (ecpp), which on their turn are generated into cpp files, which are compiled into the application (they are listed below in the WEB_COMPONENTS so for each component there is the associated .ecpp file and the .cpp that will be generated from it).
And here is the CMakeLists.txt (simplified)
cmake_minimum_required (VERSION 2.6)
set (PROJECT sinfonifry)
set (ECPPC /usr/local/bin/ecppc)
set (WEB_COMPONENTS
images
menu
css
)
set(${PROJECT}_SOURCES
""
CACHE INTERNAL ${PROJECT}_SOURCES
)
foreach(comp ${WEB_COMPONENTS})
list(APPEND ${PROJECT}_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${comp}.cpp )
execute_process(COMMAND ${ECPPC} -o ${CMAKE_CURRENT_BINARY_DIR}/${comp}.cpp -v
${CMAKE_CURRENT_SOURCE_DIR}/${comp}.ecpp
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_QUIET
)
endforeach()
list(APPEND ${PROJECT}_SOURCES main.cpp )
add_executable(${PROJECT}_exe ${${PROJECT}_SOURCES})
target_link_libraries(${PROJECT}_exe cxxtools dl tntnet tntdb)
Now, what happens: for the very first time (ie: make the build directory, run cmake-gui, select web component, configure, generate, make) the CMake nicely executes the ${ECPPC} command, ie. it generates the required CPP files in the binary directory, and links them together.
After a while, obviously while I work, I modify one of the component files (such as images.ecpp) and run make again in the build directory. But now, CMake does not pick up the changes of the ecpp files. I have to go to cmake-gui, delete cache, restart everything from zero. This is very tiresome and slow.
So, two questions:
Cand I tell CMake to track the changes of the images.ecpp and call the ${ECPPC} compiler on it if it changed?
How can I make clean so that it also removes the generated cpp files.
Thank you for your time, f.
Instead of execute_process() you want to use add_custom_command(). See here: https://stackoverflow.com/a/2362222/4323
Basically you tell CMake the OUTPUT (the generated filename), COMMAND, and DEPENDS (the .ecpp filename). This makes it understand how to turn the source into the necessary C++ generated file. Then, add the generated file to some target, e.g. add_executable(), or to an add_custom_command() dependency (if it didn't need to be compiled you'd more likely need that).

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.