IMPLICIT_DEPENDS - does not work

I have been trying to do this:

add_custom_command(
OUTPUT …
COMMAND …
IMPLICIT_DEPENDS C “C:/WS/project1/timer_x.c”
VERBATIM
)

but I cannot get the dependencies scanner to work correctly
What I want is, if any headers declared (including “recursive header”, ie. header included by an header) in timer_x.c changes, the custom command should run

But it does not work!

I am using GNU Makefiles
I am trying to replace existing Eclipse CDT Builder with CMake

Help!

Based on the file extension (.c), I guess you want to compile C code. If so, you should not use add_custom_command to do that at all.

What do you want to compile that C code into? An executable? Then, you should use add_executable. A shared or static library? Then, you should use add_library. A loadable module (also known as “plugin” in some communities)? Then, you should use add_library as well.

Dear Alain,
Yes I want to compile C code into executable
What I want to achieve is, if timer_x_if.h is modified, “make all” should re-build incrementally the project (without running CMake again). Similarly, if any other *.h files (included by timer_x_if.h) is modified, “make all” should re-build incrementally the project as well

project(My_Project)

add_executable(My_Project ${SOURCE}) #ALL *.c files

add_custom_command(
OUTPUT “${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/My_Project.dir/timer_x/src/timer_x.c.obj”
COMMAND #compiler command to output timer_x.c.obj based on timer-x.c
COMMENT “\n SHOULD only run if timer_x_if.h changes”
IMPLICIT_DEPENDS C ${CMAKE_CURRENT_SOURCE_DIR}/timer_x/src/timer_x.c
VERBATIM
)

add_custom_target(
header-target
DEPENDS “${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/My_Project.dir/timer_x/src/timer_x.c.obj”
COMMENT “\n SHOULD only run if timer_x_if.h changes”
VERBATIM
)

add_dependencies(${CMAKE_PROJECT_NAME} header-target)

timer_x

//timer_x.c
#include "timer_x_if.h"
#include “timer_x_config.h”

//timer_x_if.h - include other headers
#include “timer_x_if_api.h”
#include “timer_x_if_static.h”

You shouldn’t need add_custom_command to do that.

This is what I would write:

project(My_Project)

add_executable(My_Project
  "timer_x/src/timer_x.c" 
  # any other *.c file
)

Nothing more. CMake and make should take care of dependency tracking.

Dear Alain,

That’s what I did, ${SOURCE} includes all the *.c file already:

add_executable(My_Project ${SOURCE}) #ALL *.c files

Nothing more. CMake and make should take care of dependency tracking.

It will track changes in *.c file, but not *.h file. Which means if any of the *.c file is modified, incremental compilation will happen to that *.c file only

But it just does not track changes to *.h file. If any of the *.h changes, “make all” does not re-build

I learnt somewhere (and verified it myself) that even if I add *.h in add_executable(…), the build target is not sensitive to changes in *.h file

Of course I can do a “make clean” before build, but that defeat the purpose of being able to compile incrementally with the use of makefile

Or did I miss or get something wrong entirely?

I don’t think you necessarily missed something, but there’s definitely something wrong with your setup, because normally, the way CMake handles inclusion dependencies works. There must be something strange in your CMakeList, in the source files, or in your toolchain. Can you provide a self-contained example which reproduces the incorrect behaviour you observe, so that we could analyse what’s wrong?

The things to include in the example would be:

  • The CMakeLists.txt file
  • The source files & header files
  • The CMakeCache.txt file generated in your build directory
  • A description of your platform & build tools
  • Any other necessary files

Ideally, minimise the example by removing anything which can be removed without the bug disappearing.

The problem you’re experiencing sounds strange; I’d love to get to the bottom of it if my skills allow.

Out of curiosity, are you cross compiling?

I found that my tool (Tensilica Clang) wasn’t for some reason being recognized by CMake as Clang, so it wasn’t generating dependencies correctly.

When you do a reconfig and it tests for the C/C++ compilers, does it say “compiler unknown” or does it give a version and manufacturer of the compiler?

Out of curiosity, are you cross compiling?

Nope

When you do a reconfig and it tests for the C/C++ compilers, does it say “compiler unknown” or does it give a version and manufacturer of the compiler?

It was able to find the compiler:
– CMAKE_C_COMPILER=C:/xxx/xxx/xxx/bin/xxx.exe

In some cases when using the CMake Makefile generator will not pickup up all the dependencies one would expect.

  1. https://gitlab.kitware.com/cmake/cmake/-/issues/16277
  2. https://stackoverflow.com/questions/7461000/handling-header-files-dependencies-with-cmake
  3. https://stackoverflow.com/questions/13703647/how-to-properly-add-include-directories-with-cmake

Like others have pointed out that adding timer_x/src/timer_x.c with add_executable should have sufficed. You could try switching to the Ninja generator and see if that gives you better results (as it handles dependency tracking differently).

Swoo.quek,

I mean, what does the Cmake show when it runs the test compilation to identify the C compiler?

Mine shows this:

[cmake] – Selecting Windows SDK version 10.0.17763.0 to target Windows 10.0.19041.
[cmake] – The C compiler identification is MSVC 19.16.27025.1
[cmake] – The CXX compiler identification is MSVC 19.16.27025.1
[cmake] – Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.16.27023/bin/Hostx86/x64/cl.exe
[cmake] – Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.16.27023/bin/Hostx86/x64/cl.exe – works
[cmake] – Detecting C compiler ABI info
[cmake] – Detecting C compiler ABI info - done
[cmake] – Detecting C compile features
[cmake] – Detecting C compile features - done

If CMake can’t identify your compiler, it doesn’t know how to get the implicit dependencies, so it doesn’t.

Also, see A Kit with toolchainFile fails to determine CMAKE_(C|CXX)_COMPILER without the user adding both variables to the cache file · Issue #1188 · microsoft/vscode-cmake-tools · GitHub where it suggests you need to set both CMAKE_C_COMPILER and CMAKE_CXX_COMPILER, but also see iar - How to specify a compiler in CMake? - Stack Overflow where it recommends not using SET() to tell cmake where your compiler is.

Dear Russell,

How to generate these message? Can you show me?
I can only manage to print the macros like CMAKE_C_COMPILER, etc

Run CMake in an empty binary directory, and it should output its compiler detection information.

Ok, you are cross compiling, then. (Compiling in windows for the Renesas part). Cross compiling with CMake is not the most documented of features, unfortunately.

I’m assuming Renesas tools are GCC based. I don’t know exactly how to help you since I haven’t ever compiled for Renesas, but make sure you have CMAKE_C_COMPILER, CMAKE_CXX_COMPILER and CMAKE_ASM_COMPILER defined in your toolchain file.

Here’s our IAR for ARM toolchain file:

### BEGIN CMAKE_TOOLCHAIN_FILE
set(CMAKE_SYSTEM_NAME iar)
set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES "TARGET_PROCESSOR;CONFIG_TARGET;EW_ROOT_DIR")
# Set the EW installation root directory
#(Avoid spaces in the path or you need to escape them)
# This magic command will find iccarm.exe in the system PATH
find_program(ICCARM_EXE iccarm.exe)
# Trim the trailing /arm/bin/iccarm.exe from the above and make this EW_ROOT_DIR
string(REGEX REPLACE "/arm/bin/iccarm.exe$" "" EW_ROOT_DIR ${ICCARM_EXE})
# Fall back to the legacy location if find_program didn't find it
if(NOT EXISTS ${EW_ROOT_DIR})
    set(EW_ROOT_DIR "c:/ew/arm-805")
endif()

# Compiler flags needed to compile for this CPU
set(CPU_FLAGS "--cpu ${TARGET_PROCESSOR}")
# Set up the CMake variables for compiler and assembler
# (The reason for both C and CXX variables is that CMake
# treats C and C++ tools individually)
set(CMAKE_C_COMPILER   "${EW_ROOT_DIR}/arm/bin/iccarm.exe"  "${CPU_FLAGS} -e --dlib_config full")
set(CMAKE_CXX_COMPILER "${EW_ROOT_DIR}/arm/bin/iccarm.exe"  "${CPU_FLAGS} --dlib_config full")
set(CMAKE_ASM_COMPILER "${EW_ROOT_DIR}/arm/bin/iasmarm.exe" "${CPU_FLAGS} ")
### END CMAKE_TOOLCHAIN_FILE   

Some of this probably isn’t useful to you, but it’s a template that is working for me when we’re cross compiling using the IAR tools for ARM.