Is there a nicer/more elegant way to do a gcc -D command conditionally in a Makefile? - c

So I'm writing some C files that utilize the classic pattern of doing this sort of business:
#ifdef DEBUG
SHOW("debugging");
#endif
And also I'm utilizing the -D command of gcc, that lets you define a constant, so that I can turn these on and off via command line args.
Here is a makefile I've written, that makes it so if I want to debug, I can simply do "make d=true", and it will conditionally define DEBUG for me:
#DEBUG: make d=true
VPATH = src
ifeq ($(d),true)
debug = -DDEBUG
endif
flags = -Wall -Werror -g $(debug)
mealplan: items.o mealplan.h
gcc $(flags) -o mealplan mealplan.c items.o
items.o: items.c items.h
gcc $(flags) -c src/items.c
clean:
rm mealplan items.o
Don't get me wrong, it works 100% as I intended it to. But it looks a bit barbaric to me, is there a cleaner/shorter/more idiomatic way to achieve this conditional setting of the DEBUG define via make?
So I tried this because I read in the make documentation a few facts:
You can pass in custom variables by doing "var=value"
An undefined variable just has the value of empty string
You can do conditionals with "ifeq"
So I kind of just slapped this together and it works but I feel like a better solution exists.

The simplest solution is to use target-specific variables:
VPATH = src
flags = -Wall -Werror -g $(debug)
mealplan: items.o mealplan.h
gcc $(flags) -o mealplan mealplan.c items.o
items.o: items.c items.h
gcc $(flags) -c src/items.c
clean:
rm mealplan items.o
.PHONY: debug
debug: mealplan
debug: debug = -DDEBUG
Now if you run make it will build without the debug options and if you run make debug it will build with the debug options.
Or, to rewrite your makefile to be more idiomatic:
VPATH = src
CC = gcc
CFLAGS = -Wall -Werror -g $(debug)
mealplan: mealplan.o items.o
mealplan.o: mealplan.c mealplan.h
items.o: items.c items.h
clean:
rm mealplan items.o
.PHONY: debug
debug: mealplan
debug: debug = -DDEBUG
Just to note this is really not a great way to handle things. It's far too easy to forget to do a clean between the build of debug and non-debug and now you have a mix of some objects compiled with debugging and some without. A more reliable way to do it is write your makefile to put the debug output files in one directory and the non-debug output files in a different directory.

Related

GNU Makefile : How to use pattern-rule adding a compilation flag to a match-anything pattern-rule by using pattern-specific variable value?

I have created several test files test_*.c, each testing a single function of a c library I built.
I first wrote a Makefile for compiling each c file to produce its corresponding binary:
TEST_SRCS = ${wildcard *.c}
TEST_EXECS = ${TEST_SRCS:.c=}
PROJECT_PATH = ../my_project
CFLAGS = -Wall -Wextra -Werror
%:: %.c ${PROJECT_PATH}/libmyproject.a
gcc ${CFLAGS} $< -L${PROJECT_PATH} -lmyproject -o $#
all: ${TEST_EXECS}
clean:
rm -f ${TEST_EXECS}
re: clean all
.PHONY: all clean re
This works as expected, so when i type make test_<name_of_my_function> and if the 'test_<name_of_my_function>.c' file exists, it compiles it to create the 'test_<name_of_my_function>' binary.
But now i want to add a rule that creates the binary in debug mode by adding the gcc flag -g to the command if i run the command make debug_test_<name_of_my_function>.
I tried adding the pattern-specific rule debug_% and use a pattern-specific value for appending -g to CFLAGS:
TEST_SRCS = ${wildcard *.c}
TEST_EXECS = ${TEST_SRCS:.c=}
PROJECT_PATH = ../my_project
CFLAGS = -Wall -Wextra -Werror
%:: %.c ${PROJECT_PATH}/libmyproject.a
gcc ${CFLAGS} $< -L${PROJECT_PATH} -lmyproject -o $#
all: ${TEST_EXECS}
debug_%: CFLAGS += -g
debug_%: %
clean:
rm -f ${TEST_EXECS}
re: clean all
.PHONY: all clean re
But when i run for example debug_test_function1, i get the following ouptut :
make: *** No rule to make target 'debug_test_function1'. Stop.
Note that whatever prerequisite rule i use for the target debug_%, it isn't executed (even if the prerequisite is not a pattern rule).
Note also that if i replace in the makefile
debug_%: CFLAGS += -g
debug_%: %
by
debug%: %.c ${PROJECT_PATH}/libft.a
gcc ${CFLAGS} -g $< -L${PROJECT_PATH} -lmyproject -o $#
it works. But i'd lose the benefit of using a pattern-specific variable here.
Any clue oh how to use pattern-specific variable values to do it?
Thank you for your help !
This:
debug_%: %
does not define a pattern rule. It deletes a pattern rule. See the GNU make manual. So, you've not defined any rules here that know how to build debug_test_function1 (because there's no file debug_test_function1.c to build it from and the only rule you have available is the match-anything rule).
You have to provide the recipe here, you cannot omit it. But obviously you can still add a pattern-specific variable; why not?:
debug_%: CFLAGS += -g
debug_%: %.c ${PROJECT_PATH}/libft.a
gcc ${CFLAGS} $< -L${PROJECT_PATH} -lmyproject -o $#
I'm not really sure what "benefit of pattern-specific variables" you are referring to since you don't seem to build an other prerequisites that need to inherit the pattern-specific variable. But the above should work.

