# - - - - - - - - - # # 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 # - $(SRCS:%.c=$(ODIR)/%.o) is a built-in function called a # "substitute reference", an abbreviation for the # expansion function "patsubst" : # $(patsubst %.c,$(ODIR)/%.o,$(SRCS)) # - % match everything, the value of the first % in # "replacement" is replaced by the text matched by the # first one in "pattern", it only works for the firsts OBJ = $(SRCS:%.c=$(ODIR)/%.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 # - if DEP (libft.h) is modified the library is remade # - $@ means everything left to ":" # - $< means first argument right to ":" $(NAME): $(OBJ) $(DEP) ar -rc $@ $< @ranlib $@ # - this rule depend of the list of files.c # - if any of these files are more recent than the file # builds/file.o equivalent then the rule will rebuild # this .o file # - the flag -o is there to put explicit name of the .o # files since they must be written within a directory # path (builds/) $(ODIR)/%.o: %.c $(CC) $(CFLAGS) -c -o $@ $< # clean the objects file # rm is use with it's native path to avoid using an alias clean: /bin/rm -rf $(ODIR) # clean the objects and executable files fclean: clean /bin/rm -f $(NAME) # remake the compilation re: fclean all .PHONY: clean fclean re all # ------------------------------------------------------ # # 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 : compiler et linker # - - - - - - - - - - - - - - - # 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 # # gcc -c main.c --> donne main.o # # cette etape qui consiste a transformer les fichiers en # objets .o s'appelle la compilation, il faut ensuite # linker les objets, ce qui avec gcc se fait simplement : # # # gcc main.o 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 for a basic makefile : # # [architecture] # # main.c # # function01.c # # function02.c # # header.h # # libtest.a # # Makefile # # [Makefile] # # #-------------# # # # VARIABLES # # # #-------------# # # NAME = program_test # # CC = gcc # # CFLAGS = -I. # # LFLAGS = -L. -ltest # # DEPS = header.h # # SRCS = main.c \ # # function01.c \ # # function02.c # # ODIR = ./builds # # OBJS = $(SRCS:%.c=%.o) # # # # #---------# # # # RULES # # # #---------# # # all: $(ODIR) $(NAME) # # $(NAME): $(OBJS) $(DEPS) # # $(CC) $(CFLAGS) -o $@ $(OBJS) $(LFLAGS) # # %.o: %.c # # $(CC) $(CFLAGS) -c -o $@ $< # # clean: # # /bin/rm -rf $(ODIR) # # fclean: clean # # /bin/rm -f $(NAME) # # re: fclean all # # .PHONY: all clean fclean re # # # prompt "make" execute the first rule ("all") # # [archtecture] # # main.c # # main.o # # function01.c # # function01.o ++ # # function02.c # # function02.o ++ # # header.h # # libtest.a # # Makefile # # program_test ++ # # ----- # # shape of a rule : # # target: prerequisites ... # recipe ... # # when a target is called, its execution depends of the # prerequisites, if they have been more recently modify # than the target the makefile execute the recipe # # to execute a rull you have to call it's name with make : # "make rule", and by default the first rule is execute # # ----- # # "automatic variables" # # $@ is the name of the target that called the rule # $< is the name of the first prerequisite # $^ is the name of all the prerequisites # # ----- # # $(SRCS:%.c=%.o) # # is a built-in function called a "substitute reference", # an abbreviation for the expansion function "patsubst" : # # $(patsubst pattern,replacement,text) # $(text:pattern=replacement) # # $() is a variable which expand in its value # # % match everything, the value of the first % in # "replacement" is replaced with the text matched by the # first % in "pattern", it only works for the firsts # # ----- # # all: $(ODIR) $(NAME) # # ALL depends on ODIR and NAME, so if ODIR doesn't exist # it will be created, and then NAME is called # # ----- # # $(NAME): $(OBJS) $(DEPS) # $(CC) $(CFLAGS) -o $@ $(OBJS) $(LFLAGS) # # NAME depends on OBJS and DEPS, so if any .o or any .h # have more recent date of modification NAME will execute # only for them # # ----- # # %.o: %.c # $(CC) $(CFLAGS) -c -o $@ $< # # when $(OBJS) expand in the list of .o files at the call # of the NAME rule, each of them call the rule "%.o" wich # depends on the equivalent .c, so if one has been # modified since last execution of ALL, it's recompiled in # object file with use of automatic variables $@ and $< # # ----- # # #clean: # /bin/rm -rf $(ODIR) # # "make clean" suppress all the .o files # # ----- # # fclean: clean # /bin/rm -f $(NAME) # # "make fclean" call CLEAN and suppress the executable # # ----- # # re: fclean all # # "make fclean" basically rerun make # # ----- # # .PHONY: all clean fclean re # # a phony target is one that is not the name of a file # if a file called "clean" for instance exist, the rule # CLEAN will never be executed when prompt "make clean" # because it will be considered up to date, but if it's # declared to be phony it will be executed in any case # - - - - - - - - - - - - - - - # makefile with subdirectories # - - - - - - - - - - - - - - - # put the .o in a "builds/" directory # # have the .c in a "srcs/" directory # # have the .h in an "includes" directory # # have multiples .a files # # [archtecture] # # srcs/ # # main.c # # function01.c # # function02.c # # includes/ # # header01.h # # header02.h # # libtest.a # # liboption.a # # Makefile # # # #-------------# # # # VARIABLES # # # #-------------# # # NAME = program_test # # CC = gcc # # VPATH = srcs # # # # IDIR = includes # # _DEPS = header01.h \ # # header02.h # # DEPS = $(_DEPS:%.h=$(IDIR)/%.h) # # # # LDIR = ./ # # _LIBS = libtest.a \ # # liboption.a # # LIBS = $(_LIBS:lib%.a=%) # # # # SRCS = main.c \ # # function01.c # # function02.c # # ODIR = ./builds # # OBJS = $(SRCS:%.c=$(ODIR)/%.o) # # # # CFLAGS = -I$(IDIR) # # LFLAGS = -L$(LDIR) -l$(LIBS) # # # # #---------# # # # RULES # # # #---------# # # all: $(ODIR) $(NAME) # # $(NAME): $(OBJS) $(DEPS) # # $(CC) $(CFLAGS) -o $@ $(OBJS) $(LFLAGS) # # $(ODIR): # # mkdir -p $@ # # $(ODIR)/%.o: %.c # # $(CC) $(CFLAGS) -c -o $@ $< # # clean: # # /bin/rm -rf $(ODIR) # # fclean: clean # # /bin/rm -f $(NAME) # # re: fclean all # # .PHONY: all clean fclean re # # # prompt "make" execute the first rule ("all") # # [archtecture] # # srcs/ # # main.c # # function01.c # # function02.c # # includes/ # # header01.h # # header02.h # # builds/ # # main.o # # function01.o # # function02.o # # libtest.a # # liboption.a # # Makefile # # program_test # # ----- # # LIBS = $(_LIBS:lib%.a=%) # # from "libtest.a" abd "liboption.a" it creates "test" # and option # # as said, the value of the % in "replacement" is # replaced with the text matched by the % in "pattern" # # ----- # # OBJS = $(SRCS:%.c=$(ODIR)/%.o) # # creates the list of .o from the .c with addition of the # path directory "builds/main.o" # # ----- # # all: $(ODIR) $(NAME) # $(ODIR): # mkdir -p $@ # # ALL call the rull ODIR wich verify if the objects files # directory already exist and if not, create it # # ----- # # $(NAME): $(OBJS) $(DEPS) # $(CC) $(CFLAGS) -o $@ $(OBJS) $(LFLAGS) # # NAME depends on OBJS and DEPS, so if any .o or any .h # have more recent date of modification NAME will execute # only for them # # ----- # # clean: # /bin/rm -rf $(ODIR) # # "make clean" suppress all the .o files # # ----- # # # # # # # # # # # # - - - - - - - - - - - - - - - - - - - # makefile qui make un autre makefile # - - - - - - - - - - - - - - - - - - - # - - - - - - - - - - - - - - - # makefile pour une librairie # - - - - - - - - - - - - - - - # # 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 # # - if DEP (libft.h) is modified the library is remade # # - $@ means everything left to ":" # # - $< means first argument right to ":" # $(NAME): $(OBJ) $(DEP) # ar -rc $@ $< # @ranlib $@ # # # - this rule depend of the list of files.c # # - if any of these files are more recent than the file # # builds/file.o equivalent then the rule will rebuild # # this .o file # # - the flag -o is there to put explicit name of the .o # # files since they must be written within a directory # # path (builds/) # $(ODIR)/%.o: %.c # $(CC) $(CFLAGS) -c -o $@ $< # # # clean the objects file # # rm is use with it's native path to avoid using an alias # clean: # /bin/rm -rf $(ODIR) # # # clean the objects and executable files # fclean: clean # /bin/rm -f $(NAME) # # # remake the compilation # re: fclean all # # .PHONY: clean fclean # 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 # NAME = fdf # CC = gcc -g3 # SRCDIR = srcs # INCLUDESDIR = includes # LIBFTDIR = libft # VPATH = $(INCLUDESDIR) \ # $(SRCDIR) \ # $(SRCDIR)/init \ # $(SRCDIR)/print \ # $(SRCDIR)/calcul \ # $(SRCDIR)/projection/isometrique \ # $(SRCDIR)/projection/parallel \ # $(SRCDIR)/parser \ # $(SRCDIR)/loop_and_event # LIBFT = $(LIBFTDIR)/libft.a # INCLUDES = fdf.h \ # init.h \ # print.h \ # calcul.h \ # projection.h \ # parser.h \ # loop.h # SRCS = main.c \ # create_header.c \ # create_map.c \ # color_map.c \ # color_line.c \ # color_map_pixel.c \ # print_3d.c \ # create_mapping.c \ # straight_line.c \ # add_point.c \ # quadrant_one.c \ # quadrant_two.c \ # quadrant_three.c \ # get_points.c \ # zoom.c \ # shift.c \ # alpha_omega.c \ # increase_z.c \ # key_dispatcher.c \ # set_new_color.c \ # set_projection.c # ODIR = objs/ # OBJS = $(addprefix $(OBJDIR), $(SRCS:.c=.o)) # INC = -I $(INCLUDESDIR) -I $(LIBFTDIR)/$(INCLUDESDIR) # all: # @$(MAKE) -C $(MLX_DIR); # @$(MAKE) -C $(LIBFTDIR) # @$(ECHO) "$(FLAGS_COLOR)Compiling with flags $(CFLAGS) $(EOC)" # @$(MAKE) $(NAME) # debug: # @$(MAKE) re DEBUG=1 # $(LIBFT): # @$(MAKE) -C $(LIBFTDIR) # $(NAME): $(LIBFT) $(OBJDIR) $(OBJECTS) # @$(CC) -o $@ $(OBJECTS) $(CFLAGS) $(LFLAGS) $(MLX_LIB) # @$(ECHO) "$(OK_COLOR)$(NAME) linked with success !$(EOC)" # $(OBJDIR): # @$(MKDIR) $@ # $(OBJDIR)%.o: $(SRC_DIR)%.c $(INCLUDES) # @$(CC) -c $< -o $@ $(CFLAGS) # @$(ECHO) "${COMP_COLOR}$< ${EOC}" # clean: # @$(MAKE) clean -C $(MLX_DIR) # @$(MAKE) clean -C $(LIBFTDIR) # @$(RM) $(OBJECTS) # @$(RM) -r $(OBJDIR) && $(ECHO) "${OK_COLOR}Successfully cleaned $(NAME) objects files ${EOC}" # fclean: clean # @$(MAKE) fclean -C $(MLX_DIR) # @$(MAKE) fclean -C $(LIBFTDIR) # @$(RM) $(NAME) && $(ECHO) "${OK_COLOR}Successfully cleaned $(NAME) ${EOC}" # re: fclean all # rere: # @$(RM) $(OBJECTS) # @$(RM) -r $(OBJDIR) # @$(RM) $(BINDIR)/$(NAME) # @$(MAKE) all # .PHONY: all clean fclean re debug