I have a set of .xsd files in a subdirectory 'xsd'. I generate a set of .hxx and cxx files for XML-binding with an external command.
add_custom_command(
PRE_BUILD
OUTPUT ${PROJECT_SOURCE_DIR}/xsd/file1.hxx ${PROJECT_SOURCE_DIR}/xsd/file1.cxx
COMMAND ${XSD_EXECUTABLE} cxx-tree
--file-list-prologue 'set(XSD_GENERATED_FILES '
--file-list ${PROJECT_SOURCE_DIR}/cmake_xsd_filelist.txt
--file-list-epilogue ')'
--output-dir ${PROJECT_SOURCE_DIR}/xsd
${XSD_SOURCE_FILES}
DEPENDS ${XSD_SOURCE_FILES}
)
The tool creates a list of generated files. I have it in the following form:
set(XSD_GENERATED_FILES
file1.cpp
file1.hpp
file2.cpp
file2.hpp
)
Fine up to here.
Now the part where I don't know how to continue. I want to include the generated file list and add them to the executable.
include(${PROJECT_SOURCE_DIR}/cmake_xsd_filelist.txt)
add_executable(Prog ${SOURCE_FILES} ${XSD_GENERATED_FILES})
But this does not do what I want. Actually, it does nothing because initially cmake_xsd_filelist.txt is empty.
My goal is the following:
when a file in the 'xsd' folder changes, execute XSD_EXECUTABLE for generating the .cpp and hpp files.
only compile once the generated .cpp files when they have changed
add the generated source files to the executable.
Is this even possible with cmake? If yes, how?
Include sources
include(${PROJECT_SOURCE_DIR}/cmake_xsd_filelist.txt)
add_executable(Prog ${SOURCE_FILES} ${XSD_GENERATED_FILES})
This means that you need to have a cmake_xsd_filelist.txt file on configure step, i.e. xsd must be invoked by execute_process. Also I think it's better to generate files to binary directory (follow out-of-source concept):
# Create directory if needed
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/generated")
execute_process(
COMMAND
${XSD_EXECUTABLE} cxx-tree
--file-list-prologue 'set(XSD_GENERATED_FILES '
--file-list "${CMAKE_BINARY_DIR}/generated/cmake_xsd_filelist.txt"
--file-list-epilogue ')'
--output-dir "${CMAKE_BINARY_DIR}/generated/xsd"
${XSD_SOURCE_FILES}
RESULT_VARIABLE result
)
Do not forget to check successfull exit status:
if(NOT result EQUAL 0)
message(FATAL_ERROR "XSD failed")
endif()
Now you can include sources:
include(${CMAKE_BINARY_DIR}/generated/cmake_xsd_filelist.txt)
add_executable(Prog ${SOURCE_FILES} ${XSD_GENERATED_FILES})
XSD sources update
when a file in the 'xsd' folder changes, execute XSD_EXECUTABLE
for generating the .cpp and hpp files.
I.e. CMake must run reconfigure if one of the xsd file changes. Here is the trick that helps:
foreach(x ${XSD_SOURCE_FILES})
configure_file(${x} ${x} COPYONLY)
endforeach()
Now if any of the XSD_SOURCE_FILES changes CMake will reconfigure the project effectively re-running XSD_EXECUTABLE and re-including cmake_xsd_filelist.txt.
Related
I use qt5_add_translation to run lrelease and generate the .qm files. By default the .qm files are put at the root level of the build dir, no matter where you put the .ts files in the source dir.
How can I specify a subdir for those files in the build ?
Set a property on the .ts files before calling the Qt macro :
set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION your_output_path)
Where TS_FILES contains the list of the .ts files and your_output_path is the path where to put the .qm files (relative to the build directory or absolute).
Because the macro will retrieve the property to make the path of the .qm files (tested with Qt 5.9) :
get_source_file_property(output_location ${_abs_FILE} OUTPUT_LOCATION)
if(output_location)
file(MAKE_DIRECTORY "${output_location}")
set(qm "${output_location}/${qm}.qm")
else()
set(qm "${CMAKE_CURRENT_BINARY_DIR}/${qm}.qm")
endif()
Use manual call of lrelease and lupdate utilities
set(TS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/translations")
set(TS_FILES
"${TS_DIR}/${PROJECT_NAME}_ru_RU.ts"
)
find_program(LUPDATE_EXECUTABLE lupdate)
find_program(LRELEASE_EXECUTABLE lrelease)
foreach(_ts_file ${TS_FILES})
execute_process(
COMMAND ${LUPDATE_EXECUTABLE} -recursive ${CMAKE_SOURCE_DIR} -ts ${_ts_file})
execute_process(
COMMAND ${LRELEASE_EXECUTABLE} ${_ts_file})
endforeach()
Rephrased the question.
I have the following problem:
My project has several binaries and libraries that reside in distinct sub-directories under the main project folder.
It is useful to be able to debug only a subset of them, without recompiling the whole project in Debug mode.
I want to be able to only change the compilation mode for this subset in a semi-automatic fashion.
How can I accomplish this using CMake?
If you change the build type, the whole project will be recompiled from scratch. Usually you keep 2 separated build tree, one configured debug and one configured release.
Note that CMAKE_BUILD_TYPE can be set from command line or from cmake-gui, you shouldn't set it in the CMakeLists.txt file.
To compile only some part of your project in debug mode, you can proceed as follow. In your main CMakeLists.txt, before including any subdirectory, define the following macro:
macro (CHECK_IF_DEBUG)
if (NOT (CMAKE_BUILD_TYPE MATCHES Debug))
get_filename_component(THIS_DIR ${CMAKE_CURRENT_SOURCE_DIR} NAME)
STRING(REGEX REPLACE " " "_" THIS_DIR ${THIS_DIR}) #Replace spaces with underscores
if (DEFINED DEBUG_${THIS_DIR})
message(STATUS "Note: Targets in directory ${THIS_DIR} will be built Debug") #A reminder
set (CMAKE_BUILD_TYPE Debug)
endif()
endif()
endmacro()
Then, in each subdirectory add (at the beginning of the CMakelists.txt) the macro call
CHECK_IF_DEBUG()
When you need to temporarily debug a part (subdirectory) of your project, open your project in cmake-gui, and define a variable ("Add Entry") with name DEBUG_<DirectoryName>. You can define multiple ones. If your directory name contains spaces, in the variable name replace them with underscores. Don't forget to click Configure and Generate from cmake-gui to make the change effective. The value assigned to the variable is not important, it can be left empty.
When you are finished debugging, go back to cmake-gui and remove the corresponding entries. Don't forget to Configure and Generate.
I have tested it in a small project and it seems to work properly.
Note: If you create more than one target (add_library or add_executable) in the same CMakeLists.txt (=in the same subdirectory), I haven't found a way to have one target built in one way and one target in another: the only thing that seems to count is the value of the CMAKE_BUILD_TYPE directory when CMake is finished parsing the file.
In answer to your comment, you can have a reminder printed at build time by adding in the block the following line:
add_custom_target(info_${THIS_DIR} ALL COMMAND echo Targets in directory ${THIS_DIR} are built Debug)
See add_custom_target.
The solution I came with is as follows:
I created a macro that will read a config file in the binary directory and set the compilation type based on its content.
When this file changes, cMake will rerun on that directory only and it will recompile it also, because its dependencies changed. This is exactly what I needed.
The macro follows:
macro(set_buildmode _local_app_name)
if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/BUILDMODE)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/BUILDMODE ${CMAKE_BUILD_TYPE})
message(" *** Creating file 'BUILDMODE' with type '${CMAKE_BUILD_TYPE}' for '${_local_app_name}'")
endif()
configure_file( ${CMAKE_CURRENT_BINARY_DIR}/BUILDMODE
${CMAKE_CURRENT_BINARY_DIR}/BUILDMODE.junk)
file(READ ${CMAKE_CURRENT_BINARY_DIR}/BUILDMODE CMAKE_BUILD_TYPE)
string(STRIP ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE)
endmacro()
To make sure I always know if I have compiled in any other mode other than release, I also have this macro:
macro(add_buildmode_message _local_app_name_full)
get_filename_component(_local_app_name ${_local_app_name_full} NAME)
add_custom_target(Checking_BUILDMODE_for_${_local_app_name} ALL
COMMAND bash -c "if [ \"${CMAKE_BUILD_TYPE}\" != \"Release\" ] ; then echo -e \" ${BoldRed}${_local_app_name} was built with type '${CMAKE_BUILD_TYPE}'${ColourReset}\" ; fi"
VERBATIM)
endmacro()
Here is an example of its usage:
set(appname example)
###### Call set_buildmode here
set_buildmode(${appname})
set(${appname}_SRCS
example.cpp
main.cpp)
set(${appname}_HDRS
example.h)
set(${appname}_LIBS)
set(BUILD_SHARED_LIBS ON)
execute_process(COMMAND ln -frs ${${appname}_HDRS} ${CMAKE_BINARY_DIR}/include/
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set(MYLIB_VERSION_MAJOR 2)
set(MYLIB_VERSION_MINOR 1)
set(MYLIB_VERSION_PATCH 0)
set(MYLIB_VERSION_STRING
${MYLIB_VERSION_MAJOR}.${MYLIB_VERSION_MINOR}.${MYLIB_VERSION_PATCH})
add_library(${appname} ${${appname}_SRCS})
target_link_libraries (${appname} ${${appname}_LIBS} ${CMAKE_THREAD_LIBS_INIT})
##### Add the fake target for checking the build mode
add_buildmode_message(${appname})
set_target_properties(${appname} PROPERTIES VERSION ${MYLIB_VERSION_STRING}
SOVERSION ${MYLIB_VERSION_MAJOR})
install(TARGETS ${appname} DESTINATION lib)
I am making a small program which requires an image file foo.bmp to run
so i can compile the program but to run it, i have to copy foo.bmp to 'build' subdirectory manually
what command should i use in CMakeLists.txt to automatically add foo.bmp to build subdirectory as the program compiles?
In case of this might help, I tried another solution using file command. There is the option COPY that simply copy a file or directory from source to dest.
Like this:
FILE(COPY yourImg.png DESTINATION "${CMAKE_BINARY_DIR}")
Relative path also works for destination (You can simply use . for instance)
Doc reference: https://cmake.org/cmake/help/v3.0/command/file.html
To do that you should use add_custom_command to generate build rules for file you needs in the build directory. Then add dependencies from your targets to those files: CMake only build something if it's needed by a target.
You should also make sure to only copy files if you're not building from the source directory.
Something like this:
project(foo)
cmake_minimum_required(VERSION 2.8)
# we don't want to copy if we're building in the source dir
if (NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
# list of files for which we add a copy rule
set(data_SHADOW yourimg.png)
foreach(item IN LISTS data_SHADOW)
message(STATUS ${item})
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${item}"
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/${item}" "${CMAKE_CURRENT_BINARY_DIR}/${item}"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${item}"
)
endforeach()
endif()
# files are only copied if a target depends on them
add_custom_target(data-target ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/yourimg.png")
In this case I'm using a "ALL" custom target with a dependency on the yourimg.png file to force the copy, but you can also add dependency from one of your existing targets.
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.
What I'd like to do is write just Lazy C++ .lzz files and then have lzz run before a build to generate .cpp and .h files that will be built into the final application, sort of like how moc works with Qt.
Is there any way to do this?
Here is an example of how to do this... First you need to find the lzz program, for that use the find_program command:
find_program(LZZ_COMMAND lzz)
This sets LZZ_COMMAND to the path of the compiler. Then use a CMake custom command to compile the LZZ file to their C++ header/implementation files:
add_custom_command(
OUTPUT ${output}
COMMAND ${LZZ_COMMAND} -o ${CMAKE_CURRENT_BINARY_DIR} ${filename})
That generates the files in the current build directory, in case you do out-of-source builds. You will also need to specify that the outputs are generated files:
set_source_files_properties(${output} PROPERTIES GENERATED TRUE)
Put that all together and you get a CMakeLists.txt file something like this:
cmake_minimum_required(VERSION 2.8)
project(lazy_test)
find_program(LZZ_COMMAND lzz)
function(lazy_compile filename)
get_filename_component(base ${filename} NAME_WE)
set(base_abs ${CMAKE_CURRENT_BINARY_DIR}/${base})
set(output ${base_abs}.cpp ${base_abs}.h)
add_custom_command(
OUTPUT ${output}
COMMAND ${LZZ_COMMAND} -o ${CMAKE_CURRENT_BINARY_DIR} ${filename})
set_source_files_properties(${output} PROPERTIES GENERATED TRUE)
endfunction()
lazy_compile(${CMAKE_CURRENT_SOURCE_DIR}/example.lzz)
add_executable(test example.cpp example.h)
You would probably also want to add include path and other options to lzz eventually. If you placed all the Lazy C++ stuff into a module file and included that from the CMakeLists.txt it would be a bit cleaner. But this is the basic idea.
I just wanted to share my CMakeLists.txt, which builds upon richq's script. The *.cpp and *.hpp files now properly depend on the *.lzz files. The *.lzz files are added to the project (which answers absense's question above) but kept separate from the generated files using the source_group command.
The only remaining dealbreaker for me is the inability to compile the current file for *.lzz files.
cmake_minimum_required(VERSION 2.8)
PROJECT(LzzTest)
find_program(LZZ_COMMAND lzz.exe)
# Syntax:
# add_lzz_file(<output> <lzz file>)
# Adds a build rule for the specified lzz file. The absolute paths of the generated
# files are added to the <output> list. The files are generated in the binary dir.
#
# TODO: Support for generating template files etc.
function(add_lzz_file output filename)
# Only process *.lzz files
get_filename_component(ext ${filename} EXT)
if(NOT ext STREQUAL ".lzz")
return()
endif()
set(header_extension "hpp")
get_filename_component(base ${filename} NAME_WE)
set(base_abs ${CMAKE_CURRENT_BINARY_DIR}/${base})
set(outfiles ${base_abs}.cpp ${base_abs}.${header_extension})
set(${output} ${${output}} ${outfiles} PARENT_SCOPE)
#message("outfiles=${outfiles}, DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${filename}")
add_custom_command(
OUTPUT ${outfiles}
COMMAND ${LZZ_COMMAND}
-o ${CMAKE_CURRENT_BINARY_DIR} # output dir
-hx ${header_extension}
-sl -hl -il -tl -nl -x # insert #line commands w/ absolute paths
-sd -hd -id -td -nd # don't output files that didn't change
${CMAKE_CURRENT_SOURCE_DIR}/${filename}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${filename}"
)
set_source_files_properties(${outfiles} PROPERTIES GENERATED TRUE)
endfunction()
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(SOURCES
A.lzz
B.lzz
main.cpp
)
foreach(file ${SOURCES})
add_lzz_file(GENERATED_SOURCES ${file})
endforeach()
source_group("" FILES ${SOURCES})
source_group(generated FILES ${GENERATED_SOURCES})
add_executable(LzzTest ${SOURCES} ${GENERATED_SOURCES})
For make:
sourcecode.h sourcecode.cpp: sourcecode.lzz
<TAB>lazy-cpp sourcecode.lzz
fill in sourcecode.h, sourcecode.cpp, and lazy-cpp with the correct values. I don't know them.