Imbue
Imbue

Reputation: 3907

Getting base name of the source file at compile time

I'm using GCC; __FILE__ returns the current source file's entire path and name: /path/to/file.cpp. Is there a way to get just the file's name file.cpp (without its path) at compile time? Is it possible to do this in a portable way? Can template meta programming be applied to strings?

I am using this in an error logging macro. I really do not want my source's full path making its way into the executable.

Upvotes: 18

Views: 11675

Answers (12)

The question is already 12 years old and back in 2008 this solution wasn't available, but

Starting with GCC 8 and CLANG 10, one can use the option -fmacro-prefix-map.
Acording to GCC Manual:

-fmacro-prefix-map=old=new
When preprocessing files residing in directory ‘old’, expand the __FILE__ and __BASE_FILE__ macros as if the files resided in directory ‘new’ instead. This can be used to change an absolute path to a relative path by using ‘.’ for new which can result in more reproducible builds that are location independent. This option also affects __builtin_FILE() during compilation. See also ‘-ffile-prefix-map’.

For instance, the makefile in my IDE (Eclipse) includes the following parameter for GCC for some files: -fmacro-prefix-map="../Sources/"=.
Thus, my debug logs always show only the filenames, without the paths.

Note: GCC 8.1 and Clang 10 were released in May 2018 and March 2020, respectively. So, currently, in September of 2020, only some of my environments support -fmacro-prefix-map.

Upvotes: 3

paxdiablo
paxdiablo

Reputation: 881093

If you're using a make program, you should be able to munge the filename beforehand and pass it as a macro to gcc to be used in your program. For example, in your makefile, change the line:

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

to:

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

This will allow you to use MYFILE in your code instead of __FILE__.

The use of basename of the source file $< means you can use it in generalized rules such as .c.o. The following code illustrates how it works. First, a 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

Then a file in a subdirectory, src/main.c:

#include <stdio.h>

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

Finally, a transcript showing it running:

pax:~$ mainprog
file = main.c

Note the file = line which contains only the base name of the file, not the directory name as well.

Upvotes: 14

puchu
puchu

Reputation: 3652

It is easy with 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 ()

Somewhere in 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

That's it. Now you can make pretty log messages.

#define ..._LOG_HEADER(target) \
  fprintf(target, "%s %s:%u - ", __func__, __RELATIVE_FILE_PATH__, __LINE__);

func src/main.c:22 - my error

PS It is better to declear in config.h.in -> config.h

#ifndef __RELATIVE_FILE_PATH__
#define __RELATIVE_FILE_PATH__ __FILE__
#endif

So your linter wan't provide rain of errors.

Upvotes: 2

fenugrec
fenugrec

Reputation: 61

Since you tagged CMake, here's a neat solution to add to your CMakeLists.txt: (copied from http://www.cmake.org/pipermail/cmake/2011-December/048281.html ). (Note : some compilers don't support per-file COMPILE_DEFINITIONS ! but it works with 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})

Note : For my application I needed to escape the filename string like this:

COMPILE_DEFINITIONS "MYSRCNAME=\"${b}\"")

Upvotes: 6

tHeDoc
tHeDoc

Reputation: 21

Just got the same issue; found a different resolution, just thought I'd share it:

In a header file included in all my other files:

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

Hope this is useful to someone else as well :)

Upvotes: 0

John Dibling
John Dibling

Reputation: 101446

You can assign __FILE__ to a string, and then call _splitpath() to rip the pieces out of it. This might be a Windows/MSVC-only solution, honestly I don't know.

I know you were looking for a compile-time solution and this is a run-time solution, but I figured since you were using the filename to do (presumably run-time) error logging, this could be a simple straightforward way to get you what you need.

Upvotes: 1

Jonathan Leffler
Jonathan Leffler

Reputation: 753455

Taking the idea from Glomek, it can be automated like this:

Source file x.c

#line 1 MY_FILE_NAME
#include <stdio.h>

int main(void)
{
    puts(__FILE__);
    return(0);
}

Compilation line (beware the single quotes outside the double quotes):

gcc -DMY_FILE_NAME='"abcd.c"' -o x x.c

The output is 'abcd.c'.

Upvotes: 2

Jonathan Leffler
Jonathan Leffler

Reputation: 753455

Consider this simple source code:

#include <stdio.h>
int main(void)
{
    puts(__FILE__);
    return(0);
}

On Solaris, with GCC 4.3.1, if I compile this using:

gcc -o x x.c && ./x

the output is 'x.c' If I compile it using:

gcc -o x $PWD/x.c && ./x

then __FILE__ maps to the full path ('/work1/jleffler/tmp/x.c'). If I compile it using:

gcc -o x ../tmp/x.c && ./x

then __FILE__ maps to '../tmp/x.c'.

So, basically, __FILE__ is the pathname of the source file. If you build with the name you want to see in the object, all is well.

If that is impossible (for whatever reason), then you will have to get into the fixes suggested by other people.

Upvotes: 5

Paweł Hajdan
Paweł Hajdan

Reputation: 18542

You can take __FILE__ and the strip off the part of path you don't want (programatically). If basedir satisfies your needs, then fine. Otherwise, get source dir root from your build system, and the rest should be doable.

Upvotes: 0

1800 INFORMATION
1800 INFORMATION

Reputation: 135245

What does your error logging macro do? I would presume at some point the macro eventually calls a function of some kind in order to do the logging, why not have the called function strip off the path component at runtime?

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

Upvotes: 4

Andru Luvisi
Andru Luvisi

Reputation: 25308

I don't know of a direct way. You could use:

#line 1 "filename.c"

at the top of the source file to set the value of __FILE__, but I'm not sure that that's much better than hard coding it. or just using a #define to create your own macro.

Another option might be to pass the name from your Makefile using -D and $(shell basename $<)

Edit: If you use a #define or the -D option, you should create your own new name and not try to redefine __FILE__.

Upvotes: 6

Head Geek
Head Geek

Reputation: 39838

You might be able to do it with template metaprogramming, but there's no built-in way to do it.

EDIT: Hm, correction. According to one page I just saw, GCC uses the path that it's given for the file. If it's given the full name, it'll embed it; if it's only given a relative one, it'll only embed that. I haven't tried it myself though.

Upvotes: 3

Related Questions