Makefile deferred compilation - c++

I have a simple makefile with a variable for the compiler flags, it also contains some targets that modify that variable and append some flags.
My point with it is to be able to run, for example:
make debug perf
that would in turn add to the variable the flags required to build under that configuration. The problem is that if I run it, it does compile the executable with the debug info, and then tries to compile with the performance tools, and obviously does nothing. Can make only execute the compilation step after both targets run?
Makefile:
CFLAGS = -std=c11 -Wall -pedantic -D_GNU_SOURCE
executable: gcc $(CFLAGS) main.c -o exe
debug: CFLAGS += -g
debug: executable
perf: CFLAGS += -D__PERF__
perf: executable
Make version 4.2.1

One approach is to have different executable files for different flag combinations, ie
executable # default to release build
executable.dbg # with -g
executable.perf # with -D__PERF__
executable.dbgperf # with both
The two advantages are
you're never unsure how some executable file was built, and you never accidentally test the wrong thing / waste time trying to debug something with no debug symbols, etc.
Make's file dependency logic actually works correctly
This does assume you have a manageable number of flag combinations. If there are too many, the profusion of targets may make your Makefile impractical.

I exactly have the same requirements! :-) As this, I compile my executables with all the options in the filename like: myprog-debug-coverage and so on. All the object files goes to a directory like build-myprog-debug-coverage as example. All this has the advantage that there is no need to recompile all if you need it once for debug, do a change and compile for coverage and after next change for debug again.
The first thing you must do is to filter out all non-targets which are needed for the options like debug with:
debug:
#:
to simply do nothing.
In addition, you filter out the non-targets with something like:
export FOUND_ACTIONS=$(filter $(ACTIONS), $(MAKECMDGOALS))
and process these variables later on into the flags you need for compilation like:
$(eval $(call CHECK_FOR_CMDGOAL,debug,export LCXXFLAGS+= -DDEBUG -DGDB_HELPER -g,build_dir_ext=$(build_dir_ext)-debug))
and after that you can write your standard recipes with the found flags from the variables.
I can't give you a ready to use Makefile here as my one is a bit larger while processing a lot of flags like different compilers, memory debug libraries as efence, build for coverage and much more.
I'm in hope that you can catch the idea how to process the pseudo targets for flags and the real targets and how a Makefile can split both.
And yes, that is not how a Makefile works typically. But I believe it is really convenient at the end of the day. Giving manual CXXFLAGS and so on did not the job, as you as a user have a complete list of flags, libs and options on the comand line, which is a nightmare.

This makes no sense to me. Why would you want to compile file(s) as debug, generating a program executable that is built with debug, then in the same make command recompile the same file(s) with optimization, generating a program executable that is built with optimization.
In a single directory there can be only one file named executable, so how can you build the same file twice and have two different versions at the same time? How do you intend to run the debug executable versus the perf executable?
You either need to build different files, or you need to build them into different directories (one for debug and one for perf). I recommend the latter.
But to answer your specific question: it is not possible in make to build the same target multiple different times in the same invocation of make. Once a single make invocation tries to build a target named foo, it will never try to build that target again no matter how many other targets might list it as a prerequisite. If you re-run make again, it will try again to build foo (one time).

Related

Best way to create a C++ Makefile that mkaes a prod build and creates debugging information?

I'm pretty confused about the idea of makefiles, can't really grasp how to execute stuff on it. I have a program which I would like to create a makefile
Would this be able to produce a build of my program and also output debug info? Honestly completely confused.
There are two targets in the makefile.
The first target is named TMA1Question3.exe, depends on Loop.cpp and will call g++ -o Loop.exe Loop.cpp. It will build the artifact Loop.exe without debug info.
The second target is named debug, has no dependencies and will call g++ -g Loop.cpp. It will build the artifact a.out containing debug info.

Adjusting CMake compiler commands to account for weirdness

I'm working with a third-party-modified version of clang 7.0.0 that has hijacked the -c option, replacing it with a flag -ccdsp for... reasons...
This has created two problems for CMake:
The -ccdsp option seems to be required to be the very first option to clang, before anything else, including -D... and -I... parameters. There is a BEFORE modifier for target_compile_options that I can use to make sure it appears at the start of the option list, but I don't seem to have any control over the ordering of non-options like definitions and include-paths, which results in -ccdsp occurring later in the overall set of parameters. I haven't been able to find a way to ensure that this is the very first option using modern CMake methods. Instead I have had to resort to setting CMAKE_CXX_FLAGS explicitly.
Now that the -c option to specify a source file is no longer available, I need to find a way to get CMake to omit it. It turns out that, as far as I can tell, this flag isn't really required, as clang -c foo.cpp -o foo.o and clang foo.cpp -o foo.o appear to do much the same thing. So I need a way to get CMake to drop the -c option.
I realise that this is an unusual question and it's a situation entirely created by the organisation that provided the modified version of clang. Unfortunately my support requests to them remain (to date) unanswered.
Does anyone know of a way I can work around this issue? Can I create a custom compiler handler in CMake, based on clang, where I add -ccdsp and remove -c somehow?
In CMake a compiler's command line is specified in CMAKE_<LANG>_COMPILE_OBJECT variable.
You may set this variable in a separate file:
my_clang_override.cmake:
set(CMAKE_CXX_COMPILE_OBJECT "<CMAKE_CXX_COMPILER> -ccdsp <FLAGS> -o <OBJECT>")
and include this file into the project's CMakeLists.txt by specifying CMAKE_USER_MAKE_RULES_OVERRIDE variable with either of two ways:
Specify the variable in the CMakeLists.txt itself, before the project() call:
CMakeLists.txt:
# Assume file 'my_clang_override.cmake' to be in the project's source directory.
set(CMAKE_USER_MAKE_RULES_OVERRIDE "my_clang_override.cmake")
Pass the variable's setting to cmake when configure the project:
cmake -DCMAKE_USER_MAKE_RULES_OVERRIDE=<path/to/my_clang_override.cmake> <other-parameters>

