# - - - - - - - - - # # variables names # # - - - - - - - - - # # each variable will expand in its value when used NAME = libft.a DEP = libft.h CC = gcc CFLAGS = -Wall -Wextra -Werror -I. -c SRCS = ft_atoi.c \ ft_bzero.c \ ft_isalnum.c \ ft_isalpha.c \ ft_isascii.c \ ft_isdigit.c \ ft_isprint.c \ ft_memccpy.c \ ft_memchr.c \ ft_memcmp.c \ ft_memcpy.c \ ft_memmove.c \ ft_memset.c \ ft_strcat.c \ ft_strchr.c \ ft_strcmp.c \ ft_strcpy.c \ ft_strdup.c \ ft_strlcat.c \ ft_strlen.c \ ft_strncat.c \ ft_strncmp.c \ ft_strncpy.c \ ft_strnstr.c \ ft_strrchr.c \ ft_strstr.c \ ft_tolower.c \ ft_toupper.c \ \ ft_itoa.c \ ft_memalloc.c \ ft_memdel.c \ ft_putchar.c \ ft_putchar_fd.c \ ft_putendl.c \ ft_putendl_fd.c \ ft_putnbr.c \ ft_putnbr_fd.c \ ft_putstr.c \ ft_putstr_fd.c \ ft_strclr.c \ ft_strdel.c \ ft_strequ.c \ ft_striter.c \ ft_striteri.c \ ft_strjoin.c \ ft_strmap.c \ ft_strmapi.c \ ft_strnequ.c \ ft_strnew.c \ ft_strsplit.c \ ft_strsub.c \ ft_strtrim.c \ \ ft_lstnew.c \ ft_lstdelone.c \ ft_lstdel.c \ ft_lstadd.c \ ft_lstiter.c \ ft_lstmap.c \ \ ft_any.c \ ft_atoibase.c \ ft_convertbase.c \ ft_foreach.c \ ft_issort.c \ ft_arraymap.c \ ft_putnbrbase.c \ ft_strmultisplit.c ODIR = ./builds # - "addprefix" is a built-in expansion function used by # makefile to perform a transformation on file names, in # this case we want to put all the .o files in a # subdirectory named "build" # - $(SRCS:.c=.o) is a "substitute reference", an # abbreviation for the expansion function "patsubst" : # $(patsubst %.c,%.o,$(SRCS)) OBJ = $(addprefix $(ODIR)/, $(SRCS:.c=.o)) # - - - - - - - - - - - # # rules to execute # # - - - - - - - - - - - # # - when you write "make" in command line it will execute # the first rule wrote in the Makefile, wich is "all" by # convention because usually when you type "make" you # expect it to build all # - it first verify if ODIR is created and if not create # it, then it calls NAME to create the library all: $(ODIR) $(NAME) # create the folder where all the .o files will be stored $(ODIR): mkdir -p $(ODIR) # - NAME will create the library libft.a # - first it checks if any OBJ (files.o) have more recent # date of modification than NAME (libft.a), and for each # it will execute # - $(OBJ) will expand in the list of files.o and each of # them will call the rule "$(ODIR)/%.o:" below because it # has its exact pattern # - NAME will execute only for the .c that has been # modified since last creation of NAME # - ranlib is precedeed by a "@" to avoid being print $(NAME): $(OBJ) $(DEP) ar -rc $@ $< @ranlib $@ # - this rule depend of the list of files.c and file.h # - if any of these files are more recent than the file # builds/file.o equivalent then the rule will rebuild # this .o file $(ODIR)/%.o: %.c $(CC) $(CFLAGS) -c $< clean: /bin/rm -rf $(ODIR) fclean: clean /bin/rm -f $(NAME) re: fclean all .PHONY: clean # ------------------------------------------------------ # # explication complete etapes par etapes depuis le debut # # ------------------------------------------------------ # ## SOMMAIRE [140 G] ## 1 - compiler avec gcc [161 G] # compiler un programme... [165 G] # compiler en plusieurs fichiers... [190 G] # les .o : pre-compiler... [238 G] ## 2 - creer une librairie [272 G] # ar rc [276 G] # ar rc en plusieurs fois [301 G] # ranlib [322 G] ## 3 - fichiers .h [335 G] # a quoi sert un header [] # ecrire un header [] # comment ca se compile [] ## 4 - ecrire un make file... [] # makefile basique [] # ne pas tout recompiler... [] # fichiers .o dans un dossier... [] # make un autre makefile [] ## ---------------------- ## 1 - compiler avec gcc ## ---------------------- # - - - - - - - - - - - - - - - - - - - - - - # compiler un programme qui contient un main # - - - - - - - - - - - - - - - - - - - - - - # quand on ecrit un programme il contient un main et les # fonctions dont le main a besoin (ex ft_putchar) : # # # #include # # # # void ft_putchar(char c) # # { # # write(1, &c, 1); # # } # # # # int main() # # { # # ft_putchar('0'); # # return (0); # # } # # on peut compiler ce fichier avec gcc en faisant : # gcc file.c # et ca sort un executable a.out # si on l'execute "./a.out" ca ecrit 0 dans la console # - - - - - - - - - - - - - - - - - - - - - - - # compiler un programme en plusieurs fichiers # - - - - - - - - - - - - - - - - - - - - - - - # on va vite vouloir mettre chaque fonctions utilisee # dans un fichier a part afin de mieux gerer les modules # d'un programme. donc si on sort ft_putchar du fichier : # # # int main() # # { # # ft_putchar('0'); # # return (0); # # } # # et qu'on execute "gcc file.c" on va avoir une erreur # car file.c utilise ft_putchar mais gcc ne sait pas ce # que c'est, donc il faut faire deux choses : # # 1 - d'une part indiquer a main() que ft_putchar() # existe, en ecrivant au dessus non pas toute la fonction # (puisqu'on veut la mettre dans un fichier a part) mais # uniquement son prototype : # # # void ft_putchar(char c); # # 2 - et d'autre part qu'on rajoute le fichier contenant # la fonction ft_putchar a la compilation : # # [ft_putchar.c] # # #include # # # # void ft_putchar(char c) # # { # # write(1, &c, 1); # # } # # [main.c] # # void ft_putchar(char c); # # # # int main() # # { # # ft_putchar('0'); # # return (0); # # } # # si on compile les deux ca marche : # gcc main.c ft_putchar.c # - - - - - - - - - - - - - - - - - - - - - - - # les .o : pre-compiler certains fichiers pour # ne pas les recompiler a chaque fois # - - - - - - - - - - - - - - - - - - - - - - - # ca fonctionne mais gcc doit a chaque fois recompiler # ft_putchar.c alors qu'il n'est pas modifie, donc on peut # le compiler une bonne fois pour toute et l'ajouter a la # compilation finale quand on en a besoin sans que l'ordi # ait a tout retraduire dans son langage # # mais si on essaye de compiler ft_putchar seul # # # gcc ft_putchar.c # # ca nous donne une erreur car pour compiler, gcc a besoin # de trouver un main ! # # on va donc utiliser l'option -c pour creer un fichier # objet .o qui est deja traduit en langue d'ordinateur # et pret a etre rajoute a la compilation : # # # gcc -c ft_putchar.c --> donne ft_putchar.o # # qu'on peut compiler avec le fichier contenant le main : # # # gcc file.c ft_putchar.o # # on va maintenant voir comment faire une libraire qui # contient tous nos fichiers objets ## ------------------------ ## 2 - creer une librairie ## ------------------------ # - - - - # ar rc # - - - - # pour mettre tous les fichiers .o dans un seul fichier .a # on utilise un programme d'archive ar avec les options rc # - r indique d'inserer les .o en remplacant si necessaire # - c de creer une nouvelle archive # # le nom de l'archive doit commencer par lib et # finir par .a : # # # ar rc libtest.a ft_putchar.o # # on obtient un fichier libtest.a qui contient les # fichiers objets .o # # on peut l'utiliser a la compilation de cette manniere : # # # gcc main.c -L. -ltest # # -L indique ou est la librairie (ici elle est dans le # dossier courant .) # -l indique son nom (TEST car on n'indique pas lib et .a) # - - - - - - - - - - - - - # ar rc en plusieurs fois # - - - - - - - - - - - - - # on n'est pas oblige de creer une librairie en une seule # fois, ce qui est bien pratique nottament pour les # makefiles, on verra ca apres. # # ca signifie que si notre librairie contient plusieurs # fonctions, par exmple la librairie libfruits.a contient # banane.o et orange.o, on peut creer la librairie # avec banane.o puis y ajouter orange.o, sans auncune # manipulation speciale : # # # ar rc libfruits.a banane.o # # ar rc libfruits.a orange.o # # revient au meme qu'ecrire : # # # ar rc libfruits.a banane.o orange.o # - - - - # ranlib # - - - - # ranlib creer un index du contenu de la librairie et le # place dedans, c'est utile des que la librairie est tres # grande afin que gcc sache acceder rapidement aux # fonctions dont il a besoin dedans # # # ranlib libtest.a # # ranlib s'utilise apres avoir creer la lib avec ar rc ## ---------------- ## 3 - fichiers .h ## ---------------- # - - - - - - - - - - - - # a quoi sert un header # - - - - - - - - - - - - # si on utilise une librairie c'est parce qu'on peut avoir # souvent besoin des memes fonctions dans un programme # mais si a chaque fois qu'on utilise une fonction de la # librairie on doit ecrire son prototype, ca va poser deux # problemes : # # le prototype d'une seule fonction peut etre ecrit a # pleins d'endroits, donc si la fonction change un peu on # doit retrouver tous les emplacements des prototypes et # les modifier un par un # # et d'autre part, si on utilise pleins de fonctions dans # l'ecriture d'un programme on va devoir reecrire a chaque # fois son prototype et c'est tres relou # # afin d'eviter ca on ecrit tous les prototypes, ainsi que # les includes, et les structures, les defines, et autres, # dans un seul fichier pour tout un programme, ou dans le # cas present pour toute la librairie, et on l'inclus # systematiquement au debut d'un fichier contenant des # fonctions de la librairie, gcc s'occupera de l'expandre # a la precompilation # # - - - - - - - - - # ecrire un header # - - - - - - - - - # [ft_putchar.c] # # #include "libtest.h" # # # # void ft_putchar(char c) # # { # # write(1, &c, 1); # # } # # [main.c] # # #include "libtest.h" # # # # int main() # # { # # ft_putchar('0'); # # return (0); # # } # # [libetest.h] # # # ifndef LIBTEST_H # # # define LIBTEST_H # # # # #include # # # # void ft_putchar(char c); # # # # # endif # # en tete de chaque fichier de fonctions on ecrit # l'include de la librairie, comme un include classique, # precede d'un #, mais avec le nom de la librairie entre # guillemets et non pas entre signes comparateurs # # dans le fichier de la librairie on ajoute les includes # dont on peut avoir besoin pour que la librairie ou des # fonctions auont besoin, et les prototypes des fonctions # # on entoure toutes ces infos par une definition, afin # de proteger le .h d'etre expand plusieurs fois dans # un fichier # - - - - - - - - - - - - # comment ca se compile # - - - - - - - - - - - - # au moment de la compilation il faut indiquer a gcc ou # se trouve le fichier .h avec le flag -I # # par exemple pour l'architecture de dossier suivante : # # # file # # -> main.c # # -> ft_putchar.c # # -> libtest.h # # il faut ecrire : # # # gcc main.c ft_putchar.c -I. # # et si la compilation utilise une librairie, par exemple # si on met la fonction ft_putchar.c dans une librairie : # # # gcc -c ft_putchar.c # # (donne ft_putchar.o) # # ar rc libtest.a ft_putchar.o # # on peut alors compiler avec la librairie .a et le # fichier .h : # # # gcc main.c -I. -L. -ltest -o test # # qui sort l'executable "test" ## ---------------------------------------------------- ## 4 - ecrire un make file pour aider a la compilation ## ---------------------------------------------------- # - - - - - - - - - # makefile basique # - - - - - - - - - # exemple d'un makefilede basic # # # |DECLARATION VARIABLES # # NAME = libtest.h # # CC = gcc # # CFLAGS = -I. -c # # SRCS = example01.c \ # # example02.c # # OBJ = $(SRCS:.c=.o) |ecrit les fichiers .c en .o # # # # all: $(NAME) |make execute sa premiere regle NAME # # $(NAME): $(OBJ) |NAME execute d'abord OBJ # # ar -rc $@ $< | # # Make a des built-in pattern rules : # https://www.gnu.org/software/make/manual/html_node/Catalogue-of-Rules.html # par exemple pour construire des .o a partir de .c # qui sont utilisees par défaut si les variables # sont bien nomee (genre CC ou CFLAGS) # - - - - - - - - - - - - - - - - - - - - - - - - - # ne pas tout recompiler a la moindre modification # - - - - - - - - - - - - - - - - - - - - - - - - - # - - - - - - - - - - - - - - - - - - - - # mettre les fichiers .o dans un dossier # - - - - - - - - - - - - - - - - - - - - # - - - - - - - - - - - - # make un autre makefile # - - - - - - - - - - - - # # cependant si on veut mettre les fichiers .o dans un # sous-fichier BUILDS il n'y a pas de built-in pattern # il faut donc l'ecrire nous-meme : # # # NAME = libtest.h # # CC = gcc # # CFLAGS = -I. # # SRCS = example01.c \ # # example02.c # # ODIR = ./builds # # OBJS = $(addprefix $(ODIR)/, $(SRCS:.c=.o)) # # # # all: $(NAME) # # $(NAME): $(OBJS) # # ar -rc $@ $< # # # # $(ODIR)/%.o : %.c # # $(COMPILE.c) -o $@ $< # # cette regle est appellee par $(OBJS) puisque # cette variable appelle la regle $(ODIR/file.o) # # COMPILE est une built-in variable qui execute # les regles CC et CFLAGS avec l'option -c # # % = "tout" # $@ = "la valeur a gauche de :" # $< = "la premiere valeur a droite de :" # $^ = "toutes les valeurs a droite de :" # # shape of a rule : # # target: prerequisites # recipe # # before executing its recipes, makefile verify if any of # the prerequisites has been more recently modify than # the target, and if so execute the recipes