Reading makefiles. Meaning of symbols - linux

I am trying to learn how to read makefiles and came across this one. My question is referring to the rule with target %.c. On the first command. where it says
%.c: %.psvn psvn2c_core.c psvn2c_state_map.c psvn2c_abstraction.c
../psvn2c $(PSVNOPT) --name=$(*F) < $< > $#
What does $(*F) < $ < > $# mean? I have posted the whole makefile below.
CC = gcc
CXX = g++
OPT = -g -Wall -O3 -Wno-unused-function -Wno-unused-variable -std=c++11
PSVNOPT = --no_state_map --no_backwards_moves --history_len=0 --abstraction --state_map
psvn2c_core.c:
cp ../psvn2c_core.c ./psvn2c_core.c
psvn2c_state_map.c:
cp ../psvn2c_state_map.c ./psvn2c_state_map.c
psvn2c_abstraction.c:
cp ../psvn2c_abstraction.c ./psvn2c_abstraction.c
%.c: %.psvn psvn2c_core.c psvn2c_state_map.c psvn2c_abstraction.c
../psvn2c $(PSVNOPT) --name=$(*F) < $< > $#
rm -f ./psvn2c_core.c ./psvn2c_state_map.c ./psvn2c_abstraction.c
I want to understand this as a first step towards learning how to run a c++ debugger such as gdb with eclipes or visual studio.

Anything that begins with a $ in a makefile is a variable reference (or, in GNU make, a built-in function), unless it's escaped with another $ (i.e., is $$). The name of the variable can either be a single character, like $#, $A, etc., or it can be one or more characters enclosed in parentheses or braces, like $(#), ${A} (the same as the last ones), $(FOO), ${FOO}, etc.
The GNU make manual has lots of information about all the pre-defined and special variables. These odd-looking variables in particular are automatic variables.
If it's not a variable, and it's part of a recipe, then it's sent to the shell, so you should look at the shell documentation to understand it.

Is it correct to say that < means pipe the input in from and then $< is the first file in the list of dependancies. and > means pipe output to and $# is the output file ie. the file on the left hand side of the : symbol?

Related

custom object rules inside Makefile.am for automake

Typical makefile.am, which works, would look like this for my project:
noinst_LTLIBRARIES = libwinsane.la
CLEANFILES = init.cpp
libwinsane_la_CXXFLAGS = \
-I$(top_srcdir)
libwinsane_la_SOURCES = \
init.cpp
noinst_HEADERS = \
init.h
the issue is that I need to also process resource files. I need to use something like this:
noinst_LTLIBRARIES = libwinsane.la
libwinsane.o: init.o manifest.o
ld -relocatable -o $# init.o manifest.o
manifest.o: manifest.rc utf8.xml
windres -o $# manifest.rc
clean:
rm -f init.o manifest.o libwinsane.o
While this last example is valid makefile by itself (except noinst_LIBRARIES), its obviously wrong syntax for automake. But maybe there is a way for automake to just accept it and produce library in the end somehow?
Off the top of my head, and without 100% verifying its consistency with https://www.gnu.org/software/automake/manual/html_node/Program-and-Library-Variables.html or testing it, something like this should work more or less:
CLEANFILES =
noinst_LTLIBRARIES = libwinsane.la
libwinsane_la_SOURCES =
libwinsane_la_SOURCES += init.cpp
libwinsane_la_SOURCES += init.h
libwinsane_la_CXXFLAGS = -I$(top_srcdir)
libwinsane_la_DEPENDENCIES = manifest.$(OBJEXT)
libwinsane_la_LIBADD = manifest.$(OBJEXT)
CLEANFILES += manifest.$(OBJEXT)
manifest.$(OBJEXT): manifest.rc utf8.xml
windres -o $# $(srcdir)/manifest.rc
A few additional remarks:
The windres recipe probably does not work for out of source builds if utf8.xml has not just been buitl in $(builddir) and is therefore located in $(srcdir). Adding something like -I $(srcdir) to the windres command line will probably fix this, but cannot verify that without a MWE.
You will want to make windres configurable and replace the hard coded windres call by $(WINDRES), made user configurable by AC_ARG_VAR and (if $WINDRES has not been set) AC_CHECK_TOOL, and probably protected with a check in configure.ac that $WINDRES actually works. Depending on your project you can then either AC_MSG_ERROR(...) in case $WINDRES does not work, or AM_CONDITIONAL([HAVE_WINDRES], ...) and then put the Makefile.am parts related to manifest.$(OBJEXT) inside a if HAVE_WINDRES / endif conditional. The actual windres command might be called something like x86_64-w64-mingw32-windres when cross-compiling.
Why do you have CLEANFILES += init.cpp? I have left that out, as you do not want make clean to delete your source files. Or does init.cpp belong to BUILT_SOURCES?
Update: Apparently, I have overlooked something for compiling and linking with libtool: cannot build libtool library from non-libtool objects - any workaround?

Is there a way to define custom implicit GNU Make rules?

