How to fix "debian-rules-is-dh_make-template"? - makefile

Problem
I had a following error(debian-rules-is-dh_make-template) from lintian.
How should I fix to pass the error?
The message showed me that I didn't modify debian/rules, but I already modified (I added override_dh_auto_clean:), so I guess that my debian/rules is insufficient but I can't figure out why my debian/rules is insufficient...
$ lintian hello_2.10-0ubuntu1.dsc --info
...
E: hello source: debian-rules-is-dh_make-template
N:
N: The debian/rules file appears to be an unmodified or insufficiently
N: modified copy of the dh_make template.
N:
N: Please double-check the rules file.
N:
N: Severity: important, Certainty: certain
N:
N: Check: rules, Type: source
N:
...
my dibian/rules is below.
#!/usr/bin/make -f
# See debhelper(7) (uncomment to enable)
# output every command that modifies files on the build system.
#export DH_VERBOSE = 1
# see FEATURE AREAS in dpkg-buildflags(1)
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
# see ENVIRONMENT in dpkg-buildflags(1)
# package maintainers to append CFLAGS
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
# package maintainers to append LDFLAGS
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
%:
dh $#
override_dh_auto_clean:
echo clean
# dh_make generated override targets
# This is example for Cmake (See https://bugs.debian.org/641051 )
#override_dh_auto_configure:
# dh_auto_configure -- # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH)
Background
I'm trying to do the tutorial(https://packaging.ubuntu.com/html/packaging-new-software.html#starting-a-package).
By using default debian/rules, I had a error like this(https://bugs.launchpad.net/ubuntu-packaging-guide/+bug/1248353/).
Therefore, I added override_dh_auto_clean: in my debian/rules for workaround(https://bugs.launchpad.net/ubuntu-packaging-guide/+bug/1248353/comments/10).

How about delete commentary in debian/rules?
#!/usr/bin/make -f
%:
dh $#
override_dh_auto_clean:
echo clean

Related

Makefile - Assigning Variable according to a Conditional

I'm trying to adapt the Example from the Official Documentation of GNU make to my Use Case:
GNU make - Example of a Conditional
libs_for_gcc = -lgnu
normal_libs =
ifeq ($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif
foo: $(objects)
$(CC) -o foo $(objects) $(libs)
So, I created this Prototype:
libs_for_gcc="gcc libs"
normal_libs="normal libs"
libs=
all: do_nothing
#echo "all: done."
do_nothing:
#echo "do_nothing: done."
example:
#echo "example: go ..."
#echo "example - cc: '$(CC)'"
#echo "libs_for_gcc: $(libs_for_gcc)"
#echo "normal_libs: $(normal_libs)"
#echo "libs: $(libs)"
ifeq ($(CC),gcc)
libs=$(libs_for_gcc) && echo "libs: '$$libs'"
#echo "example - libs: '$(libs)'"
else
libs=$(normal_libs) && echo "libs: '$$libs'"
#echo example - libs: $(libs)
endif
#echo "example - libs: '$(libs)'"
#echo "example: done."
test: libs += " -Ddebug"
test: example foo
bar:
#echo "bar: go ..."
ifeq (${libs}, "")
#echo "bar - libs: empty"
#echo "assigning libs"
libs=$(libs_for_gcc)
else
#echo "bar - libs: not empty"
#echo "bar - libs: '${libs}'"
endif
#echo "bar - libs: '${libs}'"
#echo "bar: done."
foo:
#echo "foo: go ..."
ifneq ("$(libs)", "")
#echo "foo - libs: not empty"
#echo "foo - libs: '$(libs)'"
else
#echo "foo - libs: empty"
endif
#echo "foo: done."
Now when I run the Default Target with $ make
it just produces:
$ make
example: go ...
example - cc: 'cc'
libs_for_gcc: gcc libs
normal_libs: normal libs
libs:
libs="normal libs"
example - libs:
example - libs: ''
example: done.
I see that the value of libs was not changed as intended.
When I run the make bar Target it produces:
$ make bar
bar: go ...
bar - libs: not empty
bar - libs: ''
bar - libs: ''
bar: done.
Here libs is not empty but it has nothing inside.
And when I run the make foo target it produces:
$ make foo
foo: go ...
foo - libs: empty
foo: done.
Here libs is understood as empty
As I see that libs is not changed correctly I tried to change the syntax to:
example:
# [...]
ifeq ($(CC),gcc)
libs := $(libs_for_gcc)
#echo "example - libs: '$(libs)'"
else
libs := $(normal_libs)
#echo example - libs: $(libs)
endif
But then I get the GNU make Error:
$ make
example: go ...
example - cc: 'cc'
libs_for_gcc: gcc libs
normal_libs: normal libs
libs:
libs := "normal libs"
/bin/sh: 1: libs: not found
Makefile:7: recipe for target 'example' failed
make: *** [example] Error 127
I couldn't find any documentation about this behaviour so I appreciate any advise.
Edit:
Added all and test Targets.
Background:
The GNU make Command is an important part of the toolchain for packaging and deploying software and thus important in the daily work of System Administrators and DevOps Engineers.
The Debian and RPM Packaging uses GNU make to package software.
Makefile Driven Packaging
It runs the ./configure && make && make install command sequence.
The Travis CI Workflow uses GNU make for running testsuites.
C Language Automated Testing
It runs the ./configure && make && make test sequence.
All the completely different Use Case are managed by the same Makefile.
Now for my concrete Use Case I'm working on setting up the Travis CI Workflow Sequence to enable Automated Testing for my static linked Source Code Library.
So, contrary to the Packaging Sequence the Automated Testing Sequence requires Debug Features and advanced Output Evaluation to produce a meaningful Test.
I want the Test to check the Error Report and also the Memory Usage Report to alert me of any hidden errors.
Using the Advice about setting the Variable at the Target Declaration Line I was able to change libs for test, example and foo targets.
I also saw the important hint about the Bash Variable libs which is only valid on the same line:
$ make
do_nothing: done.
all: done.
$ make test
example: go ...
example - cc: 'cc'
libs_for_gcc: gcc libs
normal_libs: normal libs
libs: -Ddebug
libs="normal libs" && echo "libs: '$libs'" ;
libs: 'normal libs'
example - libs: -Ddebug
example - libs: ' -Ddebug'
example: done.
foo: go ...
foo - libs: empty
foo - libs: ' -Ddebug'
foo: done.
The Recipe libs=$(libs_for_gcc) && echo "libs: '$$libs'" shows that a new Bash Variable libs was created and it did not affect the GNU make Variable.
Still the Conditional ifneq ($(libs),) cannot detect that libs was already set for the test target.
The difference between your example and the one in the GNU make manual is that in the GNU make manual they are setting a make variable named libs (they are assigning the variable outside of any recipe).
In your usage you are assigning a shell variable named libs (you are assigning it inside the recipe, indented with a TAB).
That's why you get an error when you try to use :=, because that's a make assignment and is not a valid shell assignment.
Then in your echo you print the make variable $(libs) which has not been set at all. Further, every logical line of the recipe is run in inside it's own shell. So you are running the equivalent of:
/bin/sh -c 'libs="gcc libs"'
/bin/sh -c 'echo '
so even if you DID change your echo command to print the shell variable (via $$libs) it would be empty because the previous shell, where it was set, has exited.
You want to use the same syntax as in the example: take the assignment of the variable OUT of the recipe and set the make variable instead:
libs_for_gcc = gcc libs
normal_libs = normal libs
ifeq ($(CC),gcc)
libs = $(libs_for_gcc)
else
libs = $(normal_libs)
endif
example:
#echo "example: go ..."
#echo "example - cc: '$(CC)'"
#echo "libs_for_gcc: $(libs_for_gcc)"
#echo "normal_libs: $(normal_libs)"
#echo "libs: $(libs)"
#echo "example - libs: '$(libs)'"
#echo "example: done."
Finally I achieved to get my Makefile make test Target working.
make test Test Suite with GNU make
The key was to understand that there are 3 different contexts where a GNU make Variable can be assigned.
And that GNU make often needs helper functions because of some strange issues with Conditionals
These issues are mostly undocumented and only can be solved by Try and Error and searching on StackOverflow.com
1. The first, easiest and best documented context to assign a Variable is in Global Context as seen in the official documentation and the first answer.
GNU make - Example of a Conditional
ifeq ($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif
The Global Variable ${libs} has global visibility.
2. The next context which is not that obvious but still documented in the Official Documentation is in the Target Definition Line
GNU make - Target-specific Variable Values
test: libs += " -Ddebug"
test: example foo
Here ${libs} is valid for the test Target and all dependent Targets that it calls.
But the only Condition is the Target itself.
3. To assign dynamically a Variable in the context of a Target is to use the $(eval ) Function in combination with the $(shell ) Function as seen in this StackOverflow.com Post:
Assign a Variable as Result of a Command
test: examples_debug
# [...]
$(eval error := $(shell cat ./demo_error.log))
ifeq ($(strip $(error)),)
#$(ECHO) "NO Error: '${error}'"
else
#$(ECHO) "Error detected: '${error}'"
$(error "Demo failed with Error [Code: '${error_code}']")
#exit 1
endif
Here ${error} is read from a File. Additionally the $(strip ) Function is required to be able to check if it is empty, which is some of the undocumented issues that GNU make has and are wierd to Bash Developers.
4. Another method that works but does not use a Makefile Variable and is somewhat bulky is evaluating the Variable entirely in Bash in 1 single Line which can be found at: Check Variable with Bash Command Recipe and was also hinted by the previous Answer.
Which would look like:
test: test_debug
# [...]
leak=`cat ./test_heap.log | grep -i "unfreed memory blocks" | awk '{print $$1}'` ; if [ $$leak -gt 1 ]; then echo "Memory Leaks: $$leak" && exit 1; else echo "Memory Leaks: NONE"; fi ;
Here $$leak is a Bash Variable and only valid within the same Line.
An approach that is somewhat similar to the GitHub Workflow Command Logic. (Actually is was directly ported from the GitHub Workflow for the same Project)
As about Evaluation of Conditionals there are many undocumented issues in GNU make that require Bash Workarounds to achieve the goal.
As documented at:
Preprocessing Numerical Values for the Makefile Evaluation
There are issues with comparing Numbers and it is impossible to compare against 0 which is extremely important Exit Code for Command Line Applications.
So the Workaround looked somewhat like:
test: test_debug
# Run the Testsuite Application
# [...]
$(eval error_code := $(shell export HEAPTRC="log=test_heap.log" ; echo -n "" >./test_heap.log ; ${wrkdir}/tests_hash-lists.dbg.run 2>./tests_error.log 1>./tests_exec.log ; echo "$$?"))
#$(ECHO) Application Tests: Execution finished with [${error_code}]
#Application Tests Execution Report
# [...]
$(eval is_error := $(shell if [ "${error_code}" = "0" ]; then echo false ; else echo true ; fi ;))
ifeq (${is_error}, true)
#$(ECHO) Exit Code non cero: '${error_code}'
#$(ECHO) "Tests failed with Error [Code: '${error_code}']"
#exit ${error_code}
endif
In this use case ${error_code} is tested with a Bash Conditional to populate the Helper Variable ${is_error} with true or false which then can be checked in GNU make.
Discussion:
The test Target cannot just exit on error.
For troubleshooting a failed Automated Test it is crucial to see the Exit Code, the Error Message and the Heap Report.

Makefile: Modify variable in target

I have a variable containing a list of files. They will be passed to my simulation environment.
VHDL_SOURCES += $(PWD)/common/ip/file1.vhd \
$(PWD)/common/ip/file2.vhd \
$(PWD)/common/ip/file3.vhd \
$(PWD)/common/ip/file4.vhd \
One of the targets creates a bunch of new files and they should be added to the list.
# sim target: Add requirement filelist.txt
.PHONY: sim
sim: add_xilip_sources
# all other definitions are defined in the common makefile
include $(PWD)/scripts/cocotb/common_makefile/Makefile
.PHONY: add_xilip_sources
add_xilip_sources: $(BUILD_PWD)/filelist.txt
XILIP_FILES = $(shell cat $<) # read the filelist into an variable
# prepend the new filelist to the VHDL_SOURCES list.
$(BUILD_PWD)/filelist.txt: $(SCRIPT_PWD)/make_switch_ip.tcl
vivado -mode tcl -source $(SCRIPT_PWD)/make_switch_ip.tcl
How can I modify the variable in my target?
You can't. Make is declarative, or two-step; first specifying the build environment, then executing. You can't modify the build environment such as variables while executing it, or at least not between different recipes.
What you can do is use files instead of variables:
# vhdl_sources.txt containing VHDL_SOURCES
# vhdl_sources.txt.full containing VHDL_SOURCES plus generated filelist.txt content
vhdl_sources.txt.full: $(BUILD_PWD)/filelist.txt vhdl_sources.txt
cat $^ > $#

Defining SHELL: why some targets fail with error "invalid syntax" while another doesn't fail?

I have a Makefile as follows (excerpt):
# be a POSIX guy!
SHELL = /bin/dash
# avoid accursed tabs
.RECIPEPREFIX +=
PROJECT = my_project
# before-commit stuff
CHANGED_FILES = $(shell git ls-files --modified)
files ?= $(CHANGED_FILES)
lint:
pyflakes $(files)
lint-all:
pyflakes $(PROJECT)
STAGING_DB_PORT = 5437
staging-db-start:
ssh -fNL 0.0.0.0:$(STAGING_DB_PORT):localhost:$(STAGING_DB_PORT) staging-db
ss -tlpn sport eq :$(STAGING_DB_PORT)
staging-db-stop:
ssh -O check staging-db
ssh -O stop staging-db
ss -tlpn sport eq :$(STAGING_DB_PORT)
staging-db-check:
ss -tlpn sport eq :$(STAGING_DB_PORT)
ssh -O check staging-db
.PHONY: lint, lint-all, staging-db-start, staging-db-stop, staging-db-check
When I run target, say staging-db-check it works just fine. Although, when I run target lint, it fails with error:
Makefile:2:9: invalid syntax
SHELL = /bin/dash
^
For me, it is very strange. I read docs, they say that you always must set SHELL variable, so I decided to do so. But I can not figure out where there is an error?
I have GNU make, version 4.2.1.
GNU Make never generates diagnostics of form of:
Makefile:2:9: invalid syntax
SHELL = /bin/dash
^
But pyflakes does, which is the program run by your lint target's recipe:
lint:
pyflakes $(files)
As you know, pyflakes lints Python source files. Your $(files), as assigned
by:
# before-commit stuff
CHANGED_FILES = $(shell git ls-files --modified)
files ?= $(CHANGED_FILES)
expands to a list of files that includes Makefile. Your Makefile is not
a Python source file and the first line in Makefile that is not syntactically
valid Python is:
SHELL = /bin/dash
Here's a shorter makefile:
Makefile
# be a POSIX guy!
SHELL = /bin/dash
.PHONY: all
all:
echo "Hello World"
with which to reproduce your error:
$ pyflakes Makefile
Makefile:2:9: invalid syntax
SHELL = /bin/dash
^
Later
Is there a way to exclude non-python files from $files variable?
Yes. Assuming that Python files are files with the extension .py, change:
CHANGED_FILES = $(shell git ls-files --modified)
to:
CHANGED_FILES = $(filter %.py,$(shell git ls-files --modified))
See functions:
$(filter pattern...,text)
$(filter-out pattern...,text)
in 8.2 Functions for String Substitution and Analysis
in the GNU Make manual
And if you do that, maybe better change CHANGED_FILES to CHANGED_PYTHON_FILES.

Non-recursive Automake and custom suffix rules (for LateX documentation)

As a follow-up to my question from a few years ago, I'm currently converting a very similar project from using recursive automake to a single non-recursive Makefile. This works fine for C++ source code and checks. However, when building the documentation (i.e. running PDFLaTeX to convert .tex files to .pdf files) I run into trouble: make works fine, but make distcheck fails with the following error:
make[1]: *** No rule to make target `doc//UserGuide.tex', needed by `doc//UserGuide.aux'. Stop.
The directory structure is as follows:
project/
|-- Makefile.am
|-- configure.ac
|-- src/ # containing all .cpp and .h files
|-- doc/
\-- UserGuide.tex
configure.ac has some code to detect the presence of pdflatex and allow the user to disable compiling the documentation (e.g. in case a certain LaTeX package isn't installed):
# The user can disable building of the PDF of the manual, for example
# if the required LaTeX packages are not installed
AC_ARG_ENABLE([latex-doc],
[AS_HELP_STRING([--disable-latex-doc], [disable building the PDF
documentation from LaTeX source])],
[latexdoc=no],
[latexdoc=yes])
if test "x$latexdoc" = "xyes"; then
AC_MSG_NOTICE([building of the PDF of the user manual from LaTeX source is enabled])
# Check for presence of pdfLaTeX
AC_CHECK_PROGS(PDFLATEX, pdflatex)
if test -z "$PDFLATEX"; then
AC_MSG_NOTICE([pdflatex not found - Unable to create PDF version of the user manual])
fi
fi
AM_CONDITIONAL([HAVE_PDFLATEX], test -n "$PDFLATEX")
AM_CONDITIONAL([BUILD_latexdoc], test "x$latexdoc" = "xyes")
And in Makefile.am I defined the following relevant parts:
## Stuff needed for documentation in the doc/ directory
dist_doc_DATA = doc/howtocompile.txt doc/UserGuide.tex \
COPYING INSTALL ChangeLog AUTHORS
## Build the PDF documentation if building of the LaTeX docs is
## enabled via ./configure.
if BUILD_latexdoc
if HAVE_PDFLATEX
DOCDIR = doc/
MANNAME = $(DOCDIR)/UserGuide
MANPDF = $(MANNAME).pdf
MANTEXSRC = $(MANNAME).tex
MANAUX = $(MANNAME).aux
doc_DATA = $(MANPDF)
PDFLATEXOPTS = --output-directory=$(DOCDIR)
CLEANFILES += $(MANPDF) $(MANNAME).log $(MANNAME).idx $(MANNAME).out \
$(MANNAME).toc $(MANNAME).idx $(MANNAME).ilg $(MANNAME).ind $(MANAUX) .btmp
endif
endif
The problem is most likely with the custom suffix rules I set up (which work fine in the case of recursive make):
# Several make rules to generate the PDF from the LaTeX source
.aux.pdf:
#echo === Making PDF: $# from $^ ===
$(PDFLATEX) $(PDFLATEXOPTS) $(srcdir)/$(MANTEXSRC)
#while ( grep "Rerun to " \
$(MANNAME).log ); do \
echo '** Re-running LaTeX **'; \
$(PDFLATEX) $(PDFLATEXOPTS) $(srcdir)/$(MANTEXSRC); \
done
if [ -f $(MANNAME).idx ]; then \
echo === Making index ===; \
makeindex $(MANNAME); \
fi
#echo === Making final PDF ===
$(PDFLATEX) $(PDFLATEXOPTS) $(srcdir)/$(MANTEXSRC)
.tex.aux:
#echo === Making $# file from $^ ===
$(PDFLATEX) $(PDFLATEXOPTS) $(srcdir)/$(MANTEXSRC)
## Look for citations. Make sure grep never returns an error code.
#grep "^\\\\citation" $(MANAUX) > .btmp.new || true
## If the citations are not changed, don't do anything. Otherwise replace
## the .btmp file to make sure BibTeX will be run.
#if ( diff .btmp.new .btmp > /dev/null ); then \
rm .btmp.new; \
else \
mv .btmp.new .btmp; \
fi
# A target needed to keep track of the nr. of LaTeX runs
.btmp:
I tried changing the .tex.aux rule to a GNU Make pattern rule (%.aux: $(srcdir)/%.tex), which seemed to work (although Automake gave a warning about this being a GNU Make functionality), but when I tried to do the same for the .aux.pdf rule this didn't work, giving the following error message:
make: *** No rule to make target `UserGuide.pdf', needed by `all-am'. Stop.
My guess is that it has something to do with the VPATH build that make distcheck performs as the doc/ directory doesn't show up in the _build directory.
Any suggestions on how to make this work or improve it?
Well, it turns out there were two things I needed to change:
Explicitly mention the extensions .tex, .aux and .pdf in a SUFFIX rule
Tell Automake to create the target directory where the PDF documentation will end up (the $(DOCDIR) variable in my case.
The last section of code from the question now reads:
SUFFIXES = .tex .aux .pdf
# Several make rules to generate the PDF from the LaTeX source
.aux.pdf:
#echo === Making PDF: $# from $^ ===
$(MKDIR_P) $(DOCDIR)
$(PDFLATEX) $(PDFLATEXOPTS) $(srcdir)/$(MANTEXSRC)
#while ( grep "Rerun to " \
$(MANNAME).log ); do \
echo '** Re-running LaTeX **'; \
$(PDFLATEX) $(PDFLATEXOPTS) $(srcdir)/$(MANTEXSRC); \
done
if [ -f $(MANNAME).idx ]; then \
echo === Making index ===; \
makeindex $(MANNAME); \
fi
#echo === Making final PDF ===
$(PDFLATEX) $(PDFLATEXOPTS) $(srcdir)/$(MANTEXSRC)
.tex.aux:
#echo === Making $# file from $^ ===
$(MKDIR_P) $(DOCDIR)
$(PDFLATEX) $(PDFLATEXOPTS) $(srcdir)/$(MANTEXSRC)
## Look for citations. Make sure grep never returns an error code.
#grep "^\\\\citation" $(MANAUX) > .btmp.new || true
## If the citations are not changed, don't do anything. Otherwise replace
## the .btmp file to make sure BibTeX will be run.
#if ( diff .btmp.new .btmp > /dev/null ); then \
rm .btmp.new; \
else \
mv .btmp.new .btmp; \
fi
# A target needed to keep track of the nr. of LaTeX runs
.btmp:
I've added the $(MKDIR_P) lines to both suffix rules, just to be sure. Probably it'll be enough to only have it in the .tex.aux rule.

How to generate gcc debug symbol outside the build target?

I know I can generate debug symbol using -g option. However the symbol is embeded in the target file. Could gcc generate debug symbol outside the result executable/library? Like .pdb file of windows VC++ compiler did.
You need to use objcopy to separate the debug information:
objcopy --only-keep-debug "${tostripfile}" "${debugdir}/${debugfile}"
strip --strip-debug --strip-unneeded "${tostripfile}"
objcopy --add-gnu-debuglink="${debugdir}/${debugfile}" "${tostripfile}"
I use the bash script below to separate the debug information into files with a .debug extension in a .debug directory. This way I can tar the libraries and executables in one tar file and the .debug directories in another. If I want to add the debug info later on I simply extract the debug tar file and voila I have symbolic debug information.
This is the bash script:
#!/bin/bash
scriptdir=`dirname ${0}`
scriptdir=`(cd ${scriptdir}; pwd)`
scriptname=`basename ${0}`
set -e
function errorexit()
{
errorcode=${1}
shift
echo $#
exit ${errorcode}
}
function usage()
{
echo "USAGE ${scriptname} <tostrip>"
}
tostripdir=`dirname "$1"`
tostripfile=`basename "$1"`
if [ -z ${tostripfile} ] ; then
usage
errorexit 0 "tostrip must be specified"
fi
cd "${tostripdir}"
debugdir=.debug
debugfile="${tostripfile}.debug"
if [ ! -d "${debugdir}" ] ; then
echo "creating dir ${tostripdir}/${debugdir}"
mkdir -p "${debugdir}"
fi
echo "stripping ${tostripfile}, putting debug info into ${debugfile}"
objcopy --only-keep-debug "${tostripfile}" "${debugdir}/${debugfile}"
strip --strip-debug --strip-unneeded "${tostripfile}"
objcopy --add-gnu-debuglink="${debugdir}/${debugfile}" "${tostripfile}"
chmod -x "${debugdir}/${debugfile}"
Compile with debug information:
gcc -g -o main main.c
Separate the debug information:
objcopy --only-keep-debug main main.debug
or
cp main main.debug
strip --only-keep-debug main.debug
Strip debug information from origin file:
objcopy --strip-debug main
or
strip --strip-debug --strip-unneeded main
debug by debuglink mode:
objcopy --add-gnu-debuglink main.debug main
gdb main
You can also use exec file and symbol file separatly:
gdb -s main.debug -e main
or
gdb
(gdb) exec-file main
(gdb) symbol-file main.debug
For details:
(gdb) help exec-file
(gdb) help symbol-file
Ref:
https://sourceware.org/gdb/onlinedocs/gdb/Files.html#Files
https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
NOTE: Programs compiled with high-optimization levels (-O3, -O4) cannot generate many debugging symbols for optimized variables, in-lined functions and unrolled loops, regardless of the symbols being embedded (-g) or extracted (objcopy) into a '.debug' file.
Alternate approaches are
Embed the versioning (VCS, git, svn) data into the program, for compiler optimized executables (-O3, -O4).
Build a 2nd non-optimized version of the executable.
The first option provides a means to rebuild the production code with full debugging and symbols at a later date. Being able to re-build the original production code with no optimizations is a tremendous help for debugging. (NOTE: This assumes testing was done with the optimized version of the program).
Your build system can create a .c file loaded with the compile date, commit, and other VCS details. Here is a 'make + git' example:
program: program.o version.o
program.o: program.cpp program.h
build_version.o: build_version.c
build_version.c:
#echo "const char *build1=\"VCS: Commit: $(shell git log -1 --pretty=%H)\";" > "$#"
#echo "const char *build2=\"VCS: Date: $(shell git log -1 --pretty=%cd)\";" >> "$#"
#echo "const char *build3=\"VCS: Author: $(shell git log -1 --pretty="%an %ae")\";" >> "$#"
#echo "const char *build4=\"VCS: Branch: $(shell git symbolic-ref HEAD)\";" >> "$#"
# TODO: Add compiler options and other build details
.TEMPORARY: build_version.c
After the program is compiled you can locate the original 'commit' for your code by using the command: strings -a my_program | grep VCS
VCS: PROGRAM_NAME=my_program
VCS: Commit=190aa9cace3b12e2b58b692f068d4f5cf22b0145
VCS: BRANCH=refs/heads/PRJ123_feature_desc
VCS: AUTHOR=Joe Developer joe.developer#somewhere.com
VCS: COMMIT_DATE=2013-12-19
All that is left is to check-out the original code, re-compile without optimizations, and start debugging.
Check out the "--only-keep-debug" option of the strip command.
From the link:
The intention is that this option will be used in conjunction with --add-gnu-debuglink to create a two part executable. One a stripped binary which will occupy less space in RAM and in a distribution and the second a debugging information file which is only needed if debugging abilities are required.
No answer so far mentions eu-strip --strip-debug -f <out.debug> <input>.
This is provided by elfutils package.
The result will be that <input> file has been stripped of debug symbols which are now all in <out.debug>.

Resources