diff options
Diffstat (limited to 'how_to_build_software_with_make.html')
-rw-r--r-- | how_to_build_software_with_make.html | 210 |
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 <stdio.h> +#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> |