CMake, CMP0046, autogenerated sources. How to suppress warnings? - cmake

Consider the following toy project for the Visual Studio, built with CMake.
File CMakeLists.txt:
cmake_minimum_required (VERSION 2.8)
project(CMP0046)
# cmake_policy(SET CMP0046 OLD) <----
add_executable(an_exec a.cpp)
configure_file(data_file.txt data_file.txt.c #ONLY)
add_custom_command(OUTPUT data_file.txt.c
COMMAND ${CMAKE_COMMAND} data_file.txt data_file.txt.c -P ${PROJECT_SOURCE_DIR}/gen_file.cmake
MAIN_DEPENDENCY data_file.txt)
add_dependencies(an_exec data_file.txt.c)
File gen_file.cmake
if (CMAKE_ARGV2)
configure_file(${CMAKE_ARGV1} ${CMAKE_ARGV2})
endif()
Files a.cpp and data_file.txt are currently empty because their contents don't matter.
Tree points:
the C source data_file.txt.c is generated from data_file.txt with CMake;
original data file changes during development, and I want to have data_file.txt.c automatically regenerated to reflect all recent changes
I'd like to use this solution in another projects without changes, therefore, I should consider that amount and names of such files vary. For example, these could be OpenCL sources.
The current CMake script doesn't change CMP0046 policy (the commented line marked with an arrow above), and this causes warnings about missing dependencies when I press generate button in the CMake GUI.
I have two goals:
to avoid these warnings
avoid appearance of these intermediate autogenerated .c sources in the solution
If I uncomment that line and set CMP0046 to OLD, CMake is quiet. However, I was suggested here: What is the scope of CMake policies?, that it is incorrect.
I prefer add_custom_command over add_custom_target, because custom target appears in the generated VS Solution as a separate project. For example, these lines, added to CMakeLists.txt:
add_custom_target(data_file_txt_c
COMMAND ${CMAKE_COMMAND} data_file.txt data_file.txt.c -P ${PROJECT_SOURCE_DIR}/gen_file.cmake
DEPENDS data_file.txt)
add_dependencies(an_exec data_file_txt_c)
result in the Visual Studio solution, where a new project is added, named data_file_txt_c, which I would like to avoid.
So, what is the best solution?

Related

Can you have a common cmake minimum included from another file?

I am quite new to cmake with a makefile background.
I like to use things like include(cmake_utils/header.cmake) to include common snippets of cmake files so that I can include them in my projects but only change them in one once in one place. Where cmake_utils is a git repo.
This is working nicely, but every single CMakeLists.txt I write has to have a cmake_minimum_required.
That is fine, but I may want to change this one day - lets say when one of my common files uses a feature from a newer version of cmake. In that case I don't want to go around changing all the CMakeLists.txt - I just want to change it in one place (ideally).
Here is my current CMakeFile.txt:
cmake_minimum_required(VERSION 3.10.2)
# Include common elements
include(cmake_utils/header.cmake)
include(cmake_utils/cpp_flags.cmake)
# Include path
include_directories(
inc
inc/log4cpp
)
# Include source files by wild card
file(GLOB SOURCES "src/log4cpp/*.cpp")
# Setup output and libs
include(cmake_utils/output_lib_shared.cmake)
include(cmake_utils/common_libs.cmake)
I really want to move the line cmake_minimum_required(VERSION 3.10.2) into my cmake_utils/header.cmake file.
But when I do this I get the following error right at the end of calling cmake:
CMake Error in CMakeLists.txt:
No cmake_minimum_required command is present. A line of code such as
cmake_minimum_required(VERSION 3.10)
should be added at the top of the file. The version specified may be lower
if you wish to support older CMake versions for this project. For more
information run "cmake --help-policy CMP0000".
Is this just a limitation of cmake that I have to live with, or is there a way to archive this?
It's also possible that I am still thinking like a gnu make writer and I have this all horribly wrong :o
Per the documentation cmake_minimum_required: Call the cmake_minimum_required() command at the beginning of the top-level CMakeLists.txt file even before calling the project() command. It is important to establish version and policy settings before invoking other commands whose behavior they may affect.
There is no way of getting around this.

CMake Windows implicit linking

What is the defacto way of handling implicit linking in CMake projects?
In windows, .dll's are linked during runtime, thus CMake only needs to link the .lib files during compilation. This is better done using find_package. However, during compilation, CMake never copies over the .dll corresponding to the linked .lib file to the linked executable's output path.
Is this something that CMake leaves up to the user?
It seems kind of messy, not to mention non-cross platform, to have to manually find dll's for each library you link and either manually copy them over to your output directory or write individual CMake commands, as shown below, to do this per package you find/library you link.
add_custom_command(
TARGET MyTarget POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${PROJECT_SOURCE_DIR}/libs/SDL2.dll"
$<TARGET_FILE_DIR:MyTarget>
)

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).

Copy or move libs after build with cmake

In my project there is a folder that contains third party libraries (google test, zlib, ...). I want to put the libraries in a common folder when all this libraries are built. I'm trying to do this with cmake but I'm having problems. I'm trying this code:
add_subdirectory(gtest-1.6.0)
add_custom_command(
TARGET gtest_main
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${SRC_LIB_DIR}/*.a ${DST_LIB_DIR})
I think the problem could be that the target gtest main in not defined at this level but in a lower level
If you set the CMAKE_LIBRARY_OUTPUT_DIRECTORY variable in your CMakeLists.txt before calling add_subdirectory (and the subproject does not override it), libraries should fall into the right place.
Additionally like this the target in the subproject keeps their dependency to the output-file. Which wouldn't work with your copy - the target would be always out-of-date and thus rebuilt.
EDIT: I overlooked that your copy-command is copying .a-files and thanks to #Fraser's comment I learned as well that CMAKE_ARCHIVE_OUTPUT_DIRECTORY should to do the trick.

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.