From 1beb5684e6b27d198dfd123ddd9626db49445c0a Mon Sep 17 00:00:00 2001 From: Matei Stanuca Date: Tue, 10 Mar 2026 11:07:46 +0200 Subject: [PATCH 1/5] Introduce script for deleting open pull requests Introduce a mechanism to delete all open pull requests from a repository. The script allows excluding specific PRs by providing usernames as command-line arguments. Signed-off-by: Matei Stanuca --- delete-pull-requests.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 delete-pull-requests.sh diff --git a/delete-pull-requests.sh b/delete-pull-requests.sh new file mode 100755 index 0000000..47b993e --- /dev/null +++ b/delete-pull-requests.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Check if arguments were provided +if [ $# -eq 0 ]; then + echo "Usage: $0 username1 username2 ..." + echo "Example: $0 adrian_dev bot-helper john_doe" + exit 1 +fi + +# Convert arguments into a JSON array for jq +EXCEPTIONS_JSON=$(printf '%s\n' "$@" | jq -R . | jq -s .) + +echo "Closing open pull requests (up to 1000), except those created by: $@" + +# Filter and close process +# Added --limit 1000 to bypass the default 30 results pagination +gh pr list --state open --limit 1000 --json number,author | \ +jq -r --argjson exceptions "$EXCEPTIONS_JSON" \ + '.[] | select(.author.login as $auth | $exceptions | index($auth) | not) | .number' | \ +xargs -I {} gh pr close {} --comment "Automatically closed: Author is not in the approved users list." From b89da1643c8d193494294891825319cdb3c8eb86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83dulescu?= <87699729+andreibadulescu@users.noreply.github.com> Date: Thu, 19 Feb 2026 18:50:22 +0200 Subject: [PATCH 2/5] Add preliminary notes for GNU Make with examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added some preliminary notes for support materials. Added two example Makefiles that demonstrate the contents of the preliminary notes. Signed-off-by: Andrei Bădulescu --- README.md | 12 ++++- makefiles/makefile1 | 14 ++++++ makefiles/makefile2 | 18 +++++++ makefiles/rezumat | 112 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 makefiles/makefile1 create mode 100644 makefiles/makefile2 create mode 100644 makefiles/rezumat diff --git a/README.md b/README.md index 6ab48c8..9e7e187 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ -# Markdown Workshop +# Markdown and Makefile Workshop -This is a practical workshop about the syntax and the use of the [Markdown format](https://www.markdownguide.org/basic-syntax/). +This is a practical workshop about the syntax and the use of the [Markdown format](https://www.markdownguide.org/basic-syntax/) and of the [GNU make](https://www.gnu.org/software/make/manual/make.html) utility. In particular, we will focus on the [GitHub Flavored Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax), used by GitHub. See the full specification of the GitHub Flavored Markdown [here](https://github.github.com/gfm/). > [!NOTE] > Markdown is not only used in GitHub / GitLab and other software development platforms, but also in chat message engines such as [Discord](https://discord.com/), note taking apps such as [HackMD](https://hackmd.io/) and wiki engines such as [MDwiki](https://mdwiki.info). > Parts of the Markdown syntax are also used on WhatsApp, MS Teams and other chat message engines, particularly for text formatting. +> Makefiles are used to efficiently build and debug software in all types of projects, from message applications to hypervisors and operating system modules. What makes them so special is the ability to create a chain of command between different Makefiles, which has the wonderful effect of modularization. ## Set Up @@ -301,6 +302,13 @@ Follow the steps: Push commits to the GitHub remote repository. Now check your resulting profile page on your GitHub page. +## Use of GNU make in public projects + +We will study different implementations of Makefiles across multiple projects to get a feel of its purpose and strengths. + +### This Repository + + ## Create a Website with GitHub and Markdown We can use the contents of a GitHub repository, written with Markdown, to create a website. diff --git a/makefiles/makefile1 b/makefiles/makefile1 new file mode 100644 index 0000000..c98278c --- /dev/null +++ b/makefiles/makefile1 @@ -0,0 +1,14 @@ +exec: main.o util.o processing.o + gcc -o exec main.o util.o processing.o + +main.o: main.c header.h + gcc -c main.c + +util.o: util.c header.h + gcc -c util.o + +processing.o: processing.c header.h + gcc -c processing.c + +clean: + rm exec main.o util.o processing.o diff --git a/makefiles/makefile2 b/makefiles/makefile2 new file mode 100644 index 0000000..f859e41 --- /dev/null +++ b/makefiles/makefile2 @@ -0,0 +1,18 @@ +objects = main.o util.o processing.o + +exec: $(objects) + gcc -o exec $(objects) + +main.o: main.c header.h + gcc -c main.c + +util.o: util.c header.h + gcc -c util.o + +processing.o: processing.c header.h + gcc -c processing.c + +.PHONY: clean + +clean: + -rm exec $(objects) diff --git a/makefiles/rezumat b/makefiles/rezumat new file mode 100644 index 0000000..587b49e --- /dev/null +++ b/makefiles/rezumat @@ -0,0 +1,112 @@ +Un makefile e format din reguli, reguli care sunt sub urmatoarea forma: +target ... : prerequisites ... + recipe + ... + ... + ... + +target e de obicei numele fisierului generat de un program (ex. executabile sau fisiere obiect), dar poate fi si numele actiunii pe care vrem sa o intreprindem (clean de exemplu). prerequisites sunt fisierule utilizate ca input pentru a crea target. recipe sunt actiunile pe care utilitarul make le intreprinde in cadrul unei reguli anume (atentie! este obligatoriu ca orice instructiune din recipe sa fie precedata de un tab). anumite reguli nu au nevoie de prerequisites (vezi clean, care face doar o actiune de delete). asadar, make tot ce face este sa urmeze niste reguli pe care noi le impunem printr-un asa-numit makefile, care contine pe larg un set de reguli. + +studiati regulile din `makefile1` si cum se compun una pe alta. pentru a crea executabilul, rulam `make`. pentru a curata folderul de lucru de fisiere obiect si executabile, folosim `make clean`. + +makefile-ul stie sa aplice selectiv anumite reguli, adica daca doua fisiere obiect din sapte ale unui executabil nu mai sunt de actualitate, le va recompila doar pe acelea, nu pe toate sapte, iar mai apoi executabilul daca asta i-a fost cerut. o sa vedem in exemplu ca daca am compilat programul si incercam imediat dupa sa compilam din nou, make ne va spune ca nu e nimic de facut. + +exista o regula standard pentru makefile-uri care se numeste clean, nu reprezinta un fisier, ci actiunea care este desfasurata. clean nu provine din nicio alta regula si nu ruleaza decat daca ii spunem utilitarului make in mod explicit asta. tintele care nu sunt backed de fisiere, cum este clean, se numesc tinte phony, vom vorbi mai tarziu in detaliu despre ele. + +primul target din makefile este target-ul default, care ruleaza atunci cand rulam doar make, fara alte argumente. in exemplu, make va incerca sa construiasca exec, insa fisierele obiect inca nu au fost verificate si sunt prerequisites, deci se duce la ele si dupa se ocupa de exec. comportamentul acesta se oglindeste pentru fiecare regula (seamana foarte tare cu o parcurgere in adancime, DFS). regulile care nu sunt prerequisites sunt ignorate / sarite. + +gnu make permite pe langa reguli si utilizarea variabilelor in cadrul unui makefile. in exemplul oferit mai devreme, am folosit objects pentru a evita greselile de scriere atunci cand vine vorba de mai multe obiecte. am creat variabila objects, care tine loc pentru toate fisierele de tip obiect, iar atunci cand vrem sa o folosim, utilizam structura $(). astfel putem asigura o mai mare usurinta in editarea fisierelor make si un numar mai mic de greseli atunci cand este creat sau editat. + +ar trebui investigat daca trebuie mentionat si comportamentul gnu make cand vine vorba de reguli implicite (vezi in documentatia oferita in workshop-markdow:/README.md daca are sens sa incarcam studentii cu asa ceva). + +punand clean in setul de reguli phony, devine explicit faptul ca regula clean nu are tinta niciun fisier, mai degraba este doar o denumire, iar daca adaugam o liniuta fix inainte de rm, make va continua sa execute comenzile din regula, chiar daca rm va da eroare (nu se aplica doar pentru rm, este doar dat ca exemplu). pentru a face utilitarul make sa ruleze comenzile din regula clean, va trebui sa rulam in terminal `make clean`. + + +makefile-urile sunt formate din reguli explicite si implicite, definitii de variabile, directive si comentarii, le vom explica in detaliu mai jos: + +- o regula explicita mentioneaza cand si cum reconstruieste unul sau mai multe fisiere cunoscute drept targets si are mai multe fisiere de care depinde, numite prerequisites si contine o "reteta" pentru a reconstrui tintele. + +- o regula implicita mentioneaza cand si cum reconstruieste o clasa de fisiere bazat pe numele lor. descrie cum o tinta depinde de un fisier cu un nume similar si ofera o reteta pentru a reconstrui tinta respectiva. ele nu fac scopul acestui workshop si exista multiple discutii daca ele ar trebui utilizate sau nu (prezinta un risc de ambiguitate cu privire la modul in care este construit un target). + +- o definitie de variabila este o linie care asociaza o eticheta unui string de caractere. + +- o directiva este o instructiune pe care make sa o faca in timp ce citeste makefile-ul, ele pot fi de obicei: citirea altui makefile, decizia de ignora bazat pe valorile unor variabile daca sa ignore sau sa foloseasca o parte a unui makefile (parti conditionale) sau definirea unei variabile dintr-un string multi-line + +- un comentariu, precedat tot timpul de '#', iar daca ne dorim ca acel comentariu sa continue pe linia urmatoare, la finalul liniei adaugam '\'. daca vrem sa folosim # in makefile, dar nu pentru marcarea unui comentariu, trebuie sa il "escapam" folosind structura "\#". daca make intalneste un # in mijlocul unei definitii de variabile sau unui apel de functie, nu il va trata ca marcaj al unui comentariu. comentariile pot fi plasate si la sfarsitul unei linii, fara ca # sa fie tratat ca facand parte din comanda sau din variabila. + +liniile foarte lungi pot fi sparte in mai multe linii folosind backslash ('\') pentru a creste gradul de lizibilitate al makefile-ului, care este convertit de gnu make automat intr-un spatiu la citirea fisierului, alipind inapoi liniile separate de backslash-uri. + +fisierele in care sunt puse regulile pentru make trebuie denumite intr-un mod specific. GNU make cauta mai intai in folderul de lucru fisierul "makefile" si apoi "Makefile". daca nu il gaseste, nu va face nimic. pot fi utilizate si alte denumiri pentru makefile, insa functionalitatea respectiva nu este recomandata in industrie si atunci nu face scopul acestui workshop. + +daca vrem sa construim o oarecare structura "arborescenta" de makefile-uri, putem folosi in partea incipienta a unui fisier comanda "include ", care va citi makefile-urile subordonate inainte de a continua citirea makefile-ului unde s-a intalnit cuvantul cheie include. + +mare grija la indentarea liniilor! liniile care incep cu un tab sunt considerate ca facand parte dintr-o reteta a unei reguli. + +exista mai multe caractere wildcard care pot usura compunerea unui makefile. acestea sunt '*', '?' si '[...]', le vom discuta mai in detaliu mai jos: + +- '*' tine loc pentru toate fisierele. de exemplu, daca spun ca o regula are drept prerequisites '*.c', asta inseamna ca toate fisierele al caror nume se termina in '.c' din directorul de lucru vor fi preluate drept necesare pentru acea regula. lista returnata de wildcard-ul * este sortata. + +- '~' tine loc pentru calea directorului home (are acelasi comportament ca '~' din bash). daca vrem sa prescurtam caile, putem spune de exemplu '~/bin' in loc de '/home/my_user/bin'. + +exista si asa-zise "automatic variables", cum ar fi '$^', '$@' si altele, le vom discuta in cele ce urmeaza: + +- '$^' este un inlocuitor pentru toate prerequisite-urile unei reguli, inlocuirea fiind facuta automat la executarea unei anumite reguli + +- '$@' este un inlocuitor pentru tinta regulei. atentie! daca regula are un nume abstract (sau phonynumit in GNU make), vom construi o tinta care are numele regulei, mare grija la utilizarea acestei variabile automate. + +- '$<' este un inlocuitor pentru primul obiect din prerequisites. de exemplu, daca am: +``` +foo.o: foo.c defs.h hack.h + gcc -c $< -o $@ +``` +va fi dat spre compilare doar foo.c, nu si headerele. + +target-urile phony sunt cele care nu au numele unui fisier, este mai degraba doar un nume pentru un set de instructiuni care trebuie executat cand se cere asta in mod explicit. exista doua motive pentru utilizarea unei astfel de tinte, evitarea unui conflict cu un fisier cu acelasi nume si imbunatatirea performantei. daca tintele phony nu sunt marcate explicit ca fiind phony (discutam mai tarziu putin despre cum facem asta), daca la un moment dat in folderul de lucru va aparea un fisier cu acelasi nume, regula nu va fi executata niciodata deoarece fisierul va fi considerat de actualitate. pentru a evita asta, folosim un marcator special: + +``` +.PHONY: clean +clean + rm *.o temp +``` + +pentru a folosi o variabila, folosim structura '$()' in retete sau in lista de target-uri sau prerequisites. + +in proiectele mai mari, atunci cand ne este indicat sa folosim comanda ```make -j ```, acel argument de dupa `-j` reprezinta numarul de job-uri, mai exact numarul de retete, care vor fi executate simultan. cu toate acestea, trebuie sa ne asiguram ca makefile-ul este bine scris, astfel incat paralelizarea task-urilor sa nu duca la o eroare, cum ar fi un fisier cautat care inca nu a fost construit de o alta regula. + +partile conditionale ale unui makefile sunt structurate ca un bloc if-else, unde conditia evaluata are mereu de-a face cu o variabila din makefile. + +``` +# in acest exemplu, presupunem ca variabila CC este setata de noi ca gcc, poate fi de asemenea clang, g++ et cetera +CC = gcc + +foo: foo.c +ifeq ($(CC), gcc) + $(CC) -o foo $^ +else + echo "Warning! The program logic was written with the particularities of gcc in mind. Stopping make foo rule..." +endif +``` + +pe langa ifeq, exista si ifneq, care in loc de `==`, face `!=`, mai exista si ifdef , care verifica daca o var este definita, dar si ifndef, care verifica daca variabila NU este definita, in acest caz intorcand true. + +GNU make intoarce 0 daca comanda make s-a incheiat cu succes, 2 daca in timpul citirii sau executarii apare o eroare, iar 1 este rezervat pentru flag-ul '-q' si make considera ca una dintre tinte nu mai este de actualitate. + +pentru a rula o regula specifica din makefile, rulam comanda `make `. mai jos este o lista de reguli standard care pot aparea in cadrul unui makefile. Atentie! aceste reguli nu sunt definite automat de catre GNU make, ele reprezinta doar niste conventii care sunt lasate in seama celui care scrie fisierului respectiv. + +- 'all' construieste toate target-urile top-level cunoscute makefile-ului; +- 'clean' sterge toate fisierele care sunt create in mod normal de make; +- 'mostlyclean' este asemanator cu 'clean', insa nu sterge fisierele a caror compilare ia foarte mult timp; +- 'distclean', 'realclean', 'clobber' sunt tinte care pot sau nu sa fie definite si de obicei au un comportament mai agresiv decat 'clean' (de exemplu, sterg si fisierele utilizate pentru configurarea procesului de constructie). +- 'install' copiaza fisierele executabile si fisierele auxiliare in folderele specifice pentru programul respectiv; +- 'print' listeaza fisierele sursa care s-au schimbat de la ultima rulare a lui make; +- 'test' ruleaza niste teste automate asupra executabilului construit de catre makefile; + +de asemenea, este o conventie generala ca fiecare makefile ar trebui sa inceapa cu linia +``` +SHELL = Date: Tue, 17 Mar 2026 21:17:40 +0200 Subject: [PATCH 3/5] README: Add GNU Make workshop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added the Makefile part of the workshop. Moved recap to /pitch. Added support files for exercises. Signed-off-by: Andrei Bădulescu --- README.md | 232 +++++++++++++++++- gnumake/Makefile | 5 + gnumake/clean | 1 + gnumake/example.c | 6 + .../makefile_placeholder1 | 0 .../makefile_placeholder2 | 0 makefiles/rezumat => pitch/make-recap.txt | 79 ++++-- 7 files changed, 304 insertions(+), 19 deletions(-) create mode 100644 gnumake/Makefile create mode 100644 gnumake/clean create mode 100644 gnumake/example.c rename makefiles/makefile1 => gnumake/makefile_placeholder1 (100%) rename makefiles/makefile2 => gnumake/makefile_placeholder2 (100%) rename makefiles/rezumat => pitch/make-recap.txt (86%) diff --git a/README.md b/README.md index 9e7e187..58c6474 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Markdown and Makefile Workshop This is a practical workshop about the syntax and the use of the [Markdown format](https://www.markdownguide.org/basic-syntax/) and of the [GNU make](https://www.gnu.org/software/make/manual/make.html) utility. -In particular, we will focus on the [GitHub Flavored Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax), used by GitHub. +The Markdown part of this workshop will focus on the [GitHub Flavored Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) version, used by GitHub. See the full specification of the GitHub Flavored Markdown [here](https://github.github.com/gfm/). > [!NOTE] @@ -57,7 +57,7 @@ Identify syntax aspects in the documentation for [GitHub Flavored Markdown](http See: - The use of `#`, `##`, `###` for section headings. -- The use of backticks for typewriter font, used for the names of files, functions, and +- The use of backticks for typewriter font, used for the names of files, functions, and - The use `-` and `\*` for unordered lists. - The use of `1.` for ordered lists. - The syntax used for links. @@ -252,6 +252,234 @@ Check the GitHub web view of the [upstream repository](https://github.com/rosedu After the pull request is merged, go to the same steps as above to clean your pull request. +## What is GNU make? + +Make is a toolchain included in the GNU open-source software application collection that is used to set up and organise building, installing and cleaning procedures in a development repository. + +We will look at some examples from the open-source world. + +### This Repository + +Let's look at the file named `Makefile`: + +```console +cat Makefile +``` + +Although it does not have any file extension, it is basically a text file. It may seem like a coincidence that the file we are using GNU make with is named Makefile, but this is actually a recommendation and a global standard between developers. + +If we look at this file more closely, we will see some syntax rules vital for a Makefile: + + - There are multiple rules that contain multiple lines indented with a tab, which are part of a so-called *recipe* + - Each rule starts with a name or a file, we will from now on call it a *target* + - After the colons there are one or more files, which are known as *prerequisites* + - At the beginning (but not exclusively) there are a couple of *variables* + +To see the effects of this file, run: + +```console +make demo +``` + +### Operating Systems (from Open Education Hub) + +Read the Makefile in the [Operating Systems (Open Education Hub / `cs-pub-ro`) repository](https://github.com/cs-pub-ro/operating-systems). There are a couple of rules that build on top of each other, we will go over this concept later on. + +### KraftKit (from Unikraft) + +Read the Makefile in the [KraftKit repository](https://github.com/unikraft/kraftkit/tree/staging). This file contains advanced syntax, so you should focus on variable definitions, the use of `.PHONY` and conditional blocks. + +## Building a Makefile + +In order to properly grasp how one writes a Makefile, we will discuss each step individually. First, let's write our first rule! + +### Rules and recipes + +The main part of a Makefile are its rules and the recipes associated to each rule. They abide by the following format: + +```console + ... : ... + + + ... +``` +> [!NOTE] +> All recipe commands must be indented using tab! This is how Make knows a text line belongs to a specific recipe. +> Equally important is that a rule may contain more than one target or more than one prerequisite (even zero prerequisites!), depending on the build flow of the application. + +We will start by writing our first build rule. In order to do that, go to the `gnumake` folder. You will find there an empty Makefile waiting for you. + +You will also find some source files containing code written in C. Those will be our *prerequisites* for the length of this demo, given that we are building our application from them. We will build `example.c`. + +Based on what we know, in order to obtain our binary, we must begin with `example`, as this is our target, our end goal. To build it, we need `example.c` and a C compiler (i.e. `gcc`) to run `gcc -o example example.c`. Now we can build our rule: + +```make +example: example.c + gcc -o example example.c +``` + +In order to run any rule from a Makefile, the usual command is: + +```console +make +``` + +If a rule has more than one target, you can choose any target to run the same recipe. Run `make example` and execute the freshly-made application. Congratulations, you wrote your first Make rule! + +### Cleanup using Make + +There are moments when one may want to delete the products of a build process, which is widely known as *cleaning* our repository. This is usually done to eliminate parasite effects of old binaries or to clear up the structure of the working directory. + +Make proposes a standard rule named `clean`, which is responsible for eliminating all byproducts of our source files. There is a catch however; what could be the target of such rule? The answer is: none. It does not have any prerequisites either. + +#### .PHONY + +Some targets are not representing any files in our working directory, they are just names, tags for our recipes. Rules that have such targets are categorised as *phony* because they can be misleading for Make. For example, let's say we have this rule: + +```make +clean: + rm example +``` + +Write it in the Makefile and see if it does anything. + +Why does Make stop execution of this rule? Well, there is a file in our working directory named `clean` and our rule does not have any prerequisites. That means, there isn't anything that can change the file named `clean`, so Make will waste time by trying to build our target again and stops. This is the default behaviour of the toolchain. + +How do we fix it? We tell Make that the target of this rule is merely a name, not an actual file. That way, Make will ignore the file and run the rule every time we request it. This is how it should look: + +```make +.PHONY: clean + +clean: + rm example +``` + +There can be multiple rules labeled as phony in a single statement; they are just mentioned and separed with a single space. + +This should be the final result: + +```make +example: example.c + gcc -o example example.c + +.PHONY: clean + +clean: + rm example +``` + +**Well done, you just built your first Makefile!** + +## Bits and pieces of Makefiles + +Makefiles are written using the following structures: + +- **Explicit Rules:** targets and prerequisites are mentioned and a recipe is clearly laid out; + +- **Implicit Rules:** they apply to classes of files and describe a target's dependency of a similarly-named source file; + +- **Variables:** it associates a tag / name to a string of text; + +- **Directives:** special instructions to be carried out by Make as it reads the Makefile, for example reading another Makefile, or ignoring specific parts of a Makefile based on the value of evaluated variables; + +- **Comments:** starting with `#`, can be continued on the next line if we add at the end of the line `\`; backslash can also be used to break up long commands in order to improve readability; + +The first rule written in the Makefile is the default rule that runs when we use `make`. When running a rule, if there is a prerequisite that is the target of another rule it will run that before. Think of it as a depth-first search on graphs, where the nodes are the rules and the vertices are the dependencies on one another! + +Using these components, engineers can build massive projects without wrapping their head with compiler commands and dependencies. Without further ado, let's get into using these! + +### Variables + +Variables are useful in multiple scenarios. Let's say that someone wants to change the compiler that is being used inside the Makefile. Changing hundreds of lines of recipes is gruesome, so instead we use a variable that contains the compiler's name to allow easier update and refinement of various instructions. Here is an example: + +```make +example_variable = src1.c src2.c +# when expanded with $(example_variable) +# it will be replaced by "src1.c src2.c" + +CC = gcc + +example: example.c + $(CC) -o example example.c + # Make is therefore running + # "gcc -o example example.c" +``` + +Variables can also be used to reference targets and prerequisites in an easier manner. + +### Wildcards and automatic variables + +Wildcards are characters that expand automatically to predefined values. They have the same meaning as the ones in Bash. The usual suspects are `*`, `~` and `?`. We will describe them below: + + - `*` is a placeholder for zero or more characters; + + - `~` is a placeholder for the home directory of the current user; + + - `?` is a placeholder for exactly one character. + +Automatic variables are pairs of characters that start with `$`. Their values vary depending on the executed rule and other factors: + + - `$^` is replaced with all prerequisites of a rule automatically at recipe execution; + + - `$@` is replaced with the targets of the rule automatically at execution, even if the rule is declared as .PHONY. It basically replaces character by character this sequence with the targets sequence. + + - `$<` is replaced with the first prerequisite automatically. + +Let's see an example to clear things up! In the folder with the Makefile given below, there are two files, namely `example1.c` and `example2.c`. + +```make +example: *.c + gcc -o $@ $^ + +.PHONY: clean distclean + +clean: + rm example + +# this deletes even the source files +# given that this is an example, it is a niche case +# in real world applications, this would never be a rule!!! + +distclean: clean + rm example?.c + +# also, the dependency on clean means that distclean runs it before running its own recipe! +``` + +## Good Makefile Practices + +There are several recommendations regarding the naming of Makefile rules and how they should behave. Not only that, but Makefiles should usually bear a name that will be automatically recognised by Make. + +### Makefile naming convention + +Based on how Make is implemented in the GNU library, in order to run a `make` command without explicitly specifying the Makefile's location one should name it: + +- "GNUmakefile" +- "makefile" +- "Makefile" + +The first is rarely met and developers usually opt for the last 2 options. This is the order in which Make looks for Makefiles in the current working directory. When it finds a Makefile with one of these names, it stops searching and starts executing recipes. + +> [!NOTE] +> Makefiles do not bear any file extension! + +> [!NOTE] +> We do not recommend naming Makefiles in any other way except those recognised automatically by Make. It can represent a serious obstacle for developers that do not know the actual name of the project's Makefile. + +### Multi-thread execution + +Make supports executing multiple recipes simulatenously. This is achieved using the `-j ` option and it is usually the norm to insert the number of logical processors / threads your machine has. On Linux / macOS, this usually is `-j $(nproc)`. + +If no thread limit is given, Make will create as many threads as it needs, usually leading to getting your Operating System stuck with too many threads. + +### Multi-file structure + +Make supports fragmenting rules into multiple Makefiles, creating something similar to an arborescent structure containing primary and secondary Makefiles. This is achieved by using: + +`include ...` + +Make will read these Makefiles first before starting to execute any rule requested by the user. + ## GitHub Profile Page GitHub provides you to option to have a GitHub profile page, that you can use as a form of open source CV / portfolio. diff --git a/gnumake/Makefile b/gnumake/Makefile new file mode 100644 index 0000000..5f5e31b --- /dev/null +++ b/gnumake/Makefile @@ -0,0 +1,5 @@ +# Extra tip: +# Comments in Makefiles start with '#' and stretch +# to the end of the line! + +# Start writing below this line... diff --git a/gnumake/clean b/gnumake/clean new file mode 100644 index 0000000..ab8d90b --- /dev/null +++ b/gnumake/clean @@ -0,0 +1 @@ +Do not modify the contents of this file. Thanks! \ No newline at end of file diff --git a/gnumake/example.c b/gnumake/example.c new file mode 100644 index 0000000..561974b --- /dev/null +++ b/gnumake/example.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + printf("Good job! You just built a binary with Make!"); + return 0; +} \ No newline at end of file diff --git a/makefiles/makefile1 b/gnumake/makefile_placeholder1 similarity index 100% rename from makefiles/makefile1 rename to gnumake/makefile_placeholder1 diff --git a/makefiles/makefile2 b/gnumake/makefile_placeholder2 similarity index 100% rename from makefiles/makefile2 rename to gnumake/makefile_placeholder2 diff --git a/makefiles/rezumat b/pitch/make-recap.txt similarity index 86% rename from makefiles/rezumat rename to pitch/make-recap.txt index 587b49e..bd7d353 100644 --- a/makefiles/rezumat +++ b/pitch/make-recap.txt @@ -1,3 +1,4 @@ +**ACOPERIT** Un makefile e format din reguli, reguli care sunt sub urmatoarea forma: target ... : prerequisites ... recipe @@ -5,62 +6,85 @@ target ... : prerequisites ... ... ... +**ACOPERIT** target e de obicei numele fisierului generat de un program (ex. executabile sau fisiere obiect), dar poate fi si numele actiunii pe care vrem sa o intreprindem (clean de exemplu). prerequisites sunt fisierule utilizate ca input pentru a crea target. recipe sunt actiunile pe care utilitarul make le intreprinde in cadrul unei reguli anume (atentie! este obligatoriu ca orice instructiune din recipe sa fie precedata de un tab). anumite reguli nu au nevoie de prerequisites (vezi clean, care face doar o actiune de delete). asadar, make tot ce face este sa urmeze niste reguli pe care noi le impunem printr-un asa-numit makefile, care contine pe larg un set de reguli. +**SKIP** studiati regulile din `makefile1` si cum se compun una pe alta. pentru a crea executabilul, rulam `make`. pentru a curata folderul de lucru de fisiere obiect si executabile, folosim `make clean`. +**ACOPERIT** +pentru a rula o regula specifica din makefile, rulam comanda `make `. mai jos este o lista de reguli standard care pot aparea in cadrul unui makefile. Atentie! aceste reguli nu sunt definite automat de catre GNU make, ele reprezinta doar niste conventii care sunt lasate in seama celui care scrie fisierului respectiv. + +**ACOPERIT** makefile-ul stie sa aplice selectiv anumite reguli, adica daca doua fisiere obiect din sapte ale unui executabil nu mai sunt de actualitate, le va recompila doar pe acelea, nu pe toate sapte, iar mai apoi executabilul daca asta i-a fost cerut. o sa vedem in exemplu ca daca am compilat programul si incercam imediat dupa sa compilam din nou, make ne va spune ca nu e nimic de facut. +**ACOPERIT** exista o regula standard pentru makefile-uri care se numeste clean, nu reprezinta un fisier, ci actiunea care este desfasurata. clean nu provine din nicio alta regula si nu ruleaza decat daca ii spunem utilitarului make in mod explicit asta. tintele care nu sunt backed de fisiere, cum este clean, se numesc tinte phony, vom vorbi mai tarziu in detaliu despre ele. +**ACOPERIT** primul target din makefile este target-ul default, care ruleaza atunci cand rulam doar make, fara alte argumente. in exemplu, make va incerca sa construiasca exec, insa fisierele obiect inca nu au fost verificate si sunt prerequisites, deci se duce la ele si dupa se ocupa de exec. comportamentul acesta se oglindeste pentru fiecare regula (seamana foarte tare cu o parcurgere in adancime, DFS). regulile care nu sunt prerequisites sunt ignorate / sarite. +**ACOPERIT** gnu make permite pe langa reguli si utilizarea variabilelor in cadrul unui makefile. in exemplul oferit mai devreme, am folosit objects pentru a evita greselile de scriere atunci cand vine vorba de mai multe obiecte. am creat variabila objects, care tine loc pentru toate fisierele de tip obiect, iar atunci cand vrem sa o folosim, utilizam structura $(). astfel putem asigura o mai mare usurinta in editarea fisierelor make si un numar mai mic de greseli atunci cand este creat sau editat. +**SKIP** ar trebui investigat daca trebuie mentionat si comportamentul gnu make cand vine vorba de reguli implicite (vezi in documentatia oferita in workshop-markdow:/README.md daca are sens sa incarcam studentii cu asa ceva). +**ACOPERIT** punand clean in setul de reguli phony, devine explicit faptul ca regula clean nu are tinta niciun fisier, mai degraba este doar o denumire, iar daca adaugam o liniuta fix inainte de rm, make va continua sa execute comenzile din regula, chiar daca rm va da eroare (nu se aplica doar pentru rm, este doar dat ca exemplu). pentru a face utilitarul make sa ruleze comenzile din regula clean, va trebui sa rulam in terminal `make clean`. - +**ACOPERIT** makefile-urile sunt formate din reguli explicite si implicite, definitii de variabile, directive si comentarii, le vom explica in detaliu mai jos: +**ACOPERIT** - o regula explicita mentioneaza cand si cum reconstruieste unul sau mai multe fisiere cunoscute drept targets si are mai multe fisiere de care depinde, numite prerequisites si contine o "reteta" pentru a reconstrui tintele. +**ACOPERIT** - o regula implicita mentioneaza cand si cum reconstruieste o clasa de fisiere bazat pe numele lor. descrie cum o tinta depinde de un fisier cu un nume similar si ofera o reteta pentru a reconstrui tinta respectiva. ele nu fac scopul acestui workshop si exista multiple discutii daca ele ar trebui utilizate sau nu (prezinta un risc de ambiguitate cu privire la modul in care este construit un target). +**ACOPERIT** - o definitie de variabila este o linie care asociaza o eticheta unui string de caractere. +**ACOPERIT** - o directiva este o instructiune pe care make sa o faca in timp ce citeste makefile-ul, ele pot fi de obicei: citirea altui makefile, decizia de ignora bazat pe valorile unor variabile daca sa ignore sau sa foloseasca o parte a unui makefile (parti conditionale) sau definirea unei variabile dintr-un string multi-line +**ACOPERIT** - un comentariu, precedat tot timpul de '#', iar daca ne dorim ca acel comentariu sa continue pe linia urmatoare, la finalul liniei adaugam '\'. daca vrem sa folosim # in makefile, dar nu pentru marcarea unui comentariu, trebuie sa il "escapam" folosind structura "\#". daca make intalneste un # in mijlocul unei definitii de variabile sau unui apel de functie, nu il va trata ca marcaj al unui comentariu. comentariile pot fi plasate si la sfarsitul unei linii, fara ca # sa fie tratat ca facand parte din comanda sau din variabila. +**ACOPERIT** liniile foarte lungi pot fi sparte in mai multe linii folosind backslash ('\') pentru a creste gradul de lizibilitate al makefile-ului, care este convertit de gnu make automat intr-un spatiu la citirea fisierului, alipind inapoi liniile separate de backslash-uri. +**ACOPERIT** fisierele in care sunt puse regulile pentru make trebuie denumite intr-un mod specific. GNU make cauta mai intai in folderul de lucru fisierul "makefile" si apoi "Makefile". daca nu il gaseste, nu va face nimic. pot fi utilizate si alte denumiri pentru makefile, insa functionalitatea respectiva nu este recomandata in industrie si atunci nu face scopul acestui workshop. +**ACOPERIT** daca vrem sa construim o oarecare structura "arborescenta" de makefile-uri, putem folosi in partea incipienta a unui fisier comanda "include ", care va citi makefile-urile subordonate inainte de a continua citirea makefile-ului unde s-a intalnit cuvantul cheie include. +**ACOPERIT** mare grija la indentarea liniilor! liniile care incep cu un tab sunt considerate ca facand parte dintr-o reteta a unei reguli. +**ACOPERIT** exista mai multe caractere wildcard care pot usura compunerea unui makefile. acestea sunt '*', '?' si '[...]', le vom discuta mai in detaliu mai jos: -- '*' tine loc pentru toate fisierele. de exemplu, daca spun ca o regula are drept prerequisites '*.c', asta inseamna ca toate fisierele al caror nume se termina in '.c' din directorul de lucru vor fi preluate drept necesare pentru acea regula. lista returnata de wildcard-ul * este sortata. + - '*' tine loc pentru toate fisierele. de exemplu, daca spun ca o regula are drept prerequisites '*.c', asta inseamna ca toate fisierele al caror nume se termina in '.c' din directorul de lucru vor fi preluate drept necesare pentru acea regula. lista returnata de wildcard-ul * este sortata. -- '~' tine loc pentru calea directorului home (are acelasi comportament ca '~' din bash). daca vrem sa prescurtam caile, putem spune de exemplu '~/bin' in loc de '/home/my_user/bin'. + - '~' tine loc pentru calea directorului home (are acelasi comportament ca '~' din bash). daca vrem sa prescurtam caile, putem spune de exemplu '~/bin' in loc de '/home/my_user/bin'. +**ACOPERIT** exista si asa-zise "automatic variables", cum ar fi '$^', '$@' si altele, le vom discuta in cele ce urmeaza: -- '$^' este un inlocuitor pentru toate prerequisite-urile unei reguli, inlocuirea fiind facuta automat la executarea unei anumite reguli + - '$^' este un inlocuitor pentru toate prerequisite-urile unei reguli, inlocuirea fiind facuta automat la executarea unei anumite reguli -- '$@' este un inlocuitor pentru tinta regulei. atentie! daca regula are un nume abstract (sau phonynumit in GNU make), vom construi o tinta care are numele regulei, mare grija la utilizarea acestei variabile automate. + - '$@' este un inlocuitor pentru tinta regulei. atentie! daca regula are un nume abstract (sau phony numit in GNU make), vom construi o tinta care are numele regulei, mare grija la utilizarea acestei variabile automate. -- '$<' este un inlocuitor pentru primul obiect din prerequisites. de exemplu, daca am: -``` -foo.o: foo.c defs.h hack.h - gcc -c $< -o $@ -``` -va fi dat spre compilare doar foo.c, nu si headerele. + - '$<' este un inlocuitor pentru primul obiect din prerequisites. de exemplu, daca am: + ``` + foo.o: foo.c defs.h hack.h + gcc -c $< -o $@ + ``` + va fi dat spre compilare doar foo.c, nu si headerele. +**ACOPERIT** target-urile phony sunt cele care nu au numele unui fisier, este mai degraba doar un nume pentru un set de instructiuni care trebuie executat cand se cere asta in mod explicit. exista doua motive pentru utilizarea unei astfel de tinte, evitarea unui conflict cu un fisier cu acelasi nume si imbunatatirea performantei. daca tintele phony nu sunt marcate explicit ca fiind phony (discutam mai tarziu putin despre cum facem asta), daca la un moment dat in folderul de lucru va aparea un fisier cu acelasi nume, regula nu va fi executata niciodata deoarece fisierul va fi considerat de actualitate. pentru a evita asta, folosim un marcator special: ``` @@ -69,10 +93,15 @@ clean rm *.o temp ``` +**ACOPERIT** pentru a folosi o variabila, folosim structura '$()' in retete sau in lista de target-uri sau prerequisites. +**ACOPERIT** in proiectele mai mari, atunci cand ne este indicat sa folosim comanda ```make -j ```, acel argument de dupa `-j` reprezinta numarul de job-uri, mai exact numarul de retete, care vor fi executate simultan. cu toate acestea, trebuie sa ne asiguram ca makefile-ul este bine scris, astfel incat paralelizarea task-urilor sa nu duca la o eroare, cum ar fi un fisier cautat care inca nu a fost construit de o alta regula. +**LIVE** +**LIVE** +**LIVE** partile conditionale ale unui makefile sunt structurate ca un bloc if-else, unde conditia evaluata are mereu de-a face cu o variabila din makefile. ``` @@ -88,11 +117,16 @@ endif ``` pe langa ifeq, exista si ifneq, care in loc de `==`, face `!=`, mai exista si ifdef , care verifica daca o var este definita, dar si ifndef, care verifica daca variabila NU este definita, in acest caz intorcand true. +**LIVE** +**LIVE** +**LIVE** +**SKIP** GNU make intoarce 0 daca comanda make s-a incheiat cu succes, 2 daca in timpul citirii sau executarii apare o eroare, iar 1 este rezervat pentru flag-ul '-q' si make considera ca una dintre tinte nu mai este de actualitate. -pentru a rula o regula specifica din makefile, rulam comanda `make `. mai jos este o lista de reguli standard care pot aparea in cadrul unui makefile. Atentie! aceste reguli nu sunt definite automat de catre GNU make, ele reprezinta doar niste conventii care sunt lasate in seama celui care scrie fisierului respectiv. - +**LIVE** +**LIVE** +**LIVE** - 'all' construieste toate target-urile top-level cunoscute makefile-ului; - 'clean' sterge toate fisierele care sunt create in mod normal de make; - 'mostlyclean' este asemanator cu 'clean', insa nu sterge fisierele a caror compilare ia foarte mult timp; @@ -100,13 +134,24 @@ pentru a rula o regula specifica din makefile, rulam comanda `make - 'install' copiaza fisierele executabile si fisierele auxiliare in folderele specifice pentru programul respectiv; - 'print' listeaza fisierele sursa care s-au schimbat de la ultima rulare a lui make; - 'test' ruleaza niste teste automate asupra executabilului construit de catre makefile; +**LIVE** +**LIVE** +**LIVE** +**LIVE** +**LIVE** +**LIVE** +pentru a evita confuziile cu privire la shell-ul utilizat de catre GNU make in cazul acestui makefile. e bine ca optiunile pentru compilare, cat si numele efectiv al compilatorului utilizat sa fie utilizat ca o variabila, pentru a permite actualizari in intregul makefile mai rapide si fara erori (de exemplu uit sa substitui un gcc pentru un g++ sau uit un flag -Wall la una din comenzile de compilare). folderele specifice de build sau install este de asemenea recomandat sa le punem in interiorul unei variabile si mai apoi sa o utilizam pe aceasta in reguli. +**LIVE** +**LIVE** +**LIVE** + +**SKIP** de asemenea, este o conventie generala ca fiecare makefile ar trebui sa inceapa cu linia -``` -SHELL = # un exemplu ar fi # SHELL = /bin/bash ``` -pentru a evita confuziile cu privire la shell-ul utilizat de catre GNU make in cazul acestui makefile. e bine ca optiunile pentru compilare, cat si numele efectiv al compilatorului utilizat sa fie utilizat ca o variabila, pentru a permite actualizari in intregul makefile mai rapide si fara erori (de exemplu uit sa substitui un gcc pentru un g++ sau uit un flag -Wall la una din comenzile de compilare). folderele specifice de build sau install este de asemenea recomandat sa le punem in interiorul unei variabile si mai apoi sa o utilizam pe aceasta in reguli. - From b2163a5347cb22b60a14a15daa5a0aa15b359077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83dulescu?= Date: Wed, 18 Mar 2026 15:07:51 +0200 Subject: [PATCH 4/5] DRILLS: Add GNU Make exercises MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added exercises for the Makefile part of the workshop. Added two examples to /pitch for live explanations. Improved README.md to encourage participants to send their work for review. Signed-off-by: Andrei Bădulescu --- Makefile | 12 + README.md | 296 +++++++++++++++--- gnumake/clean | 2 +- gnumake/custom/greeting.c | 16 + gnumake/custom/header.h | 5 + gnumake/custom/part1.c | 30 ++ gnumake/custom/part2.c | 30 ++ gnumake/example.c | 2 +- .../makefile_placeholder1 => pitch/example1 | 0 .../makefile_placeholder2 => pitch/example2 | 10 +- pitch/make-recap.txt | 157 ---------- 11 files changed, 351 insertions(+), 209 deletions(-) create mode 100644 Makefile create mode 100644 gnumake/custom/greeting.c create mode 100644 gnumake/custom/header.h create mode 100644 gnumake/custom/part1.c create mode 100644 gnumake/custom/part2.c rename gnumake/makefile_placeholder1 => pitch/example1 (100%) rename gnumake/makefile_placeholder2 => pitch/example2 (62%) delete mode 100644 pitch/make-recap.txt diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7653ff5 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +CC = gcc +CFLAGS = -Wall -Wextra -O2 + +all: demo + +.PHONY: clean + +demo: helloworld/helloworld.c + $(CC) $(CFLAGS) -o $@ $^ + +clean: + rm demo diff --git a/README.md b/README.md index 58c6474..5934730 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ See the full specification of the GitHub Flavored Markdown [here](https://github > [!NOTE] > Markdown is not only used in GitHub / GitLab and other software development platforms, but also in chat message engines such as [Discord](https://discord.com/), note taking apps such as [HackMD](https://hackmd.io/) and wiki engines such as [MDwiki](https://mdwiki.info). > Parts of the Markdown syntax are also used on WhatsApp, MS Teams and other chat message engines, particularly for text formatting. -> Makefiles are used to efficiently build and debug software in all types of projects, from message applications to hypervisors and operating system modules. What makes them so special is the ability to create a chain of command between different Makefiles, which has the wonderful effect of modularization. +> Makefiles are used to efficiently build and debug software in all types of projects, from message applications to hypervisors and operating system modules. +> What makes them so special is the ability to create a chain of command between different Makefiles, which has the wonderful effect of modularization. ## Set Up @@ -252,7 +253,7 @@ Check the GitHub web view of the [upstream repository](https://github.com/rosedu After the pull request is merged, go to the same steps as above to clean your pull request. -## What is GNU make? +## What is GNU Make? Make is a toolchain included in the GNU open-source software application collection that is used to set up and organise building, installing and cleaning procedures in a development repository. @@ -266,7 +267,8 @@ Let's look at the file named `Makefile`: cat Makefile ``` -Although it does not have any file extension, it is basically a text file. It may seem like a coincidence that the file we are using GNU make with is named Makefile, but this is actually a recommendation and a global standard between developers. +Although it does not have any file extension, it is basically a text file. +It may seem like a coincidence that the file we are using GNU make with is named Makefile, but this is actually a recommendation and a global standard between developers. If we look at this file more closely, we will see some syntax rules vital for a Makefile: @@ -283,19 +285,23 @@ make demo ### Operating Systems (from Open Education Hub) -Read the Makefile in the [Operating Systems (Open Education Hub / `cs-pub-ro`) repository](https://github.com/cs-pub-ro/operating-systems). There are a couple of rules that build on top of each other, we will go over this concept later on. +Read the Makefile in the [Operating Systems (Open Education Hub / `cs-pub-ro`) repository](https://github.com/cs-pub-ro/operating-systems). +There are a couple of rules that build on top of each other, we will go over this concept later on. ### KraftKit (from Unikraft) -Read the Makefile in the [KraftKit repository](https://github.com/unikraft/kraftkit/tree/staging). This file contains advanced syntax, so you should focus on variable definitions, the use of `.PHONY` and conditional blocks. +Read the Makefile in the [KraftKit repository](https://github.com/unikraft/kraftkit/tree/staging). +This file contains advanced syntax, so you should focus on variable definitions, the use of `.PHONY` and conditional blocks. ## Building a Makefile -In order to properly grasp how one writes a Makefile, we will discuss each step individually. First, let's write our first rule! +In order to properly grasp how one writes a Makefile, we will discuss each step individually. +First, let's write our first rule! ### Rules and recipes -The main part of a Makefile are its rules and the recipes associated to each rule. They abide by the following format: +The main part of a Makefile are its rules and the recipes associated to each rule. +They abide by the following format: ```console ... : ... @@ -304,14 +310,21 @@ The main part of a Makefile are its rules and the recipes associated to each rul ... ``` > [!NOTE] -> All recipe commands must be indented using tab! This is how Make knows a text line belongs to a specific recipe. +> All recipe commands must be indented using tab! +> This is how Make knows a text line belongs to a specific recipe. > Equally important is that a rule may contain more than one target or more than one prerequisite (even zero prerequisites!), depending on the build flow of the application. -We will start by writing our first build rule. In order to do that, go to the `gnumake` folder. You will find there an empty Makefile waiting for you. +We will start by writing our first build rule. +In order to do that, go to the `gnumake` folder. +You will find there an empty Makefile waiting for you. -You will also find some source files containing code written in C. Those will be our *prerequisites* for the length of this demo, given that we are building our application from them. We will build `example.c`. +You will also find some source files containing code written in C. +Those will be our *prerequisites* for the length of this demo, given that we are building our application from them. +We will build `example.c`. -Based on what we know, in order to obtain our binary, we must begin with `example`, as this is our target, our end goal. To build it, we need `example.c` and a C compiler (i.e. `gcc`) to run `gcc -o example example.c`. Now we can build our rule: +Based on what we know, in order to obtain our binary, we must begin with `example`, as this is our target, our end goal. +To build it, we need `example.c` and a C compiler (i.e. `gcc`) to run `gcc -o example example.c`. +Now we can build our rule: ```make example: example.c @@ -324,17 +337,26 @@ In order to run any rule from a Makefile, the usual command is: make ``` -If a rule has more than one target, you can choose any target to run the same recipe. Run `make example` and execute the freshly-made application. Congratulations, you wrote your first Make rule! +If a rule has more than one target, you can choose any target to run the same recipe. +Run `make example` and execute the freshly-made application. + +Congratulations, you wrote your first Make rule! ### Cleanup using Make -There are moments when one may want to delete the products of a build process, which is widely known as *cleaning* our repository. This is usually done to eliminate parasite effects of old binaries or to clear up the structure of the working directory. +There are moments when one may want to delete the products of a build process, which is widely known as *cleaning* our repository. +This is usually done to eliminate parasite effects of old binaries or to clear up the structure of the working directory. -Make proposes a standard rule named `clean`, which is responsible for eliminating all byproducts of our source files. There is a catch however; what could be the target of such rule? The answer is: none. It does not have any prerequisites either. +Make proposes a standard rule named `clean`, which is responsible for eliminating all byproducts of our source files. +There is a catch however; what could be the target of such rule? +The answer is: none. +It does not have any prerequisites either. #### .PHONY -Some targets are not representing any files in our working directory, they are just names, tags for our recipes. Rules that have such targets are categorised as *phony* because they can be misleading for Make. For example, let's say we have this rule: +Some targets are not representing any files in our working directory, they are just names, tags for our recipes. +Rules that have such targets are categorised as *phony* because they can be misleading for Make. +For example, let's say we have this rule: ```make clean: @@ -343,9 +365,15 @@ clean: Write it in the Makefile and see if it does anything. -Why does Make stop execution of this rule? Well, there is a file in our working directory named `clean` and our rule does not have any prerequisites. That means, there isn't anything that can change the file named `clean`, so Make will waste time by trying to build our target again and stops. This is the default behaviour of the toolchain. +Why does Make stop execution of this rule? +Well, there is a file in our working directory named `clean` and our rule does not have any prerequisites. +That means, there isn't anything that can change the file named `clean`, so Make will waste time by trying to build our target again and stops. +This is the default behaviour of the toolchain. -How do we fix it? We tell Make that the target of this rule is merely a name, not an actual file. That way, Make will ignore the file and run the rule every time we request it. This is how it should look: +How do we fix it? +We tell Make that the target of this rule is merely a name, not an actual file. +That way, Make will ignore the file and run the rule every time we request it. +This is how it should look: ```make .PHONY: clean @@ -354,7 +382,8 @@ clean: rm example ``` -There can be multiple rules labeled as phony in a single statement; they are just mentioned and separed with a single space. +There can be multiple rules labeled as phony in a single statement; +they are just mentioned and separed with a single space. This should be the final result: @@ -370,6 +399,66 @@ clean: **Well done, you just built your first Makefile!** +## Submit your first Makefile + +Submit the [`Makefile`](gnumake/Makefile) from the `gnumake` folder of this workshop. +Make sure you have implemented all the rules described above. + +Follow the instructions above to create the pull request. +Make sure you have good commit messages and a good pull request description. + +Target the pull request **to** your assigned branch. + +Ask the instructors to review your pull request. +Make updates as required. +Have your pull request approved and merged on top of your assigned branch. + +Check the GitHub web view of the [upstream repository](https://github.com/rosedu/workshop-markdown) for your assigned branch. +Click on the button with `main` (the branch button) and select your branch. + +Check the rules of the [`Makefile`](gnumake/Makefile) and see if the recipes are working correctly. + +### Clean Up After Pull Request + +After the pull request is merged, clean up your work environment. +That is: + +1. Go the pull request GitHub view and delete the remote branch. + +1. Remove the reference to the remove branch in your clone: + + ```console + git remote prune origin + ``` + +1. Checkout the `main` branch: + + ```console + git checkout main + ``` + +1. Remove the local branch that you used for creating the pull request. + It has the same name as the one you remote branch you removed above: + + ```console + git branch -D + ``` + +1. Fetch the updates for your assigned branch. + Your assigned branch is now updated after the pull request was merged: + + ```console + git fetch upstream + git checkout + git rebase upstream/ + ``` + +1. Check the branch: + + ```console + git log + ``` + ## Bits and pieces of Makefiles Makefiles are written using the following structures: @@ -382,15 +471,27 @@ Makefiles are written using the following structures: - **Directives:** special instructions to be carried out by Make as it reads the Makefile, for example reading another Makefile, or ignoring specific parts of a Makefile based on the value of evaluated variables; -- **Comments:** starting with `#`, can be continued on the next line if we add at the end of the line `\`; backslash can also be used to break up long commands in order to improve readability; +- **Comments:** starting with `#`, can be continued on the next line if we add at the end of the line `\`; +backslash can also be used to break up long commands in order to improve readability; -The first rule written in the Makefile is the default rule that runs when we use `make`. When running a rule, if there is a prerequisite that is the target of another rule it will run that before. Think of it as a depth-first search on graphs, where the nodes are the rules and the vertices are the dependencies on one another! +The first rule written in the Makefile is the default rule that runs when we use `make`. +When running a rule, if there is a prerequisite that is +the target of another rule it will run that before. +Think of it as a depth-first search on graphs, +where the nodes are the rules and the vertices are the +dependencies on one another! -Using these components, engineers can build massive projects without wrapping their head with compiler commands and dependencies. Without further ado, let's get into using these! +Using these components, engineers can build massive projects without wrapping their head with compiler commands and dependencies. +Without further ado, let's get into using these! ### Variables -Variables are useful in multiple scenarios. Let's say that someone wants to change the compiler that is being used inside the Makefile. Changing hundreds of lines of recipes is gruesome, so instead we use a variable that contains the compiler's name to allow easier update and refinement of various instructions. Here is an example: +Variables are useful in multiple scenarios. +Let's say that someone wants to change the compiler that is being used inside the Makefile. +Changing hundreds of lines of recipes is gruesome, +so instead we use a variable that contains the compiler's name +to allow easier update and refinement of various instructions. +Here is an example: ```make example_variable = src1.c src2.c @@ -409,23 +510,26 @@ Variables can also be used to reference targets and prerequisites in an easier m ### Wildcards and automatic variables -Wildcards are characters that expand automatically to predefined values. They have the same meaning as the ones in Bash. The usual suspects are `*`, `~` and `?`. We will describe them below: +Wildcards are characters that expand automatically to predefined values. +They have the same meaning as the ones in Bash. +The usual suspects are `*`, `~` and `?`. +We will describe them below: - `*` is a placeholder for zero or more characters; - - `~` is a placeholder for the home directory of the current user; - - `?` is a placeholder for exactly one character. -Automatic variables are pairs of characters that start with `$`. Their values vary depending on the executed rule and other factors: +Automatic variables are pairs of characters that start with `$`. +Their values vary depending on the executed rule and other factors: - `$^` is replaced with all prerequisites of a rule automatically at recipe execution; - - - `$@` is replaced with the targets of the rule automatically at execution, even if the rule is declared as .PHONY. It basically replaces character by character this sequence with the targets sequence. - + - `$@` is replaced with the targets of the rule automatically at execution, even if the rule is declared as .PHONY. + It basically replaces character by character this sequence with the targets sequence. - `$<` is replaced with the first prerequisite automatically. -Let's see an example to clear things up! In the folder with the Makefile given below, there are two files, namely `example1.c` and `example2.c`. +Let's see an example to clear things up! +In the folder with the Makefile given below, +there are two files, namely `example1.c` and `example2.c`. ```make example: *.c @@ -446,39 +550,148 @@ distclean: clean # also, the dependency on clean means that distclean runs it before running its own recipe! ``` +### Conditionals + +Conditional blocks are structured very similarly with the if-else statement in C, +where the evaluation statement is always checking something with regard to the contents of a variable. +Here is an example of a simple if statement: + +```make +# CC could also be set to msvc, clang, mingw et cetera... +CC = gcc + +foo: foo.c +ifeq ($(CC), gcc) + $(CC) -o foo $^ +else + echo "Warning! The program logic was written with the particularities of gcc in mind. Stopping recipe execution..." +endif +``` + +We can use the following operators: + - `ifeq`: checks if the contents of the variable are *equal* to the value given inside the statement; + - `ifneq`: checks if the contents of the variable are *different* from the value given inside the statement; + - `ifdef`: checks whether a variable with a specific name is defined; + - `ifndef`: checks wheter a variable with a specific name is *NOT* defined; + +### Functions + +Functions are useful to process text inside a Makefile. +The results of these functions can be used to process files or alter execution flow. +One of the more important functions is `shell`, because it communicates with the outer environment. +In order to use it and save its results to a variable, we can use the following structure: + +```make +pwd_files := $(shell ls) +``` + +It is also important to mention that the `:=` operator expands variables directly +referenced before setting its value. + +There are multiple functions that can be used. +You can check them out in the [GNU Make documentation](https://www.gnu.org/software/make/manual/make.html#Text-Functions). + +You may need to create a new string of text containing forbidden characters, like braces, commas, spaces and so on. +To circumvent the fact that Make does not support escaping characters with `\`, you can use the following logic: + +```make +comma:= , +empty:= +space:= $(empty) $(empty) +foo:= a b c +bar:= $(subst $(space),$(comma),$(foo)) +# bar is now ‘a,b,c’. +``` + +After that, you can call your new function using `$( ,,...)`. +The function result will be expanded in-place, exactly like a variable! + ## Good Makefile Practices -There are several recommendations regarding the naming of Makefile rules and how they should behave. Not only that, but Makefiles should usually bear a name that will be automatically recognised by Make. +There are several recommendations regarding the naming of Makefile rules and how they should behave. +Not only that, but Makefiles should usually bear a name that will be automatically recognised by Make. ### Makefile naming convention -Based on how Make is implemented in the GNU library, in order to run a `make` command without explicitly specifying the Makefile's location one should name it: +Based on how Make is implemented in the GNU library, +in order to run a `make` command without explicitly specifying +the Makefile's location one should name it: - "GNUmakefile" - "makefile" - "Makefile" -The first is rarely met and developers usually opt for the last 2 options. This is the order in which Make looks for Makefiles in the current working directory. When it finds a Makefile with one of these names, it stops searching and starts executing recipes. +The first is rarely met and developers usually opt for the last 2 options. +This is the order in which Make looks for Makefiles in the current working directory. +When it finds a Makefile with one of these names, +it stops searching and starts executing recipes. > [!NOTE] > Makefiles do not bear any file extension! > [!NOTE] -> We do not recommend naming Makefiles in any other way except those recognised automatically by Make. It can represent a serious obstacle for developers that do not know the actual name of the project's Makefile. +> We do not recommend naming Makefiles in any other way except those recognised automatically by Make. +> It can represent a serious obstacle for developers that do not know the actual name of the project's Makefile. +> All being said, if you need to use a specific file that is not automatically picked, one can use the `-f ` option. ### Multi-thread execution -Make supports executing multiple recipes simulatenously. This is achieved using the `-j ` option and it is usually the norm to insert the number of logical processors / threads your machine has. On Linux / macOS, this usually is `-j $(nproc)`. +Make supports executing multiple recipes simulatenously. +This is achieved using the `-j ` option and it is usually +the norm to insert the number of logical processors / threads your machine has. +On Linux / macOS, this usually is `-j $(nproc)`. -If no thread limit is given, Make will create as many threads as it needs, usually leading to getting your Operating System stuck with too many threads. +If no thread limit is given, Make will create as many threads as it needs, +usually leading to getting your Operating System stuck with too many threads. ### Multi-file structure -Make supports fragmenting rules into multiple Makefiles, creating something similar to an arborescent structure containing primary and secondary Makefiles. This is achieved by using: +Make supports fragmenting rules into multiple Makefiles, +creating something similar to an arborescent structure containing +primary and secondary Makefiles. This is achieved by using: `include ...` -Make will read these Makefiles first before starting to execute any rule requested by the user. +Make will read these Makefiles first before starting to execute +any rule requested by the user. + +## Your Turn + +Create a Makefile in the `gnumake/custom` folder that follows the rules listed below. +Use as many Make features as possible. +If you believe you need additional source files, you may also add them to the pull request. + +Your Makefile must offer the following features: + +- an all rule (at the beginning of the file) +- a clean rule +- comments regarding the behaviour and result of each rule +- declaration of all phony rules +- a rule for `greeting.c` +- a rule for compiling `part1.c` and `part2.c` +- two variables (for filenames, compilers or flags) +- a conditional block +- a function +- one of the rules should be in another Makefile and included / referenced in the main Makefile! + +Be careful! `part1.c`, `part2.c` and `header.h` should be used in the same command! + +Submit the Makefile as part of a pull request. + +Follow the instructions above to create the pull request. +Make sure you have good commit messages and a good pull request description. + +Target the pull request **to** your assigned branch. + +Ask the instructors to review your pull request. +Make updates as required. +Have your pull request approved and merged on top of your assigned branch. + +Check the GitHub web view of the [upstream repository](https://github.com/rosedu/workshop-markdown) for your assigned branch. + +### Clean Up After Pull Request + +After the pull request is merged, go to the same steps as above to clean your pull request. ## GitHub Profile Page @@ -530,13 +743,6 @@ Follow the steps: Push commits to the GitHub remote repository. Now check your resulting profile page on your GitHub page. -## Use of GNU make in public projects - -We will study different implementations of Makefiles across multiple projects to get a feel of its purpose and strengths. - -### This Repository - - ## Create a Website with GitHub and Markdown We can use the contents of a GitHub repository, written with Markdown, to create a website. diff --git a/gnumake/clean b/gnumake/clean index ab8d90b..574b188 100644 --- a/gnumake/clean +++ b/gnumake/clean @@ -1 +1 @@ -Do not modify the contents of this file. Thanks! \ No newline at end of file +Do not modify the contents of this file. It's used to the demonstrate the `clean` rule. diff --git a/gnumake/custom/greeting.c b/gnumake/custom/greeting.c new file mode 100644 index 0000000..b5befef --- /dev/null +++ b/gnumake/custom/greeting.c @@ -0,0 +1,16 @@ +#include + +int main(void) { + printf("What's your name?\n"); + char *name = calloc(101, sizeof(char)); + if (name == NULL) { + printf("calloc() failed; exiting...\n"); + return -1; + } + + fgets(name, 101, stdin); + printf("Greetings, %s!\n", name); + + free(name); + return 0; +} diff --git a/gnumake/custom/header.h b/gnumake/custom/header.h new file mode 100644 index 0000000..f692ccb --- /dev/null +++ b/gnumake/custom/header.h @@ -0,0 +1,5 @@ +#include +#include +#include + +char* reply(char *initial); diff --git a/gnumake/custom/part1.c b/gnumake/custom/part1.c new file mode 100644 index 0000000..a53cc68 --- /dev/null +++ b/gnumake/custom/part1.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include "header.h" + +int main(void) { + char *mesg = calloc(101, sizeof(char)); + if (mesg == NULL) { + goto error; + } + + printf("Send a \"ping\" to get a pong;\n"); + printf("Or send something else and you get wrong!\n"); + fgets(mesg, 101, stdin); + + char *answer = reply(mesg); + if (answer == NULL) { + goto error; + } + + printf("%s\n", answer); + + free(mesg); + free(answer); + return 0; + +error: + printf("calloc() failed; exiting...\n"); + return -1; +} diff --git a/gnumake/custom/part2.c b/gnumake/custom/part2.c new file mode 100644 index 0000000..2840945 --- /dev/null +++ b/gnumake/custom/part2.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include "header.h" + +char* reply(char *initial) { + char *result = NULL; + + if (strcmp(initial, "ping") == 0) { + result = calloc(5, sizeof(char)); + if (result == NULL) { + goto error; + } + + strcpy(result, "pong"); + } else { + result = calloc(11, sizeof(char)); + if (result == NULL) { + goto error; + } + + strcpy(result, "Try again!"); + } + + return result; + +error: + printf("calloc() failed; exiting..."); + return NULL; +} diff --git a/gnumake/example.c b/gnumake/example.c index 561974b..6dd92b0 100644 --- a/gnumake/example.c +++ b/gnumake/example.c @@ -3,4 +3,4 @@ int main(void) { printf("Good job! You just built a binary with Make!"); return 0; -} \ No newline at end of file +} diff --git a/gnumake/makefile_placeholder1 b/pitch/example1 similarity index 100% rename from gnumake/makefile_placeholder1 rename to pitch/example1 diff --git a/gnumake/makefile_placeholder2 b/pitch/example2 similarity index 62% rename from gnumake/makefile_placeholder2 rename to pitch/example2 index f859e41..2dd7ee4 100644 --- a/gnumake/makefile_placeholder2 +++ b/pitch/example2 @@ -1,18 +1,18 @@ objects = main.o util.o processing.o exec: $(objects) - gcc -o exec $(objects) + gcc -o exec $(objects) main.o: main.c header.h - gcc -c main.c + gcc -c $< util.o: util.c header.h - gcc -c util.o + gcc -c $< processing.o: processing.c header.h - gcc -c processing.c + gcc -c $^ .PHONY: clean clean: - -rm exec $(objects) + -rm exec $(objects) diff --git a/pitch/make-recap.txt b/pitch/make-recap.txt deleted file mode 100644 index bd7d353..0000000 --- a/pitch/make-recap.txt +++ /dev/null @@ -1,157 +0,0 @@ -**ACOPERIT** -Un makefile e format din reguli, reguli care sunt sub urmatoarea forma: -target ... : prerequisites ... - recipe - ... - ... - ... - -**ACOPERIT** -target e de obicei numele fisierului generat de un program (ex. executabile sau fisiere obiect), dar poate fi si numele actiunii pe care vrem sa o intreprindem (clean de exemplu). prerequisites sunt fisierule utilizate ca input pentru a crea target. recipe sunt actiunile pe care utilitarul make le intreprinde in cadrul unei reguli anume (atentie! este obligatoriu ca orice instructiune din recipe sa fie precedata de un tab). anumite reguli nu au nevoie de prerequisites (vezi clean, care face doar o actiune de delete). asadar, make tot ce face este sa urmeze niste reguli pe care noi le impunem printr-un asa-numit makefile, care contine pe larg un set de reguli. - -**SKIP** -studiati regulile din `makefile1` si cum se compun una pe alta. pentru a crea executabilul, rulam `make`. pentru a curata folderul de lucru de fisiere obiect si executabile, folosim `make clean`. - -**ACOPERIT** -pentru a rula o regula specifica din makefile, rulam comanda `make `. mai jos este o lista de reguli standard care pot aparea in cadrul unui makefile. Atentie! aceste reguli nu sunt definite automat de catre GNU make, ele reprezinta doar niste conventii care sunt lasate in seama celui care scrie fisierului respectiv. - -**ACOPERIT** -makefile-ul stie sa aplice selectiv anumite reguli, adica daca doua fisiere obiect din sapte ale unui executabil nu mai sunt de actualitate, le va recompila doar pe acelea, nu pe toate sapte, iar mai apoi executabilul daca asta i-a fost cerut. o sa vedem in exemplu ca daca am compilat programul si incercam imediat dupa sa compilam din nou, make ne va spune ca nu e nimic de facut. - -**ACOPERIT** -exista o regula standard pentru makefile-uri care se numeste clean, nu reprezinta un fisier, ci actiunea care este desfasurata. clean nu provine din nicio alta regula si nu ruleaza decat daca ii spunem utilitarului make in mod explicit asta. tintele care nu sunt backed de fisiere, cum este clean, se numesc tinte phony, vom vorbi mai tarziu in detaliu despre ele. - -**ACOPERIT** -primul target din makefile este target-ul default, care ruleaza atunci cand rulam doar make, fara alte argumente. in exemplu, make va incerca sa construiasca exec, insa fisierele obiect inca nu au fost verificate si sunt prerequisites, deci se duce la ele si dupa se ocupa de exec. comportamentul acesta se oglindeste pentru fiecare regula (seamana foarte tare cu o parcurgere in adancime, DFS). regulile care nu sunt prerequisites sunt ignorate / sarite. - -**ACOPERIT** -gnu make permite pe langa reguli si utilizarea variabilelor in cadrul unui makefile. in exemplul oferit mai devreme, am folosit objects pentru a evita greselile de scriere atunci cand vine vorba de mai multe obiecte. am creat variabila objects, care tine loc pentru toate fisierele de tip obiect, iar atunci cand vrem sa o folosim, utilizam structura $(). astfel putem asigura o mai mare usurinta in editarea fisierelor make si un numar mai mic de greseli atunci cand este creat sau editat. - -**SKIP** -ar trebui investigat daca trebuie mentionat si comportamentul gnu make cand vine vorba de reguli implicite (vezi in documentatia oferita in workshop-markdow:/README.md daca are sens sa incarcam studentii cu asa ceva). - -**ACOPERIT** -punand clean in setul de reguli phony, devine explicit faptul ca regula clean nu are tinta niciun fisier, mai degraba este doar o denumire, iar daca adaugam o liniuta fix inainte de rm, make va continua sa execute comenzile din regula, chiar daca rm va da eroare (nu se aplica doar pentru rm, este doar dat ca exemplu). pentru a face utilitarul make sa ruleze comenzile din regula clean, va trebui sa rulam in terminal `make clean`. - -**ACOPERIT** -makefile-urile sunt formate din reguli explicite si implicite, definitii de variabile, directive si comentarii, le vom explica in detaliu mai jos: - -**ACOPERIT** -- o regula explicita mentioneaza cand si cum reconstruieste unul sau mai multe fisiere cunoscute drept targets si are mai multe fisiere de care depinde, numite prerequisites si contine o "reteta" pentru a reconstrui tintele. - -**ACOPERIT** -- o regula implicita mentioneaza cand si cum reconstruieste o clasa de fisiere bazat pe numele lor. descrie cum o tinta depinde de un fisier cu un nume similar si ofera o reteta pentru a reconstrui tinta respectiva. ele nu fac scopul acestui workshop si exista multiple discutii daca ele ar trebui utilizate sau nu (prezinta un risc de ambiguitate cu privire la modul in care este construit un target). - -**ACOPERIT** -- o definitie de variabila este o linie care asociaza o eticheta unui string de caractere. - -**ACOPERIT** -- o directiva este o instructiune pe care make sa o faca in timp ce citeste makefile-ul, ele pot fi de obicei: citirea altui makefile, decizia de ignora bazat pe valorile unor variabile daca sa ignore sau sa foloseasca o parte a unui makefile (parti conditionale) sau definirea unei variabile dintr-un string multi-line - -**ACOPERIT** -- un comentariu, precedat tot timpul de '#', iar daca ne dorim ca acel comentariu sa continue pe linia urmatoare, la finalul liniei adaugam '\'. daca vrem sa folosim # in makefile, dar nu pentru marcarea unui comentariu, trebuie sa il "escapam" folosind structura "\#". daca make intalneste un # in mijlocul unei definitii de variabile sau unui apel de functie, nu il va trata ca marcaj al unui comentariu. comentariile pot fi plasate si la sfarsitul unei linii, fara ca # sa fie tratat ca facand parte din comanda sau din variabila. - -**ACOPERIT** -liniile foarte lungi pot fi sparte in mai multe linii folosind backslash ('\') pentru a creste gradul de lizibilitate al makefile-ului, care este convertit de gnu make automat intr-un spatiu la citirea fisierului, alipind inapoi liniile separate de backslash-uri. - -**ACOPERIT** -fisierele in care sunt puse regulile pentru make trebuie denumite intr-un mod specific. GNU make cauta mai intai in folderul de lucru fisierul "makefile" si apoi "Makefile". daca nu il gaseste, nu va face nimic. pot fi utilizate si alte denumiri pentru makefile, insa functionalitatea respectiva nu este recomandata in industrie si atunci nu face scopul acestui workshop. - -**ACOPERIT** -daca vrem sa construim o oarecare structura "arborescenta" de makefile-uri, putem folosi in partea incipienta a unui fisier comanda "include ", care va citi makefile-urile subordonate inainte de a continua citirea makefile-ului unde s-a intalnit cuvantul cheie include. - -**ACOPERIT** -mare grija la indentarea liniilor! liniile care incep cu un tab sunt considerate ca facand parte dintr-o reteta a unei reguli. - -**ACOPERIT** -exista mai multe caractere wildcard care pot usura compunerea unui makefile. acestea sunt '*', '?' si '[...]', le vom discuta mai in detaliu mai jos: - - - '*' tine loc pentru toate fisierele. de exemplu, daca spun ca o regula are drept prerequisites '*.c', asta inseamna ca toate fisierele al caror nume se termina in '.c' din directorul de lucru vor fi preluate drept necesare pentru acea regula. lista returnata de wildcard-ul * este sortata. - - - '~' tine loc pentru calea directorului home (are acelasi comportament ca '~' din bash). daca vrem sa prescurtam caile, putem spune de exemplu '~/bin' in loc de '/home/my_user/bin'. - -**ACOPERIT** -exista si asa-zise "automatic variables", cum ar fi '$^', '$@' si altele, le vom discuta in cele ce urmeaza: - - - '$^' este un inlocuitor pentru toate prerequisite-urile unei reguli, inlocuirea fiind facuta automat la executarea unei anumite reguli - - - '$@' este un inlocuitor pentru tinta regulei. atentie! daca regula are un nume abstract (sau phony numit in GNU make), vom construi o tinta care are numele regulei, mare grija la utilizarea acestei variabile automate. - - - '$<' este un inlocuitor pentru primul obiect din prerequisites. de exemplu, daca am: - ``` - foo.o: foo.c defs.h hack.h - gcc -c $< -o $@ - ``` - va fi dat spre compilare doar foo.c, nu si headerele. - -**ACOPERIT** -target-urile phony sunt cele care nu au numele unui fisier, este mai degraba doar un nume pentru un set de instructiuni care trebuie executat cand se cere asta in mod explicit. exista doua motive pentru utilizarea unei astfel de tinte, evitarea unui conflict cu un fisier cu acelasi nume si imbunatatirea performantei. daca tintele phony nu sunt marcate explicit ca fiind phony (discutam mai tarziu putin despre cum facem asta), daca la un moment dat in folderul de lucru va aparea un fisier cu acelasi nume, regula nu va fi executata niciodata deoarece fisierul va fi considerat de actualitate. pentru a evita asta, folosim un marcator special: - -``` -.PHONY: clean -clean - rm *.o temp -``` - -**ACOPERIT** -pentru a folosi o variabila, folosim structura '$()' in retete sau in lista de target-uri sau prerequisites. - -**ACOPERIT** -in proiectele mai mari, atunci cand ne este indicat sa folosim comanda ```make -j ```, acel argument de dupa `-j` reprezinta numarul de job-uri, mai exact numarul de retete, care vor fi executate simultan. cu toate acestea, trebuie sa ne asiguram ca makefile-ul este bine scris, astfel incat paralelizarea task-urilor sa nu duca la o eroare, cum ar fi un fisier cautat care inca nu a fost construit de o alta regula. - -**LIVE** -**LIVE** -**LIVE** -partile conditionale ale unui makefile sunt structurate ca un bloc if-else, unde conditia evaluata are mereu de-a face cu o variabila din makefile. - -``` -# in acest exemplu, presupunem ca variabila CC este setata de noi ca gcc, poate fi de asemenea clang, g++ et cetera -CC = gcc - -foo: foo.c -ifeq ($(CC), gcc) - $(CC) -o foo $^ -else - echo "Warning! The program logic was written with the particularities of gcc in mind. Stopping make foo rule..." -endif -``` - -pe langa ifeq, exista si ifneq, care in loc de `==`, face `!=`, mai exista si ifdef , care verifica daca o var este definita, dar si ifndef, care verifica daca variabila NU este definita, in acest caz intorcand true. -**LIVE** -**LIVE** -**LIVE** - -**SKIP** -GNU make intoarce 0 daca comanda make s-a incheiat cu succes, 2 daca in timpul citirii sau executarii apare o eroare, iar 1 este rezervat pentru flag-ul '-q' si make considera ca una dintre tinte nu mai este de actualitate. - -**LIVE** -**LIVE** -**LIVE** -- 'all' construieste toate target-urile top-level cunoscute makefile-ului; -- 'clean' sterge toate fisierele care sunt create in mod normal de make; -- 'mostlyclean' este asemanator cu 'clean', insa nu sterge fisierele a caror compilare ia foarte mult timp; -- 'distclean', 'realclean', 'clobber' sunt tinte care pot sau nu sa fie definite si de obicei au un comportament mai agresiv decat 'clean' (de exemplu, sterg si fisierele utilizate pentru configurarea procesului de constructie). -- 'install' copiaza fisierele executabile si fisierele auxiliare in folderele specifice pentru programul respectiv; -- 'print' listeaza fisierele sursa care s-au schimbat de la ultima rulare a lui make; -- 'test' ruleaza niste teste automate asupra executabilului construit de catre makefile; -**LIVE** -**LIVE** -**LIVE** - -**LIVE** -**LIVE** -**LIVE** -pentru a evita confuziile cu privire la shell-ul utilizat de catre GNU make in cazul acestui makefile. e bine ca optiunile pentru compilare, cat si numele efectiv al compilatorului utilizat sa fie utilizat ca o variabila, pentru a permite actualizari in intregul makefile mai rapide si fara erori (de exemplu uit sa substitui un gcc pentru un g++ sau uit un flag -Wall la una din comenzile de compilare). folderele specifice de build sau install este de asemenea recomandat sa le punem in interiorul unei variabile si mai apoi sa o utilizam pe aceasta in reguli. -**LIVE** -**LIVE** -**LIVE** - -**SKIP** -de asemenea, este o conventie generala ca fiecare makefile ar trebui sa inceapa cu linia - -```console -SHELL = - -# un exemplu ar fi -# SHELL = /bin/bash -``` From f53d7ba4a57c6e410acc839ea4b9eed6075167da Mon Sep 17 00:00:00 2001 From: "Busuioc P. Claudiu" Date: Sat, 27 Jun 2026 12:44:20 +0300 Subject: [PATCH 5/5] Add claudiu.md with personal developer setup and tools --- claudiu.md | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 claudiu.md diff --git a/claudiu.md b/claudiu.md new file mode 100644 index 0000000..76a2b0c --- /dev/null +++ b/claudiu.md @@ -0,0 +1,73 @@ +# My Developer Setup :computer: + +A quick overview of the tools and workflows I use day-to-day as a developer. + +## Why This Matters + +Having a consistent, well-configured setup saves hours every week. +The tools below are what I reach for on every project, regardless of language or platform. + +--- + +## Essential Tools + +| Tool | Purpose | Install | +| --- | --- | --- | +| [git](https://git-scm.com/) | Version control | `apt install git` | +| [tmux](https://github.com/tmux/tmux) | Terminal multiplexer | `apt install tmux` | +| [ripgrep](https://github.com/BurntSushi/ripgrep) | Fast code search | `apt install ripgrep` | +| [fzf](https://github.com/junegunn/fzf) | Fuzzy finder | `apt install fzf` | +| [htop](https://htop.dev/) | Process monitor | `apt install htop` | + +--- + +## My Most-Used Git Aliases :rocket: + +```bash +git config --global alias.st status +git config --global alias.co checkout +git config --global alias.lg "log --oneline --graph --decorate" +``` + +After that, `git lg` gives a compact, readable history: + +``` +* 4a3f1c2 (HEAD -> main) Fix memory leak in parser +* 7b91de0 Add unit tests for tokenizer +* c3e2a11 Initial commit +``` + +--- + +## Things I Always Do on a New Machine + +1. Install `git`, `tmux`, and `ripgrep` +2. Copy over my dotfiles from [my dotfiles repo](https://github.com/busuyoc) +3. Set up SSH keys for GitHub +4. Configure `git` with name and email +5. Install language toolchains (Go, Rust, Python) + +--- + +## Things I Never Do + +- Push directly to `main` +- Use `rm -rf` without double-checking the path :grimacing: +- Commit without a meaningful message +- Open a PR without testing it locally first + +--- + +## Favourite Quote + +> "Programs must be written for people to read, and only incidentally for machines to execute." +> — Harold Abelson + +--- + +## A Note on Editors + +I use [Neovim](https://neovim.io/) as my primary editor. +The learning curve is real, but the payoff is worth it. + +![Neovim logo](https://upload.wikimedia.org/wikipedia/commons/thumb/4/4f/Neovim-logo.svg/320px-Neovim-logo.svg.png)