Make gcc put relative filenames in debug information - debugging

The project I'm compiling uses CMake, which loves absolute pathnames.
When I compile with debugging information enabled, gcc puts those long names into .debug_str sections, which is bad for debugging. I'd like to have short relative-to-project-root pathnames there instead.
Is there some option to tell gcc to strip some part of pathname before emitting debug data? Or, maybe, there is some tool that could do that on compiled binaries?
I've tried using SET(CMAKE_USE_RELATIVE_PATHS ON) (which seems to be frowned upon by devs) option, but as I'm using out-of-source builds, pathnames are still not in the form I'd want them to be. I.e. they're ./../src/mod_foo/foo.c instead of mod_foo/foo.c.

You can use the -fdebug-prefix-map flag to remap the debugging information paths. For example, to make the paths relative to the build location use: -fdebug-prefix-map=/full/build/path=.

You can set the RULE_LAUNCH_COMPILE property of a CMake target to have CMake invoke a shell script which transforms the source file path to a project relative path before invoking gcc. Use the CMake function configure_file to generate a shell script which knows about the PROJECT_SOURCE_DIR and PROJECT_BINARY_DIR of your project.
In your outermost CMakeLists.txt add the following code:
configure_file(
"${PROJECT_SOURCE_DIR}/gcc_debug_fix.sh.in"
"${PROJECT_BINARY_DIR}/gcc_debug_fix.sh"
#ONLY)
add_executable (MyExecutable ...)
set_target_properties(MyExecutable PROPERTIES
RULE_LAUNCH_COMPILE "${PROJECT_BINARY_DIR}/gcc_debug_fix.sh")
The following template shell script gcc_debug_fix.sh.in needs to go to the root directory of the CMake project:
#!/bin/sh
PROJECT_BINARY_DIR="#PROJECT_BINARY_DIR#"
PROJECT_SOURCE_DIR="#PROJECT_SOURCE_DIR#"
# shell script invoked with the following arguments
# $(CXX) $(CXX_DEFINES) $(CXX_FLAGS) -o OBJECT_FILE -c SOURCE_FILE
# extract parameters
SOURCE_FILE="${#: -1:1}"
OBJECT_FILE="${#: -3:1}"
COMPILER_AND_FLAGS=${#:1:$#-4}
# make source file path relative to project source dir
SOURCE_FILE_RELATIVE="${SOURCE_FILE:${#PROJECT_SOURCE_DIR} + 1}"
# make object file path absolute
OBJECT_FILE_ABSOLUTE="$PROJECT_BINARY_DIR/$OBJECT_FILE"
cd "$PROJECT_SOURCE_DIR"
# invoke compiler
exec $COMPILER_AND_FLAGS -c "${SOURCE_FILE_RELATIVE}" -o "${OBJECT_FILE_ABSOLUTE}"
The shell script uses the information from the variables PROJECT_BINARY_DIR and PROJECT_SOURCE_DIR to transform the path of the source file to a path relative to the project root and the object file's path to an absolute path. Because gcc gets passed a project relative path now, .debug_str should use that path, too.
The following caveats apply:
Be sure to set the executable bit of gcc_debug_fix.sh.in.
For the script to work CMAKE_USE_RELATIVE_PATHS has to set to OFF again.
The script makes assumptions about the location of the file paths on the command line. This may not work if CMake uses a different rule to invoke the compiler. A more robust solution would be to scan the script arguments for the -o and -c flags.

If I really couldn't fix the make file/tool to do this properly, I would write a wrapper script for gcc that recognises absolute pathnames and converts then to relative ones.
It might look something like this in bash:
#!/bin/bash
out=()
for arg; do
out=("${out[#]}" $(echo "$arg" | sed 's:/my/absolute/directory/:../:'))
done
exec gcc "${out[#]}"
If your source directory has subdirectories then you'll need to handle those carefully, but the above should work for a flat source directory. I've not tested it though, and I wouldn't be surprised if I've got the quoting wrong, but that'll only be a problem if you have pathnames with spaces in. It also doesn't handle parameters like -I/whatever/include, but you can fix that.

Related

cmake 'add_custom_command' to pre-process header files?

i'm working on a project requiring cmake. i'd like to add some custom rules to my makefile, but can't quite get my head around how to do it.
both c source files and header files are in the same directory. also in this same directory are a number of .def files, which are the sources for some of the header files #included in the source during compilation.
if i were to do this in a makefile, i'd use a simple rule like
.SUFFIXES: .def
.def.h:
$(PREPROC) $< > $#
how can i do this with cmake ??
i've tried various permutations of the following, both with and without cmake working directory specifications :
add_custom_command(
OUTPUT vvr_const.h
PRE_BUILD
COMMAND preproc vvr_const.def > vvr_const.h
DEPENDS vvr_const.def
)
add_custom_target(vvr_const.h DEPENDS vvr_const.def)
but the header file isn't generated by the time the c source file is compiled, so the compile fails. i've also tried a variation where i replace the last line above with
set_property(SOURCE main.c APPEND PROPERTY OBJECT_DEPENDS vvr_const.h)
in this case, the header file is correctly generated in advance, but make can't find it, and complains that there's no rule to make the target .h.
ideally this would be a general rule, like the make rule above, but i'm not opposed to making a separate rule for each of the .def files if that's what it takes.
cheers.
There are 2 problems with the add_custom_command approach you present:
You did not specify a working directory; by default the command is run in the build directory, not in the source directory.
You rely on shell functionality here (the redirect to a file). Even though this probably still works. You should go with an approach that does not rely on the shell.
To solve issues 1 and 2 I recommend creating a seperate cmake script file receiving the absolute paths to input and output files and using those in the custom command. This allows you to use execute_process to specify the file to write without relying on the platform.
preprocess_def.cmake
# preprocess def file
# parameters INPUT_FILE and OUTPUT_FILE denote the file to use as source
# and the file to write the results to respectively
# use preproc tool to get data to write to the output file
execute_process(COMMAND preproc "${INPUT_FILE}"
RESULT_VARIABLE _EXIT_CODE
OUTPUT_FILE "${OUTPUT_FILE}")
if (_EXIT_CODE)
message(FATAL_ERROR "An error occured when preprocessing the file ${INPUT_FILE}")
endif()
CMakeLists.txt
set(_INPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/vvr_const.def")
set(_OUTPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/vvr_const.h")
# not necessary to use build event here, if we mark the output file as generated
add_custom_command(OUTPUT "${_OUTPUT_FILE}"
COMMAND "${CMAKE_BUILD_TOOL}" -D "OUPUT_FILE=${_OUTPUT_FILE}" -D "INPUT_FILE=${_INPUT_FILE}" -P "${CMAKE_CURRENT_SOURCE_DIR}/preprocess_def.cmake"
DEPENDS "${_INPUT_FILE}")
add_executable(my_target vvr_const.h ...)
set_source_files_properties(vvr_const.h PROPERTIES GENERATED 1)
Documentation from cmake:
PRE_BUILD
On Visual Studio Generators, run before any other rules are executed within the target. On other generators, run just before PRE_LINK commands.
So possibly your command is just running too late.

Cmake doesn't recognize custom command as valid source

I have a problem modifying existing CMake based build-system. All I want to do is add different build rule for some of .c files. For the sake of this problem, I will focus on only one file.
The simplified directories tree looks like this
Project:
./src
- file_of_interest.c
- CmakeFiles.txt
other_files.c
CmakeFiles.txt
So in order to compile file_of_interest.c in a different way I do:
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/file_of_interest.s
COMMAND xt-xcc -S ${CMAKE_CURRENT_SOURCE_DIR}/file_of_interest.c
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/file.c
COMMENT "Generating file_of_interest.s"
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/file_of_interest.c.o
COMMAND xt-xcc ${CMAKE_CURRENT_BINARY_DIR}/file_of_interest.s -o file_of_interest.c.o -c
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/file_of_interest.s
COMMENT "Generating file_of_interest.c.o"
)
message(" ${CMAKE_CURRENT_BINARY_DIR} \n ${CMAKE_CURRENT_SOURCE_DIR}")
target_sources(target_name PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/file_of_interest.c.o)
As you can see I used CMake's message() to print paths to be sure all is set up properly. It should work, but it doesn't! I expect CMake to register file_of_interest.c.o as source of target_name (and this is probably working), then "tie" it with my custom command which produces OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/file_of_interest.c.o and then to tie again ${CMAKE_CURRENT_BINARY_DIR}/file_of_interest.c.o with OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/file_of_interest.s and this certainly doesn't happen as CMake shows error message saying CMake Error at CMakeLists.txt:170 (add_executable): Cannot find source file: path/file_of_interest.c.o
The path is OK. So the question is why does CMake cannot recognize recipes for that file?
It seems that you create an executable (call add_executable) in the top-level CMakeLists.txt, but add_custom_command are called from the subdirectory (src/CMakeLists.txt).
This doesn't work: when CMake processes add_executable and searches for the commands generating its sources, it sees only add_custom_commands created in the same CMakeLists.txt.
From the add_custom_command documentation:
A target created in the same directory (CMakeLists.txt file) that specifies any output of the custom command as a source file is given a rule to generate the file using the command at build time.
It is unrelated that target_sources is called from the same directory as add_custom_command: the target is created by add_executable command, and target_sources just modifies its properties.

How to recursively substitute variable for a path in bash variable in configure.ac?

I am dealing with autotools and here is the situation:
By default libdir is set to '${exec_prefix}/lib' with exec_prefix set to '${prefix}' and prefix set to '/usr/local'.
Key here recursion and at first level libdir variable contain another variable and a following path.
So how to convert ${libdir} into a variable containing '/usr/local/lib' ?
Info : all 3 (libdir, exec_prefix, prefix) can change according configuration.
Consider the following files:
configure.ac
AC_PREREQ([2.59])
AC_INIT([test], [0.0.0] )
AM_INIT_AUTOMAKE()
AC_CONFIG_SRCDIR([test.cpp])
AC_LANG([C++])
AC_PROG_CXXCPP
AC_PROG_CXX
AC_CONFIG_FILES([Makefile path.conf])
AC_MSG_NOTICE([">>> Before ac_output prefix=${prefix}"])
AC_OUTPUT
AC_MSG_NOTICE([">>> after ac_output prefix=${prefix}"])
Makefile.am
bin_PROGRAMS = test
test_SOURCES = test.cpp
test.cpp
int main()
{}
path.conf.in
#libdir#
Then after invoking :
aclocal && autoconf && automake -a --foreign && ./configure
configure log show:
configure: ">>> Before ac_output prefix=NONE"
...
...
...
configure: ">>> after ac_output prefix=/usr/local"
And generated file path.conf contains
${exec_prefix}/lib
The goal is to have a variable containing the expanded version of the path to be used in a path.conf.in so autotools generate path.conf with that expanded path.
Edit: Bash only solution
Digging related topics and helped by #Aserre answer, I manage to do the following with a regex.
while expr match "${libdir}" '^.*\$.*$' 1>/dev/null;
do
echo ">${libdir}"
libdir="$(eval echo ${libdir})"
done
Which means : While $libdir contain one $ expand with eval.
But does not work in configure.ac script before AC_OUTPUT
The goal is to have a variable containing the expanded version of the path to be used in a path.conf.in so autotools generate path.conf with that expanded path.
The Autotools provide special handling for default values of the installation-directory variables, in order to enable the user to specify or override the installation prefix at make install time:
make install prefix=/my/special/prefix
What you propose to do will break that. If a user specifies a different installation prefix at the installation stage than they tell configure (or that they let configure choose by default) then you will end up with a broken installation.
The best way to do address problems like this is to build the configuration file under make's control, at make install time, instead of doing it at configuration time. If the project uses Automake, then that might mean something like this:
install-data-local:
$(SED) -e 's,[#]libdir[#],$(libdir),' path.conf.in > $(sysconfdir)/path.conf
chmod 0644 $(sysconfdir)/path.conf
chown root:root $(sysconfdir)/path.conf
uninstall-local:
rm $(sysconfdir)/path.conf
You can of course substitute more output variables than that if you like. It's pretty close to what configure does itself.
And of course, if you do it this way then you do not need to worry about performing extra expansions.
If you are 100% sure of the content of the ${exec_prefix} variable, you could use the following line to achieve what you want :
libdir="$(eval echo ${exec_prefix})"
Note that in a lot of cases, the use of eval is discouraged. Here, if the user has overriden the content of the variable exec_prefix, for instance with exec_prefix='a; rm -rf /', all the code written will be executed.
If you are in total control of your environment (i.e. you are certain of the value of the variables when you launch the script), there should be no problem, otherwise be wary of the potential side effects

How to get a relative path to a target with CMake?

I have a project that uses CMake to generate build scripts, and each platform puts the executable that are generated in a different place. We have a non-programmer on our team who does not have build tools and I want to be able to bundle up all the files, scripts and executables necessary to run the project so that he can run the project.
To do that, I've added a custom target that takes all the necessary files and zips them up. It also generates a very simple script (which is included in the zip file) that he can just click on that should run a script then launch the executable, like so:
add_custom_target(runcommand
COMMAND echo '\#!/bin/bash -eu' > run.command &&
echo 'cd `dirname $$0`' >> run.command &&
echo './scripts/prerun_script.sh && ${MY_EXECUTABLE}' >> run.command &&
chmod +x run.command)
The problem with this is that MY_EXECUTABLE is a hardcoded path to the executable on my system. I would like it to be a relative path so that I can just take this resultant zip file, unzip it anywhere and run it from there. What I would like is to get the path to MY_EXECUTABLE relative to the root directory of my project so that this script can be run from anywhere.
You can use:
file(RELATIVE_PATH variable directory file)
Concretely, assume that MY_ROOT contains the root directory (there might be a suitable predefined CMake variable for this), this will set rel to the relative path of MY_EXECUTABLE:
file(RELATIVE_PATH rel ${MY_ROOT} ${MY_EXECUTABLE})
In case where you want to get path relative to project root, you may use PROJECT_SOURCE_DIR CMake variable:
file(RELATIVE_PATH rel ${PROJECT_SOURCE_DIR} ${MY_EXECUTABLE})
Remember, that MY_EXECUTABLE must contain full path to file.
get the relative path to the current source directory
file(RELATIVE_PATH relative ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
Test:
message(STATUS "source base: \"${CMAKE_SOURCE_DIR}\"")
message(STATUS "current: \"${CMAKE_CURRENT_SOURCE_DIR}\"")
message(STATUS "relative: \"${relative}\"")
Result:
--source base: "C:/Project/Software/Test/source"
--current: "C:/Project/Software/Test/source/unittest/libs/fsm
--relative: "unittest/libs/fsm"
Something like this would work:
THISDIR=`pwd`
MY_EXECUTABLE=$THISDIR/test/someexec
REL_EXECUTABLE=${MY_EXECUTABLE##$THISDIR/}
echo $REL_EXECUTABLE
Basically, this chops off the base path from $MY_EXECUTABLE. In this example, I just set up MY_EXECUTABLE as a test to give it a full path. The one-line equivalent for what you want is:
${MY_EXECUTABLE##`pwd`/}
I guess this assumes pwd is the root of your project. Otherwise, see the Cmake variable doc.
(As an aside, this makes me nostalgic for the ksh days.)

Makefile -L command

If I have this line in the make file:\
libpqxx_Libs = -L/share/home/cb -lpqxx-2.6.9 -lpq
Does this indicate the compiler to use the lpqxx-2.6.9.so shared object file or does this indciate the compiler to use all the .so in the foler lpqxx-2.6.9? Or is this something else altogether?
Thanks for the help!
-L in this context is an argument to the linker, that adds the specified directory to the list of directories that the linker will search for necessary libraries, e.g. libraries that you've specified using -l.
It isn't a makefile command, even though it's usually seen in makefiles for C projects.
The -L is actually not a makefile command (as you state it in the title of your question).
What actually happens in this line is an assignment of a value to the variable libpqxx_Libs -- nothing more and nothing less. You will have to search in your makefile where that variable is used via $(libpqxx_Libs) or ${libpqxx_Libs}. That is most likely as a argument in a link command, or a compile command that includes linking.
In that context, the meaning of -L and -l can be found in, for example, the gcc man pages, which state that
-llibrary
Use the library named library when linking.
The linker searches a standard list of directories for the li-
brary, which is actually a file named `liblibrary.a'. The linker
then uses this file as if it had been specified precisely by
name.
The directories searched include several standard system direc-
tories plus any that you specify with `-L'.

Resources