summaryrefslogtreecommitdiff
path: root/how_to_build_software_with_make.html
blob: 25f3792da04ee00799279567c1596895e71a0f97 (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
<!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, make, building, compiling, C">
<meta name="published-on" content="2023-03-27T22:35:00+02:00">
<link rel="icon" type="image/png" href="favicon.png">
<link rel="stylesheet" href="style.css">

<title>How to Build Software With Make</title>

<header>
<nav><a href="https://ignore.pl">ignore.pl</a></nav>
<time>27 March 2023</time>
<h1>How to Build Software With Make</h1>
</header>

<article>
<p>To compile an executable from a single C source file we need just one compiler invocation in our shell:
<pre>
$ cc myprogram.c -o myprogram
$ ./myprogram
Hello, World!
</pre>
<p>As we expand our projects they naturally become harder to build. If not due to dependencies then due to pure
repetitiveness of the task. <a href="https://en.wikipedia.org/wiki/Build_automation">Build systems</a> are a response to
that and <a href="https://www.gnu.org/software/make/">GNU Make</a> is one of them. GNU Make is part of a larger family
of programs that usually share the <b>make</b>(1) (or similar, e.g., nmake, mk). Although timeline doesn't necessarily
agree, we can say that <b>make</b> is defined in
<a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html">POSIX standard</a>.
<p>Principles of Make are straight-forward: as a user we define a set of rules that consist of targets, dependencies and
commands. Targets are usually filenames that describe what rule is expected to produce. Dependencies are names of other
targets or source files. That's it. Later this concept is expanded upon to reduce the amount of lines we need to write.
<p>When we build native software we are mainly interested in:
<ul>
<li>Executables (e.g., ELF, PE *.exe)
<li>Static libraries (e.g., *.a, *.lib)
<li>Dynamic/shared libraries (e.g., *.so, *.dll)
</ul>
<p>Since <b>make</b> live close to the compiler we will be also interested in object files (e.g., *.o, *.obj).

<h2>How to Build Executables With Make</h2>
<p><b>Make</b> helps us to reduce number of characters we need to type in terminal and lines we need to put to setup the
build. In fact, the simplest case of one source file to one executable, like the one I showed above, does not need any
preparation at all:
<pre>
$ make myprogram
cc myprogram.c -o myprogram
$ ./myprogram
Hello, World!
</pre>
<p>First thing that we should observe is that <b>make</b> nonchalantly prints the commands it executes to build stuff
for us. I'll keep them visible in samples so it's hopefully more clear what's happening.
<p>We invoke <b>make</b> directly with a name of the target we intend to build. Remember: targets are usually filenames
that are expected to be produced by a rule. Based on the target name and built-in rules <b>make</b> guesses what to do
to get <i>myprogram</i>. It finds <i>myprogram.c</i> in the working directory and selects an appropriate rule.
<p>Yet, we still need to type the target name each time we want to build it. We can avoid it by creating a file called
<i>Makefile</i> in the project directory. This is the file containing all build rules and other helpful definitions. If
<b>make</b> is ran without any targets then the first target found in <i>Makefile</i> is used. Consider <i>Makefile</i>:
<pre>
myprogram:
</pre>
<p>Let's try it:
<pre>
$ make
cc myprogram.c -o myprogram
</pre>
<p>Selects the first and the only target as intended.</p>
<img src="how_to_build_software_with_make-1.png" alt="broom">
<p>There are <a href="https://www.gnu.org/software/make/manual/html_node/Standard-Targets.html">traditional targets</a>
that users of our makefiles may expect. A less bare-bone makefile would look like this:
<pre>
all: myprogram
clean:
	rm -f myprogram
.PHONY: all clean
</pre>
<p>Now, someone can run following to ensure that a clean build is made:
<pre>
$ make clean all
rm -f myprogram
cc myprogram.c -o myprogram
</pre>
<p>Let's note that we can specify multiple targets in the command line. They will be build in order.
<p>Next, <code>all: myprogram</code> - this notation specifies that target <i>all</i> depends on <i>myprogram</i>. We
can specify multiple dependencies if needed. All dependencies will be built and once they are done the dependant target
will be processed.
<p><code>.PHONY</code> is a special target that is used to mark targets that are not expected to produce files. We are
not expecting <i>all</i> or <i>clean</i> files to be present at any stage. Instead we can imagine these as tasks or
actions. Other such targets would be for example <i>install</i>, <i>uninstall</i> or <i>dist</i>.

<h2>How to Build Object Files With Make</h2>
<p>Usually, we won't implement our projects inside a single source file. We want to split logical parts of the software
into several files. Consider <i>greeting.h</i>:
<pre>
#pragma once
void greet(void);
</pre>
<p>With accompanying <i>greeting.c</i>:
<pre>
#include &lt;stdio.h&gt;
#include "greeting.h"
void greet(void)
{
	printf("Hello, World!");
}
</pre>
<p>And our program that uses them, let's stay with <i>myprogram.c</i>:
<pre>
#include "greeting.h"
int main()
{
	greet();
}
</pre>
<p>As simple as it gets while it still illustrates the problem.</p>
<img src="how_to_build_software_with_make-2.png" alt="files">
<p>When we build our program from a single source the compiler does two things: compiles and links. Now we want to do
these separately. An intermediate result of compilation is an object file. On Linux and Unix platforms they usually use
*.o extension. We can instruct <b>make</b> to use them by adding new rules to our existing makefile:
<pre>
myprogram: myprogram.o greeting.o
myprogram.o: greeting.h
greeting.o: greeting.h
</pre>
<p>First we declare that in order to build <i>myprogram</i> two object files are needed. Then we define some additional
relationships between the object files and the only header file that we have.
<p>Note that we did not specify explicitly dependency between the object files and the respective source files.
Technically we could also skip the relationship between <i>myprogram</i> and <i>myprogram.o</i> and <b>make</b> would
still figure it out. This is a matter of what we want to declare explicitly, how lazy we are at the time of writing and
how permissive are the built-in rules that we use.
<p>Let's try it out:
<pre>
$ make
cc -c -o myprogram.o myprogram.c
cc -c -o greeting.o greeting.c
cc myprogram.o greeting.o -o myprogram
$ ./myprogram
Hello, World!
</pre>

<h2>How to Build Static Libraries With Make</h2>
<p>The best part here is that we're pretty much already set. Static libraries are archives containing object files. They
are created and updates using <b>ar</b>(1). There are built-in rules available for them, too, but with one additional
gimmick. They use archive targets that look like this:
<pre>
myprogram: myprogram.o libgreeting.a
libgreeting.a: libgreeting.a(greeting.o)
</pre>
<p>We replaced <i>greeting.o</i> prerequisite with <i>libgreeting.a</i> which is defined line below. Dependencies of the
library target are expressed as
<a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html#tag_20_76_13_07">archive members</a>.

<h2>How to Build Shared Libraries With Make</h2>
<p>Shared/dynamic libraries are different. The process of building them with <b>make</b> is similar to executables, but
we want to modify compilation and linking options, and write our own command:
<pre>
CFLAGS=-fPIC
LDFLAGS=-shared
libgreeting.so: greeting.o
	$(LD) -shared $(LDLIBS) $(LDFLAGS) $^ -o $@
</pre>
<p>First two lines are variable definitions (called "macros" in POSIX). We set <i>CFLAGS</i> - C compiler flags - to
<code>-fPIC</code> to enable <a href="https://en.wikipedia.org/wiki/Position-independent_code">position-independent
code</a>, which is needed when compiling code for shared libraries that are expected to work nicely on Linux. Then, we
set <i>LDFLAGS</i> - linker flags - to <code>-shared</code> which (put simply) tells the linker to build a shared
object.
<p>Now let's analyse linker command itself. Starting with <code>$(LD)</code>; it's substituted with the linker
executable. We already know <code>$(LDFLAGS)</code> and <code>$(LDLIBS)</code> sounds quite similar. This one is
intended to store all external libraries that linker should use. Finally, we have two
<a href="https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html">automatic variables</a> also known
as <a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html#tag_20_76_13_08">internal
macros</a>: <code>$^</code> (substituted with all dependencies) and <code>$@</code> (substituted with target name). Note
that <code>$^</code> is not part of POSIX.
<p>When we finally run <b>make</b> we can observe predicted commands:
<pre>
$ make
cc -fPIC -c -o greeting.o greeting.c
ld -shared greeting.o -o libgreeting.so
</pre>
<p>Value for <code>$(LD)</code> is provided by default by <b>make</b>.
<p>In some cases, we may want to invoke the linker via the compiler to have all defaults implicitly defined. For example
for C++ we would replace <code>$(LD)</code> with <code>$(CXX)</code> to imply e.g., <code>-lstdc++</code>.

<h2>How to Build Against External Libraries With Make</h2>
<p>This one starts to be tricky. The simple case where we use libraries installed in <i>/usr</i> or <i>/usr/local</i> is
exactly that - simple. All we need to do is put linker options in a variable (macro) that we have already seen:
<code>$(LDLIBS)</code>:
<pre>
LDLIBS=-llua -lm
</pre>
<p>Since managing these manually easily becomes a problem there are tools that can generate list of these options for
us. One of them is <b>pkgconfig</b>(1). In most <b>make</b> implementations we can use backticks to run command and use
its output:
<pre>
CFLAGS=`pkg-config --cflags lua`
LDLIBS=`pkg-config --libs lua`
</pre>
<p>This will link Lua and make its headers available.

<h2>What's next?</h2>
<p>This is only a beginning. I hope it's a good overview, but the true journey awaits. This is but a dump of information
and playing around with <b>make</b> sounds like a reasonable next step. Because of its simple design <b>make</b> is not
limited to C, C++, yacc/bison or Fortran. I use it for most small weekend projects I do, be it RST to HTML generation or
image rendering. Anything that can be described as processing from one file to another (or generation from thin air) is
good enough to be put into a makefile.
<p>Next step after playing around some is diving deeper into automatic variables and
<a href="https://www.gnu.org/software/make/manual/html_node/Pattern-Rules.html">pattern rules</a>. I'd recommend to
skip over <a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html#tag_20_76_13_06">inference
rules/suffixes</a>.
</article>
<script src="https://stats.ignore.pl/track.js"></script>