Get CMake to not be silent about sources it doesn't understand? - cmake

Suppose you have a very simple CMakeLists.txt
add_executable(silent T.cpp A.asm)
CMake will happily generate a C++ target for building silent, with T.cpp in it, but will silently drop any and all reference to A.asm, because it doesn't know what to do with the suffix.
Is there any way to get CMake to loudly complain about this source file it doesn't understand (to aid in porting a Makefile to CMake).

Ignoring unknown file extensions is - unfortunately for your case - by design.
If I look at the code of cmGeneratorTarget::ComputeKindedSources() anything unknown ends up to be classified as SourceKindExtra (to be added as such to generated IDE files).
So I tested a little and came up with the following script that evaluates your executable target source files for valid file extensions by overwriting add_executable() itself:
cmake_minimum_required(VERSION 3.3)
project(silent CXX)
file(WRITE T.cpp "int main() { return 0; }")
file(WRITE T.h "")
file(WRITE A.asm "")
function(add_executable _target)
_add_executable(${_target} ${ARGN})
get_property(_langs GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach(_lang IN LISTS _langs)
list(APPEND _ignore "${CMAKE_${_lang}_IGNORE_EXTENSIONS}")
endforeach()
get_target_property(_srcs ${_target} SOURCES)
foreach(_src IN LISTS _srcs)
get_source_file_property(_lang "${_src}" LANGUAGE)
get_filename_component(_ext "${_src}" EXT)
string(SUBSTRING "${_ext}" 1 -1 _ext) # remove leading dot
if (NOT _lang AND NOT _ext IN_LIST _ignore)
message(FATAL_ERROR "Target ${_target}: Unknown source file type '${_src}'")
endif()
endforeach()
endfunction()
add_executable(silent T.cpp T.h A.asm)
Since you wanted a rather loudly complain by CMake I declared it an FATAL_ERROR in this example implementation.

CMake doesn't just drop unknown files in add_executable().
If alongside with
add_executable(silent T.cpp A.asm)
you have
add_custom_command(OUTPUT A.asm COMMAND <...>
DEPENDS <dependees>)
then whenever <dependees> changed CMake will rerun command for create A.asm before compiling the executable.
Note, that automatical headers scanning doesn't provide such functionality: if your executable includes foo.h then executable will be rebuilt only when foo.h itself is changed. Any custom command creating this header will be ignored.
However, you may change behavior of add_executable by redefining it. See #Florian's answer for example of such redefinition.

Related

CMake target dependency to compile protobuf files

I want to build a c++ static lib out of some protobuf definitions with cmake/make.
I made a custom COMMAND to compile the protobuf to c++, and I set it as a PRE_BUILD dependency to my static lib.
project(mylib)
set(PROTO_PATH "${CMAKE_CURRENT_SOURCE_DIR}/proto_definitions")
file(GLOB PROTO_FILES "${PROTO_PATH}/*.proto")
foreach(PROTO_FILE in ${PROTO_FILES})
string(REGEX REPLACE "[.]proto$" ".pb.cc" OUTPUT_SOURCE ${PROTO_FILE})
list(APPEND OUTPUT_SOURCES ${OUTPUT_SOURCE})
endforeach()
add_custom_command(TARGET ${PROJECT_NAME}
PRE_BUILD
COMMAND protoc --cpp_out=${CMAKE_CURRENT_SOURCE_DIR}/compiled_proto ${PROTO_FILES}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "some comment")
add_library(${PROJECT_NAME} STATIC ${OUTPUT_SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
I get the following error when running cmake:
CMake Error: cannot determine link language for target "mylib"
Regardless of this error, the makefiles are generated, but when I make mylib, it does not trigger any proto compilation
The approach more native to CMake would be to add custom commands with the OUTPUT signature to generate the .cc files, and then use them as sources for the library normally. That way, they CMake will know what they are and how to produce them:
project(mylib)
set(PROTO_PATH "${CMAKE_CURRENT_SOURCE_DIR}/proto_definitions")
file(GLOB PROTO_FILES "${PROTO_PATH}/*.proto")
foreach(PROTO_FILE in ${PROTO_FILES})
string(REGEX REPLACE "[.]proto$" ".pb.cc" OUTPUT_SOURCE ${PROTO_FILE})
list(APPEND OUTPUT_SOURCES ${OUTPUT_SOURCE})
endforeach()
add_custom_command(OUTPUT ${OUTPUT_SOURCES}
COMMAND protoc --cpp_out=${CMAKE_CURRENT_SOURCE_DIR}/compiled_proto ${PROTO_FILES}
DEPENDS ${PROTO_FILES}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "some comment")
add_library(${PROJECT_NAME} STATIC ${OUTPUT_SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
Done this way, there will be one command which reads all the .proto files and produces all the .cc files — which means that if any of the .proto file changes, all the .cc files will be re-generated. I am not familiar with Protobuffers so I cannot know whether that's sane or not. If they are independent, it would be better to introduce one add_custom_command for each output file.
Also, given the arguments you're passing to protocc, you might have to modify the paths in OUTPUT_SOURCES to correctly point to the generated files.
Also note that CMake comes with a FindProtobuf module which defines a protobuf_generate_cpp() command, so you might want to use that instead of hand-coding the Protobuf support.
Comment for the answer above: There is no in in cmake foreach, which spend me some time to solve it.
After studying his answer(very thanks!), I summarized a method that works for me, which has the following characteristics:
Proto dir and Output dir can be different
Generate grpc_output
Create a custom_command for each proto
My project strcuture is:
hwpb/ (a library for super project)
cpp/
hwpb/ (store *.pb.cc, *.pb.h)
CMakeLists.txt
proto/ (store protos)
go/ (and other languages, not mentioned here)
And this is my CMakeListx.txt:
set(PROTO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../proto")
set(OUTPUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/hwpb")
file(GLOB PROTO_FILES "${PROTO_DIR}/*.proto")
foreach(PROTO_FILE ${PROTO_FILES})
get_filename_component (FILENAME ${PROTO_FILE} NAME_WLE)
set(PROTO_SRC "${OUTPUT_DIR}/${FILENAME}.pb.cc")
set(PROTO_HDR "${OUTPUT_DIR}/${FILENAME}.pb.h")
set(GRPC_SRC "${OUTPUT_DIR}/${FILENAME}.grpc.pb.cc")
set(GRPC_HDR "${OUTPUT_DIR}/${FILENAME}.grpc.pb.h")
add_custom_command(
OUTPUT "${PROTO_SRC}" "${PROTO_HDR}" "${GRPC_SRC}" "${GRPC_HDR}"
COMMAND ${_PROTOBUF_PROTOC}
ARGS --plugin=protoc-gen-grpc=${_GRPC_CPP_PLUGIN_EXECUTABLE}
--cpp_out="${OUTPUT_DIR}" --grpc_out="${OUTPUT_DIR}"
-I"${PROTO_DIR}" "${PROTO_FILE}"
DEPENDS ${PROTO_FILE}
)
list(APPEND OUTPUT_SOURCES ${PROTO_SRC} ${GRPC_HDR})
endforeach()
add_library(hwpb ${OUTPUT_SOURCES})
target_link_libraries(hwpb ${_GRPC_GRPCPP_UNSECURE} ${_PROTOBUF_LIBPROTOBUF})
target_include_directories(hwpb PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

CMAKE custom compiler not overwriting

In a CMAKE project, I need to define a custom command for a file type (.osl) which is compiled by a tool (oslc) which compiles to another file type (.oso)
I managed to do it by a function that I can run on a list of source files:
find_program(OSLC_EXECUTABLE oslc)
function(compile_osl out_var)
set(result)
foreach(osl_f ${ARGN})
file(RELATIVE_PATH osl_f_base ${CMAKE_CURRENT_SOURCE_DIR} ${osl_f})
string(REGEX REPLACE "\\.osl$" ".oso" oso_f ${osl_f_base})
set(oso_f "${CMAKE_CURRENT_BINARY_DIR}/${oso_f}")
get_filename_component(oso_f_dir ${oso_f} DIRECTORY)
file(MAKE_DIRECTORY ${oso_f_dir})
add_custom_command(OUTPUT ${oso_f}
COMMAND ${OSLC_EXECUTABLE} ${osl_f} -o ${oso_f}
DEPENDS ${osl_f}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Creating compiled OSL file ${oso_f}"
VERBATIM
)
list(APPEND result ${oso_f})
endforeach()
set(${out_var} "${result}" PARENT_SCOPE)
endfunction()
Thanks to the DEPENDS directive, the compiler is only run if the sources are newer than the output files. However, the compiler does not overwrite output files, so it does not work.
Deleting with file(REMOVE ...) just before the custom command does not work, as it deletes all files, not only the ones requiring recompilation. Also, it deletes at cmake execution, not at make time.
I could maybe define another custom command with "rm" but this not cross platform (I would need to add specific lines for Windows, which I do not like).
Any idea?
Thanks!

CMake per file optimizations

Elsewhere the question has been asked, "How do I turn off optimizations on one file?" The answer is usually something like this:
cmake_minimum_required( VERSION 3.8 )
project( Hello )
add_executable( hello hello.c foo.c bar.c )
set( CMAKE_C_FLAGS_RELEASE "" )
set( CMAKE_CXX_FLAGS_RELEASE "" )
set_source_files_properties( hello.c
PROPERTIES
COMPILE_FLAGS -O0 )
This works unless you invoke cmake like this:
cmake -GNinja -DCMAKE_BUILD_TYPE=Release ../hello
And you get this in your build.ninja
FLAGS = -O3 -DNDEBUG -O0
Checking the documentation on COMPILE_FLAGS
Additional flags to be added when compiling this source file.
This makes sense, it is added to the list of COMPILE_FLAGS, it does not override existing compiler flags.
So, within CMake how can you override the optimisation level on a single file and being able to compile the rest of the project in Release? Otherwise you can force the compile to CMAKE_BUILD_TYPE="" which is the default behavior, but that somewhat defeats a selling point of Cmake.
You can't overwrite compiler options with the makefile CMake generators on source file level. Options are always appended (see my answer at Is Cmake set variable recursive? for the complete formula).
This is - as far as I know - only supported with the Visual Studio solution/project generators. These generators have flag tables to identify flags that are in the same group/that does overwrite a previous defined flag.
So yours is more like a feature request to also add compiler option tables to CMake's makefile generators.
Alternatives
I just wanted to add some crazy CMake magic I came up with as a workaround. Add the following to your main CMakeLists.txt after the project() command:
if (CMAKE_BUILD_TYPE)
define_property(
SOURCE
PROPERTY COMPILE_FLAGS
INHERITED
BRIEF_DOCS "brief-doc"
FULL_DOCS "full-doc"
)
string(TOUPPER ${CMAKE_BUILD_TYPE} _build_type)
set_directory_properties(PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS_${_build_type}}")
set(CMAKE_CXX_FLAGS_${_build_type} "")
endif()
This example moves the CMAKE_CXX_FLAGS_<build type> content into an new COMPILE_FLAGS directory property that is then linked to COMPILE_FLAGS source file property via define_property(... INHERITED ...).
Now the build type specific flags are only defined in COMPILE_FLAGS for each source file and you can overwrite/change them e.g. with the code snippet from your example:
set_source_files_properties(
hello.c
PROPERTIES
COMPILE_FLAGS -O0
)
References
Directory properties and subdirectories
CMake: How do I change properties on subdirectory project targets?

How to make cMake use Debug compilation mode for only a subset of directories in a project?

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)

Conditional add_custom_command in CMake

I am using a macro to create precompiled headers for my cmake project. For gcc, this macro uses add_custom_command to create a *.h.gch file which can then be added to the target along with the other source files with add_executable/add_library. The problem is that sometimes the same *.h.gch file is used for two different targets, because some libraries are built both as static and dynamic libs.
I need to call the macro after each of the add_library calls because for MSVC/Xcode, one needs to adjust the target properties to enable PCH usage/compilation. But for gcc, this results in an error as I'm trying to use add_custom_command with an output that already has a build rule (the .gch). Currently I am avoiding this error by just skipping the add_custom_command for any target that contains "Static" in its name - this happens to work because all the static libraries in the project have a "Static" postfix, but its obviously not a very elegant solution.
Is there a way in cmake to check if a target already has a build rule, or alternatively, a way to allow add_custom_command to fail silently without causing an error? Or is there a way to change my design so that I can avoid the problem entirely? I suppose one "solution" would be to add a conditional check in each of the CMakeLists, but I really don't want to do that.
This is the code I am currently using:
The Macro:
macro(SET_PRECOMPILED_HEADER targetName PCHFile)
if(MSVC)
# PCH for MSVC
elseif(${CMAKE_GENERATOR} MATCHES "Xcode")
# PCH for Xcode
else() #gcc
if(NOT ${targetName} MATCHES "Static") ## <-- this is bad
## set the correct "compilerArgs"
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PCHFile}.gch
COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${compilerArgs}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PCHFile}
)
endif()
endmacro(SET_PRECOMPILED_HEADER targetName PCHFile)
...then in the CMakeLists, something like this:
# Dynamic version:
set(MODULE_NAME MyLib)
project(${MODULE_NAME})
## set ${sources}
add_library(${MODULE_NAME} SHARED ${sources} "src/precompiled.h.${PCH_EXT}")
set_target_properties(${MODULE_NAME} PROPERTIES COMPILE_DEFINITIONS MY_DLL_DEFINITION)
SET_PRECOMPILED_HEADER(${MODULE_NAME} "src/precompiled.h")
# Static version:
set(MODULE_NAME MyLibStatic)
project(${MODULE_NAME})
add_library(${MODULE_NAME} ${sources} "src/precompiled.h.${PCH_EXT}")
set_target_properties(${MODULE_NAME} PROPERTIES COMPILE_DEFINITIONS MY_STATIC_DEFINITION)
SET_PRECOMPILED_HEADER(${MODULE_NAME} "src/precompiled.h")
Thanks for your help! I'm sorry if this is a duplicate - there are already several questions on add_custom_command, but none of them quite seem to address what I'm after.
First, you can create target for each PCH and then use this before declaring new target:
if(TARGET ${PCHFile}.gch)
Another way:
In the root CMakeLists.txt:
set(PRECOMPILED_HEADERS "" CACHE INTERNAL "")
In the macro:
list(FIND PRECOMPILED_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/${PCHFile}.gch res)
if(NOT res EQUAL -1)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PCHFile}.gch
COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${compilerArgs}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PCHFile}
)
list(APPEND PRECOMPILED_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/${PCHFile}.gch)
endif()