summaryrefslogtreecommitdiff
path: root/how_to_build_software_with_make.html
diff options
context:
space:
mode:
authorAki <please@ignore.pl>2023-03-27 22:36:21 +0200
committerAki <please@ignore.pl>2023-03-27 22:36:21 +0200
commite683f80bb5a847cc11251c45ff8b9553826b6eb5 (patch)
tree243e2d62e694a71c75d1d9aaf5eb946e2cdfa31b /how_to_build_software_with_make.html
parentea823149b20e9c3bec66369a5480353e48869279 (diff)
downloadignore.pl-e683f80bb5a847cc11251c45ff8b9553826b6eb5.zip
ignore.pl-e683f80bb5a847cc11251c45ff8b9553826b6eb5.tar.gz
ignore.pl-e683f80bb5a847cc11251c45ff8b9553826b6eb5.tar.bz2
Rewrote make introduction post
Diffstat (limited to 'how_to_build_software_with_make.html')
-rw-r--r--how_to_build_software_with_make.html210
1 files changed, 210 insertions, 0 deletions
diff --git a/how_to_build_software_with_make.html b/how_to_build_software_with_make.html
new file mode 100644
index 0000000..dce05aa
--- /dev/null
+++ b/how_to_build_software_with_make.html
@@ -0,0 +1,210 @@
+<!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">
+<link rel="icon" type="image/png" href="favicon.png">
+<link rel="stylesheet" href="style.css">
+
+<title>How to Build Software With Make</title>
+
+<nav><p><a href="https://ignore.pl">ignore.pl</a></p></nav>
+
+<article>
+<h1>How to Build Software With Make</h1>
+<p class="subtitle">Published on 2023-03-21 20:00:00+02:00
+<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> which is substituted with linker
+executable. We already know <code>$(LDFLAGS)</code>, so <code>$(LDLIBS)</code> sounds like "linker libraries" and that's
+correct. 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 by all dependencies) and <code>$@</code> (substituted by target name). Note
+that <code>$^</code> is not part of POSIX.
+<p>When we finally run <b>make</b> we will see how it performs all predicted substitutions:
+<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>