# - - - - - - - - - # variables names # - - - - - - - - - NAME = libft.a 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 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) # ODIR 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 # first it checks if the date of modification of any .o (OBJ) # is more recent thant the one of libft.a (NAME) # and for each of them it will execute # if only one .c has been modified then only one .o will have been too # so NAME will execute only for this one # ranlib doesn't need to be seen so it's precedeed by a "@" $(NAME): $(OBJ) ar -rc $@ $< @ranlib $@ $(ODIR)/%.o: %.c $(COMPILE.c) -o $@ $< clean: /bin/rm -rf $(ODIR) fclean: clean /bin/rm -f $(NAME) re: fclean all .PHONY: clean all: $(NAME) $(NAME): $(OBJ) ar rc $(NAME) $(OBJ) @ranlib $(NAME) clean: /bin/rm -f $(OBJ) fclean: clean /bin/rm -f $(NAME) re: fclean all .PHONY: clean fclean all re # ------------------------------------------------------ # # 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] # ecrir 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 ## ---------------- # - - - - - - - - - - - - # ecrire 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 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - - - - - - - - - - - # comment ca se compile # - - - - - - - - - - - - # # # # # # # # # # # # # # # # ## ---------------------------------------------------- ## 4 - ecrire un make file pour aider a la compilation ## ---------------------------------------------------- # - - - - - - - - - # makefile basique # - - - - - - - - - # - - - - - - - - - - - - - - - - - - - - - - - - - # ne pas tout recompiler a la moindre modification # - - - - - - - - - - - - - - - - - - - - - - - - - # - - - - - - - - - - - - - - - - - - - - # mettre les fichiers .o dans un dossier # - - - - - - - - - - - - - - - - - - - - # - - - - - - - - - - - - # make un autre makefile # - - - - - - - - - - - - # exemple d'un makefilede basic # # # 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) # # 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 :" #