I am currently trying to set up a CMake-based build system for a small C library of mine. Since this library depends on libacl, CMake should verify that it is present on the system, or fail otherwise.
Since there is no predefined FindACL module, I used the one from the KDE project:
[...] check_include_files [...] find_library [...]
if (ACL_HEADERS_FOUND AND ACL_LIBS AND ATTR_LIBS)
set(ACL_FOUND TRUE)
set(ACL_LIBS ${ACL_LIBS} ${ATTR_LIBS})
message(STATUS "Found ACL support: ${ACL_LIBS}")
endif (ACL_HEADERS_FOUND AND ACL_LIBS AND ATTR_LIBS)
I call it using the following (minimal) CMakeLists.txt:
cmake_minimum_required(VERSION 3.12)
project(
cmaketest
VERSION 1.0
DESCRIPTION "cmake test"
LANGUAGES C
)
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
find_package(ACL REQUIRED) # ACL_LIBS
It correctly detects that my system is lacking the libacl includes, but does not stop processing, even though the manual states that
The REQUIRED option stops processing with an error message if the package cannot be found.
Do I have to explicitly check whether ACL_FOUND is set, through an if statement?
I am using CMake 3.13.4. Terminal output:
-- The C compiler identification is GNU 8.3.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Looking for include file attr/libattr.h
-- Looking for include file attr/libattr.h - not found
-- Looking for include file sys/xattr.h
-- Looking for include file sys/xattr.h - found
-- Looking for include file sys/acl.h
-- Looking for include file sys/acl.h - not found
-- Looking for include file acl/libacl.h
-- Looking for include file acl/libacl.h - not found
-- Configuring done
-- Generating done
-- Build files have been written to: [redacted]/build
It seems that since some time CMake expects a FindXXX.cmake script (used in MODULE mode of find_package) to check REQUIRED option and emit an error if needed. Such change in CMake behavior makes old legacy "Find" scripts, like FindACL.cmake, to be broken.
Indirect confirmation of that can be found in this "Common bug in find module", described in CMake wiki:
The module does not check _FIND_REQUIRED or
_FIND_QUIETLY - and thus the find_package arguments QUIET
and REQUIRED will have no effect
So, FindACL.cmake script could be rewritten as follows:
# FindACL.cmake
# ...
if (ACL_HEADERS_FOUND AND ACL_LIBS AND ATTR_LIBS)
set(ACL_FOUND TRUE)
set(ACL_LIBS ${ACL_LIBS} ${ATTR_LIBS})
message(STATUS "Found ACL support: ${ACL_LIBS}")
elif (ACL_FIND_REQUIRED)
# ACL hasn't been found but REQUIRED. Emit an error.
message(FATAL_ERROR "Cannot find ACL")
endif (ACL_HEADERS_FOUND AND ACL_LIBS AND ATTR_LIBS)
Actually, most "Find" scripts use the special helper which sets result of find_package and cares about its options. FindACL.cmake could also use this helper:
# FindACL.cmake
# ...
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ACL
# Use default message on fail.
DEFAULT_MSG
# Variables which should be true-evaluated for assume the package to be found
ACL_HEADERS_FOUND ACL_LIBS ATTR_LIBS
)
The things are different for XXXConfig.cmake scripts, which are processed in CONFIG mode of find_package. These scripts should only set XXX_FOUND variable to false, and CMake will handle REQUIRED option by itself.
Related
I'm trying to find a library that's built alongside a subsection of a project, the library is built with a "externalproject_add" call and builds successfully. It puts a library (.so.2) in a subdirectory within itself
I've specified a directory with PATHS (I've also tried HINTS, and adding the trailing /lib as part of the path), and am using debug mode to get CMake to print the information as it tries to find this library.
find_library(METADX1_LIB NAMES libmdx1host.so.2 PATHS ${CMAKE_CURRENT_SOURCE_DIR}/meta-dx1/sdk/builds/debug/ PATH_SUFFIXES lib NO_DEFAULT_PATH)
I'm very clear here about the path I have my library in. I've also tried suspending the leading lib, trailing *.so.2, etc to no avail, it looks like CMake isn't even trying:
CMake Debug Log at Sub-project/CMakeLists.txt:187 (find_library):
find_library called with the following settings:
VAR: METADX1_LIB
NAMES: "libmdx1host.so.2"
Documentation: Path to a library.
Framework
Only Search Frameworks: 0
Search Frameworks Last: 0
Search Frameworks First: 0
AppBundle
Only Search AppBundle: 0
Search AppBundle Last: 0
Search AppBundle First: 0
NO_DEFAULT_PATH Enabled
find_library considered the following locations:
The item was not found.
If I supply /usr/bin as a path to PATHS as an experiment, CMake appends that to some output directory somewhere deep within the project, even though it's an absolute path.
I'm finding CMake to be extremely counterintuitive. I've given it an exact location where there is a library, how can I get it to actually look in that directory?
Edit
IT seems find_library is modifying the paths for some reason:
CMake Debug Log at TMX-4400/CMakeLists.txt:190 (find_library):
find_library(METADX1_LIB) removed original suffix
/home/brydon/embedded/product/../../tools/optelinux/bdrt/output/rootfs.tmx4400/staging/home/brydon/embedded/product/TMX-4400/meta-dx1/sdk/builds/debug/
from PATH_SUFFIXES while adding architecture paths for suffix '64'
I'm not sure what else is going on in your CMakeLists.txt, but I can't replicate your failure with the code and paths you provided.
First, I created a CMakeLists.txt file with the following contents:
cmake_minimum_required(VERSION 3.23)
project(test)
find_library(
METADX1_LIB
NAMES libmdx1host.so.2
PATHS ${CMAKE_CURRENT_SOURCE_DIR}/meta-dx1/sdk/builds/debug/
PATH_SUFFIXES lib
NO_DEFAULT_PATH
)
message(STATUS "METADX1_LIB = ${METADX1_LIB}")
Then, I replicated the directory layout described in your post and created an empty library file with the appropriate name:
$ mkdir -p meta-dx1/sdk/builds/debug
$ touch meta-dx1/sdk/builds/debug/libmdx1host.so.2
$ tree
.
├── CMakeLists.txt
└── meta-dx1
└── sdk
└── builds
└── debug
└── libmdx1host.so.2
Finally, I configured the build using Ninja and the --debug-find-var option of CMake 3.23 to see how the search proceeded.
$ cmake -G Ninja -S . -B build --debug-find-var=METADX1_LIB
Running with debug output on for the variable(s) METADX1_LIB.
-- The C compiler identification is GNU 11.3.0
-- The CXX compiler identification is GNU 11.3.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
CMake Debug Log at CMakeLists.txt:4 (find_library):
find_library called with the following settings:
VAR: METADX1_LIB
NAMES: "libmdx1host.so.2"
Documentation: Path to a library.
Framework
Only Search Frameworks: 0
Search Frameworks Last: 0
Search Frameworks First: 0
AppBundle
Only Search AppBundle: 0
Search AppBundle Last: 0
Search AppBundle First: 0
NO_DEFAULT_PATH Enabled
find_library considered the following locations:
/home/alex/test2/meta-dx1/sdk/builds/debug/lib//(lib)libmdx1host.so.2(\.so|\.a)
/home/alex/test2/meta-dx1/sdk/builds/debug/lib/(lib)libmdx1host.so.2(\.so|\.a)
The item was found at
/home/alex/test2/meta-dx1/sdk/builds/debug/
-- METADX1_LIB = /home/alex/test2/meta-dx1/sdk/builds/debug/libmdx1host.so.2
-- Configuring done
-- Generating done
-- Build files have been written to: /home/alex/test2/build
And this apparently works.
The library locations are cached by cmake, so if you added the path location and cmake found the library previously elsewhere, make sure to clear the build directory.
I'm trying to add a target to generate API doc with Doxygen to my CMake project by find_package(Doxygen ...) and doxygen_add_docs(). I'm in a MSYS2 environment on Windows 10. For some reason CMake and Doxygen seem to not handle paths in the expected way (see the example).
At first, Doxygen complains that it cannot create the output directory. The DOXYGEN_OUTPUT_DIRECTORY prepared by CMake is a Windows path with regular forward slashes. Doxygen seems to not recognize this as an absolute path and prepends it with its working directory in the Unix format (/c/Users/...). I tried this by executing the generated target as well as calling Doxygen manually with the generated Doxyfile.doc. It works if I convert the path to a Unix path with cygpath.
The second problem is about finding dot. I have GraphViz installed as a native Windows package and generally the MSYS2 CMake finds this. Then, in the FindDoxygen.cmake DOXYGEN_DOT_PATH is set as the path portion of the dot executable and is modified by file(TO_NATIVE_PATH ...) because I'm on Windows. Trying this manually I could see that it sets that path to "C:/Program Files (x86)/Graphviz/bin" and converts it to "C:/Program\ Files\ (x86)/Graphviz/bin". The latter ends up in Doxyfile.doc. If I remove the backslashes there, it works. I suppose Doxygen considers these backslashes which are meant to escape the spaces as path separators.
For the first problem I have a (hopefully) portable workaround, but the second I can only fix if I manually repeat the retrieval of DOXYGEN_DOT_PATH from the Find module without the conversion step. This doesn't feel like a good solution.
Do I miss something here about path handling in CMake, e.g. some documentation or setting/policy?
Is this anyways the expected behavior, esp. for file(TO_NATIVE_PATH ...), in MSYS2?
Example
Very basic example: Just a CMakeLists.txt to generate Makefiles and invoke Doxygen. The output of make doc shows the 2 problems described above - the error is for #1, the warning for #2.
CMakeLists.txt
cmake_minimum_required(VERSION 3.19)
project(DoxyTest VERSION 0.1)
find_package(Doxygen OPTIONAL_COMPONENTS dot)
doxygen_add_docs(doc)
Trying to build the doc target
$ cmake -G"MSYS Makefiles" ..
-- The C compiler identification is GNU 10.2.0
-- The CXX compiler identification is GNU 10.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/msys64/mingw64/bin/gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/msys64/mingw64/bin/g++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found Doxygen: C:/msys64/usr/bin/doxygen.exe (found version "1.9.1 (ec8203f43232e7e7b16b1c85351c7c48d128f924)") found components: doxygen dot
-- Configuring done
-- Generating done
-- Build files have been written to: C:/Users/myUser/tmp/foo/build
$ make doc
Scanning dependencies of target doc
[100%] Generate API documentation for doc
warning: the dot tool could not be found at C:/Program\ Files\ (x86)/Graphviz/bin
Doxygen version used: 1.9.1 (ec8203f43232e7e7b16b1c85351c7c48d128f924)
error: Could not create output directory /C/Users/myUser/tmp/foo/C:/Users/myUser/tmp/foo/build/html
Exiting...
make[3]: *** [CMakeFiles/doc.dir/build.make:78: CMakeFiles/doc] Error 1
make[2]: *** [CMakeFiles/Makefile2:95: CMakeFiles/doc.dir/all] Error 2
make[1]: *** [CMakeFiles/Makefile2:102: CMakeFiles/doc.dir/rule] Error 2
make: *** [Makefile:137: doc] Error 2
It turned out that the proper Doxygen package was not installed in the MSYS2 environment.
I use the MinGW64 shell, which uses the binaries from the mingw-w64-x86_64-* packages. The CMake output from the question shows that the binaries for the C and CXX compilers are under C:/msys64/mingw64/ (as well as the CMake binary) while Doxygen is in C:/msys64/usr/bin/.
Installing mingw-w64-x86_64-doxygen solved the issue. Now, the CMake output shows the mingw64 path also for Doxygen in the MSYS2 MinGW64-bit shell and API doc generation works flawlessly. Trying the MSYS2 MSYS shell also works fine with CMake using binaries from C:/msys64/usr/bin/.
In an attempt to create a cross-compilation CMake toolchain template with the SDCC compiler, I have come across a very weird issue.
As described in this link, if the toolchain.cmake file defines a CMAKE_SYSTEM_NAME, CMake will look for the file with the ${CMAKE_SYSTEM_NAME}.cmake under the Module/Platform directory. And this file should define platform-specific options. In my case, I am using it to find the sdcc compiler and setting some compiler flags.
This works just fine for me. Using cmake -DCMAKE_MODULE_PATH="${PATH_TO_MY_MODULES}" -DCMAKE_TOOLCHAIN_FILE="${PATH_TO_MY_TOOLCHAIN}" -DSDCC_SYSROOT="SOME_VALUE", CMake finds all the correct toolchain and platform files.
It seems like the toolchain and the platform file are executed (not sure if that's the correct term) a few times during the configuration process. In the first few times, the variable SDCC_SYSROOT I passed in the CMake command has the value SOME_VALUE as expected. However, the same variable SDCC_SYSROOT seems to lose the value in the last time these toolchain/platform files are executed. So they are empty. This causes my script to generate a fatal error.
toolchain.cmake has the following contents:
set(CMAKE_SYSTEM_NAME SDCC_PIC_16F877A)
# Finding resource settings
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
# Set default MCU family and model
if (NOT MICROCHIP_FAMILY)
set(MICROCHIP_FAMILY "pic16")
endif()
if (MICROCHIP_MODEL STREQUAL "pic16")
set(MICROCHIP_MODEL "16f877a")
endif()
# Need a better way to detect the supported models here
if (NOT MICROCHIP_FAMILY STREQUAL "pic16" AND NOT MICROCHIP_MODEL STREQUAL "16f877a")
message(FATAL_ERROR "Settings not supported. Please drop a request.")
endif()
if (NOT SDCC_ROOT)
message(FATA_ERROR "Need to provide the root (from toolchain.)")
endif()
# Cache those variables
set(SDCC_ROOT "${SDCC_ROOT}"
CACHE INTERNAL "Root directory of SDCC installation")
set(MICROCHIP_FAMILY "${MICROCHIP_FAMILY}"
CACHE INTERNAL "Family of the chip to compile for")
set(MICROCHIP_MODEL "${MICROCHIP_MODEL}"
CACHE INTERNAL "Model of the chip to compile for")
the Module/Platform/SDCC_PIC_16F877A.cmake file has the contents:
# Check if the shit exists
message("!!! The value of root is ${SDCC_ROOT}")
if (NOT SDCC_ROOT)
message(FATAL_ERROR
"SDCC_ROOT is not defined. Please set this variable e.g.\n"
"cmake -DSDCC_ROOT=\"C:/Program Files/sdcc\"")
endif()
# Finding the compilers
find_program(CMAKE_C_COMPILER
sdcc
PATHS ${SDCC_ROOT}
PATH_SUFFIXES "bin"
DOC "path to the SDCC C compiler.")
and my CMakeLists.txt is the following:
cmake_minimum_required(VERSION 3.10)
project(PicExample)
message("THE COMPILER IS ${CMAKE_C_COMPILER}")
add_executable(pic_example main.c)
what I invoke from my project/build directory and the error I get:
cmake -DCMAKE_MODULE_PATH:FILEPATH="/mnt/c/Users/mathe/Desktop/coding/sdcc-pic-template/Modules" -DCMAKE_TOOLCHAIN_FILE:FILEPATH="/mnt/c/Users/mathe/Desktop/coding/sdcc-pic-template/Modules/toolchain.cmake" -DSDCC_ROOT="testing/" ..
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
!!! The value of root is testing/
!!! The value of root is testing/
-- Check for working C compiler: /usr/bin/cc
FATA_ERRORNeed to provide the root (from toolchain.)
!!! The value of root is
CMake Error at /mnt/c/Users/mathe/Desktop/coding/sdcc-pic-template/Modules/Platform/SDCC_PIC_16F877A.cmake:4 (message):
SDCC_ROOT is not defined. Please set this variable e.g.
cmake -DSDCC_ROOT="C:/Program Files/sdcc"
Call Stack (most recent call first):
/usr/share/cmake-3.16/Modules/CMakeSystemSpecificInformation.cmake:26 (include)
/mnt/c/Users/mathe/Desktop/coding/sdcc-pic-template/build/CMakeFiles/CMakeTmp/CMakeLists.txt:3 (project)
CMake Error at /usr/share/cmake-3.16/Modules/CMakeTestCCompiler.cmake:44 (try_compile):
Failed to configure test project build system.
Call Stack (most recent call first):
CMakeLists.txt:2 (project)
-- Configuring incomplete, errors occurred!
See also "/mnt/c/Users/mathe/Desktop/coding/sdcc-pic-template/build/CMakeFiles/CMakeOutput.log".
Why do the toolchain files get "executed" more than once by CMake and has no access to cache in the latest runs? I've been finding CMake documentation for cross-compilation very difficult, especially if you are working with a non-standard compiler.
I am aware that other people have had same issues before, but I am not simply asking for a simple hacky solution (setting environment variables). I actually want to know why this happens (which the previous answers don't tackle).
Tsyvarev answered the why the toolchain is used multiple times in CMake. TLDR; CMake needs it for multiple try_compile() calls it uses internally for error checking and other things.
This works just fine for me.
-DCMAKE_MODULE_PATH="${PATH_TO_MY_MODULES}" -DCMAKE_TOOLCHAIN_FILE="${PATH_TO_MY_TOOLCHAIN}" -DSDCC_SYSROOT="SOME_VALUE",
To fix your problem here is what you need to do.
Essentially you are passing an argument to your toolchain file. And this argument SDCC_SYSROOT essentially goes out of scope.
To fix this problem here is what you need to do.
# Use list(APPEND) rather than set() so that any variables added by CMake aren't lost!
#
# Here is the docs for this variable:
# https://cmake.org/cmake/help/latest/variable/CMAKE_TRY_COMPILE_PLATFORM_VARIABLES.html
list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES ${SDCC_SYSROOT})
If you want to see how many times your toolchain script gets executed try putting in a message() call in there for fun.
And if you are really interested look inside your build folder and see what it is CMake is doing.
If you are wondering how I know this information it's because I read the toolchain section in Craig Scott's CMake book "Professional CMake:
A Practical Guide"
Here is a link: https://crascit.com/professional-cmake/
For determine, whether some feature is supported by the compiler or by some library, CMake uses try_compile approach: during the configuration phase, it creates separate CMake project and immediately configures and builds it. Because it is a separate project, its configuration has the same steps as the main project and it loads the toolchain file too.
try_compile could be used by the (user) project for check features of the library or of the compiler. There are many CMake modules which use try_compile in their implementation. E.g. CheckSymbolExists.
try_compile is also used by CMake itself, in platform files, when it perform basics checks for the compiler. In your log you could find the line:
CMake Error at /usr/share/cmake-3.16/Modules/CMakeTestCCompiler.cmake:44 (try_compile)
Aside from try_compile, the new CMake project is created in ExternalProject_Add command. That creation is also accompanied by the reading of the toolchain file. (More correctly, the new project is created not when ExternalProject_Add invocation is processed but when corresponding project is configured. This configuration is performed on the build stage of the main project.)
I want to cross compile an embedded application using Tasking TriCore toolchain and CMake (3.16 is ok).
I started with a complete toolchain file, where all paths to binaries and rules were specified, but then I read this thread and used the Compiler/IAR* scripts as an example (https://github.com/Kitware/CMake/blob/master/Modules/Compiler) to end up with a cleaner way.
I know that by default in C/CXX projects, CMake tries to compile a simple test program CMakeCCompilerId.c to detect the compiler id vendor and version. With IAR-DetermineCompiler.cmake I created my own Tasking-DetermineCompiler.cmake which would used symbols defined by the ctc compiler. But even if this file is executed by CMake (syntax errors in there are detected) it does not seem to have any impact on the generated test source file.
This means that the default mechanism for compiler identification does not work for my compiler and I get the message "The C compiler identification is unknown". On my toolchain file I need to force the identification:
set(CMAKE_SYSTEM_NAME Generic)
# will load Platform/Generic.cmake under CMAKE_MODULE_PATH
# Normalize, convert Windows backslashes to forward slashes or CMake will crash
get_filename_component(TASKING_ROOT_PATH "$ENV{TASKING_TRICORE_PATH}" ABSOLUTE)
# Specify the compiler
# will load Platform/Generic-ctc.cmake CMAKE_MODULE_PATH
foreach (_prefix C CXX)
if ("${CMAKE_${_prefix}_COMPILER}" STREQUAL "")
set(CMAKE_${_prefix}_COMPILER "${TASKING_ROOT_PATH}/bin/cctc.exe")
endif()
# This is used only if we skip auto compiler identification
set(CMAKE_${_prefix}_COMPILER_ID "Tasking")
set(CMAKE_${_prefix}_COMPILER_VERSION "6.3.1r1")
# Skip compiler ID identification: use "Tasking"
set(CMAKE_${_prefix}_COMPILER_ID_RUN TRUE)
set(CMAKE_${_prefix}_COMPILER_FORCED TRUE)
endforeach()
# will load Compiler/Tasking.cmake under CMAKE_MODULE_PATH
# and Compiler/Tasking-FindBinUtils.cmake as well
So far, this is working but I would like to understand whether it is possible to use the default CMake way of identifying the compiler id and version. I could not find how to add other vendors in the documentation!
Many thanks
See last status update
Initial conditions
code generator that generates set of c++ sources taking one input file as parameter
input file may include other input files
already solved task of getting list of output files, parsing input codegen files to get full list of codegen inputs.
I.e. add_custom_command is provided with correct set of dependencies for the first time:
add_custom_command(OUTPUT ${generatedSources}
COMMAND ${codegenCommand} ARGS ${codegenArgs}
DEPENDS ${codegenInputFiles})
Problem scenario
current system works well until someone modifies one of codegen input files to include new input file or remove include of existing one.
This case, it is required to update list of codegen input file provided to add_custom_command as dependencies, but I have no idea how
What is missing
ability to update add_custom_command dependencies over project rebuilds
Is there any way to solve it without making full project rebuild ?
UPDATE - Alternative (better?) problem description
I`ve found similar not answered question on cmake mailing list, post it here for better clarity:
http://article.gmane.org/gmane.comp.programming.tools.cmake.user/52279
I am trying to get a code generation tool to behave "the same as" a C source file with respect to dependencies. By that I mean, suppose you have a C file "a.c". Because it can #include files, every time the content of a.c changes, its dependencies may have changed also. The dependencies get rescanned with -MMD. I would like some way to emulate this for my code generator.
First I tried add_custom_command, which takes a fixed DEPENDS list, determined at the time the custom command is defined. Concretely, I mean something like this:
function(add_generated_library)
figure_out_dependencies(deps ${ARGN})
add_custom_command(... DEPENDS ${deps})
endfunction()
But that only captures the dependencies at build-system-generation time. Every time the custom command runs, the DEPENDS list may need to change, because the changes may imply new dependencies.How should I go about doing this?
UPDATE 2 - Possible solution
Following I consider as facts
- there are voices across the web regarding cmake support of dynamic
dependencies, which is required for smooth integration of many
non-trivial code generation tools
- there`s no ready-for-use optimal solution available, as what we actually need is hook to add support of custom DSL to IMPLICIT_DEPENDS
From cmake manual:
The IMPLICIT_DEPENDS option requests scanning of implicit dependencies of an input file. The language given specifies the programming language whose corresponding dependency scanner should be used. Currently only C and CXX language scanners are supported. The language has to be specified for every file in the IMPLICIT_DEPENDS list. Dependencies discovered from the scanning are added to those of the custom command at build time.
Solution below adheres (hopefully) to following criteria:
avoid not necessary dependency scanning on rebuild
avoid not necessary code generator run on rebuild
allow providing cmake functions to clients to register their models and
generate code/create libraries from that code, without imposing any project structure requirements (i.e. no sub-project responsible for code generation, models are distributed across project hierarchy using project-specific policy)
Solution idea
It is not possible to register custom language scanner, but it is possible to reuse existing one. The idea is that dependencies / hierarchy of custom model files get reflected as hierarchy of "C" header files. Each hierarchy node get added on registering of model file and C file includes match model file includes. If model file includes get changed, C file includes get changed. Therefore, each codegen invocation would depend on just one generated C header reflecting passed model. Each reflected file will have a dependency on the model file and get touched on model file change.
To sum up : probably, my wording is not that clear at this point, but with respect to other people needs and community helped me to investigate this problem, I will post generic solution (+ link to github or new cmake wiki page) without my project specifics once it is ready (in 1-3 days).
Can you show how you initialize the variable codegenInputFiles? You can probably use a file(GLOB ... ) or file(GLOB_RECURSE ... ) command there.
See documentation.
Note, though, that you will have to rerun cmake to have your command being generated. Are you working with git? Then you can have a hook that forces a cmake call every time you pull (so that if somebody modified the codegenInputFiles your auto generated files will be updated).
After clarification of the problem, you should be able to find a workaround by using IMPLICIT_DEPENDS instead of DEPENDS. Limitations:
It can only work if your input file is C/C++ (check the syntax, as you must specify the language for each file specified)
You might need to check your cmake version supports that command, even though it looks that it has been around for a while
It's only supported for Makefile generator, which sounds pretty bad...
EDIT
After some iterations, I finally got what is your problem.
I propose the following solution: separate the file generation in a separate cmake subproject. When you will build your main project (by calling make), you will trigger both cmake and make for your subproject. Calling cmake is necessary for keeping updated the dependencies, while calling make to actually build your auto-generated sources.
Here I show an example of a project and a subproject, with the project invoking cmake and make for the subproject.
Structure:
.
├── CMakeLists.txt
├── a.cpp
├── build
└── subProject
└── CMakeLists.txt
File content
./CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
add_custom_target(subProjectTarget ALL)
add_custom_command(TARGET subProjectTarget PRE_BUILD COMMAND mkdir -p ${CMAKE_BINARY_DIR}/subProject && cd ${CMAKE_BINARY_DIR}/subProject && ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR}/subProject && make)
include_directories(${CMAKE_BINARY_DIR}/subProject)
add_executable (dummy a.cpp)
add_dependencies (dummy subProjectTarget)
./a.cpp (Note that b.h doesn't exist yet)
#include "b.h"
int main () {
}
./SubProject/CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
file(WRITE ${CMAKE_BINARY_DIR}/b.h "//I am a dummy file\n")
Building the project (using default make)
me#here:~/example/build$ cmake ..
-- The C compiler identification is GNU 4.8.2
-- The CXX compiler identification is GNU 4.8.2
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/me/example/build
me#here:~/example/build$ make
Scanning dependencies of target subProjectTarget
-- The C compiler identification is GNU 4.8.2
-- The CXX compiler identification is GNU 4.8.2
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/me/example/build/subProject
[ 0%] Built target subProjectTarget
Scanning dependencies of target dummy
[100%] Building CXX object CMakeFiles/dummy.dir/a.cpp.o
Linking CXX executable dummy
[100%] Built target dummy
Note that the second time the cmake calls is on the subproject.
At the next call everything is even faster:
me#here:~/example/build$ make
-- Configuring done
-- Generating done
-- Build files have been written to: /home/me/example/build/subProject
[ 0%] Built target subProjectTarget
Scanning dependencies of target dummy
[100%] Building CXX object CMakeFiles/dummy.dir/a.cpp.o
Linking CXX executable dummy
[100%] Built target dummy
(Although here the file b.h is written every time causing a.cpp to be recompiled)
This stub can be very much improved by using cmake commands to generate the output directories (and not mkdir) and cascading the generator chosen for the main project (here I am assuming everything is using make)
Let me know if you need any further clarification.
I think that ${codegenInputFiles} should contain a list of hardcoded source files and include files required by the custom command. Documentation of file(GLOB ...) states:
We do not recommend using GLOB to collect a list of source files from
your source tree. If no CMakeLists.txt file changes when a source is
added or removed then the generated build system cannot know when to
ask CMake to regenerate.
The hard work (for which we are paid) is to keep the ${codegenInputFiles} up to date (causing the full project rebuild). Anyway, you would have similar problem if someone created a new source file and hadn't added it to the ${codegenInputFiles}, right? So I believe the additional dependency on include file should be treated the same.