My experience learning make
Posted on
When I have been looking for a new language to learn, or gauging the
usefulness of a language, I tend to appreciate build systems as part
of the language. I think an important property of a tool is its
simplicity of use, and for a long time I avoided C because it did not
have a mvn
, cargo
, or npm
. I thought the lack of an in-built
build tool made C unnecessarily complicated to get started with. After
reading bits and pieces of the K&R, making an almost whole game in C,
and still not having touched makefiles, I have come to the conclusion
that I was wrong. I have been happily writing C and building it with
shell-scripts that build the program for each specific system. For
example, I have a batch script for Windows that builds the program
with cl.exe
, providing the correct flags, one for Linux, and so on.
The reason I wrote the shell scripts instead of makefiles is because I
already knew how shell scripts work, and learning to use cc
and
cl.exe
was quite similar to learning to use the aforementioned build
tools. If the compilers did not do something I needed from a build
tool, I would just write that part in the script myself. In addition
to this, I have heard a lot about makefiles not being super portable,
and the existence of qmake
and cmake
seem to prove that idea. And
in my own experience, when I have had to call make
manually, it has
stumbled on my system being misconfigured somehow, which makes it seem
prone to breakage.
That said, I definitely feel like I should learn how to write a
Makefile
, if for no other reason than to make my negative opinion of
them more valid. Or invalid, as the case may be ;)
Learning make
A few days ago, I got a copy of The Unix Programming Environment by
Brian W. Kernighan and Rob Pike from the library. Why? The main reason
was because I wanted to explore the Unix philosophy a bit further than
what I had learned from hearing it repeatedly in tech articles and
social media (not much). The other reason was because I imagined it
would have a bit on make
.
It does, sort-of. The eighth chapter is about writing a big program,
and has three digressions on make
. Based on that, I got the
following impression of it: makefiles define dependencies between
generated and manually edited files, and how those dependencies
manifest (that is, how you get from the manually written files to the
generated ones). The connection to C becomes obvious once you think
about C programming in those terms: first you manually edit the C
source code, then you generate object files out of that, and then you
generate a final executable from those.
In this light, make
seems like an obvious fit for C, but very
general as well. I did not realize what the specific role of make
was before, probably because other programming languages have built
similar functionality into their build tools, but after this
realization, it is obvious.
On one hand, I appreciate how cc
and make
split the job of making
lots of source code into an executable (something something Unix
philosophy), but on the other hand, I can not help but feel like it is
pointlessly complicated for the end-user. Almost every program follows
a similar build-pipeline, or could at least be refactored to fit, so
why not combine the functionalities to a singular build tool? Why do I
need to learn yet another syntax just to build my C? I imagine that is
what a lot of people thought, since at least a few trillion different
tools have been written to build a C program. I think. Ninja
just
sounds so out-there, that all the good names must have been taken
already, which would infer many such tools. I do not actually know if
ninja
is a build tool. But I digress.
Conclusion
Make(1)
is neat. I wrote a Makefile
for this blog to test it out,
and I enjoyed the experience after ironing out a few
misunderstandings. It is a shame that make
is the most universal
tool to build C programs, I would much prefer a more cargo
/go build
-like experience, but it is not that bad either. If you are of a
similar mindset to mine at the beginning of this post, just go ahead
and use make
. It is not evil. At the very least, it can not be as
bad as picking one of the million other C build tools, because you do
not get anywhere by making yet another standard, and you learn
a new tool you can use for many other things as well. And make sure to
be POSIX-compatible, just to be a good citizen :)
As a final note, The Unix Programming Environment has been a pretty fun read, if a bit obsolete. At least the C is, did you know functions looked like this in the 1970s?
main(argc, argv)
char *argv[];
{
// ...
}