正如我已经在评论中提到的,您可以创建一个 CMake 函数,该函数循环遍历给定目标的所有源文件,并将宏定义添加到该源文件的编译标志中。
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()
这现在在作为您传递给它的目标的一部分的每个 TU 中定义宏 FILEPATH。
但有一个警告:与常规的 __FILE__ 宏相比,您不能在头文件中使用 FILEPATH 宏,因为在不同的翻译单元中包含一个这样的头会立即导致违反 ODR,因此行为未定义。
演示
让我们通过一个非常小的示例来看看它是如何工作的。从以下项目结构开始:
.
├── cmake
│ └── add_filepath_macro.cmake
├── CMakeLists.txt
├── lib
│ ├── a.cpp
│ ├── a.h
│ └── CMakeLists.txt
└── main.cpp
cmake/add_filepath_macro.cmake 文件只包含上面显示的代码。
我们有库文件
// lib/a.h
#pragma once
char const* foo();
// lib/a.cpp
#include "a.h"
char const* foo() {
return FILEPATH;
}
与对应的CMakeLists.txt
# lib/CMakeLists.txt
add_library(liba
a.cpp
)
target_include_directories(liba PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_filepath_macro(liba)
以及一个小的演示可执行文件
// main.cpp
#include <iostream>
#include "a.h"
int main() {
std::cout << foo() << '\n' << FILEPATH << '\n';
}
最后是项目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)
运行生成的可执行文件然后产生以下输出
$ ./demo
lib/a.cpp
main.cpp