Generating test coverage of C++ static library as called by separate test classes

I am using QT Creator to work on a medium-sized project in C++.
The project structure basically looks like this
Project
Group A
Library A1
Group B
Library B1
Library B2
...etc
Test
LibA1_Test
LibB1_Test
LibB2_Test
...etc
The libraries are tested by the executables in the test project. I've managed to compile the tests themselves with gcov enabled, and produce code coverage reports with lcov, but all that they were showing the coverage for were the test cases, not the actual code that I'm testing. I've also tried compiling the static libraries with gcov as well, but when I run the tests against those libraries it does not generate any of the gcov output files.
How could I generate the gcov output files by linking my project libraries against the tests? I want to see if there are any gaps in my unit tests.
From the ld manual
--whole-archive
For each archive mentioned on the command line after the --whole-archive option, include every object file in the archive
in the link, rather than searching the archive for the required object
files.
So link your static-library into your test using --whole-archive, which will result in your test binary having the entire static-library, and give gcov visibility of the entire code
To be honest, you don't really need --whole-archive, it's enough to just add the following to all the staticlibs and Unit-Tests, that you want gcov information from:
# only apply codecoverage when CodeCoverage is set and debug-mode is active
CodeCoverage
{
debug:QMAKE_CXXFLAGS_DEBUG += --coverage # -fprofile-arcs -ftest-coverage
debug:QMAKE_LFLAGS_DEBUG += --coverage # -lgcov
}
--coverage is context-sensitive and the meaning of it depends on whether you pass it to CXXFLAGS or LFLAGS:
https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#Instrumentation-Options
Also mentioned in https://stackoverflow.com/a/5352713/7820869.
So no need to add -fprofile-arcs or any other mixture of flags.
In your case, add those Flags to A1.pro, B1.pro and B2.pro.
The added scope around the flags just prevent that the codecoverage-flags pollute your build in Release- or Debug-mode. Just pass "CONFIG+=CodeCoverage" to qmake.exe, so it will build with code-coverage:
https://doc.qt.io/archives/qt-4.8/qmake-advanced-usage.html#configuration-and-scopes
After that just check whether after compilation the *.gcno files and after running the Unit-Test's the *.gcda files have been generated in the build-directory and call lcov on them:
https://gcc.gnu.org/onlinedocs/gcc/Gcov-Data-Files.html#Gcov-Data-Files
Additional Info
The stackoverflow, that helped me solving my problems I run into:
Where are the gcov symbols?
in case you are running into linker errors like (undefined reference to __gcov_init and __gcov_merge_add), watch out that you are really adding those flags to every unit:
https://www.spinics.net/lists/gcchelp/msg29518.html

multiple gcc versions in makefile

In my Makefile I have
CC=g++
When I do mgrep gcc, I have several versions listed like:
gnu/gcc/4.2.1
gnu/gcc/4.7.3
etc
I can do a module load to change my gcc version.
Now suppose I want to use multiple versions simultaneously in different makefiles, how do I do it?
The module system is basically just setting up a path to the requested module. If you want a particular compiler in a particular makefile, then you can do three things:
Expect the user of the makefile to load the correct version before calling Make. Possibly combined with some condition based on gcc -v|grep ${GCC_VERSION} to check that it's the right version.
Perform module load gnu/gcc/${GCC_VERSION} inside your makefile.
Use CC=/somewhere/path-to-gcc-version/bin/g++ instead of CC=g++.
Personally, I prefer 1 or 3. You can find out what the path is by doing module load ... and then which g++.
[By the way, I would use CXX=g++ and CC=gcc - assuming you are not compiling files called *.c as C++-code]

Why doesn't GDB recognize my library's symbols?

I have a project hierarchy similar to the following:
src/
code.c
ext/
lib/
lib.c
lib.a
bin/
bin-code (+x)
obj/
code.o
lib.c compiles into lib.a using the -g2 flag and then ar.
code.c compiles into bin/obj/code.o using the -g2 flag.
lib.a and code.o are then linked into binary bin-code.
I'm facing a bug within bin-code and I'm trying to use GDB to load the symbols/source for lib so I can examine it with TUI.
I'm adding a breakpoint for a function inside lib.c, which it seems to find as it echos out an address and says it successfully set the breakpoint.
When I run the program and hit the breakpoint, I open TUI with CtrlX / CtrlA, but it claims no source could be found.
Two things worth mentioning:
I have used set substitute-path due to the fact my build system (Tup) uses a FUSE filesystem to enforce read/write operations.
I have tried adding directory entries to the search paths, to no avail.
Am I missing something here? Is there a command I can issue GDB in order for it to rescan directories or something? I can't get the library's source to show up, even though it appears symbols have been loaded.
Most likely gdb fails to find sources of you static library and can't show it's source code.
This may happen if binary was moved to another machine after it was built or sources were moved to another directory. In this case you should properly set substitute-path. You can see where gdb tries to find sources of static library (lib.c) using info sources command. Compare this path with the one where lib.c is actually located and this should help to set proper path substitution.