summaryrefslogtreecommitdiff
path: root/the_gentlest_introduction_to_building_with_makefiles.html
blob: f4b2cac71fa554608f790412871b8d78ad405932 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
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>