Base filename gets set to compiler_depend.ts after update to CMake 3.20

One project I work on used to add a __FILENAME__ macro definition when the generator used was GNU Make, as follows:

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__FILENAME__='\"$(notdir $(abspath $<))\"'")

This is similar to how one would do it directly in a Makefile by setting CFLAGS or CXXFLAGS to -D__FILENAME__='"$(notdir $(abspath $<))"'.

This made __FILENAME__ similar to __FILE__, but kept only the base file name, so that code like this:

    // this is in src/main.c
    printf("__FILE__ %s\n", __FILE__);
    printf("__FILENAME__ %s\n", __FILENAME__);

will output:

__FILE__ /path/to/project/src/main.c
__FILENAME__ main.c

This worked with older versions of CMake: 3.12, 3.16, and 3.18. After an update to 3.20 the second line becomes __FILENAME__ compiler_depend.ts. This happens even if cmake_minimum_required is set to 3.16 when using 3.20.

compiler_depends.ts is a file generated by CMake and as far as I’m aware it wasn’t previously generated.

Here’s a minimal example:

cmake_minimum_required(VERSION 3.16)

project(example)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__FILENAME__='\"$(notdir $(abspath $<))\"'")

add_executable(exe src/main.c)

Looking at the files generated, compiler_depends.ts appears in a single place: in CMakeFiles/exe.dir/build.make:

# Include the compile flags for this target's objects.
include CMakeFiles/exe.dir/flags.make

CMakeFiles/exe.dir/src/main.c.o: CMakeFiles/exe.dir/flags.make
CMakeFiles/exe.dir/src/main.c.o: ../src/main.c
CMakeFiles/exe.dir/src/main.c.o: CMakeFiles/exe.dir/compiler_depend.ts

I first suspected that flags.cmake looks different, but it doesn’t.

I know that messing with CMAKE_C_FLAGS like this is not necessarily portable, or good-style, but is this a bug in CMake and how the build files are generated?

EDIT: After thinking a bit more about this, just because something works with Make it doesn’t mean that it should work when using CMake to generate makefiles. Even with Make, you still end up with broken builds/unexpected behaviors if you append to CFLAGS after doing this. I’m only thinking that this could be considered a bug because it breaks something that used to work with older versions, even if cmake_minimum_required asks for an older version. At the same time I don’t see how a policy could be introduce to keep this behavior, as it is something really fragile that can break at any time due to someone appending something to CMAKE_C_FLAGS.

I think this is probably related to some of the Makefile stuff that changed in 3.20. Alas, stuffing make-specific syntax into flags is not, AFAIK, strictly supported. That notdir expression is certainly…busted with the Ninja generator for instance. I’m actually kind of interested that you got the $ through the makefile escaper more than anything else :slight_smile: (though, alas, not quite surprised).

Cc: @craig.scott @marc.chevrier

@brad.king Is this something within our compatibility guarantee? I would suspect not because nothing ever guaranteed how CMake structures the rules it uses to execute the compilation line (e.g., CMake could have written a shell script and executed it which leaves you with no Makefile magic at all).

1 Like

That notdir expression is certainly…busted with the Ninja generator for instance.

We only got away with it so long because we do this only when using the Make generator and we only use that on our internal builds, so it was never a problem. When using other generators we got full paths for __FILENAME__, but only a few people did that for local builds so no one complained. The entire CMakeLists.txt for that project is a bit messy in my opinion, we just never got around to cleaning it up.

I’m actually kind of interested that you got the $ through the makefile escaper more than anything else :slight_smile: (though, alas, not quite surprised).

I was initially thinking that the flags are differently parsed now, but looking at flags.cmake C_FLAGS is the same with 3.16 and 3.20: C_FLAGS = -D__FILENAME__='"$(notdir $(abspath $<))"'

It would be interesting to see if CMake could provide a way of solving the underlying problem (get the base name of a file at compile time) in a way that does not force users to depend on specific generator or compiler behavior. This SO answer seems to do the trick.

The problem comes from the usage of $<. This Make variable returns the first prerequisite of the target. The behavior you expect relies on the layout of the makefile generated by CMake. And this layout has changed between 3.19 and 3.20.
This is definitively a very bad approach to rely on the makefile layout! If you can’t avoid to use your __FILENAME__ macro, I recommend you to use Make variable $@.

The behavior you expect relies on the layout of the makefile generated by CMake . And this layout has changed between 3.19 and 3.20 .

And there are no guarantees made about that layout, right?

This is definitively a very bad approach to rely on the makefile layout! If you can’t avoid to use your __FILENAME__ macro, I recommend you to use Make variable $@ .

Using $@ works. I think I’ll just remove the make-specific stuff. My guess is that it is an artifact of a transition from make to CMake.

Exactly. makefiles content format is private and internal to CMake.

1 Like

This issue seems to be getting a number of use cases lately.

Hello, i have cmake version 3.18.4
I have same issue as discussed here but $@ didn’t work for me. Am i using it wrongly?
set(CMAKE_C_FLAGS_RELEASE “${CMAKE_C_FLAGS_RELEASE} -D__FILENAME__=’”$(notdir $(abspath $@))"’")

Adding image to describe issue:
Screenshot_20210719_210226

That won’t work for non-Makefiles generators for sure. CMake probably tries hard to make sure it goes through properly quoted for even the Makefile generators though.