makefile - how to exclude file extension suffix from a variable

the next makefile receive the file to compile from its command line arg -ARGS. For example
make ARGS="out.c"
I would like to replace the name of the created executable "run" with the variable ARGS excluding the suffix
in this example : run="out"
all: Task1
Task1: outputs/output.o
gcc -g -m32 -Wall -o run outputs/output.o
outputs/output.o: outputs/${ARGS}
gcc -m32 -g -w -Wall -ansi -c -o outputs/output.o outputs/${ARGS}
.PHONY: clean
run: clean Task1
clean:
rm -f outputs\output.o Task1
The crude way to do what you ask is simply:
EXEC := $(basename $(ARGS))
all: Task1
Task1: outputs/output.o
gcc -g -m32 -Wall -o $(EXEC) outputs/output.o
A better way is:
EXEC := $(basename $(ARGS))
all: $(EXEC)
$(EXEC): outputs/output.o
gcc -g -m32 -Wall -o $(EXEC) outputs/output.o
Better still:
EXEC := $(basename $(ARGS))
all: $(EXEC)
$(EXEC): outputs/output.o
gcc -g -m32 -Wall -o $# $^
If using GNU make you need the basename function, perhaps as $(basename $(AUX)). Maybe variables like $(*F) might be useful too (please read the documentation). However, your Makefile is probably wrong.
I can't suggest an improvement, because what you want to do and to happen is unclear.
BTW, use remake (as remake -x) or at least make --trace (with a recent enough GNU make 4.x) to understand what make is doing and why.
Also, you'll find several examples of Makefile-s: here & there
etc... Don't forget that make has a many builtin rules, you'll get them by running make -p
You won't lose your time by reading the documentation of GNU make, and some tutorials, and some examples of Makefile-s.

Makefile -std=c99 error

all: matrices.c calculations.o
gcc -std=c99 matrices.c calculations.o -o -lm PROGRAM2_EXE
lib: matrices.h calculations.c
gcc -c -std=c99 calculations.c -o calculations.o
clean:
rm matrices.o calculations.o PROGRAM2_EXE
This is my makefile for my project. Inside my matrices and my calculations.c there are multiple for loops. The error it gives me is error: 'for' loop initial declarations are only allowed in c99 mode but i have my -std=c99 in my file. My questions is
How do i fix my makefile to run with the for loops?
This action:
gcc -std=c99 matrices.c calculations.o -o -lm PROGRAM2_EXE
is telling gcc to produce an executable called -lm, which is probably not what you want.
Since you have no rule to produce connections.o, make will use its default rule to build it from connections.c, which is likely something like (from gmake):
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $# $<
Since you don't set -std=c99 in CFGLAGS or CPPFLAGS, it won't be used.
You don't have a rule for calculations.o. That means that the makefile does not specify how to generate calculations.o. You only have a rule for generating all and lib.
Gmake has some default rules. One of them is that if you do not have a rule for filename.o then it is compiled using $(CC) $(CPPFLAGS) $(CFLAGS) -c filename.c.
Your make all line is:
all: matrices.c calculations.o
When you issue make or make all, gmake checks the dependencies first. matrices.c already exists so that's fine. But if calculations.o does not exist, then it decides it needs to build calculations.o. You didn't specify a rule for calculations.o, so the implicit rule is used, which doesn't have -std=c99 in it.
Note that your lib rule is badly written. All rules should either create a file of the corresponding name, or be declared as .PHONY. It seems as if you think that make should somehow deduce that it needs to do make lib if the calculations.o file does not exist, but that isn't how make works.
To fix your problem , just change lib: to calculations.o:. Also, it would be good style to fix the all line. The rule does not make all, it makes PROGRAM2_EXE, so:
.PHONY: all
all: PROGRAM2_EXE
PROGRAM2_EXE: matrices.c calculations.o
gcc -std=c99 matrices.c calculations.o -o PROGRAM2_EXE -lm
(Edit: as Chris Dodd points out, the -o switch must be immediately followed by the filename)
It would be better style to have a rule matrices.o that compiles matrices.c, and then a rule PROGRAM2_EXE: matrices.o calculations.o that links the two.
I use the following CFLAGS:
CFLAGS = -Wall -g -std=c99
It works very good for me.