I'm often creating png files out of dot (graphviz format) files. The command to do so is the following:
$ dot my_graph.dot -o my_graph.png -Tpng
However, I would like to be able to have a shorter command format like $ make my_graph.dot to automatically generate my png file.
For the moment, I'm using a Makefile in which I've defined the following rule, but the recipe is only available in the directory containing the Makefile
%.eps: %.dot
dot $< -o $# -Teps
Is it possible to define custom implicit GNU Make recipes ? Which would allow the above recipe to be available system-wide
If not, what solution do you use to solve those kind of problem ?
Setup:
Fedora Linux with ZSH/Bash
You could define shell functions in your shell's startup files, e.g.
dotpng()
{
echo dot ${1%.dot}.dot -o ${1%.dot}.png -Tpng;
}
This function can be called like
dotpng my_graph.dot
or
dotpng my_graph
The code ${1%.dot}.dot strips .dot from the file name if present and appends it (again) to allow both my_graph.dot and my_graph as function argument.
Is it possible to define custom implicit GNU Make recipes ?
Not without modifying the source code of GNU Make.
If not, what solution do you use to solve those kind of problem ?
I wouldn't be a fan o modyfying the system globally, but you could do:
Create a file /usr/local/lib/make/myimplicitrules.make with the content
%.eps: %.dot
dot $< -o $# -Teps
Use include /usr/local/lib/make/myimplicitrules.make in your Makefile.
I would rather use a git submodule or similar to share common configuration between projects, rather than depending on global configuration. Depending on global environment will make your program hard to test and non-portable.
I would rather go with a shell function, something along:
mymake() {
make -f <(cat <<'EOF'
%.eps: %.dot
dot $< -o $# -Teps
EOF
) "$#"
}
mymake my_graph.dot
GNU Make lets you specify extra makefiles to read using the MAKEFILES
environment variable. Quoting from info '(make)MAKEFILES Variable':
the default goal is never taken from one of these makefiles (or any
makefile included by them) and it is not an error if the files listed
in 'MAKEFILES' are not found
if you are running 'make' without a specific makefile, a makefile
in 'MAKEFILES' can do useful things to help the built-in implicit
rules work better
As an example, with no makefile in the current directory and the
following .mk files in make's include path (e.g. via
MAKEFLAGS=--include-dir="$HOME"/.local/lib/make/) you can create
subdir gen/ and convert my_graph.dot or dot/my_graph.dot by
running:
MAKEFILES=dot.mk make gen/my_graph.png
To further save some typing it's tempting to add MAKEFILES=dot.mk
to a session environment but defining MAKEFILES in startup files
can make things completely nontransparent. For that reason I prefer
seeing MAKEFILES=… on the command line.
File: dot.mk
include common.mk
genDir ?= gen/
dotDir ?= dot/
dotFlags ?= $(if $(DEBUG),-v)
Tvariant ?= :cairo:cairo
vpath %.dot $(dotDir)
$(genDir)%.png $(genDir)%.svg $(genDir)%.eps : %.dot | $(genDir).
dot $(dotFlags) $< -o $# -T'$(patsubst .%,%,$(suffix $#))$(Tvariant)'
The included common.mk is where you'd store general definitions to
manage directory creation, diagnostics etc., e.g.
.PRECIOUS: %/. ## preempt 'unlink: ...: Is a directory'
%/. : ; $(if $(wildcard $#),,mkdir -p -- $(#D))
References:
?= = := … - info '(make)Reading Makefiles'
vpath - info '(make)Selective Search'
order-only prerequisites (e.g. | $(genDir).) - info '(make)Prerequisite Types'
.PRECIOUS - info '(make)Chained Rules'

Standard error file when there is no error

I'm new to Linux & shell and I'm struggling with checking if the compilation is successful.
g++ code.cpp -o code.o 2>error.txt
if [ ! -e error.txt ]
then
do something
else
echo "Failed to compile"
I guess an error file is created even if the compilation is successful. What is the content of the error file when there is no error? I need to change the if condition to check if the compilation is successful.
It's just the order of things. What happens when the shell parses the string g++ code.cpp -o code.o 2>error.txt is:
The shell creates error.txt, truncating the file if that name already exists.
g++ is called with its error output redirected to the new file.
If g++ does not write any data, then the file remains as it was (empty) at the end of step 1.
You probably aren't so much interested in the error file as you are the return value. You probably ought to just do:
if g++ code.cpp -o code; then : do something; done
or even just:
g++ code .cpp -o code && : do something
but if really want to do something else with the errors, you can do:
if g++ code.cpp -o code.o 2> error.txt; then
rm error.txt
: do something
else
echo >&2 Failed to compile code.cpp.\ See "$(pwd)"/error.txt for details.
fi
Make sure you escape at least one of the spaces after the . so that you get 2 spaces after the period (or just quote the whole argument to echo). Although it's become fashionable lately to claim that you only need one space, all of those arguments rely on the use of variable width fonts and any command line tool worth using will be used most often in an environment where fixed width fonts are still dominant. This last point is totally unrelated to your question, but is worth remembering.

How to let MAKEFILE retain the backslash sequences within a string when used in a make rule?

This is my first question on Stackoverflow so forgive me if I ask anything ridiculous :D.
Problem:
Suppose I want to compile a program that is in the directory "my dir/" with a space in it. Say the pathname of the program is "my dir/test.c".
Here is the sample makefile that I was trying out:
CC = gcc
DIR = my\ dir
$(DIR)/test.out: $(DIR)/test.c
# $(CC) $< -o $#
$(CC) $(DIR)/test.c -o $(DIR)/test.out
As you can see that in the last line(line-5) I have written the pathnames of the source and the output files directly as written in the prerequisite and the target, respectively. Doing this works fine because it yields the command:gcc my\ dir/test.c -o my\ dir/test.outwhich a syntactically correct way of passing filenames(with spaces) to gcc or any other shell command.
The second last line(line-4) is where the problem is(commented line). I've used automatic variables $# (Target) and $< (First and the only Prerequisite) to produce the filename arguments for gcc which I expected to bemy\ dir/test.out and my\ dir/test.c, respectively. But here, for some reason, the produced filenames are my dir/test.out and my dir/test.c and hence the yielded command is: gcc my dir/test.c -o my dir/test.out
Now here, gcc considers my and dir/test.c as different two different input filenames and the command generates errors.
Here is a screenshot of the generated error output when I uncomment line-4 and comment line-5 of the above Makefile:
My Question:
Is there any way to retain those backslashes even by using automatic variables the way I did? Or is there any alternative that will achieve the same goal as using automatic variables and also solve my problem? Because flexibility is important here.
Thanks in advance for your help!!!
Use double or single quotes for the automatic variables.
Use single quotes, if you want to avoid shell expansion of the values referenced by the automatic variables:
$(DIR)/test.out: $(DIR)/test.c
$(CC) '$<' -o '$#'
Double quotes allow shell expansion. For example, if there was a dollar sign in DIR:
DIR := $$my\ dir
then "$#" would expand to "$my dir", and the shell would interpret $my as variable.

Best way to handle pipes and their exit status in a makefile

If a command fails in make, such as gcc, it exits...
gcc
gcc: fatal error: no input files
compilation terminated.
make: *** [main.o] Error 4
However, if I have a pipe the exit status of the last command in the pipe is taken. As an example, gcc | cat, will not fail because cat succeeds.
I'm aware the exit codes for the whole pipe are stored in the PIPESTATUS array and I could get the error code 4 with ${PIPESTATUS[0]}. How should I structure my makefile to handle a piped command and exit on failure as normal?
As in the comments, another example is gcc | grep something. Here, I assume the most desired behavior is still for gcc and only gcc to cause failure and not grep if it doesn't find anything.
You should be able to tell make to use bash instead of sh and get bash to have set -o pipefail set so it exits with the first failure in the pipeline.
In GNU Make 3.81 (and presumably earlier though I don't know for sure) you should be able to do this with SHELL = /bin/bash -o pipefail.
In GNU Make 3.82 (and newer) you should be able to do this with SHELL = /bin/bash and .SHELLFLAGS = -o pipefail -c (though I don't know if adding -c to the end like that is necessary or if make will add that for you even when you specify .SHELLFLAGS.
From the bash man page:
The return status of a pipeline is the exit status of the last
command, unless the pipefail option is enabled. If pipefail is
enabled, the pipeline's return status is the value of the last
(rightmost) command to exit with a non-zero status, or zero if all
commands exit successfully. If the reserved word ! precedes a
pipeline, the exit status of that pipeline is the logical negation of
the exit status as described above. The shell waits for all commands
in the pipeline to terminate before returning a value.
I would go for pipefail. But if you really don't want (or if you want to only fail on the first process -- not in case of failure from the rest of the pipe):
SHELL=bash
all:
gcc | cat ; exit "$${PIPESTATUS[0]}"
The only advantage compared to #jozxyqk self answer is that you don't loose the exit status code.
A reasonable and portable approach is to refactor your build jobs to use files instead of pipes. For example:
foo:
gcc >$#.log
grep success $#.log
cat $#.log
rm $#.log
Removing the log file after printing it is obviously not necessary; this is just a general template. The beef is the redirection to replace the pipeline. You could even refactor it to multiple recipes:
foo: foo.tmp foo.log
grep success $#.log
mv $< $#
%.tmp %.log:
gcc -o $*.tmp >$*.log
Properly cleaning up the temporary artefacts and generally managing them is an obvious drawback of this approach.
Just add in the begin of your makefile the command:
SHELL=/bin/bash -o pipefail
Now you can, for example, generate the errors.err file from objects (1st rule) without being worried it would be overwritten by the executable (2nd rule).
%.o : %.c
gcc $(CFLAGS) $(CPPFLAGS) $^ -o $# 2>&1 | tee errors.err
%.x : %.o $(OBJECTS)
gcc $(LDLIBS) $^ -o $# 2>&1 | tee errors.err
Without it, make get no errors from rule 1, and run rule 2, overwriting it. You will end up with only a single line in errors.err stating that there are no object file to run gcc
gcc: error: program.o: No such file or directory

Resources