在编译时获取源文件的基名称
Getting base name of the source file at compile time
我正在使用GCC;__FILE__返回当前源文件的整个路径和名称:/path/to/file.cpp
。有没有办法在编译时只获取文件名file.cpp
(没有路径)?是否可以以便携式方式执行此操作?模板元编程可以应用于字符串吗?
我在错误日志记录宏中使用它。我真的不希望我的源的完整路径进入可执行文件。
如果您使用的是make
程序,您应该能够事先修改文件名并将其作为宏传递给gcc
以在程序中使用的。例如,在makefile
中,更改行:
file.o: file.c
gcc -c -o file.o src/file.c
自:
file.o: src/file.c
gcc "-DMYFILE="`basename $<`"" -c -o file.o src/file.c
这将允许您在代码中使用MYFILE
而不是__FILE__
。
使用源文件$<
basename
意味着您可以在通用规则(如 .c.o
)中使用它。以下代码演示了它的工作原理。首先,一个makefile
:
mainprog: main.o makefile
gcc -o mainprog main.o
main.o: src/main.c makefile
gcc "-DMYFILE="`basename $<`"" -c -o main.o src/main.c
然后子目录中的文件src/main.c
:
#include <stdio.h>
int main (int argc, char *argv[]) {
printf ("file = %sn", MYFILE);
return 0;
}
最后,显示它正在运行的成绩单:
pax:~$ mainprog
file = main.c
请注意 file =
行,其中仅包含文件的基本名称,而不包含目录名称。
直接的方法。 您可以使用:
#line 1 "filename.c"
在源文件的顶部设置__FILE__
的值,但我不确定这是否比硬编码要好得多。 或者只是使用 #define 来创建自己的宏。
另一种选择可能是使用 -D 和 $(shell basename $<) 从 Makefile 传递名称
编辑:如果使用 #define 或 -D 选项,则应创建自己的新名称,而不是尝试重新定义__FILE__
。
既然你标记了CMake,这里有一个简洁的解决方案可以添加到你的CMakeList.txt:(摘自 http://www.cmake.org/pipermail/cmake/2011-December/048281.html)。(注意:有些编译器不支持每文件COMPILE_DEFINITIONS!但它适用于 gcc)
set(SRCS a/a.cpp b/b.cpp c/c.cpp d/d.cpp)
foreach(f IN LISTS SRCS)
get_filename_component(b ${f} NAME)
set_source_files_properties(${f} PROPERTIES
COMPILE_DEFINITIONS "MYSRCNAME=${b}")
endforeach()
add_executable(foo ${SRCS})
注意:对于我的应用程序,我需要像这样转义文件名字符串:
COMPILE_DEFINITIONS "MYSRCNAME="${b}"")
考虑这个简单的源代码:
#include <stdio.h>
int main(void)
{
puts(__FILE__);
return(0);
}
在 Solaris 上使用 GCC 4.3.1,如果我使用以下方法编译它:
gcc -o x x.c && ./x
输出是' x.c
' 如果我使用以下方法编译它:
gcc -o x $PWD/x.c && ./x
然后__FILE__映射到完整路径 (' /work1/jleffler/tmp/x.c
')。 如果我使用以下方法编译它:
gcc -o x ../tmp/x.c && ./x
然后__FILE__映射到" ../tmp/x.c
"。
因此,基本上,__FILE__是源文件的路径名。 如果您使用希望在对象中看到的名称进行构建,则一切都很好。
如果这是不可能的(无论出于何种原因),那么您将不得不进入其他人建议的修复程序。
您的错误日志记录宏有什么作用?我会假设在某个时候宏最终会调用某种函数以进行日志记录,为什么不在运行时让调用的函数从路径组件中删除呢?
#define LOG(message) _log(__FILE__, message)
void _log(file, message)
{
#ifndef DEBUG
strippath(file); // in some suitable way
#endif
cerr << "Log: " << file << ": " << message; // or whatever
}
你也许可以使用模板元编程来做到这一点,但没有内置的方法可以做到这一点。
编辑:嗯,更正。根据我刚刚看到的一页,GCC 使用为文件提供的路径。如果赋予它全名,它将嵌入它;如果只给它一个相对的,它只会嵌入它。不过我自己还没有尝试过。
这个问题已经有 12 年的历史了,早在 2008 年,这个解决方案就不可用了,但是
从 GCC 8 和 CLANG 10 开始,可以使用选项 -fmacro-prefix-map.
根据海湾合作委员会手册:
-fmacro-prefix-map=old=new
预处理驻留在目录"old"中的文件时,展开__FILE__
和__BASE_FILE__
宏,就好像文件驻留在目录"新"。这可用于将绝对路径更改为使用"."表示新的相对路径,这可能会导致更多与位置无关的可重现构建。此选项还在编译过程中影响__builtin_FILE()
。参见'-ffile-prefix-map'.
例如,我的IDE(Eclipse)中的makefile包含某些文件的GCC的以下参数:-fmacro-prefix-map="../Sources/"=.
因此,我的调试日志始终只显示文件名,而不显示路径。
注意:GCC 8.1 和 Clang 10 分别于 2018 年 5 月和 2020 年 3 月发布。因此,目前,在 2020 年 9 月,只有我的一些环境支持 -fmacro-prefix-map。
从 Glomek 那里得到这个想法,它可以像这样自动化:
源文件 x.c
#line 1 MY_FILE_NAME
#include <stdio.h>
int main(void)
{
puts(__FILE__);
return(0);
}
编译行(注意双引号外的单引号):
gcc -DMY_FILE_NAME='"abcd.c"' -o x x.c
输出为"abcd.c
"。
使用 cmake 很容易。
DefineRelativeFilePaths.cmake
function (cmake_define_relative_file_paths SOURCES)
foreach (SOURCE IN LISTS SOURCES)
file (
RELATIVE_PATH RELATIVE_SOURCE_PATH
${PROJECT_SOURCE_DIR} ${SOURCE}
)
set_source_files_properties (
${SOURCE} PROPERTIES
COMPILE_DEFINITIONS __RELATIVE_FILE_PATH__="${RELATIVE_SOURCE_PATH}"
)
endforeach ()
endfunction ()
在CMakeLists.txt
的某个地方
set (SOURCES ${SOURCES}
"${CMAKE_CURRENT_SOURCE_DIR}/common.c"
"${CMAKE_CURRENT_SOURCE_DIR}/main.c"
)
include (DefineRelativeFilePaths)
cmake_define_relative_file_paths ("${SOURCES}")
cmake .. && make clean && make VERBOSE=1
cc ... -D__RELATIVE_FILE_PATH__="src/main.c" ... -c src/main.c
就是这样。现在,您可以制作漂亮的日志消息。
#define ..._LOG_HEADER(target)
fprintf(target, "%s %s:%u - ", __func__, __RELATIVE_FILE_PATH__, __LINE__);
func src/main.c:22 - 我的错误
PS最好在config.h.in
中清除-> config.h
#ifndef __RELATIVE_FILE_PATH__
#define __RELATIVE_FILE_PATH__ __FILE__
#endif
所以你的棉绒不会提供错误的雨。
您可以将__FILE__
分配给字符串,然后调用 _splitpath() 从中剥离片段。 这可能是一个仅限Windows/MSVC的解决方案,老实说我不知道。
我知道您正在寻找编译时解决方案,这是一个运行时解决方案,但我认为既然您使用文件名进行(大概是运行时)错误日志记录,这可能是一种简单直接的方法,可以满足您的需求。
你可以__FILE__和你不想要的路径部分剥离(以编程方式)。如果 basedir 满足您的需求,那就好了。否则,从构建系统中获取源目录根,其余的应该是可行的。
刚刚遇到了同样的问题; 找到不同的解决方案,只是想我会分享它:
在我的所有其他文件中包含的头文件中:
static char * file_bname = NULL;
#define __STRIPPED_FILE__ (file_bname ?: (file_bname = basename(__FILE__)))
希望这对其他人也有用:)
- 生成一个生成文件,该生成文件使用Automake在一个步骤中编译和链接所有源文件
- make 命令如何避免重新编译未更改的源文件?
- 在 Visual Studio 中的 1 个项目中编译单个 C++ 源文件
- 如何在Visual Studio 2019中有条件地编译c++源文件
- 从源文件编译 QOpenGLshader 时出错
- C++ SD 总线源文件编译问题
- C :使用标题和源文件,源文件不编译
- 源文件未使用最新版本的Dev C 和OpenCV 3.x版本编译
- C++ 编译时检查微控制器引脚是否已从其他源文件初始化
- 如果我的源文件中使用常数,则如何使编译很好地停止
- 如何指示emscripten在编译过程中应在哪里找到源文件
- 在RCPP中编译多个源文件
- 开发DEV C 源文件未编译
- 编译目录中的所有源文件,并将 objs 存储在不同的目录中
- Makefile 将在文件更改时链接文件,但不会重新编译更改的源文件
- 如何让 CMake 编译包含 Boost Local Functions 的源文件
- 编译几个源文件(主文件和头文件)并在ROOT CINT中链接它们
- QtCreator - 编译问题 - 外部源文件
- 如何更改从多个源文件编译的g++生成的Linux共享库的入口点
- 用g++将多个源文件编译成一个.o