0

I have made a basic makefile which is supposed to compile my library. It goes like this :

NAME = mylib.a
CC = gcc
FLAGS = -Wall -Wextra -Werror

LIB_SRCS = $(shell find . -type f | grep -F ".c")
LIB_OBJ = $(notdir $(LIB_SRCS:%.c=%.o))
OBJ_DIR = obj/
HEADERS_DIR = ./includes

.PHONY: all
all: $(NAME)

.PHONY: $(NAME)
$(NAME):
    @$(CC) $(FLAGS) -c $(LIB_SRCS) -I $(HEADERS_DIR)
    @mkdir -p $(OBJ_DIR)
    @mv $(LIB_OBJ) $(OBJ_DIR)
    @ar rc $(NAME) $(addprefix $(OBJ_DIR), $(LIB_OBJ))
    @ranlib $(NAME)

It works pretty well, except that when I use my lib for any project, it recompiles all the files (even when I haven't changed any source from the lib). I've read this : (Makefile compiles all the files everytime) but it's said I have to create a rule for every source file (or group of source files), which I don't want since there is a lot of source files. I wanted only one rule that compiles file by file.

So I've googled a bit and found a makefile that recompiles only the sources that were modified. And it goes like this :

NAME = mylib.a
CC = gcc
FLAGS = -Wall -Wextra -Werror
INCLUDES = includes/
SRCS_DIR = srcs
OBJ_DIR = obj

ITEMS = $(shell find $(SRCS_DIR) -type f | grep -F ".c" | sed 's/$(SRCS_DIR)//g')
SRCS = $(addprefix $(SRCS_DIR), $(ITEMS))
OBJ = $(addprefix $(OBJ_DIR), $(ITEMS:%.c=%.o))
SRCSUBS = $(shell find $(SRCS_DIR) -type d)
OBJ_SUBDIR = $(SRCSUBS:$(SRCS_DIR)%=$(OBJ_DIR)%)

.PHONY: all
all: $(NAME)

$(OBJ_DIR)/%.o:$(SRCS_DIR)/%.c
    @$(CC) $(FLAGS) -o $@ -c $< -I $(INCLUDES)

$(OBJ_SUBDIR):
    @mkdir $@

.PHONY: $(NAME)
$(NAME): $(OBJ_SUBDIR) $(OBJ)
    @ar rc $(NAME) $(OBJ)
    @ranlib $(NAME)

It works exactly as expected. Since I didn't understand everything, I've read about automatic variables and managed to understand almost everything. I just don't get how the rule that compiles $(OBJ_DIR)/%.o:$(SRCS_DIR)/%.c is called from the $(NAME) $(OBJ_SUBDIR) $(OBJ) rule.

Could anyone explain me how this rule is called since there is no mention to it from the "default" rule called when I execute make command (all: $(NAME)) ?

Kreaven
  • 1
  • 1
  • You seem to view make as a scripting language that is executed in some straightforward way. It is actually an [expert system](https://stackoverflow.com/questions/1739276/is-a-makefile-basically-the-same-thing-as-a-batch-file/1739300#1739300) that determines how to build something based on a bunch of rules describing how to build stuff from other stuff. –  Sep 25 '20 at 09:49
  • But in the first makefile, it recompiles all the files every time, even though nothing has changed. Second makefile is more intelligent, it recompiles only the files that were changed since the last compile time. What do you mean by "an expert system that determines how to build something based on a bunch of rules" ? It seems like make interprets the makefile like a compiler would interpret a script. – Kreaven Sep 25 '20 at 09:58
  • Yeah okay, I've kept reading your linked post. Still I don't know how make "guesses" it has to call certain rules if they're not called. – Kreaven Sep 25 '20 at 10:01
  • The rule you call directly is `$(NAME): $(OBJ_SUBDIR) $(OBJ)`, you can examine what's inside OBJ using `@echo $(OBJ)`. And you have a general rule "how to make .o from .c" (the one with CC in it). –  Sep 25 '20 at 10:06
  • I've understood that $(OBJ) references to every object files. Since `$(NAME): $(OBJ_SUBDIR) $(OBJ)` needs every object file as a dependency, make has to call a rule to create those object files from .c files. And my general rule `$(OBJ_DIR)/%.o:$(SRCS_DIR)/%.c` does it. But I don't understand how this rule is called once for each file which was modified and not once to process them all, as some dependencies are missing. (As you pointed out, I see it a shell script, and I need to understand how it works behind) – Kreaven Sep 25 '20 at 12:55
  • I recommend you read the [manual](https://www.gnu.org/software/make/manual/), it covers all the basics. Your rule `$(NAME): $(OBJ_SUBDIR) $(OBJ)` means that `$(NAME)` depends on `$(OBJ)` and your `$(OBJ)` is a list of files. Make parses this list of files and for every file (one by one) it searches for rule to create this single file - this is where your `$(OBJ_DIR)/%.o: $(SRCS_DIR)/%.c` rule comes in. You can run `make -dr` to get detailed information how make evaluates and runs your targets, this can shed some light. – raspy Sep 27 '20 at 19:47

0 Answers0