{"id":19947301,"url":"https://github.com/clementvidon/makefile_tutor","last_synced_at":"2026-02-09T13:04:56.588Z","repository":{"id":58391074,"uuid":"531583150","full_name":"clementvidon/Makefile_tutor","owner":"clementvidon","description":"This project aims to create a crystal clear tutorial on a cryptic looking topic.","archived":false,"fork":false,"pushed_at":"2025-09-17T15:39:46.000Z","size":166,"stargazers_count":664,"open_issues_count":1,"forks_count":32,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-10-21T05:39:20.231Z","etag":null,"topics":["42","42born2code","42cursus","42network","42paris","42projects","42school","gnumake","howto","howto-tutorial","learnbydoing","make","makefile","makefile-template","makefiles","stepbystep","template","template-makefile","tutorial"],"latest_commit_sha":null,"homepage":"https://clementvidon.github.io/Makefile_tutor/","language":"Makefile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc-by-sa-4.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/clementvidon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-09-01T15:41:14.000Z","updated_at":"2025-10-16T04:12:06.000Z","dependencies_parsed_at":"2024-01-13T18:30:00.219Z","dependency_job_id":"2b71fc34-eaf3-4d09-b84f-a8bbaec08dbc","html_url":"https://github.com/clementvidon/Makefile_tutor","commit_stats":null,"previous_names":["clementvidon/makefile_tutor","clemedon/makefile_tutor"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/clementvidon/Makefile_tutor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clementvidon%2FMakefile_tutor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clementvidon%2FMakefile_tutor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clementvidon%2FMakefile_tutor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clementvidon%2FMakefile_tutor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/clementvidon","download_url":"https://codeload.github.com/clementvidon/Makefile_tutor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clementvidon%2FMakefile_tutor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29266163,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-09T12:53:16.161Z","status":"ssl_error","status_checked_at":"2026-02-09T12:52:30.244Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["42","42born2code","42cursus","42network","42paris","42projects","42school","gnumake","howto","howto-tutorial","learnbydoing","make","makefile","makefile-template","makefiles","stepbystep","template","template-makefile","tutorial"],"created_at":"2024-11-13T00:35:26.593Z","updated_at":"2026-02-09T13:04:56.564Z","avatar_url":"https://github.com/clementvidon.png","language":"Makefile","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n    MAKEFILE TUTOR (GNU)\n\u003c/h1\u003e\n\n\u003ch3 align=\"center\"\u003e\n    \u003ca href=\"#summary\"\u003eSummary\u003c/a\u003e\n    \u003cspan\u003e · \u003c/span\u003e\n    \u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\n    \u003cspan\u003e · \u003c/span\u003e\n    \u003ca href=\"#glossary\"\u003eGlossary\u003c/a\u003e\n    \u003cspan\u003e · \u003c/span\u003e\n    \u003ca href=\"#syntax\"\u003eSyntax\u003c/a\u003e\n    \u003cspan\u003e · \u003c/span\u003e\n    \u003ca href=\"#index\"\u003eIndex\u003c/a\u003e\n    \u003cspan\u003e · \u003c/span\u003e\n    \u003ca href=\"#sources\"\u003eSources\u003c/a\u003e\n    \u003cspan\u003e · \u003c/span\u003e\n    \u003ca href=\"#contact\"\u003eContact\u003c/a\u003e\n\u003c/h3\u003e\n\n# Summary\n\nAddressed to beginners and not to newcomers, the idea behind this tutorial is to\n**focus on the essential**.  Anything that is not directly related to the\ntemplate we are going to explore will not be covered here.  On the other hand\neverything that is covered in this tutorial will be carefully detailed.\n\nInitially intended to help 42 students to step up their Makefile skills through\n**a C-focused documented template** that evolves gradually, **version by\nversion**.  With the aim of making them more digestible and even tasty  🍔\n\nAllow **1 hour** to complete this tutorial, plus some additional time to work with the [**examples**](https://github.com/clemedon/Makefile_tutor/tree/main/projects). For those of you who are wondering, as I write this line, I have invested a total of 102 hours and 50 minutes in creating this tutorial.\n\n**TL;DR** Refer to the bold text.\n\n[**→ GitHub Page ←**](https://clemedon.github.io/Makefile_tutor/)\u003cbr\u003e\n[**→ GitHub Repo ←**](https://github.com/clemedon/Makefile_tutor/)\n\n[![CC BY-SA 4.0][cc-by-sa-shield]][cc-by-sa]\n\n[cc-by-sa]: http://creativecommons.org/licenses/by-sa/4.0/\n[cc-by-sa-shield]: https://img.shields.io/badge/License-CC%20BY--SA%204.0-lightgrey.svg\n\n\u003chr\u003e\n\n**Roadmap**\n\n- [x] [GitHub Page](https://clemedon.github.io/Makefile_tutor/)\n- [x] A [ready to make](#usage) project of each template version\n- [x] [bold text](#usage) that acts as a summary\n- [x] [v1](#version-1) Simplest C project\n- [x] [v2](#version-2) Project that include headers\n- [x] [v3](#version-3) Project with any kind of directory structure\n- [x] [v4](#version-4) Static library *+ introduce auto-gen dependencies*\n- [x] [v5](#version-5) Project that uses libraries\n- [ ] POSIX Makefile\n\n# Usage\n\nThis tutorial is designed to be read line by line **linearly at first**.\n\nThen it can be quickly navigated thanks to the:\n\n- **Brief** of each version which is visible from the [**Index**](#index).\n- **Text in bold** that compile the essence of this tutorial.\n- [**Return to Index ↑**](#index) buttons at the end of each version.\n\nEach version of the template has an assigned directory in the\n[**projects**](https://github.com/clemedon/Makefile_tutor/tree/main/projects)\ndirectory of the repository, to play with a Makefile open a terminal and run:\n\n```bash\ngit clone https://github.com/clemedon/Makefile_tutor.git\ncd Makefile_tutor/projects\n```\n\n```bash\ncd \u003ctemplate_version\u003e\nmake \u003ctarget\u003e\n```\n\n*PS1 **`C++` users** have to replace `CC = clang` with `CXX = g++` and `CFLAGS`\nwith `CXXFLAGS`.*\n\n*PS2 feel free to **fork me** so as to remove everything that does not\ninterest you and customize me according to your needs.*\n\n# Glossary\n\nEach **version** of the template has **3 sections**:\n\n- **Structure** is the project structure sketch.\n- **Brief** is a summary made from the bold parts of the template comments.\n- **Template** is a commented Makefile template with comments (that are always\n  placed at the end of the template part that concerns them).\n\nOur **template** will be articulated around the following **parts**:\n\n- `### BEG`         Mark the template **beginning**.\n- `INGREDIENTS`     Build **variables**.\n- `UTENSILS`        Shell **commands**.\n- `RECIPES`         Build and extra **rules**.\n- `SPEC`            **Special targets**.\n- `#### END`        Mark the template **end**.\n\nAccording to make a **`rule`** is made of:\n\n- `targets` are the names of the **goals** we want to make. It can be a file or\n  an action, the latter leading to a *pseudo target*.\n- `prerequisites` are the goals that must be achieved so that the `rule` can\n  execute.  Thus prerequisites are the **targets dependencies**.\n- `recipe` is the set of lines that **begins with a `TAB`** character and appear\n  in a rule context.\n\n```make\ntarget: prerequisite\n    recipe line 1\n    recipe line 2\n    …\n```\n\n# Syntax\n\nLike every Makefile the template uses a combination of Makefile syntax and shell\nscript syntax.  The **shell script syntax** is reserved and limited to recipe\nlines, by default those lines have to **start with a `TAB`** character to be\ndifferentiated by make (and passed to the shell).  The **Makefile syntax** is\nused for **all the other lines**.\n\nAbout the **equal signs**:\n\n- `:=` **simply expand** the defined variable (like C `=`).\n- `=` **recursively expand** the defined variable (the expression is expanded\n  afterward, when (and each time) the variable is used).\n\n```make\nA := Did $(C)\nB = Did $(C)\n\nC  = you understand ?\n\nall:\n    $(info $(A)) # output \"Did\"\n    $(info $(B)) # output \"Did you understand ?\"\n```\n\nAbout **variables** note that `${VAR}` and `$(VAR)` is exactly the same.\n\n**Automatic Variables** expansion:\n\n- `$\u003c` **leftmost prerequisite**\n- `$@` **current target**\n- `$^` **all prerequisites**\n- `$(@D)` **directory part** of the file name of the target\n- `$(@F)` **file part** of the file name of the target\n\n*Cf. Version 1 / Automatic variables in practice.*\n\n# Template\n\n## Index\n\n[**Version 1 / Simplest C project**](#version-1)\n\n\u003e - 42 C coding style conventions\n\u003e - builtin variables\n\u003e - The C compilation implicit rule\n\u003e - pattern rule contains `%` character\n\u003e - Automatic variables in practice\n\u003e - Illustration of a `make all`\n\u003e - C build recap\n\u003e - parallelization enabled by `make --jobs`\n\u003e - the `.PHONY:` special target\n\n[**Version 2 / Project that include headers**](#version-2)\n\n\u003e - preprocessor's flags\n\u003e - print a custom message\n\u003e - C compilation implicit rule is overwritten\n\u003e - *default goal* `all` appears first\n\u003e - `.SILENT:` silences the rules output\n\n[**Version 3 / Project with any kind of directory structure**](#version-3)\n\n\u003e - split the line with a `backslash`\n\u003e - substitution reference so `main.c` becomes `src/main.c`\n\u003e - generate the `OBJ_DIR` based on `SRC_DIR`\n\u003e - compilation rule uses multiple source and object directories\n\n[**Version 4 / Static library**](#version-4)\n\n\u003e - when a header file is modified the executable will rebuild\n\u003e - automatically generate a list of dependencies\n\u003e - build directory\n\u003e - dependency files must be included\n\u003e - hyphen symbol to prevent make from complaining\n\u003e - creates a static library\n\n[**Version 5 / Project that uses libraries**](#version-5)\n\n\u003e - system library linked by default\n\u003e - `addprefix` make function\n\u003e - flags and libraries used by the linker\n\u003e - `dir` function\n\u003e - Build with a library\n\u003e - Linking with a library\n\u003e - builds each of the required libraries\n\u003e - call rules recursively\n\n[**Bonus**](#bonus)\n\n\u003e - `make` and `run` the *default goal*\n\u003e - print `\u003ctarget\u003e` recipe without executing it\n\u003e - print the value of an arbitrary variable\n\u003e - update the git repository\n\n##  Version 1\n\n###     v1 Structure\n\nThe simplest, build a program called `icecream` with the following structure:\n\n```\n    before build:    after build:\n    .                .\n    ├── Makefile     ├── Makefile\n    └── main.c       ├── main.o\n                     ├── main.c\n                     └── icecream\n```\n\n###     v1 Brief\n\n- 42 C coding style conventions\n- builtin variables\n- The C compilation implicit rule\n- pattern rule contains `%` character\n- Automatic variables in practice\n- Illustration of a `make all`\n- C build recap\n- parallelization enabled by `make --jobs`\n- the `.PHONY:` special target\n\n###     v1 Template\n\n```make\n####################################### BEG_1 ####\n\nNAME        := icecream\n\n#------------------------------------------------#\n#   INGREDIENTS                                  #\n#------------------------------------------------#\n# SRCS      source files\n# OBJS      object files\n#\n# CC        compiler\n# CFLAGS    compiler flags\n\nSRCS        := main.c\nOBJS        := main.o\n\nCC          := clang\nCFLAGS      := -Wall -Wextra -Werror\n\n#------------------------------------------------#\n#   UTENSILS                                     #\n#------------------------------------------------#\n# RM        force remove\n# MAKEFLAGS make flags\n\nRM          := rm -f\nMAKEFLAGS   += --no-print-directory\n\n#------------------------------------------------#\n#   RECIPES                                      #\n#------------------------------------------------#\n# all       default goal\n# $(NAME)   linking .o -\u003e binary\n# clean     remove .o\n# fclean    remove .o + binary\n# re        remake default goal\n\nall: $(NAME)\n\n$(NAME): $(OBJS)\n    $(CC) $(OBJS) -o $(NAME)\n\nclean:\n    $(RM) $(OBJS)\n\nfclean: clean\n    $(RM) $(NAME)\n\nre:\n    $(MAKE) fclean\n    $(MAKE) all\n\n#------------------------------------------------#\n#   SPEC                                         #\n#------------------------------------------------#\n\n.PHONY: clean fclean re\n\n####################################### END_1 ####\n```\n\n- The choice of the `CC` and `CFLAGS` values, `NAME`, `clean`, `fclean`, `all`\n  and `re` as the basic rules as well as not using a wildcard to auto generate\n  the sources list is guided by the **42 C coding style conventions**, do not\n  hesitate to disagree and change it (like renaming `clean` and `fclean` to the\n  more GNU conventional `mostlyclean` and `clean` respectively).\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- `MAKE` and `MAKEFLAGS` are **builtin variables** like `CFLAGS` and a lot of\n  others that you can find in the *data-base* (`make --print-data-base`\n  command). `MAKE` value corresponds to the `make` executable being run and\n  `MAKEFLAGS` to its flags.  When a Makefile is executed from another Makefile,\n  the called's `MAKE` `MAKEFLAGS` variables inherit from the caller's `MAKE`\n  `MAKEFLAGS` values.\n\nHere we append `--no-print-directory` to `MAKEFLAGS` content to have a clearer\noutput, try to remove it and `make re` to see the difference.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- **The C compilation implicit rule** looks like this:\n\n```make\n%.o: %.c\n    $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $\u003c\n```\n\nWhere `%.o` expands to each objects, `%.c` to each sources, `$@` to the first\ntarget (which is `%.o`) and `$\u003c` to the leftmost prerequisite (which is `%.c`).\n\n*As their name implies implicit rules are implicit and do not need to be\nwritten.  As well as the builtin variables, all the implicit rules can be found\nin the data-base, accessible with `make -p -f/dev/null | less` command.*\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- A **pattern rule** is a rule whose target **contains** a **`%` character**\n  (here `%.o: %.c`).  This character means \"exactly one of them\".  It is used\n  here to say that each `.o` requires a `.c` with the same name and the `$(CC)…`\n  recipe line will execute as many times as there are `.o: .c` pairs, thus\n  creating for each source its corresponding object, one at a time.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- **Automatic variables in practice**:\n\n```make\n# Linking step\n\n$(NAME): $(OBJS)\n    $(CC) $^ -o $@\n```\n\n`$@` expands to the *current target*, here we could have used `$(NAME)` instead.\n\n`$^` expands to *all prerequisites*, here we could have used `$(OBJS)` instead\nbut not `$\u003c` that expands to the *leftmost prerequisite* and so only the first\nitem found in `$(OBJS)`.\n\n```make\n# Compiling step\n\n%.o: %.c\n    $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $\u003c\n```\n\n`$\u003c` expands to the *leftmost prerequisite*, here it could not have been\nreplaced with `$(SRCS)` because `$(SRCS)` expands to all the sources at once\nwhich is not the case of the pattern rule `%.c`.  On the other hand here `$\u003c` is\nequivalent to `$^`, both will always expand to one source: the current one\nexpanded by `%.c`.  For the same reasons `$@` expands to `%.o` not to `$(OBJS)`.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- **Illustration of a `make all`**:\n\n```make\nall: $(NAME)                            3 ← 2\n\n$(NAME): $(OBJS)                        2 ← 1\n    $(CC) $(OBJS) -o $(NAME)\n\n%.o: %.c                                1 ← 0\n    $(CC) $(CFLAGS) -c -o $@ $\u003c\n```\n\nThe `all` target requires `icecream` that requires the `objects` that require the\n`sources` that requires…  a programmer.  In other words `all` creates `icecream`\nwith the objects created with the sources that you have created.\n\nMake will first trace its path to the lower level where it finds a raw material\n`3 → 2 → 1 → 0` (the `sources`) and then do it backward while building each\nresource that is required by the direct upper level encountered `0 → 1 → 2 → 3`\n(the `icecream`, our *final goal*).\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- **C build recap** `%.o` target requires the sources to be compiled into\n  objects, the `-c` option tells the compiler to only compile without linking.\n  The `-o` option is used to specify the objects name.  Afterward the `$(NAME)`\n  target requires the linking the objects into a binary file whose name is\n  specified with the `-o` flag.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- For the `re` rule we have no choice but make an external call to our Makefile\n  because we should not rely on the order in which prerequisites are specified.\n  For example `re: fclean all` wouldn't not be reliable if **parallelization**\n  was **enabled by `make --jobs`**.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- The prerequisites given to **the `.PHONY:` special target** become targets\n  that make will run regardless of whether a file with that name exists.  In\n  short these prerequisites are our targets that don't bear the name of a file.\n\nTry to remove the `.PHONY: re`, create a file named `re` at the Makefile level\nin the project directory and run `make re`.  It won't work.\n\nNow if you do the same with `all` it won't cause any problem, as we know\nprerequisites are completed before their targets and `all` has the sole action\nof invoking `$(NAME)`, as long as a rule doesn't have a recipe, `.PHONY` is not\nnecessary.\n\n[**Return to Index ↑**](#index)\n\n##  Version 2\n\n###     v2 Structure\n\nAs above but for a project that **includes header files**:\n\n```\n    before build:     after build:\n    .                 .\n    ├── Makefile      ├── Makefile\n    ├── main.c        ├── main.o\n    └── icecream.h    ├── main.c\n                      ├── icecream.h\n                      └── icecream\n```\n\n###     v2 Brief\n\n- preprocessor's flags\n- print a custom message\n- C compilation implicit rule is overwritten\n- *default goal* `all` appears first\n- `.SILENT:` silences the rules output\n\n###     v2 Template\n\n```make\n####################################### BEG_2 ####\n\nNAME        := icecream\n\n#------------------------------------------------#\n#   INGREDIENTS                                  #\n#------------------------------------------------#\n# SRCS      source files\n# OBJS      object files\n#\n# CC        compiler\n# CFLAGS    compiler flags\n# CPPFLAGS  preprocessor flags\n\nSRCS        := main.c\nOBJS        := main.o\n\nCC          := clang\nCFLAGS      := -Wall -Wextra -Werror\nCPPFLAGS    := -I .\n```\n\n- `CPPFLAGS` is dedicated to **preprocessor's flags** like `-I \u003cinclude_dir\u003e`,\n  it allows you to no longer have to write the full path of a header but only\n  its file name in the sources: `#include \"icecream.h\"` instead of `#include\n  \"../../path/to/include/icecream.h\"`.\n\n```make\n#------------------------------------------------#\n#   UTENSILS                                     #\n#------------------------------------------------#\n# RM        force remove\n# MAKEFLAGS make flags\n\nRM          := rm -f\nMAKEFLAGS   += --no-print-directory\n\n#------------------------------------------------#\n#   RECIPES                                      #\n#------------------------------------------------#\n# all       default goal\n# $(NAME)   linking .o -\u003e binary\n# %.o       compilation .c -\u003e .o\n# clean     remove .o\n# fclean    remove .o + binary\n# re        remake default goal\n\nall: $(NAME)\n\n$(NAME): $(OBJS)\n    $(CC) $(OBJS) -o $(NAME)\n    $(info CREATED $(NAME))\n\n%.o: %.c\n    $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $\u003c\n    $(info CREATED $@)\n\nclean:\n    $(RM) $(OBJS)\n\nfclean: clean\n    $(RM) $(NAME)\n\nre:\n    $(MAKE) fclean\n    $(MAKE) all\n```\n\n- The `info` function is used here to **print a custom message** about what has\n  just been built.\n\n*We prefer `info` to shell `echo` because it is a make function.  Also unlike\n`echo` that can only be used inside a recipe, `info` can be used anywhere in a\nMakefile which makes it powerful for debugging.*\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- The **C compilation implicit rule is overwritten** with an explicit equivalent\n  which let us add an `info` statement to it.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- The order in which the rules are written does not matter as long as our\n  **default goal `all` appears first** (the rule that will be triggered by a\n  simple `make` command).\n\n```make\n#------------------------------------------------#\n#   SPEC                                         #\n#------------------------------------------------#\n\n.PHONY: clean fclean re\n.SILENT:\n```\n\n- Normally make prints each line of a rule's recipe before it is executed.  The\n  special target **`.SILENT:` silences the rules output** specified as\n  prerequisites, when it is used without prerequisites it silents all the rules\n  (implicit included).\n\n*To silence at the recipe-line level we can prefix the wanted recipe lines with an `@`\nsymbol.*\n\n```make\n####################################### END_2 ####\n```\n\n[**Return to Index ↑**](#index)\n\n##  Version 3\n\n###     v3 Structure\n\nAs above but a more complex project structure with **multiple source\ndirectories** and their **corresponding object directories**:\n\n```\n    before build:          after build:\n    .                      .\n    ├── src                ├── src\n    │   ├── base           │   ├── base\n    │   │   ├── water.c    │   │   ├── water.c\n    │   │   └── milk.c     │   │   └── milk.c\n    │   ├── arom           │   ├── arom\n    │   │   └── coco.c     │   │   └── coco.c\n    │   └── main.c         │   └── main.c\n    ├── include            ├── obj\n    │   └── icecream.h     │   ├── base\n    └── Makefile           │   │   ├── water.o\n                           │   │   └── milk.o\n                           │   ├── arom\n                           │   │   └── coco.o\n                           │   └── main.o\n                           ├── include\n                           │   └── icecream.h\n                           ├── Makefile\n                           └── icecream\n```\n\n###     v3 Brief\n\n- split the line with a `backslash`\n- substitution reference so `main.c` becomes `src/main.c`\n- generate the `OBJ_DIR` based on `SRC_DIR`\n- compilation rule uses multiple source and object directories\n\n###     v3 Template\n\n```make\n####################################### BEG_3 ####\n\nNAME        := icecream\n\n#------------------------------------------------#\n#   INGREDIENTS                                  #\n#------------------------------------------------#\n# SRC_DIR   source directory\n# OBJ_DIR   object directory\n# SRCS      source files\n# OBJS      object files\n#\n# CC        compiler\n# CFLAGS    compiler flags\n# CPPFLAGS  preprocessor flags\n\nSRC_DIR     := src\nOBJ_DIR     := obj\nSRCS        := \\\n    main.c          \\\n    arom/coco.c     \\\n    base/milk.c     \\\n    base/water.c\nSRCS        := $(SRCS:%=$(SRC_DIR)/%)\nOBJS        := $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)\n\nCC          := clang\nCFLAGS      := -Wall -Wextra -Werror\nCPPFLAGS    := -I include\n```\n\n- We can **split the line** by ending it **with a `backslash`** to increase the\n  readability of `SRCS` content and facilitate its modification.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- A string **substitution reference** substitutes the value of each item of a\n  variable with the specified alterations.  `$(SRCS:%=$(SRC_DIR)/%)` means that\n  each item of `SRCS` represented by `%` becomes itself (`%`) plus the\n  `$(SRC_DIR)/` alteration, so `main.c` becomes `src/main.c`. `OBJS` will then\n  use the same process to convert `src/main.c` into `obj/main.o`, based on `OBJ_DIR` and the new value of `SRCS`.\n\n```make\n#------------------------------------------------#\n#   UTENSILS                                     #\n#------------------------------------------------#\n# RM        force remove\n# MAKEFLAGS make flags\n# DIR_DUP   duplicate directory tree\n\nRM          := rm -f\nMAKEFLAGS   += --no-print-directory\nDIR_DUP     = mkdir -p $(@D)\n```\n\n- `DIR_DUP` will **generate the `OBJ_DIR` based on `SRC_DIR`** structure with\n  `mkdir -p` which creates the directory and the parents directories if missing,\n  and `$(@D)` that will expand to the directory part of the target, as we've seen in the [syntax](#syntax) section.\n\n*This will work with every possible kind of src directory structure.*\n\n```make\n#------------------------------------------------#\n#   RECIPES                                      #\n#------------------------------------------------#\n# all       default goal\n# $(NAME)   linking .o -\u003e binary\n# %.o       compilation .c -\u003e .o\n# clean     remove .o\n# fclean    remove .o + binary\n# re        remake default goal\n\nall: $(NAME)\n\n$(NAME): $(OBJS)\n    $(CC) $(OBJS) -o $(NAME)\n    $(info CREATED $(NAME))\n\n$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c\n    $(DIR_DUP)\n    $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $\u003c\n    $(info CREATED $@)\n\nclean:\n    $(RM) $(OBJS)\n\nfclean: clean\n    $(RM) $(NAME)\n\nre:\n    $(MAKE) fclean\n    $(MAKE) all\n```\n\n- The **compilation rule** `.o: %.c` becomes `$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c`,\n  since our structure **uses dedicated source and object directories**.\n\n```make\n#------------------------------------------------#\n#   SPEC                                         #\n#------------------------------------------------#\n\n.PHONY: clean fclean re\n.SILENT:\n\n####################################### END_3 ####\n```\n\n[**Return to Index ↑**](#index)\n\n##  Version 4\n\n###     v4 Structure\n\nBuilds a **library** so there are no `main.c`.  We generate **dependencies**\nthat are stored with the objects. Therefor we rename the `obj` directory into a\nmore general `.build` directory.\n\n```\n    before build:          after build:\n    .                      .\n    ├── src                ├── src\n    │   ├── base           │   ├── base\n    │   │   ├── water.c    │   │   ├── water.c\n    │   │   └── milk.c     │   │   └── milk.c\n    │   └── arom           │   └── arom\n    │       └── coco.c     │       └── coco.c\n    ├── include            ├── include\n    │   └── icecream.h     │   └── icecream.h\n    └── Makefile           ├── .build\n                           │   ├── base\n                           │   │   ├── water.o\n                           │   │   ├── water.d\n                           │   │   ├── milk.o\n                           │   │   └── milk.d\n                           │   └── arom\n                           │       ├── coco.o\n                           │       └── coco.d\n                           ├── Makefile\n                           └── libicecream.a\n```\n\n###     v4 Brief\n\n- when a header file is modified the executable will rebuild\n- automatically generate a list of dependencies\n- build directory\n- dependency files must be included\n- hyphen symbol to prevent make from complaining\n- creates a static library\n\n###     v4 Template\n\n```make\n####################################### BEG_4 ####\n\nNAME        := libicecream.a\n\n#------------------------------------------------#\n#   INGREDIENTS                                  #\n#------------------------------------------------#\n# SRC_DIR   source directory\n# SRCS      source files\n#\n# BUILD_DIR object directory\n# OBJS      object files\n# DEPS      dependency files\n#\n# CC        compiler\n# CFLAGS    compiler flags\n# CPPFLAGS  preprocessor flags\n\nSRC_DIR     := src\nSRCS        :=  \\\n    arom/coco.c \\\n    base/milk.c \\\n    base/water.c\nSRCS        := $(SRCS:%=$(SRC_DIR)/%)\n\nBUILD_DIR   := .build\nOBJS        := $(SRCS:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)\nDEPS        := $(OBJS:.o=.d)\n\nCC          := clang\nCFLAGS      := -Wall -Wextra -Werror\nCPPFLAGS    := -MMD -MP -I include\nAR          := ar\nARFLAGS     := -r -c -s\n```\n\n- Unlike with sources, **when a header file is modified** make has no way of\n  knowing this and will not consider **the executable** to be out of date, and\n  therefor **will** not **rebuild** it.  In order to change this behavior we\n  should add the appropriate header files as additional prerequisites:\n\n```make\n#before                     #after\nmain.o: main.c              main.o: main.c icecream.h\n    clang -c $\u003c -o $@           clang -c $\u003c -o $@\n```\n\n- Doing this manually for multiple sources and headers is both tedious and error\n  prone.  By adding `-MMD` to `CPPFLAGS` our compiler will **automatically\n  generate a list of dependencies** for each object file encountered during the\n  compilation.  The `-MP` option prevents errors that are triggered if a header\n  file has been deleted or renamed.\n\n  Dependency files must be included into our Makefile right after the objects\n  creation so to obtain their names we copy `OBJS` into `DEPS` and use\n  *substitution reference* to turn `.o` part of their name into `.d`.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- We change our old `OBJ_DIR = obj` for a `BUILD_DIR = .build`, a hidden **build\n  directory** that will contain our dependency files in addition to our objects.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- A static library is not a binary but a collection of objects so we use `ar` to\n  **creates a static library** during the linking step of the build.  `-r` to\n  replace the older objects with the new ones with `-c` to create the library if\n  it does not exist and `-s` to write an index into the archive or update an\n  existing one.\n\n```make\n#------------------------------------------------#\n#   UTENSILS                                     #\n#------------------------------------------------#\n# RM        force remove\n# MAKEFLAGS make flags\n# DIR_DUP   duplicate directory tree\n\nRM          := rm -f\nMAKEFLAGS   += --no-print-directory\nDIR_DUP     = mkdir -p $(@D)\n\n#------------------------------------------------#\n#   RECIPES                                      #\n#------------------------------------------------#\n# all       default goal\n# $(NAME)   link .o -\u003e library\n# %.o       compilation .c -\u003e .o\n# clean     remove .o\n# fclean    remove .o + binary\n# re        remake default goal\n\nall: $(NAME)\n\n$(NAME): $(OBJS)\n    $(AR) $(ARFLAGS) $(NAME) $(OBJS)\n    $(info CREATED $(NAME))\n\n$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c\n    $(DIR_DUP)\n    $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $\u003c\n    $(info CREATED $@)\n\n-include $(DEPS)\n\nclean:\n    $(RM) $(OBJS) $(DEPS)\n\nfclean: clean\n    $(RM) $(NAME)\n\nre:\n    $(MAKE) fclean\n    $(MAKE) all\n```\n\n- **Dependency files** are written in the make language and **must be included**\n  into our Makefile to be read.  The `include` directive work the same as C\n  `#include`, it tells make to suspend its current Makefile reading and read the\n  included files before continuing. Make sure to include the dependencies after\n  they are created → after the compilation rule that invoke `-MMD` via\n  `$(CPPFLAGS)`.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- The purpose of the `-include $(DEPS)` initial **hyphen symbol** is **to\n  prevent make from complaining** when a non-zero status code is encountered,\n  which can be caused here by a missing files from our generated dependency\n  files list.\n\n```make\n#------------------------------------------------#\n#   SPEC                                         #\n#------------------------------------------------#\n\n.PHONY: clean fclean re\n.SILENT:\n\n####################################### END_4 ####\n```\n\n[**Return to Index ↑**](#index)\n\n##  Version 5\n\n###     v5 Structure\n\nBuilds an `icecream` **program that uses** `libbase` and `libarom`\n**libraries**.  Both libraries are v4 based.\n\n```\n    before build:              after build:\n    .                          .\n    ├── src                    ├── src\n    │   └── main.c             │   └── main.c\n    ├── lib                    ├── lib\n    │   ├── libbase            │   ├── libbase\n    │   │   ├── src            │   │   ├── src\n    │   │   │   ├── water.c    │   │   │   ├── water.c\n    │   │   │   └── milk.c     │   │   │   └── milk.c\n    │   │   ├── include        │   │   ├── include\n    │   │   │   └── base.h     │   │   │   └── base.h\n    │   │   └── Makefile       │   │   ├── .build\n    │   └── libarom            │   │   │   ├── water.o\n    │       ├── src            │   │   │   ├── water.d\n    │       │   ├── coco.c     │   │   │   ├── milk.o\n    │       │   └── cherry.c   │   │   │   └── milk.d\n    │       ├── include        │   │   ├── Makefile\n    │       │   └── arom.h     │   │   └── libbase.a\n    │       └── Makefile       │   └── libarom\n    ├── include                │       ├── src\n    │   └── icecream.h         │       │   ├── coco.c\n    └── Makefile               │       │   └── cherry.c\n                               │       ├── include\n                               │       │   └── arom.h\n                               │       ├── .build\n                               │       │   ├── coco.o\n                               │       │   ├── coco.d\n                               │       │   ├── cherry.o\n                               │       │   └── cherry.d\n                               │       ├── Makefile\n                               │       └── libarom.a\n                               ├── include\n                               │   └── icecream.h\n                               ├── .build\n                               │   ├── main.o\n                               │   └── main.d\n                               ├── Makefile\n                               └── icecream\n```\n\n###     v5 Brief\n\n- system library linked by default\n- `addprefix` make function\n- flags and libraries used by the linker\n- `dir` function\n- Build with a library\n- Linking with a library\n- builds each of the required libraries\n- call rules recursively\n\n###     v5 Template\n\n```make\n# @author   clemedon (Clément Vidon)\n####################################### BEG_5 ####\n\nNAME        := icecream\n\n#------------------------------------------------#\n#   INGREDIENTS                                  #\n#------------------------------------------------#\n# LIBS        libraries to be used\n# LIBS_TARGET libraries to be built\n#\n# INCS        header file locations\n#\n# SRC_DIR     source directory\n# SRCS        source files\n#\n# BUILD_DIR   build directory\n# OBJS        object files\n# DEPS        dependency files\n#\n# CC          compiler\n# CFLAGS      compiler flags\n# CPPFLAGS    preprocessor flags\n# LDFLAGS     linker flags\n# LDLIBS      libraries name\n\nLIBS        := arom base m\nLIBS_TARGET :=            \\\n    lib/libarom/libarom.a \\\n    lib/libbase/libbase.a\n\nINCS        := include    \\\n    lib/libarom/include   \\\n    lib/libbase/include\n\nSRC_DIR     := src\nSRCS        := main.c\nSRCS        := $(SRCS:%=$(SRC_DIR)/%)\n\nBUILD_DIR   := .build\nOBJS        := $(SRCS:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)\nDEPS        := $(OBJS:.o=.d)\n\nCC          := clang\nCFLAGS      := -Wall -Wextra -Werror\nCPPFLAGS    := $(addprefix -I,$(INCS)) -MMD -MP\nLDFLAGS     := $(addprefix -L,$(dir $(LIBS_TARGET)))\nLDLIBS      := $(addprefix -l,$(LIBS))\n```\n\n- We can notice that the `m` library from `LIBS` is not mentionned in\n  `LIBS_TARGET` for the reason that `m` is a **system library** (`libm` for\n  mathematical functions found in `math.h`).  Unlike the `libc` which is linked\n  by default (we don't need to provide `-lc` flag to our linker) the `libm` is\n  not **linked by default**.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- In `CPPFLAGS` we use **`addprefix`** that, as its name suggests is a **make\n  function** that allows you to add a prefix, here a `-I` to each of the item\n  found in `$(INCS)` (that contains the paths to our project and libraries\n  headers).\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- `LDFLAGS` and `LDLIBS` contain the **flags and libraries** that will be **used\n  by the linker** `ld` to link the library to our project sources.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- The **`dir` function** means that we want to keep only directory part of the\n  given item, there exists a `notdir` function that does the opposite to keep\n  only the file name of the given item.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- **Build with a library** requires three flags: `-I` tell the compiler where to\n  find the lib header files, `-L` tells the linker where to look for the library\n  and `-l` the name of this library (without its conventional `lib` prefix).\n\nFor example: `-I lib/libarom/include -L lib/libarom -l arom`\n\n```make\n\n#------------------------------------------------#\n#   UTENSILS                                     #\n#------------------------------------------------#\n# RM        force remove\n# MAKEFLAGS make flags\n# DIR_DUP   duplicate directory tree\n\nRM          := rm -f\nMAKEFLAGS   += --silent --no-print-directory\nDIR_DUP     = mkdir -p $(@D)\n\n#------------------------------------------------#\n#   RECIPES                                      #\n#------------------------------------------------#\n# all       default goal\n# $(NAME)   link .o -\u003e archive\n# $(LIBS)   build libraries\n# %.o       compilation .c -\u003e .o\n# clean     remove .o\n# fclean    remove .o + binary\n# re        remake default goal\n# run       run the program\n# info      print the default goal recipe\n\nall: $(NAME)\n\n$(NAME): $(OBJS) $(LIBS_TARGET)\n    $(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $(NAME)\n    $(info CREATED $(NAME))\n\n$(LIBS_TARGET):\n    $(MAKE) -C $(@D)\n\n$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c\n    $(DIR_DUP)\n    $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $\u003c\n    $(info CREATED $@)\n\n-include $(DEPS)\n\nclean:\n    for f in $(dir $(LIBS_TARGET)); do $(MAKE) -C $$f clean; done\n    $(RM) $(OBJS) $(DEPS)\n\nfclean: clean\n    for f in $(dir $(LIBS_TARGET)); do $(MAKE) -C $$f fclean; done\n    $(RM) $(NAME)\n\nre:\n    $(MAKE) fclean\n    $(MAKE) all\n```\n\n- **Linking with a library** requires special attention to the order of the\n  linking flags.  In our case we need to make sure that `$(LDFLAGS)` and\n  `$(LDLIBS)` passes respectively before and after the `$(OBJS)` in the linking\n  recipe.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- `$(LIBS_TARGET)` rule **builds each of the required libraries** found in the\n  `INGREDIENTS` part.  It is a `$(NAME)` prerequisite for the same reason as\n  `$(OBJS)` because our *final goal* needs the libraries as well as the objects\n  to be built.\n\n\u003csub\u003e\u003csub\u003e\u003chr\u003e\u003c/sub\u003e\u003c/sub\u003e\n\n- As both rules `clean` and `fclean` appear in the Makefile of all our\n  `$(LIBS_TARGET)` we can **call** these **rules** for each of them\n  **recursively** using a shell `for` loop.  Here again we use the `dir`\n  function to only keep the directory part of the library.\n\n```make\n#------------------------------------------------#\n#   SPEC                                         #\n#------------------------------------------------#\n\n.PHONY: clean fclean re\n.SILENT:\n\n####################################### end_5 ####\n```\n\n[**Return to Index ↑**](#index)\n\n##  Bonus\n\n###     Extra rules\n\n```make\n.PHONY: run\nrun: re\n    -./$(NAME)\n```\n\n- `run` is a simple rule that **`make` and `run` the default goal**.  We start\n  the shell command with the `hyphen` symbol to prevent make from interrupting\n  its own execution if our program execution returns a non-zero value.\n\n```make\ninfo-%:\n    $(MAKE) --dry-run --always-make $* | grep -v \"info\"\n```\n\n- `info-\u003ctarget\u003e` rule will execute a `make \u003ctarget\u003e` command with `--dry-run` to\n  **print** the **`\u003ctarget\u003e` recipe without executing it**, `--always-make` to\n  `make` even if the targets already exist and `grep -v` to filter the output.\n  `$*` expands to the value given in place of the `%` at the rule call. For\n  example with `make info-re` `$*` will expand to `re`.\n\n```make\nprint-%:\n    $(info '$*'='$($*)')\n```\n\n- The `print-\u003cvariable\u003e` that works like `print-\u003crule\u003e` will **print the value\n  of an arbitrary variable**, for example a `make print-CC` will output\n  `CC=clang`.\n\n```make\n.PHONY: update\nupdate:\n    git stash\n    git pull\n    git submodule update --init\n    git stash pop\n```\n\n- The `update` rule will **update the git repository** to its last version, as\n  well as its *git submodules*. `stash` commands saves eventual uncommitted\n  changes and put them back in place once the update is done.\n\n# Sources\n\n| Topic | Website |\n| ----- | ------- |\n| doc | [**docs.w3cub.com**](https://docs.w3cub.com/gnu_make/)  |\n| manual | [**gnu.org**](https://www.gnu.org/software/make/manual/html_node)  |\n| a richer tutorial | [**makefiletutorial.com**](https://makefiletutorial.com/)  |\n| order-only exquisite | [**stackoverflow.com**](https://stackoverflow.com/a/68584653)  |\n| c libraries | [**docencia.ac.upc.edu**](https://docencia.ac.upc.edu/FIB/USO/Bibliografia/unix-c-libraries.html#creating_static_archive)  |\n| auto-deps gen | [**make.mad-scientist.net**](http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/)  |\n| auto-deps gen | [**scottmcpeak.com**](https://scottmcpeak.com/autodepend/autodepend.html)  |\n| auto-deps gen | [**microhowto.info**](http://www.microhowto.info/howto/automatically_generate_makefile_dependencies.html)  |\n| include statement | [**gnu.org**](https://www.gnu.org/software/make/manual/html_node/Include.html)  |\n| redis makefile | [**github.com**](https://github.com/redis/redis/blob/unstable/src/Makefile)  |\n\n# Contact\n\n```\ncvidon   42\nclemedon icloud\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclementvidon%2Fmakefile_tutor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclementvidon%2Fmakefile_tutor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclementvidon%2Fmakefile_tutor/lists"}