A Makefile with Multiple Executables

I am trying to write a makefile which uses macros to create multiple executables from multiple files at once. I tried searching through previously answered questions but, because I am fairly new to programming in C as well as working with gcc, I was not able to find an answer to my question.
Here is what I have so far:
CC=gcc
CFLAGS=-I.
OBJ = ex1.c ex3.c
EXECUTABLE = ex1 ex3
$(EXECUTABLE): $(OBJ)
gcc -o $# $^ $(CFLAGS)
clean:
rm -f $(EXECUTABLE)
I would like the line
$(EXECUTABLE): $(OBJ)
to create executables ex1 and ex3 from files ex1.c ex3.c respectively.
For this particular case, where each executable has a single source file with .c extension, all you need is a one line Makefile:
all: ex1 ex3
The built-in default rules for make then work already:
$ make
cc -O2 -pipe ex1.c -o ex1
cc -O2 -pipe ex3.c -o ex3
Behind the scene, make is using the POSIXly mandated built-in single suffix rule
.c:
$(CC) $(CFLAGS) $(LDFLAGS) -o $# $<
Vary the command to your liking with make CC=gcc CFLAGS=-O2 LDFLAGS=-s and similar.
Trivia of the day: in fact, if you are willing to name the targets when invoking make, you can use an empty or even run without any Makefile:
$ make -f /dev/null CC=gcc CFLAGS=-O2 LDFLAGS=-s ex1 ex3
gcc -O2 -s ex1.c -o ex1
gcc -O2 -s ex3.c -o ex3
$ rm -f Makefile ex1 ex3
$ make CC=gcc CFLAGS=-O2 LDFLAGS=-s ex1 ex3
gcc -O2 -s ex1.c -o ex1
gcc -O2 -s ex3.c -o ex3
Make magic!
As a rule of thumb, don't reinvent the wheel (or rules), use the rules that are already there. It simplifies your and make's life a lot. This makes for small and sexy makefiles to impress the ladies with :-)
Some suggestions (assuming you use GNU make, not something else)
First, run once make -p, you'll understand what builtin rules make is knowing. Look in particular for COMPILE.c and LINK.c
Then, I suggest
CFLAGS= -g -Wall -I.
(because you really want -g for debugging, and -Wall to get most warnings)
And you probably don't need
$(EXECUTABLE): $(OBJ)
gcc -o $# $^ $(CFLAGS)
However, I suggest adding before most other rules
.PHONY: all clean
all: $(EXECUTABLES)
Actually, I would code your Makefile (for GNU make!) as follow
# file Makefile
CC= gcc
RM= rm -vf
CFLAGS= -Wall -g
CPPFLAGS= -I.
SRCFILES= ex1.c ex2.c ## or perhaps $(wildcard *.c)
OBJFILES= $(patsubst %.c, %.o, $(SRCFILES))
PROGFILES= $(patsubst %.c, %, $(SRCFILES))
.PHONY: all clean
all: $(PROGFILES)
clean:
$(RM) $(OBJFILES) $(PROGFILES) *~
## eof Makefile
Remember that tab is a significant character in Makefile-s (action part of rules). In this answer, lines starting with four spaces at least should really start with a tab character.
Once everything is debugged consider running make clean to clean everything, and then make -j CFLAGS=-O2 all to compile in parallel everything with optimizations.
At last, I recommend using remake and running remake -x to debug complex Makefile-s
Of course, I'm supposing that your directory has only single-file programs.
BTW, there are other build automation tools. Perhaps you might consider using omake or ninja. For building large programs (millions of source code lines) consider also automake, ccache, cmake, icecream. In some cases, consider generating some C code with GPP, GNU bison, SWIG, etc... or using your own Python or Guile script (or C meta-program). See also this draft report.
Don't forget to use a version control system like git for your source files. It is also time to learn such a tool.
The following answer includes multiple executable such as initiate, process1, process2, ..., process4.
LOCAL_INCLUDE=./
all: clean process_first process_second init
process_first:
gcc -g -o process1 -I$(LOCAL_INCLUDE) process1.c -lzmq -L. -L./.
gcc -g -o process2 -I$(LOCAL_INCLUDE) process2.c -lzmq -L. -L./.
process_second:
gcc -g -o process3 -I$(LOCAL_INCLUDE) process3.c -lzmq -L. -L./.
gcc -g -o process4 -I$(LOCAL_INCLUDE) process4.c -lzmq -L. -L./.
init:
gcc -g -o initiate -I$(LOCAL_INCLUDE) initiate.c -lzmq -lconfig -lpthread -L. -L./. -ldl -lrt
clean:
rm -rf init_manager.o init_manager
rm -rf process1 process2 process3 process4
NOTE: It is a good practice to clean and touch all the executable files before making them again.
You're close, but you need a pattern rule:
$(EXECUTABLE): % : %.c
And then a default rule to make it build both:
all: $(EXECUTABLE)

