How do I put conditionals in makefile pattern rules? - makefile

I have an included file for a makefile that has these rules:
$(OUT_DIR)/%.0: %.c
$(CC) $(CFLAGS) -c -o $# $<
$(CC) $(CFLAGS) -E -P $< -o $(OUT_DIR)/$<.preproc
$(CC) $(CFLAGS) -E -dD $< -o $(OUT_DIR)/$<.macros
Works great. But, for some targets, I only want this:
$(OUT_DIR)/%.0: %.c
$(CC) $(CFLAGS) -c -o $# $<
can I do something like this using a predefined (or command line) variable:
$(OUT_DIR)/%.0: %.c
$(CC) $(CFLAGS) -c -o $# $<
ifeq ($(CIFLAG), 1)
$(CC) $(CFLAGS) -E -P $< -o $(OUT_DIR)/$<.preproc
$(CC) $(CFLAGS) -E -dD $< -o $(OUT_DIR)/$<.macros
endif
I'm sure I can include a different file based on the CIFLAG value, but was hoping I could do it by modifying the pattern rule.
Any ideas?
Thanks!
I did as MadScientist suggested (thanks for the education) but don't get what I want. My file now appears as:
$(OUT_DIR)/%.0: %.c
$(CC) $(CFLAGS) -c -o $# $<
[ $(CI_BUILD) -eq 0 ] \
|| {$(CC) $(CFLAGS) -E -P $< -o $(OUT_DIR)/$<.preproc \
&& $(CC) $(CFLAGS) -E -dD $< -o $(OUT_DIR)/$<.macros; }
But, when I execute, I get this (lots of irrelevant output trimmed):
cc -c -o test.o
[ 1 -eq 0 ] \
|| {cc -E -P test.c -o test.c.preproc \
&& cc -E -dD test.c -o test.c.macros; }
/bin/sh: -c: line 2: syntax error near unexpected token '}'
Is the ';' a problem?

You mustn't indent the Make conditional with tabs:
$(OUT_DIR)/%.0: %.c
$(CC) $(CFLAGS) -c -o $# $<
ifeq ($(CIFLAG), 1)
$(CC) $(CFLAGS) -E -P $< -o $(OUT_DIR)/$<.preproc
$(CC) $(CFLAGS) -E -dD $< -o $(OUT_DIR)/$<.macros
endif
See bullet 4: https://www.gnu.org/software/make/manual/html_node/Recipe-Syntax.html

