As I mentioned in a comment already, you can create a CMake function that loops over all source files of a given target and adds a macro definition to the compile flags for that source file.
function(add_filepath_macro target)
get_target_property(SOURCE_FILES ${target} SOURCES)
foreach (FILE_PATH IN LISTS SOURCE_FILES)
file(RELATIVE_PATH RELATIVE_FILE_PATH ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH})
set_property(SOURCE ${FILE_PATH} APPEND PROPERTY COMPILE_DEFINITIONS FILEPATH="${RELATIVE_FILE_PATH}")
endforeach()
endfunction()
This now defines the macro FILEPATH
in every TU that is part of the target you pass to it.
There is one caveat though: In contrast to the regular __FILE__
macro you can't use the FILEPATH
macro in header files, because including one such header in different translation units immediately causes an ODR violation and thus undefined behaviour.
Demo
Let's see how this works for a very small example. Start with the following project structure:
.
├── cmake
│ └── add_filepath_macro.cmake
├── CMakeLists.txt
├── lib
│ ├── a.cpp
│ ├── a.h
│ └── CMakeLists.txt
└── main.cpp
The cmake/add_filepath_macro.cmake
file contains nothing but the code shown above.
The we have the library files
// lib/a.h
#pragma once
char const* foo();
// lib/a.cpp
#include "a.h"
char const* foo() {
return FILEPATH;
}
with corresponding CMakeLists.txt
# lib/CMakeLists.txt
add_library(liba
a.cpp
)
target_include_directories(liba PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_filepath_macro(liba)
as well as a small demo executable
// main.cpp
#include <iostream>
#include "a.h"
int main() {
std::cout << foo() << '\n' << FILEPATH << '\n';
}
and finally the project CMakeLists.txt
# CMakeLists.txt
# not tested with older version, but I guess it should work with anything > 3.0
cmake_minimum_required(VERSION 3.9)
project(cmake_filepath_macro_demo LANGUAGE CXX)
include(cmake/add_filepath_macro.cmake)
add_subdirectory(lib)
add_executable(demo
main.cpp
)
add_filepath_macro(demo)
Running the resulting executable then produces the following output
$ ./demo
lib/a.cpp
main.cpp