Using Make for compiling C

I am trying to learn make to make my compiling easier as I learn C.
I am attempting to do:
gcc -Wall -g 3.c -o 3 -lm
using
CC = gcc
CFLAGS = -Wall -g
clean:
rm -f 3
but I don't know how and where to put -lm in the makefile. I've looked for tutorials online but they haven't specifically addressed the "-lm" option, or if they do it is without little explanation and doesn't work in my situation.
You need a "target" in which to execute the gcc command. Like:
CC = gcc
CFLAGS = -Wall -g
all:
gcc -Wall -g 3.c -o 3 -lm
clean:
rm -f 3
Then you can just replace parts of the "all" command, with your macros; CFLAGS, for example would probably have the "-lm".
It might help if you ran "make -n", that will tell you what make would do if it were to run.
Often you'll see library specific flags in a LIBS variable, e.g.:
CC = gcc
CFLAGS = -Wall -g -I/some/include/directory
LIBS = -lm -L/some/library/directory
all:
$(CC) $(CFLAGS) $(LIBS) 3.c -o 3
The variable you are looking for is called LDLFAGS. From §10.3 of the GNU Make manual:
LDFLAGS
Extra flags to give to compilers when they are supposed to invoke the linker, ‘ld’.
So, simply do:
LDFLAGS += -lm
Hope it helps.
An extremely good tutorial: Make Tutorial: How-To Write A Makefile
and here is a good generic makefile I wrote:
http://pastebin.com/PCk0gNtE
The part that would most interest you would be this section:
# C Preprocessor Flags
CPPFLAGS +=
# compiler flags
CFLAGS += -ansi -Wall -Wextra -pedantic-errors
# libraries to link to ( m == math )
program_LIBRARIES := m
# LDFLAGS is the variable to hold linker flags
LDFLAGS += $(foreach library,$(program_LIBRARIES),-l$(library))
GNU make defines a lot of default rules. For C compilation and linking, those rules are:
n.o is made automatically from n.c with a recipe of the form ‘$(CC) $(CPPFLAGS) $(CFLAGS) -c’.
n is made automatically from n.o by running the linker (usually called ld) via the C compiler. The precise recipe used is ‘$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)’.
So the way to add "-lm" option to the linker is by defining:
LDLIBS = -lm
Then when you run make with your Makefile, you following commands will be run:
gcc -Wall -g -c 3.c
gcc 3.o -o 3 -lm
(note that make will compile your C program in 2 steps, first creating the object file 3.o then linking the object file into the executable 3)
(see http://www.gnu.org/software/make/manual/ for the GNU make manual)

Resources