You cannot use make conditionals like ifeq in the recipe passed to the shell, because the shell runs the recipe not make. You can use shell conditionals:
$(OUT_DIR)/%.0: %.c
$(CC) $(CFLAGS) -c -o $# $<
[ ($(CIFLAG) -ne 1 ] \
|| { $(CC) $(CFLAGS) -E -P $< -o $(OUT_DIR)/$<.preproc \
&& $(CC) $(CFLAGS) -E -dD $< -o $(OUT_DIR)/$<.macros; }
Of course, you haven't discussed how you plan to set the CIFLAG variable for some targets but not others...

I was able to make it work by changing the code to:
$(OUT_DIR)/%.0: %.c
$(CC) $(CFLAGS) -c -o $# $<
if [ ($(CIFLAG) -ne 1 ]; then \
$(CC) $(CFLAGS) -E -P $< -o $(OUT_DIR)/$<.preproc; \
$(CC) $(CFLAGS) -E -dD $< -o $(OUT_DIR)/$<.macros; fi

Related

Makefile composed rule not executing defined other rule

Executing make fails because it cannot find the previously defined rule in the make file. Also the "dir" rule only works if I use "make dir" but when I added it to the composed rule it lists the files in the working directory....
make test_tester
dir
base_types.c dynamic_array.c gists hashtable.c linked_list.c Makefile Makefile_OLD object.c object_table.c out.txt READ_THIS.txt sstring.c tester.c tests ttime.c
build/test_tester.o
make: build/test_tester.o: Command not found
Makefile:7: recipe for target 'test_tester' failed
make: *** [test_tester] Error 127
CC=gcc
CFLAGS=-Wall -I ../include
TEST_DIR=tests
BUILD_DIR=build
test_tester:
dir
$(BUILD_DIR)/test_tester.o
$(BUILD_DIR)/tester.o
$(BUILD_DIR)/base_types.o
$(BUILD_DIR)/object.o
$(BUILD_DIR)/sstring.o
$(CC) $(CFLAGS) $(BUILD_DIR)/test_tester.o $(BUILD_DIR)/tester.o $(BUILD_DIR)/base_types.o \
$(BUILD_DIR)/object.o $(BUILD_DIR)/sstring.o
$(BUILD_DIR)/test_tester.o:
$(CC) $(CFLAGS) -c $(TEST_DIR)/test_tester.c -o $#
$(BUILD_DIR)/tester.o: tester.c
$(CC) $(CFLAGS) -c tester.c -o $#
$(BUILD_DIR)/base_types.o: base_types.c
$(CC) $(CFLAGS) -c base_types.c -o $#
$(BUILD_DIR)/object.o: object.c
$(CC) $(CFLAGS) -c object.c -o $#
$(BUILD_DIR)/sstring.o: sstring.c
$(CC) $(CFLAGS) -c sstring.c -o $#
dir: mkdir -p $(BUILD_DIR)
.PHONY: clean
clean:
rm -f $(BUILD_DIR)/*.o
I figured it out thanks.
CC=gcc
CFLAGS=-Wall -I ../include
TEST_DIR=tests
BUILD_DIR=build
test_tester: dir test_tester.o tester.o base_types.o object.o sstring.o
$(CC) $(CFLAGS) $(BUILD_DIR)/test_tester.o $(BUILD_DIR)/tester.o $(BUILD_DIR)/base_types.o $(BUILD_DIR)/object.o $(BUILD_DIR)/sstring.o -o $#
test_tester.o:
$(CC) $(CFLAGS) -c $(TEST_DIR)/test_tester.c -o $(BUILD_DIR)/$#
tester.o: tester.c
$(CC) $(CFLAGS) -c tester.c -o $(BUILD_DIR)/$#
base_types.o: base_types.c
$(CC) $(CFLAGS) -c base_types.c -o $(BUILD_DIR)/$#
object.o: object.c
$(CC) $(CFLAGS) -c object.c -o $(BUILD_DIR)/$#
sstring.o: sstring.c
$(CC) $(CFLAGS) -c sstring.c -o $(BUILD_DIR)/$#
dir:
mkdir -p $(BUILD_DIR)
.PHONY: clean
clean:
rm -fr $(BUILD_DIR)

Makefile multiple targets from same source file, with different flags

I have a binary that I need to build multiple times with different compiler flags. Therefore, I have a Makefile that states something like:
OBJECTS_A := $(addprefix $(OBJFOLDER)/, $(SOURCES:.cpp=.a.o))
OBJECTS_B := $(addprefix $(OBJFOLDER)/, $(SOURCES:.cpp=.b.o))
OBJECTS_C := $(addprefix $(OBJFOLDER)/, $(SOURCES:.cpp=.c.o))
I also define a rule to change the flags for each OBJECTS_x:
$(OBJECTS_B): DEFINES+=-D_B
$(OBJECTS_C): DEFINES+=-D_C
And this is where the problem happens: If I state the targets separately, as:
$(OBJFOLDER)/%.a.o: %.cpp
$(COMPILER) $(CFLAGS) $(INCFOLDER) $(DEFINES) -c $< -o $#
$(OBJFOLDER)/%.b.o: %.cpp
$(COMPILER) $(CFLAGS) $(INCFOLDER) $(DEFINES) -c $< -o $#
$(OBJFOLDER)/%.c.o: %.cpp
$(COMPILER) $(CFLAGS) $(INCFOLDER) $(DEFINES) -c $< -o $#
All works. However, if I merge all rules into one, only the first is evaluated:
$(OBJFOLDER)/%.a.o $(OBJFOLDER)/%.b.o $(OBJFOLDER)/%.c.o: %.cpp
$(COMPILER) $(CFLAGS) $(INCFOLDER) $(DEFINES) -c $< -o $#
What I get on a dry run is that only $(OBJFOLDER)/%.a.o objects are build, but on the linking rule each binary requires its objects (and b and c binaries fail to build, therefore).
Any ideas?
Thank you!
You can achieve this using secondary expansion :
.SECONDEXPANSION:
$(OBJFOLDER)/%.o: $$(basename $$*).cpp
$(COMPILER) $(CFLAGS) $(INCFOLDER) $(DEFINES) -c $< -o $#
Note that this is not a very idiomatic way of doing this, a more usual define / call / eval combo can be used to generate rules as in your first solution :
VARIANTS=a b c
DEFINES_FOR_a=
DEFINES_FOR_b=-D_B
DEFINES_FOR_c=-D_C
define make_target =
$$(OBJFOLDER)/%.$(1).o: %.cpp
$$(COMPILER) $$(CFLAGS) $$(INCFOLDER) $$(DEFINES_FOR_$(1)) -c $$< -o $$#
endef
$(eval $(foreach variant,$(VARIANTS),$(call make_target,$(variant))))
Another way is to create symlinks to your source files and compile those with different flags. This way the same one generic pattern rule (OBJFOLDER)/%.o: %.cpp can build all of your targets:
OBJECTS_A := $(SOURCES:%.cpp=$(OBJFOLDER)/%.a.o)
OBJECTS_B := $(SOURCES:%.cpp=$(OBJFOLDER)/%.b.o)
OBJECTS_B := $(SOURCES:%.cpp=$(OBJFOLDER)/%.c.o)
$(OBJECTS_B): DEFINES+=-D_B
$(OBJECTS_C): DEFINES+=-D_C
%.a.cpp : %.cpp
ln -s $< $#
%.b.cpp : %.cpp
ln -s $< $#
%.c.cpp : %.cpp
ln -s $< $#
$(OBJFOLDER)/%.o: %.cpp
$(COMPILER) $(CFLAGS) $(INCFOLDER) $(DEFINES) -c -o $# $<

makefile & gcc -c compilation - how to name object files

I'm playing with makefile for class project.
(1) I need to create debug rule. But I don't know how to give specific names to object files that I need (do I need?) to create that debug-enabled executable.
(2)I assume file can be further reduced to fewer lines of code. Could you suggest how?
This is my makefile:
CC = gcc
CFLAGS = -Wall -Wextra -std=c11
OBJ = hospital.o parse.o structure.o description.o patientList.o diseaseList.o
DOBJ = hospital.dbg.o parse.dbg.o structure.dbg.o description.dbg.o patientList.dbg.o diseaseList.dbg.o
all: hospital
hospital: $(OBJ)
$(CC) $^ -o $#
hospital.o: hospital.c structure.h
$(CC) $(CFLAGS) -c $^
parse.o: parse.c parse.h
$(CC) $(CFLAGS) -c $^
structure.o: structure.c structure.h
$(CC) $(CFLAGS) -c $^
patientList.o: patientList.c patientList.h
$(CC) $(CFLAGS) -c $^
diseaseList.o: diseaseList.c diseaseList.h
$(CC) $(CFLAGS) -c $^
description.o: description.c description.h
$(CC) $(CFLAGS) -c $^
debug: $(DOBJ)
$(CC) $^ -o hospital.dbg
hospital.dbg.o: hospital.c
$(CC) $(CFLAGS) -g $# $< -c
parse.dbg.o: parse.c parse.h
$(CC) $(CFLAGS) -g $# $< -c
structure.dbg.o: structure.c structure.h
$(CC) $(CFLAGS) -g $# $< -c
patientList.dbg.o: patientList.c patientList.h
$(CC) $(CFLAGS) -g $# $< -c
diseaseList.dbg.o: diseaseList.c diseaseList.h
$(CC) $(CFLAGS) -g $# $< -c
description.dbg.o: description.c description.h
$(CC) $(CFLAGS) -g $# $< -c
clean:
rm *.o hospital.dbg hospital *~
.PHONY: all clean
Thanks

How to replace parent directory in Makefile

I've the following situation:
SOURCES=home/main.cpp modelChecking/Configuracao.cpp modelChecking/Estado.cpp modelChecking/Formula.cpp modelChecking/ModelChecking.cpp lib/VisitTree.cpp
SUFIX=$(SOURCES:.cpp=.o)
OBJECTS=$(SUFIX)
all: refiner
refiner: $(OBJECTS)
$(CC) $^ -o refiner
home/main.o: home/main.cpp
$(CC) $(CFLAGS) $< -o $#
modelChecking/Configuracao.o: modelChecking/Configuracao.cpp
$(CC) $(CFLAGS) $< -o $#
modelChecking/Estado.o: modelChecking/Estado.cpp
$(CC) $(CFLAGS) $< -o $#
...
...and so on.
As you can see, I have different directories to compile my executable.
Now, I want to put every file .o in the bin/ folder and the variable OBJECT must replace the every parent directory, and I tried different ways:
OBJECTS=$(SUFIX:%/ = bin/)
OBJECTS=$(subst %/,bin/,$(SUFIX))
OBJECTS=$(patsubst %/,bin/,$(SUFIX))
When I use something like this $(subst home/,bin/,$(SUFIX)) it works, because I type the substring "home/", but I need of a regular expression to replace all directories.
And I'll need to change the target too, perhaps the code below will works:
%.o: %.cpp
$(CC) $(CFLAGS) $< -o $#
... But I prefer every target separate
You are looking for SUFIX=$(addprefix bin/,$(notdir $(SOURCES:.cpp=.o)))
The Makefile will look like:
SOURCES=home/main.cpp modelChecking/Configuracao.cpp
SUFIX=$(addprefix bin/,$(notdir $(SOURCES:.cpp=.o)))
OBJECTS=$(SUFIX)
all: refiner
refiner: $(OBJECTS)
$(CC) $^ -o refiner
bin/main.o: home/main.cpp
$(CC) $(CFLAGS) -c $< -o $#
bin/Configuracao.o: modelChecking/Configuracao.cpp
$(CC) $(CFLAGS) -c $< -o $#
However I suggest to use SUBDIRS instead. Create to Makefiles
Makefile
SUBDIRS = bin
.PHONY: subdirs $(SUBDIRS)
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $#
bin/Makefile
SOURCES=../home/main.cpp ../modelChecking/Configuracao.cpp
SUFIX=$(addprefix bin/,$(notdir $(SOURCES:.cpp=.o)))
OBJECTS=$(SUFIX)
all: refiner
refiner: $(OBJECTS)
$(CC) $^ -o refiner
main.o: ../home/main.cpp
$(CC) $(CFLAGS) -c $< -o $#
Configuracao.o: ../modelChecking/Configuracao.cpp
$(CC) $(CFLAGS) -c $< -o $#
This way you will not have to worry about object prefix.

What do $< and $# represent in a Makefile?

Can anybody please explain the meaning of $< and $# in a Makefile?
$< evaluates to the first "prerequisite" in the make rule, and $# evaluates to the "target" in the make rule.
Here's an example:
file.o : file.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $#
In this case, $< will be replaced with file.c and $# will be file.o.
These are more useful in generic rules like this:
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $#
See this manual for more info.
$# is the target of the current rule.
$< is the name of the first prerequisite ("source") of the current rule.
So for example:
.c.o:
$(CC) -c $(CFLAGS) -o $# $<
This will expand to a command something like:
gcc -c -Wall -o foo.o foo.c
See also the GNU make manual ยง 10.5.3, "Automatic Variables".

Resources