在编译时获取源文件的基本名称

时间:2008-10-26 04:15:35

标签: c++ c makefile macros compiler-construction

我正在使用GCC; __FILE__返回当前源文件的完整路径和名称:/path/to/file.cpp。有没有办法在编译时获取文件的名称file.cpp(没有它的路径)?是否可以以便携方式执行此操作?模板元编程可以应用于字符串吗?

我在错误记录宏中使用它。我真的不希望我的源代码完整路径进入可执行文件。

14 个答案:

答案 0 :(得分:12)

如果您正在使用make程序,您应该能够事先处理文件名并将其作为宏传递给gcc,以便在您的程序中使用。

在您的makefile中,更改以下行:

file.o: file.c
    gcc -c -o file.o src/file.c

为:

file.o: src/file.c
    gcc "-D__MYFILE__=\"`basename $<`\"" -c -o file.o src/file.c

这样,您就可以在代码中使用__MYFILE__而不是__FILE__

使用源文件的基名($&lt;)意味着您可以在通用规则中使用它,例如“.c.o”。

以下代码说明了它的工作原理。

文件makefile:

mainprog: main.o makefile
    gcc -o mainprog main.o

main.o: src/main.c makefile
    gcc "-D__MYFILE__=\"`basename $<`\"" -c -o main.o src/main.c

文件src / main.c:

#include <stdio.h>

int main (int argc, char *argv[]) {
    printf ("file = %s\n", __MYFILE__);
    return 0;
}

从shell运行:

pax@pax-desktop:~$ mainprog
file = main.c
pax@pax-desktop:~$

注意“file =”行,它只包含文件的基名,而不是dirname。

答案 1 :(得分:5)

考虑这个简单的源代码:

#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__是源文件的路径名。如果使用要在对象中看到的名称进行构建,一切都很顺利。

如果这是不可能的(无论出于何种原因),那么你将不得不进入其他人建议的修复。

答案 2 :(得分:4)

我不知道直接的方式。你可以使用:

#line 1 "filename.c"

在源文件的顶部设置__FILE__的值,但我不确定这比硬编码要好得多。或者只是使用#define来创建自己的宏。

另一种选择可能是使用-D和$(shell basename $&lt;)

从Makefile传递名称

编辑:如果您使用#define或-D选项,则应创建自己的新名称,而不是尝试重新定义__FILE__

答案 3 :(得分:4)

您的错误记录宏有什么作用?我想在某些时候宏最终会调用某种函数来进行日志记录,为什么不在运行时将被调用的函数条带从路径组件中删除?

#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
}

答案 4 :(得分:4)

由于你标记了CMake,这里有一个简洁的解决方案可以添加到你的CMakeLists.txt: (从http://www.cmake.org/pipermail/cmake/2011-December/048281.html复制)。 (注意:有些编译器不支持per-file 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}\"")

答案 5 :(得分:3)

您可以使用模板元编程来完成它,但没有内置的方法来实现它。

编辑:嗯,纠正。根据{{​​3}},GCC使用它为文件指定的路径。如果它被赋予全名,它将嵌入它;如果它只给了一个相对的,它只会嵌入它。我自己没试过。

答案 6 :(得分:2)

从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”。

答案 7 :(得分:1)

您可以将__FILE__分配给字符串,然后调用_splitpath()来从中删除它们。这可能是一个仅限Windows / MSVC的解决方案,说实话我不知道。

我知道您正在寻找一个编译时解决方案,这是一个运行时解决方案,但我认为,因为您使用文件名进行(可能是运行时)错误记录,这可能是一个简单的直接方式为你提供所需的东西。

答案 8 :(得分:0)

你可以将__FILE__和条带从你不想要的路径中删除(以编程方式)。如果basedir满足您的需求,那么很好。否则,从构建系统获取源目录根,其余的应该是可行的。

答案 9 :(得分:0)

只能以编程方式完成。

也许这很有用......

filename = __FILE__
len  = strlen(filename)

char *temp = &filename[len -1]
while(*temp!= filename)
    if(*temp == '\') break;

答案 10 :(得分:0)

刚刚遇到同样的问题;发现了一个不同的分辨率,只是想我会分享它:

在我的所有其他文件中包含的头文件中:

static char * file_bname = NULL;
#define __STRIPPED_FILE__   (file_bname ?: (file_bname = basename(__FILE__)))

希望这对其他人也有用:)

答案 11 :(得分:0)

使用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

因此,您的短毛绒不会出现很多错误。

答案 12 :(得分:0)

这个问题已经有18年了,早在2008年就没有此解决方案了,但是

从GCC 8和CLANG 10开始,可以使用-fmacro-prefix-map选项。
根据GCC手册:

-fmacro-prefix-map = old = new
预处理文件“旧” 中的文件时,请展开 __FILE____BASE_FILE__宏,就像文件驻留在其中一样 目录“新” 。这可以用来更改到的绝对路径 相对路径,对于 new 使用‘。’ 与位置无关的可复制构建。此选项也 在编译期间影响__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。

答案 13 :(得分:-4)

您可以使用:

bool IsDebugBuild()
{
    return !NDEBUG;
}

或者,您可以在宏中使用NDEBUG来打开/关闭这些文件路径。