summaryrefslogtreecommitdiff
path: root/the_gentlest_introduction_to_building_with_makefiles.html
diff options
context:
space:
mode:
authorAki <please@ignore.pl>2021-07-25 19:17:40 +0200
committerAki <please@ignore.pl>2021-07-25 19:17:40 +0200
commitad76e9b885c9b9692074cf5b8b880cb79f8a48e0 (patch)
tree603ebe1a1dbcd9251c84c1c954b7b4dc5b986cc3 /the_gentlest_introduction_to_building_with_makefiles.html
downloadignore.pl-ad76e9b885c9b9692074cf5b8b880cb79f8a48e0.zip
ignore.pl-ad76e9b885c9b9692074cf5b8b880cb79f8a48e0.tar.gz
ignore.pl-ad76e9b885c9b9692074cf5b8b880cb79f8a48e0.tar.bz2
Initialized website as git repository
Diffstat (limited to 'the_gentlest_introduction_to_building_with_makefiles.html')
-rw-r--r--the_gentlest_introduction_to_building_with_makefiles.html239
1 files changed, 239 insertions, 0 deletions
diff --git a/the_gentlest_introduction_to_building_with_makefiles.html b/the_gentlest_introduction_to_building_with_makefiles.html
new file mode 100644
index 0000000..f4b2cac
--- /dev/null
+++ b/the_gentlest_introduction_to_building_with_makefiles.html
@@ -0,0 +1,239 @@
+<!doctype html>
+<html lang="en">
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<meta name="author" content="aki">
+<meta name="tags" content="tutorial, gnu make, makefile, compiling, building, C, C++">
+<link rel="icon" type="image/png" href="cylo.png">
+<link rel="stylesheet" type="text/css" href="style.css">
+
+<title>The Gentlest Introduction to Building With Makefiles</title>
+
+<nav><p><a href="https://ignore.pl">ignore.pl</a></p></nav>
+
+<article>
+<h1>The Gentlest Introduction to Building With Makefiles</h1>
+<p class="subtitle">Published on 2020-05-14 18:44:00+02:00
+<p>If you are here, you are most likely in need to build C or C++ program. Chances are you were not even looking for
+tutorial about <a href="https://www.gnu.org/software/make/">GNU make</a> or Makefiles in general. Chances are that you
+need to get you assignment done by yesterday, or you want to refresh your memory from back in the day you used C for
+the last time. No matter your background, I'll try to walk you through the process of building your C or C++ program
+using make command.
+<p>Sadly, the tutorial will explain the stuff, so that you have an overview after reading it. It won't go too deeply.
+Anyway, if you are only interested in an example to copy, there is one <a href="#example">below</a>.
+<p>If you have no idea, why using build system is nice. There are plenty of reasons. I will give you two. They automate
+the building process, so that you don't have to type same things all over again, and you don't need to remember your
+configuration at all times. If used consistently, they try to build only parts of your project that were changed. It
+can affect the building time greatly.
+<h2 id="single_file">Building a single file project</h2>
+<p>You have just finished writing your first implementation of <i>Hello, world!</i>, you have terminal open or some kind
+of prompt up and running, and now you would like to build the program and execute it. You've probably seen it somewhere
+but let me remind how to do it by hand using gcc:</p>
+<pre>
+$ ls
+hello.c
+$ gcc hello.c -o hello
+$ ./hello
+Hello, world!
+</pre>
+<p>Nice! But writing <code>gcc hello.c -o hello</code> all the time when you want to rebuild the program sounds
+bothersome even if you consider using command history. If you were to extend the program with libraries or additional
+files it sounds even more tiresome.
+<p>Let's put <i>make</i> to use! All you need to do is replace <code>gcc hello.c -o</code> part with <code>make</code>,
+so that you have:</p>
+<pre>
+$ ls
+hello.c
+<mark>$ make hello</mark>
+cc hello.c -o hello
+$ ls
+hello hello.c
+$ ./hello
+Hello, world!
+</pre>
+<p>You probably noticed that <i>make</i> shamelessly prints out the command it used to build your program. How did it
+know? <i>Make</i> is a master of default variables, implicit rules, deduction, and hiding it's secrets from curious eyes
+of those who seek knowledge. Actually, no, <a href="https://www.gnu.org/software/make/manual/make.html">the
+documentation</a> is available for anyone in various forms. We'll not discuss it in detail, that wouldn't be gentle, so
+assume for now that <i>make</i> will know how to compile and link your C or C++ program. Rules that describe how
+<i>make</i> does that are called <strong>implicit rules</strong>. We'll use and affect them extensively.</p>
+<img src="the_gentlest_introduction_to_building_with_makefiles-1.png" alt="colorful toy blocks">
+<h2 id="libraries">Using libraries with implicit rules</h2>
+<p><i>Make</i> and makefiles heavily rely on your environment. If you don't know what it is, for simplicity let's say
+that the environment is a set of variables associated with your current shell/terminal/prompt session. <i>Make</i> is
+so greedy that it takes all of your environmental variables and copies them as own. The implicit rules may use those
+copied variables, and they do exactly that. Those variables are usually called <strong>implicit variables</strong>.
+<p>We can take advantage of it. Let's say we are building a game with <a href="https://www.libsdl.org/">SDL2</a>. SDL2
+requires an additional include directory, a flag, and a library in order to build with it. Firstly, we'll set selected
+variables in our environment (via <code>export VARIABLE=value</code>), and then we'll build the program:</p>
+<pre>
+$ ls
+hello-sdl.c
+$ export <mark>CFLAGS</mark>='-D_REENTRANT -I/usr/include/SDL2'
+$ export <mark>LDLIBS</mark>='-lSDL2'
+$ make hello-sdl
+gcc <mark>-D_REENTRANT -I/usr/include/SDL2</mark> hello-sdl.c <mark>-lSDL2</mark> -o hello-sdl
+$ ./hello-sdl
+</pre>
+<p>The values I've used are specific to SDL2, don't mind them. What interests us in this example are the names of the
+variables: <code>CFLAGS</code> and <code>LDLIBS</code>. First one, is a set of parameters that describe how our thing
+should be handled during compilation. <code><strong>C</strong>FLAGS</code> is for C language, for C++ programs there is
+an equivalent variable called <code>CXXFLAGS</code>. Second variable, <code>LDLIBS</code> may contain a list of
+libraries that the linker should link to our program. In the example above there is no clear difference between
+compilation and linking, and thus both variables are copied by <i>make</i> to a single command. Luckily, it makes no
+difference to us, especially when the outcome is satisfying.
+<h2 id="first_makefile">First Makefile</h2>
+<p>Obviously, we would need to repeat those <code>exports</code> each time we start new session. This would bring us
+back to the level of repeating whole <code>gcc</code> call on and on. We could put them in some kind of file, couldn't
+we? Luckily, <i>make</i> predicted that and it may read the contents of so-called makefiles. Just put a file called
+<i>Makefile</i> in the project directory and insert the variables there:</p>
+<pre>
+CFLAGS=<mark>-D_REENTRANT -I/usr/include/SDL2</mark>
+LDLIBS=<mark>-lSDL2</mark>
+</pre>
+<p>Now, if you run <i>make</i>, it will read the <i>Makefile</i> and use the variables that are defined in it:</p>
+<pre>
+$ ls
+hello-sdl.c Makefile
+$ make hello-sdl
+gcc <mark>-D_REENTRANT -I/usr/include/SDL2</mark> hello-sdl.c <mark>-lSDL2</mark> -o hello-sdl
+</pre>
+<p>Less writing is always cool. How about getting rid of the <code>hello-sdl</code> from every call to <i>make</i>?
+That's also possible. <code>hello-sdl</code> is <strong>a target</strong>. Targets are associated with
+<strong>rules</strong>, any number of them, be it implicit or user-defined. If user doesn't provide a target name as an
+argument in command line, <i>make</i> uses first target that is specified in the makefile. We can create targets by
+writing rules. The syntax to do so is rather straight forward and contains: names of targets, prerequisites needed, and
+a recipe which may be a single-line command or may span the eternity. Knowing all of that, we can write a very peculiar
+rule. Everything will be handled by an implicit rule, and we'll only give a hint to <i>make</i> which thing we want it
+to build by default:</p>
+<pre>
+CFLAGS=-D_REENTRANT -I/usr/include/SDL2
+LDLIBS=-lSDL2
+<mark>hello-sdl:</mark>
+</pre>
+<p>Surprisingly, that's enough. <code>hello-sdl</code> is the name of our target. It's a first target that appears in
+this makefile, therefore it will be the default one. <code>:</code> (colon) is a required separator between the target
+and prerequisites list. We didn't add any dependencies, as the sole dependency on <i>hello-sdl.c</i> file is
+acknowledged thanks to the implicit rule. And because we didn't write any recipe, the recipe from the implicit rule is
+used. When we use it, it looks like this:</p>
+<pre>
+$ ls
+hello-sdl.c Makefile
+$ make
+gcc -D_REENTRANT -I/usr/include/SDL2 hello-sdl.c -lSDL2 -o hello-sdl
+</pre>
+<h2 id="more_files">Adding more files</h2>
+<img src="the_gentlest_introduction_to_building_with_makefiles-2.png" alt="more files">
+<p>In a long run, it would be more useful to have more than one file in a project. <i>Make</i> also predicted that and
+allows users to build programs from multiple sources. Amazing, isn't it? Now is the moment we finally split up
+compilation from linking in a visible manner. Let's say we have a project with three files <i>hello.c</i>,
+<i>sum.h</i> and <i>sum.c</i>, their content is respectively:</p>
+<pre>
+#include &lt;stdio.h&gt;
+<mark>#include "sum.h"</mark>
+int main(int argc, char * argv[]) {
+ printf("2 + 3 = %d", <mark>sum(2, 3)</mark>);
+}
+</pre>
+<pre>
+#pragma once
+int sum(int a, int b);
+</pre>
+<pre>
+int sum(int a, int b) {
+ return a + b;
+}
+</pre>
+<p>The structure of this project is easily seen. <i>Hello.c</i> depend directly on <i>sum.h</i> due to the include, and
+it requires the <i>sum</i> function to be compiled and available when linking the final executable. First dependency is
+so stupidly easy to write, that you might be actually surprised about it: you just need to add <i>sum.h</i> file to
+prerequisites in the rule description. The other one, is slightly more interesting. We could just add <i>sum.c</i> to
+prerequisites, but we will die a horrible death after a while if we do that. Technically, it's not even the thing we're
+trying to accomplish, so <strong>don't do that</strong>.
+<p>Instead, let's use <i>.o</i> files that are products of compilation of a single source file. We can link them
+together with libraries to form an executable. We are finally clearly dividing our building process into compilation
+stage, and linking stage. Let's introduce two such files: <i>hello.o</i> and <i>sum.o</i>. They are build from their
+respective sources. This means we now have <strong>three</strong> files with compiled or linked code: <i>hello</i>,
+<i>hello.o</i> and <i>sum.o</i>. Latter doesn't depend on anything, so there is no need for us to write anything about
+it. <i>hello.o</i> depends on <i>sum.h</i> (again, due to already mentioned include). Despite the fact that we call the
+<i>sum()</i> function in it, it doesn't depend on <i>sum.o</i>. Why? Because it is just an intermediate file. It never
+executes anything. On the other hand, <i>hello</i> executes it, so it needs all of the intermediate <i>.o</i> files in
+its prerequisites list.
+<p>All in all, the <i>Makefile</i> will look like this:</p>
+<pre>
+hello: hello.o sum.o
+hello.o: sum.h
+</pre>
+<p>When we use it:</p>
+<pre>
+$ make
+cc -c -o hello.o hello.c
+cc -c -o sum.o sum.c
+cc hello.o sum.o -o hello
+$ ./hello
+2 + 3 = 5
+</pre>
+<p>Surprisingly, that's all you need to know. With that, you can build pretty much everything. With time and when you
+gain some additional knowledge you may want to write your makefiles more explicitly: things like <code>CC=gcc</code> to
+make sure that the correct compiler is used. Your own recipes for generating headers or targets that are not generating
+any files, but rather install the software or clean up the directory. Targets not associated with files are called
+<strong>.PHONY</strong> and sooner or later you will encounter them. Actually...
+<h2 id="clean">Clean up your project directory with make</h2>
+<p>For some reasons, you may want to remove all built executables and intermediate files, or any other garbage files
+that your workflow involves. In previous part, I've already noted that you can accomplish that using <i>.PHONY</i>
+targets. Such cleaning target is quite common and is usually called <code>clean</code>. Consider the following:</p>
+<pre>
+hello: hello.o sum.o
+hello.o: sum.h
+
+clean:
+<mark> $(RM) *.o hello</mark>
+
+.PHONY: clean
+</pre>
+<p>As it's not the default target, you must invoke it by name:</p>
+<pre>
+$ make <mark>clean</mark>
+rm -f *.o hello
+</pre>
+<p>The marked line is called the recipe. It describes what rule is supposed to do. Only one recipe per target is used,
+<i>make</i> discards previous recipes for the target if new one is defined, so only the bottom-most is effective.
+<code>$(RM)</code> is a default variable that is expected to describe the command that can be used to safely remove
+files no longer needed by the project. You've probably noticed that <code>.PHONY</code> exists as a target. We add
+<code>clean</code> to it's prerequisites list to let <i>make</i> know that <code>clean</code> is not expected to create
+a file called <i>clean</i>.</p>
+<img src="the_gentlest_introduction_to_building_with_makefiles-3.png" alt="sweeping dust">
+<h2 id="example">Example makefile for C++ project</h2>
+<p>Following makefile is used to build a simple C++ pager, program for opening and scrolling through a file in a
+command line interface. Please note, that by default <i>cc</i> is used as a linker. It means that, if we are building
+a C++ project, the standard C++ library will be missing. We can avoid it by writing own linking recipe, or by adding
+<code>-lstdc++</code> to libraries manually. Latter approach is used in the example.</p>
+<pre>
+CXXFLAGS=-std=c++17 -Wall -Wextra -Werror -O2
+LDLIBS=-lstdc++ -lncurses++
+
+pager: ansi.o content.o pager.o
+pager.o: ansi.h content.h
+
+clean:
+ $(RM) pager *.o
+
+.PHONY: clean
+</pre>
+<h2 id="next">What's next</h2>
+<p>Obviously, that's not everything there is to <i>make</i>. In my opinion, this is all you need for regular usage in
+small to medium projects. From this point you can extend your knowledge. I would suggest to learn more about
+<a href="https://www.gnu.org/software/make/manual/html_node/Using-Variables.html">variables</a> and built-in
+<a href="https://www.gnu.org/software/make/manual/html_node/Functions.html">functions</a>. They will help you to create
+more extendible makefiles, and write less in general. In case you'll end up needing to write a proper recipe and more
+complicated rule - head to sections:
+<a href="https://www.gnu.org/software/make/manual/html_node/Recipes.html">writing recipes</a> or
+<a href="https://www.gnu.org/software/make/manual/html_node/Rules.html">writing rules</a>.
+<a href="https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html">The automatic variables</a> are
+an amazingly useful tool when writing your own rules. Actually, these three things are usually, the first to be
+mentioned by other tutorials about <i>make</i>. However, with approach presented in here, you should be able to avoid
+them for quite a long time. <strong>Be wary</strong> though - don't be ignorant. You've been showed the basic usage of
+<i>make</i> that is heavily dependent on implicit rules and hidden mechanics. You know that they exists and now it's
+turn for you to go out there, use them and slowly learn how they really work.
+</article>
+<script src="https://stats.ignore.pl/track.js"></script>