How do I use automatic variables in makefile conditionals? - makefile

Summarize the problem
I would like to change the compilation for a single source file, like so:
%.exe: %.c
ifeq($#, cannon.exe)
$(CC) $(CFLAGS) -o $(patsubst %.exe,%,$#) $^ $(inc_flags) $(LDLIBS)
else
$(CC) $(CFLAGS) -o $(patsubst %.exe,%,$#) $^
endif
but it doesn't work!
Describe what you’ve tried
I have tried getting bash conditionals involved, but I couldn't figure it out and I shouldn't have to mix bash and make.
Here's the full Makefile:
CC := gcc
CFLAGS := -g -Wall -Wextra
LDLIBS := -lm
inc_dirs := math.h stdio.h
inc_flags := $(addprefix -I,$(inc_dirs))
executable:=cannon.exe
source:=$(executable:%.exe=%.cpp)
.DELETE_ON_ERROR:
all: $(executable)
%.exe: %.c
ifeq($#, cannon.exe)
$(CC) $(CFLAGS) -o $(patsubst %.exe,%,$#) $^ $(inc_flags) $(LDLIBS)
else
$(CC) $(CFLAGS) -o $(patsubst %.exe,%,$#) $^
endif
clean:
rm -f $(executable:%.exe=%)

It turns out that if you want to do a special case for a certain source file, you can just define its recipe explicitly! Make will take explicit over implicit rules. Full working code below.
CC := gcc
CFLAGS := -g -Wall -Wextra
LDLIBS := -lm
inc_dirs := math.h stdio.h
inc_flags := $(addprefix -I,$(inc_dirs))
executable:=cannon.exe
source:=$(executable:%.exe=%.cpp)
# no deps so no objs
.DELETE_ON_ERROR:
all: $(executable)
%.exe: %.c
$(CC) $(CFLAGS) -o $(patsubst %.exe,%,$#) $^
cannon.exe: cannon.c
$(CC) $(CFLAGS) -o $(patsubst %.exe,%,$#) $^ $(inc_flags) $(LDLIBS)
clean:
rm -f $(executable:%.exe=%)

A better / more make-ish solution is to use constructed variable names, like this:
CC := gcc
CFLAGS := -g -Wall -Wextra
LDLIBS := -lm
inc_dirs := math.h stdio.h
cannon_CFLAGS := $(addprefix -I,$(inc_dirs))
executable := cannon.exe
source := $(executable:%.exe=%.cpp)
# no deps so no objs
.DELETE_ON_ERROR:
all: $(executable)
%.exe: %.c
$(CC) $(CFLAGS) $($*_CFLAGS) -o $# $^
clean:
rm -f $(executable:%.exe=%)
Notes:
Your rules should always create the same name as the target in the makefile. It's not right for the target to be named foo.exe but the build command creates a file named foo. If you want to create a file named foo, then the makefile target should be named foo. Basically you should always create the file contained in the make variable $#.
Is it really the case that the directories you have are named math.h and stdio.h? That's.... bizarre. And very likely to cause serious problems. If math.h and stdio.h are files, then you should not add them with -I because -I takes directory names, in which to search file files. It doesn't take filenames.
If you really did create local files named math.h and stdio.h, that's also a very bad idea: those are standard header file names and you shouldn't redeclare them yourself unless you really know what you're doing.
If you didn't create these files and you're just trying to include the standard headers math.h and stdio.h in your file, then you definitely don't need to add any flags to your compile line. Just include them.

Related

Make: Compiling only one .c file though i modify the header file which is included in two .c files [duplicate]

This question already has answers here:
'make' does not recompile when source file has been edited
(3 answers)
Closed 4 years ago.
I have two .c files and one .h file which is included in both .c files.
I have make file :
CC=gcc
CFLAGS=-I.
OBJ = hellofunc.o hellomake.o
DEPS := $(OBJ:.o=.d)
-include $(DEPS)
%.o: %.c
$(CC) $(CFLAGS) -MM -MT $# -MF $(patsubst %.o,%.d,$#) $<
$(CC) -c -o $# $< $(CFLAGS)
hellomake: hellomake.o hellofunc.o
$(CC) -o $# $^
I modified hellomake.h header file and then ran above make file. It is compiling only the first file which is assigned to DEPS variable, Which is hellofunc.c
FYI, When i change the order of DEPS variable it is compiling hellomake.o , Seems like make is only picking first file assigned to DEPS variable.
Is there anything wrong in my makefile.. Please help.
As #tripleee noted in the comments, make will by default build the first target it encounters. Since the include files are included directly, as if they were cut-and-pasted in place, the first target in the first .d file (which I'm guessing you'll find to be hellofunc.c) is the first target that make encounters, so that's what make aims to build.
If you move the include line to the end of the file, then the first target in the file will be hellomake, and so that's the target make will attempt to build by default.
Meta remark: if you can, I'd say it's better to avoid this pattern of depending on .d files, and instead aim to express sufficiently many of the dependencies ‘by hand’ directly in the makefile. Doing it this .d way does work (ie, I'm not saying what you're doing is wrong) and appears to be labour-saving, but in my experience it tends to be a little brittle, partly because if you don't have the .d files to hand then you suddenly have zero dependencies. In order to have the .d files to hand, you'll have to check them in to your code repository (you are using a repository, aren't you?), but that will mean they'll frequently be trivially out of date, and... it can turn into a bit of a mess.
EDIT: considered MadScientist's comment and blog post about separate recipe for .d files.
You could tell make how to generate the .d files with a separate rule instead of putting this in another recipe (but see the above mentioned blog post for several reasons for not doing so).
And you should probably tell make that your default goal is hellomake:
.DEFAULT_GOAL := hellomake
CC=gcc
CFLAGS=-I.
OBJ = hellofunc.o hellomake.o
DEPS := $(OBJ:.o=.d)
-include $(DEPS)
%.d: %.c
$(CC) $(CFLAGS) -MM -MT $# -MF $# $<
%.o: %.c
$(CC) -c -o $# $< $(CFLAGS)
hellomake: hellomake.o hellofunc.o
$(CC) -o $# $^
And it would probably be even better if you were letting make find the source files and deduce the rest:
.DEFAULT_GOAL := hellomake
CC := gcc
CFLAGS := -I.
SRCS := $(wildcard *.c)
OBJS := $(patsubst %.c,%.o,$(SRC))
DEPS := $(patsubst %.c,%.d,$(SRC))
-include $(DEPS)
%.d: %.c
$(CC) $(CFLAGS) -MM -MT $# -MF $# $<
%.o: %.c
$(CC) -c -o $# $< $(CFLAGS)
hellomake: $(OBJS)
$(CC) -o $# $^
Finally, following MadScientist's advices, a better, more efficient, less prone to failures solution could be:
.DEFAULT_GOAL := hellomake
CC := gcc
CFLAGS := -I.
SRCS := $(wildcard *.c)
OBJS := $(patsubst %.c,%.o,$(SRC))
DEPS := $(wildcard $(patsubst %.c,%.d,$(SRC)))
include $(DEPS)
%.o: %.c %.d
$(CC) -MT $# -MMD -MP -MF $*.Td $(CFLAGS) -c -o $# $<
mv -f $*.Td $*.d && touch $#
hellomake: $(OBJS)
$(CC) -o $# $^
%.d: ;
.PRECIOUS: %.d

Defer evaluation of automatic variables in recipes

I have the following makefile:
CC ?= gcc
LD := gcc
CFLAGS := -Wall -Wextra -Werror -Wfatal-errors
LDFLAGS :=
LIBRARIES := m c
INCLUDEDIRS := .
LIBS = $(addprefix -l,$(LIBRARIES))
INCLUDES = $(addprefix -I,$(INCLUDEDIRS))
SRC := $(wildcard *.c)
TARGET = $(TARGETDIR)/test
OBJDIR = $(TARGETDIR)/obj/
OBJ = $(addprefix $(OBJDIR),$(SRC:%.c=%.c.o))
.SUFFIXES:
.SUFFIXES: .c.o
.PHONY: all debug i7avx i7avx-debug
all: TARGETDIR := generic
all: CFLAGS += -O3
all: LDFLAGS += -s
all: $(TARGET)
debug: CFLAGS += -Og
debug: TARGETDIR := generic/dbg
debug: $(TARGET)
$(OBJDIR):
#mkdir -p $(OBJDIR)
$(OBJ): | $(OBJDIR)
$(OBJDIR)%.c.o : %.c
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $#
$(TARGET) : $(OBJ)
$(LD) $(LDFLAGS) -o $# $^ $(LIBS)
The special thing here is that the output directory depends on the target.
Currently, only all and debug is defined, but the idea is to support a whole slew of architectures, and to define an outputdir per target.
Problem: this does not work. If I run this, I get:
cc -Wall -Wextra -Werror -Wfatal-errors -O3 -I. -c main.c -o /obj/main.c.o
Assembler messages:
Fatal error: can't create /obj/main.c.o: No such file or directory
make: *** [Makefile:37: /obj/main.c.o] Error 1
Which implies that the TARGETDIR variable was expanded too late.
If I replace the automatic variables with real variables, it does work:
$(OBJ): | $(OBJDIR)
$(OBJDIR)%.c.o : %.c
$(CC) $(CFLAGS) $(INCLUDES) -c $(SRC) -o $(OBJ)
$(TARGET) : $(OBJ)
$(LD) $(LDFLAGS) -o $(TARGET) $(OBJ) $(LIBS)
running this:
cc -Wall -Wextra -Werror -Wfatal-errors -O3 -I. -c main.c -o generic/obj/main.c.o
gcc -s -o generic/test generic/obj/main.c.o -lm -lc
Sooo, how can I make the autmatic variables expand after the TARGETDIR was defined?
Make does handle wildcards very deftly, or this would be a much easier problem.
As it is, I think the best solution is to use recursive Make. Just change this:
all: $(TARGET)
debug: $(TARGET)
to this:
all debug:
$(MAKE) $(TARGET) TARGETDIR=$(TARGETDIR) CFLAGS+='$(CFLAGS)' LDFLAGS=$(LDFLAGS)
First, the reason why it does not work :
You're using target-specific variables, but those are only available in the context of a target recipe (I'm quoting the manual here), not during the rules evaluation :
Make will first read your Makefile, evaluate your $(OBJDIR) and $(TARGET) rules (at this point $(TARGETDIR)is not yet defined) then, it will try to update all, and at this point set $(TARGETDIR) to the target-specific value for all (which explains why you're second example work, but it should rebuild every time).
I may have some hints to achieving what you're trying to do (I'm actually planning on doing a similar thing soon) :
you could use the eval function to generate one rule for each build/archi, like this:
#define TARGET_RULE
$(TARGET) : $(OBJ)
$$(RECIPE)
#endif
$(foreach TARGETDIR, $(BUILD_LIST), $(eval $(TARGET_RULE))
($$ is needed for the recipe to avoid it being expanded during the rule evaluation)
You should also be able to define only the rule or rules for the target you are currently building (not sure if that would make a signifiant perf difference).

Falty Makefile causes undefined reference and multiple definition errors

I'm on my path to study the wonderful world of Makefiles and this was my first attempt on my own makefile. However I have hit my small brains at a wall with errors and I can not figure the cause, I only know the issue is in the Makefile, I am doing something wrong, because when I compile my "project" manually, it compiles.
cxx := g++
dirs := obj bld_win32 bld_linux
cpp := $(wildcard src/*.cpp)
obj := $(addprefix obj/,$(notdir $(cpp:.cpp=.o)))
ifeq ($(OS), Windows_NT)
target := bld_win32/engine.exe
flags := -mwindows -lopengl32 -openal32 -lgdi32
else
target := bld_linux/engine
flags := -lX11 -lGL -lopenal -lalut
endif
all: precheck $(target)
precheck:
mkdir -p $(dirs)
$(target): $(obj)
$(cxx) -std=c++11 -Wall -o $# $^ $(flags)
obj/%.o: $(cpp)
$(cxx) -c -o $# $<
Your pattern rule is wrong, as you would have noticed if you'd tried to build object files one by one using this makefile. This rule:
obj/%.o: $(cpp)
$(cxx) -c -o $# $<
lists all source files as prerequisites of every object file, and when you try to build any object file, it compiles only the first source file in the list (src/aardvark.cpp, or whatever), so that all of your object files are the same, even though they have different names.
Change it to this:
obj/%.o: src/%.cpp
$(cxx) -c -o $# $<

Makefile wildcard rules for multiple file extensions

I have a number of assembly and C source files in a directory. I have mentioned a list of files to be compiled as C_OBJFILES and ASM_OBJFILES. I have added the rules as
%.o: %.S
$(AS) $(ASFLAGS) -o $# $<
%.o: %.c
$(CC) $(CFLAGS) -o $# $<
for generating the object files. Some of the assembly files has .s extension. Some has .S and remaining .asm. Is there a way to specify these different extensions in a single rule instead of separate %.o:%.asm and %.o:%.s?
Although it can be solved by a simple prename before make, I would like to explore the options with Makefile. Below is the complete Makefile I have written.
ASM_OBJLIST:=startup.o vectors.o lowlevel.o
C_OBJLIST:=test.o
LD_SCRIPT:=test.ld
CROSS_COMPILE:=arm-none-eabi-
AS:=$(CROSS_COMPILE)as
CC:=$(CROSS_COMPILE)gcc
LD:=$(CROSS_COMPILE)ld
OBJCOPY:=$(CROSS_COMPILE)objcopy
CFLAGS:= -c -mcpu=arm926ej-s -g
ASFLAGS:= -mcpu=arm926ej-s -g
BIN_TARGET:=test.bin
$(BIN_TARGET): $(C_OBJLIST) $(ASM_OBJLIST)
$(LD) -T $(LD_SCRIPT) -o $#.elf $^
$(OBJCOPY) -O binary $#.elf $#
%.o: %.S
$(AS) $(ASFLAGS) -o $# $<
%.o: %.c
$(CC) $(CFLAGS) -o $# $<
clean:
rm -f *.o *.elf *.bin
If handling multiple extensions in a single rule is possible, I can just use gcc for all source files instead of bothering to use as at all. (I guess)
Use static patterns and split the objects into two sets, there are a number of other improvements too
Make already has defaults for things like AS and CC, use them.
You're already using ASFLAGS and CFLAGS correctly (except for -c, see below), use the same pattern for other flags too
You missed a chance to express the bin->elf dependency
You can recycle the built-in recipes even if you need to redefine the rules (LINK.o, COMPILE.c which already has the -c flag), although the .c rule is currently superfluous as it's exactly the same as the built-in one.
clean should be PHONY
Never delete stuff with *, explicitly delete only the files you are responsible for.
I'd just use GCC to link but if you need to link with LD then you'll need to change the recipe.
ASM_OBJLIST := startup.o
S_OBLIST := vectors.o lowlevel.o
C_OBJLIST := test.o
LD_SCRIPT := test.ld
CROSS_COMPILE := arm-none-eabi-
AS := $(CROSS_COMPILE)$(AS)
CC := $(CROSS_COMPILE)$(CC)
OBJCOPY := $(CROSS_COMPILE)objcopy
CFLAGS := -mcpu=arm926ej-s -g
ASFLAGS := -mcpu=arm926ej-s -g
LDFLAGS := -Wl,-T $(LD_SCRIPT)
OBJFLAGS := -O binary
BIN_TARGET := test.bin
$(BIN_TARGET): $(BIN_TARGET).elf
$(OBJCOPY) $(OBJFLAGS) $< $#
$(BIN_TARGET).elf: $(C_OBJLIST) $(ASM_OBJLIST)
$(LINK.o) $^ $(LDLIBS) -o $#
$(ASM_OBJLIST): %.o: %.asm
$(S_OBJLIST): %.o: %.S
$(ASM_OBJLIST) $(S_OBJLIST):
$(COMPILE.S) -o $# $<
%.o: %.c
$(COMPILE.c) -o $# $<
.PHONY: clean
clean:
$(RM) $(C_OBJLIST) $(ASM_OBJLIST) $(BIN_TARGET).elf $(BIN_TARGET)

Making a better Makefile

so I learned what a Makefile was some time ago, created a template Makefile and all I do is copy and alter the same file for every program I'm doing. I changed it a few times, but it's still a very crude Makefile. How should I improve it? This is an example of my current version:
CC = g++
CFLAGS = -std=gnu++0x -m64 -O3 -Wall
IFLAGS = -I/usr/include/igraph
LFLAGS = -ligraph -lgsl -lgslcblas -lm
DFLAGS = -g -pg
# make all
all: run test
# make a fresh compilation from scratch
fresh: clean test
#makes the final executable binary
run: main.o foo1.o foo2.o
$(CC) $(CFLAGS) $(LFLAGS) $^ -o $#
#makes the test executable with debugging and profiling tags
test: test.o foo1.o foo2.o
$(CC) $(DFLAGS) $(CFLAGS) $(LFLAGS) $^ -o $#
#makes teste.o
teste.o: teste.cpp
$(CC) $(CFLAGS) $(IFLAGS) -c $^ -o $#
#makes main.o
main.o: main.cpp
$(CC) $(CFLAGS) $(IFLAGS) -c $^ -o $#
#file foo1
foo1.o: foo1.cpp
$(CC) $(CFLAGS) $(IFLAGS) -c $^ -o $#
#file foo2
foo2.o: foo2.cpp
$(CC) $(CFLAGS) $(IFLAGS) -c $^ -o $#
clean: clean-test clean-o clean-annoying
clean-test:
rm test-rfv
clean-o:
rm *.o -rfv
clean-annoying:
rm *~ -rfv
Just by visually comparing with other makefiles I saw around in the web, this seems to be not a very bright Makefile. I don't know how they work, but I can see there's significantly less boilerplate and more generic code in them.
Can this can be made better, safer, and easier to particularize for each project?
You don't want to name specific files in a makefile if you can get away with it, and 99% of the time you can. This page shows how to develop a very general makefile. The following is my own makefile, based on that page's info:
SHELL := bash
PROG := pathed.exe
OUTDIRS := bin/debug bin/rel obj/debug obj/rel
PROG_REL := bin/rel/$(PROG)
PROG_DEBUG := bin/debug/$(PROG)
SRCFILES := $(wildcard src/*.cpp)
OBJFILES_REL := $(patsubst src/%.cpp,obj/rel/%.o,$(SRCFILES))
OBJFILES_DEBUG := $(patsubst src/%.cpp,obj/debug/%.o,$(SRCFILES))
DEPFILES := $(patsubst src/%.cpp,obj/%.d,$(SRCFILES))
CFLAGS := -Iinc -Wall -Wextra -MMD -MP
DBFLAGS := -g
RELFLAGS :=
CC := g++
.PHONY: default all testmake debug release clean dirs
default: debug
all: dirs clean debug release
dirs:
#mkdir -p $(OUTDIRS)
debug: $(PROG_DEBUG)
release: $(PROG_REL)
testmake:
#echo OBJFILES_REL = $(OBJFILES_REL)
#echo OBJFILES_DEBUG = $(OBJFILES_DEBUG)
#echo SRCFILES = $(SRCFILES)
#echo DEPFILES = $(DEPFILES)
clean:
rm -f $(OBJFILES_REL) $(OBJFILES_DEBUG) $(DEPFILES) $(PROG)
$(PROG_REL): $(OBJFILES_REL)
$(CC) $(OBJFILES_REL) -o $(PROG_REL)
strip $(PROG_REL)
#echo "---- created release binary ----"
$(PROG_DEBUG): $(OBJFILES_DEBUG)
$(CC) $(OBJFILES_DEBUG) -o $(PROG_DEBUG)
#echo "---- created debug binary ----"
-include $(DEPFILES)
obj/rel/%.o: src/%.cpp
$(CC) $(RELFLAGS) $(CFLAGS) -MF $(patsubst obj/rel/%.o, obj/%.d,$#) -c $< -o $#
obj/debug/%.o: src/%.cpp
$(CC) $(DBFLAGS) $(CFLAGS) -MF $(patsubst obj/debug/%.o, obj/%.d,$#) -c $< -o $#
Do NOT use CC for the C++ compiler. The standard convention is that CC is the C compiler, CXX is the C++ compiler. CFLAGS are flags for the C compiler, CXXFLAGS are flags for the C++ compiler, and CPPFLAGS are flags for the pre-processor (eg, -I or -D flags). Use LDFLAGS for -L flags to the linker, and LDLIBS (or LOADLIBES) for -l flags.
Using the standard conventions is good not just because it makes things easier for others to understand, but also because it allows you to take advantage of implicit rules. If make needs to make a .o file from a .c file and you have not provided a rule, it will use a standard rule and honor the settings of CC, CFLAGS, and CPPFLAGS. If CC is a C++ compiler, things will probably not work.

Resources