Libft bonus rule relinks - makefile

So, right now my Makefile looks like this
SRCS = (let's say there are some .c files)
BONUS_SRCS = (and there are some _bonus.c files)
OBJS = $(SRCS:.c=.o)
BONUS_OBJS = $(BONUS_SRCS:.c=.o)
HDRS = libft.h
NAME = libft.a
RM = rm -rf
GCC = gcc
AR = ar -crs
FLAGS = -Wall -Wextra -Werror
.c.o:
$(GCC) $(FLAGS) -I includes -c $< -o $(<:.c=.o)
$(NAME): $(OBJS) $(HDRS)
$(AR) $(NAME) $(OBJS)
all: $(NAME)
bonus: $(OBJS) $(BONUS_OBJS) $(HDRS)
$(AR) $(NAME) $(OBJS) $(BONUS_OBJS)
re: clean fclean all
clean:
$(RM) $(OBJS) $(BONUS_OBJS)
fclean: clean
$(RM) $(NAME)
.PHONY:
all clean fclean re bonus
And if I try to make bonus make compiles bonus functions at first, and every other make bonus relinks. Is there any way to tell make not to relink?
By "relink" I mean recreating the libft.a every time I call make bonus.
Example make:
$> make
gcc -Wall -Wextra -Werror -I includes -c test_a.c -o test_a.o
ar -crs libft.a test_a.o
$> make
make: `libft.a' is up to date.
$>
But make bonus:
$> make bonus
gcc -Wall -Wextra -Werror -I includes -c test_a.c -o test_a.o
gcc -Wall -Wextra -Werror -I includes -c test_b.c -o test_b.o
ar -crs libft.a test_a.o test_b.o
$> make bonus
ar -crs libft.a test_a.o test_b.o
$>
And this is confusing as hell.

It's not confusing if you remember that make doesn't have some magic database of information about what it did last time. It has your makefile, and it has the files on the filesystem (and the last modified time that the filesystem tracks).
So when you run make bonus, make looks for a rule that knows how to build the target bonus and finds this rule:
bonus: $(OBJS) $(BONUS_OBJS) $(HDRS)
$(AR) $(NAME) $(OBJS) $(BONUS_OBJS)
This rule tells make that if it runs this recipe the target bonus will be updated. Make sees that the target bonus does not exist (there's no file named bonus on the disk) so it decides that target is out of date and needs to be updated.
But, of course, your recipe does NOT update the target bonus; bonus is not created or updated by this recipe at all.
So, the next time you run make bonus make again looks for a file named bonus, it still doesn't exist, so make again uses the recipe you told it would update that target. And on and on.
Check out Paul's Second Rule of Makefiles.

It should work to adjust your makefile as following
.PHONY: bonus
bonus: $(NAME)

Related

I dont understand why my makefile is relinking

I have the following files:
Makefile
libft.h
ft_atoi.c
ft_itoa.c
...
And the following Makefile:
SRC_FILES:=$(wildcard *.c)
NAME=libft.a
LIBSO=libft.so
CC=gcc
CFLAGS=-Wall -Wextra -Werror -g
OBJ_DIR=obj/
HDR_NAME=libft.h
SRC_NAMES=${SRC_FILES:.c=.o}
SRC_NAMES_O=$(addprefix $(OBJ_DIR), $(SRC_NAMES))
all: $(NAME)
$(NAME): $(OBJ_DIR) $(SRC_NAMES)
ar -rc $# $(SRC_NAMES_O)
ranlib $#
$(OBJ_DIR):
mkdir $#
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $(OBJ_DIR)$#
clean:
rm -rf $(OBJ_DIR)
sclean: clean
rm -rf $(NAME) $(LIBSO)
fclean: clean
rm -f $(NAME)
re: fclean all
so:
$(CC) -nostartfiles -fPIC $(CFLAGS) $(SRC_FILES) $(BONUS_FILES)
gcc -nostartfiles -shared -o libft.so $(SRC_FILES) $(BONUS_FILES)
.PHONY: all bonus clean sclean fclean re so
But it is relinking. What did I do Wrong? I feel like it should run but it keeps recompiling everything each time I run make.
By code-review-only, I suspect that the culprit is the following line:
$(NAME): $(OBJ_DIR) $(SRC_NAMES)
ar -rc $# $(SRC_NAMES_O)
ranlib $#
Namely, $(OBJ_DIR) is a normal prerequisite of $(NAME), so each time a file (or subdirectory) in the obj/ directory is created, renamed, or deleted, the timestamp of obj changes, and thereby triggers a rebuild of the make all target.
To avoid this, you may want to replace this target with:
$(NAME): $(SRC_NAMES) | $(OBJ_DIR)
ar -rc $# $(SRC_NAMES_O)
ranlib $#
The underlying concept of GNU Make is called order-only prerequisite:
Occasionally (…) you have a situation where you want to impose a specific ordering on the rules to be invoked without forcing the target to be updated if one of those rules is executed. In that case, you want to define order-only prerequisites. Order-only prerequisites can be specified by placing a pipe symbol (|) in the prerequisites list: any prerequisites to the left of the pipe symbol are normal; any prerequisites to the right are order-only:
targets : normal-prerequisites | order-only-prerequisites
The normal prerequisites section may of course be empty. (…)

Multiple compilers in one Makefile

I found this question here, which does exactly what I want. However, being new to makefiles, I am having a hard time seeing how to implement the second answer in my current setup.
I have a simple makefile for which I would like to use different compilers when compiling for Linux or cross-compiling for Windows. As presented, the file below works. To make for Linux I just type make, and for windows I type make os=win. In the interest of learning how makefiles work a little better, I would like to be able to implement the linked answer in the current Makefile so that I can make for windows just by typing make win or make cross as presented in the linked question.
Could someone help me understand how the structure presented in the linked answer could be implemented in the specific case I have here? Presumably there is a more elegant way than having native: and cross: targets and replicating almost the entirety of the code across both.
Any suggestions for cleaning up the Makefile in general are also welcome.
ifeq ($(os),win)
CC=x86_64-w64-mingw32-gcc
OUT=cusum.exe
else
CC=gcc
OUT=cusum
endif
CFLAGS=-D_GNU_SOURCE -O3 -Wall -Wextra -lm --static
DEPS=bessel.h detector.h io.h stepfit.h lmmin_int64.h utils.h
ODIR=obj
_OBJ=main.o bessel.o detector.o io.o lmmin_int64.o stepfit.o utils.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
LIBS=-lm
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS)
$(OUT): $(OBJ)
$(CC) -o $# $^ $(CFLAGS)
.PHONY: clean
clean:
rm -f $(OUT) $(ODIR)/*.o *~ core $(INCDIR)/*~
You only have to introduce an upper-level target. Target-specific variables are inherited by their prerequisites, so you don't have to change all the targets.
However, you cannot update the content of targets or prerequisites using this method so you can't remove the assignment of OUT. This means you can't get rid of the OS variable assignment.
One simple way to do it is to use recusive invocations of make. Write your makefile normally:
O = o
E =
CC = gcc
OUT = cusum$E
CFLAGS = -D_GNU_SOURCE -O3 -Wall -Wextra -lm --static
DEPS = bessel.h detector.h io.h stepfit.h lmmin_int64.h utils.h
ODIR = obj
_OBJ = main.$O bessel.$O detector.$O io.$O lmmin_int64.$O stepfit.$O utils.$O
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
LIBS = -lm
$(ODIR)/%.$O: %.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS)
$(OUT): $(OBJ)
$(CC) -o $# $^ $(CFLAGS)
.PHONY: clean
clean:
rm -rf $(OUT)* obj wobj *~ core $(INCDIR)/*~
Then add a target that recursively invokes make overriding the relevant variables (be sure to put it at the end or at least after the $(OUT) target):
win:
$(MAKE) CC=x86_64-w64-mingw32-gcc E=.exe O=obj ODIR=wobj
Now you can run make win and it will recursively invoke make overriding the appropriate variables.

Makefile recompiles after cleaning objects (but not the name target)

When I call the clean rule with make clean all objects are correctly deleted. However if just after I call the all rule with make it recompiles all the objects again, even if the target is already there.
As far as I know, it should not recompile the objects beacuse the $(NAME) dependecy in the all rule is already satisfied. Indeed make clean erase just the object and not the program target too.
Somebody can explain me how to avoid recompiling after a make clean call? Thank you.
Here is the makefile:
NAME = myprogram
CC = clang++
CFLAGS = -Werror -Wextra -Wall -O3 -std=c++11
DIR_SRCS = srcs/
DIR_OBJS = objs/
DIR_INCS = incs/
FILES = main.cpp \
file1.cpp \
files2.cpp \
file3.cpp \
OBJS = $(addprefix $(DIR_OBJS), $(notdir $(addprefix $(DIR_SRCS), $(FILES:.cpp=.o))))
all: $(NAME)
$(NAME): $(OBJS)
$(CC) $(OBJS) $(CFLAGS) -I$(DIR_INCS) -o $(NAME)
$(DIR_OBJS)%.o: $(DIR_SRCS)%.cpp
mkdir -p $(DIR_OBJS)
$(CC) $(CFLAGS) -I$(DIR_INCS) -c $< -o $#
clean:
rm -rf $(DIR_OBJS)
fclean: clean
rm -f $(NAME)
re: fclean all
.PHONY: all clean fclean re
The $(NAME) dependecy in the all rule is NOT satisfied, because $(NAME) depends on $(OBJS) - and $(OBJS) does not exist (after make clean). This is how make works. If any dependencies don't exists or are newer than the target - the dependecies need to be regenerated (and in turn - the target).
For example: program --> program.o --> (program.c, program.h)
If program.c or program.h is newer than program.o : program.o needs to be regenerated (updated). This causes program.o to be newer than program - program needs to be relinked (using program.o).

The function of the "ifneq ($(MAKECMDGOALS),clean)" part in the Makefile

I really don't know the function of the following part:
ifneq ($(MAKECMDGOALS),clean)
-include $(DFILES)
endif
Here is the possible explanation I get from the GNU make manual:
to avoid including ‘.d’ files during clean rules, so make won’t create
them only to immediately remove them again:
But I don't fully understand "won’t create them only to immediately remove them again".
Here is the Makefile from derivative.tar.bz2 from http://www.dirac.org/linux/gdb/03-Initialization,_Listing,_And_Running.php#wherearewegoingtogo:
TARGET = driver
# CC = colorgcc
CC = gcc
CFILES = $(wildcard *.c)
OFILES = $(patsubst %.c, %.o, $(CFILES))
DFILES = $(patsubst %.c, .deps/%.d, $(CFILES))
WARN = -W -Wall -Wstrict-prototypes -Wmissing-prototypes -Waggregate-return \
-Wpointer-arith -Wcast-qual -Wcast-align -Wmissing-declarations -pedantic \
-Wnested-externs -Wredundant-decls -Wwrite-strings -Winline -Werror
CFLAGS = -std=c99 $(WARN) -g3
LDLIBS = -lm
all: $(TARGET)
ctags *.c *.h
$(TARGET): $(OFILES)
$(CC) -o $(TARGET) $(OFILES) $(LDLIBS)
.deps/%.d: %.c
#mkdir -p .deps
#$(CC) -MM $(CPPFLAGS) $< > $#.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $# : ,g' < $#.$$$$ > $#; $(RM) -rf $#.$$$$
ifneq ($(MAKECMDGOALS),clean)
-include $(DFILES)
endif
.PHONY: clean nuke
clean:
$(RM) -rf $(TARGET) *.o core .deps tags
Let's suppose you've just untarred the archive, and for whatever reason you want to run make clean before anything else. Presumably, it is already clean. Now, without the ifneq, make would:
Execute the include $(DFILES) line. Before doing the include proper, it would...
Run the recipe for .deps/%.d: %.c because the $(DFILES) variable contains files with names that match .deps/%.d. This means running $(CC) (which is gcc by default), which is a rather expensive operation. Once the recipe is run as many times as there are .d files to generate, then...
Run the recipe for clean.
All the work done in 2 is pointless since at step 3 it will be deleted. The reason 2 exists is that when you include a file, make first checks whether it has a recipe to generate the file to be included an runs the recipe if the file does not exist or is out of date.
The ifneq bit allows the Makefile to avoid doing the work in step 2 if the goal is clean.

How to define rules in the Makefile to compile only that *.cpp files which was modified (and their dependencies), not all *.cpp files

Lets say I have files:
Libs:
one.cpp, one.h
two.cpp, two.h
three.cpp, three.h
Program:
program.cpp
Is there way, to create Makefile which will compile only that *.cpp which were modified from last compilation?
Currently I have something like that:
SRCS = one.cpp two.cpp three.cpp
OBJS = $(SRCS:.cpp=.o)
all: $(OBJS) program
.cpp.o:
g++ -Wall -c $<
program:
g++ -Wall $(OBJS) program.cpp -o program
clean:
rm -f $(OBJS) program
I works fine, but when I compile my program and then change two.cpp or two.h I need to run "make clean" first, because when I secondly run "make" I get:
Nothing to be done for 'all'.
I would like to change my Makefile in that way, it would recognize my changes and recompile that file and its dependencies (if one.cpp uses code from two.cpp which was modified, both files should be recompiled).
So if I modify two.cpp, make should do:
g++ -Wall -c two.cpp
g++ -Wall $(OBJS) program.cpp -o program
But if one.cpp uses code from two.cpp which was modified, make shold do:
g++ -Wall -c one.cpp
g++ -Wall -c two.cpp
g++ -Wall $(OBJS) program.cpp -o program
First we make the object files prerequisites of the executable. Once this is done, Make will rebuild program whenever one of the SRCS changes, so we don't need OBJS as an explicit target:
all: program
program: $(OBJS)
g++ -Wall $(OBJS) program.cpp -o program
Then we make the header files prerequisites of the objects, so that if we change three.h, Make will rebuild three.o:
$(OBJS): %.o : %.h
And finally since one.cpp uses code from two.cpp by means of two.h (I hope), we make two.h a prerequisite of one.o:
one.o: two.h
And to make things cleaner and easier to maintain we use automatic variables:
program: $(OBJS)
g++ -Wall $^ program.cpp -o $#
Put it all together and we get:
SRCS = one.cpp two.cpp three.cpp
OBJS = $(SRCS:.cpp=.o)
all: program
$(OBJS): %.o : %.h
one.o: two.h
.cpp.o:
g++ -Wall -c $<
program: $(OBJS)
g++ -Wall $^ program.cpp -o $#
clean:
rm -f $(OBJS) program
There are a few more things we could do (like adding program.o to OBJS), but this is enough for today.
Add the files a command depends upon to run to the right of the target name.
Example:
default: hello.c
gcc -o hello.bin hello.c
install: hello.bin
cp hello.bin ../
All you need to do is tell make that the .o file depends on the .cpp file:
%.cpp.o: %.cpp
g++ -Wall -c -o $# $<

Resources