
Afraid of Makefiles? Don't be - tdurden
https://matthias-endler.de/2017/makefiles/
======
ejholmes
Make's underlying design is great (it builds a DAG of dependencies, which
allows for parallel walking of the graph), but there's a number of practical
problems that make it a royal pain to use as a generic build system:

1\. Using make in a CI system doesn't really work, because of the way it
handles conditional building based on mtime. Sometimes you just don't want the
condition to be based on mtime, but rather a deterministic hash, or something
else entirely.

2\. Make is _really_ hard to use to try to compose a large build system from
small re-usable steps. If you try to break it up into multiple Makefiles, you
lose all of the benefits of a single connected graph. Read the article about
why recursive make is harmful:
[http://aegis.sourceforge.net/auug97.pdf](http://aegis.sourceforge.net/auug97.pdf)

3\. Let's be honest, nobody really wants to learn Makefile syntax.

As a shameless plug, I built a tool similar to Make and redo, but just allows
you to describe everything as a set of executables. It still builds a DAG of
the dependencies, and allows you to compose massive build systems from smaller
components:
[https://github.com/ejholmes/walk](https://github.com/ejholmes/walk). You can
use this to build anything your heart desires, as long as you can describe it
as a graph of dependencies.

~~~
majewsky
> Using make in a CI system doesn't really work, because of the way it handles
> conditional building based on mtime.

Any CI I've seen starts from a fresh repo checkout and rebuilds everything
every time, so it's not an issue with practice.

OTOH, probably all my projects were small enough for it to not hurt when the
CI builds everything from scratch every time. I might look at this from
another angle if the things I worked on were mega-LOC C++ programs, and not
kilo-LOC Go programs.

~~~
ejholmes
That works, until your build system is sufficiently large or time consuming or
not C/C++ like make was originally built for. For example, at my company we
have a build system for building all of our Amazon Machine Images (AMIs). It
doesn't make sense to re-build images unless they're dependencies have
changed, but mtime just doesn't make any sense for a build system like this.
Trying to coerce make into doing what we wanted was like pulling teeth.

~~~
wruza
Why didn't you just take make sources and fix them for <whatever> instead of
mtime() check, making it a command line option or environment variable? Or
simply .MTIME target, which would return int-like string of rank, defaulting
to mtime() if not declared.

~~~
marcosdumay
I guess it's because of his point #3.

Honestly, if I were to modify make for some custom behavior, I would look for
another tool¹ or create yet another builder tool. Because although I already
know most of the syntax, point #2 is an incredibly big problem, the GP missed
point #4 in that make does not manage external dependencies², and the syntax
is not bad only for learning, but for using too.

1 - Almost everybody seems to agree with me on this. The only problem is that
we have all migrated to language constrained tools. This is a problem worth
fixing.

2 - Wether it is installing them or simply testing if they are there, make
can't do any. Hello autotools, with it syntax set to extreme cumbersomeness.

------
chungy
I think the primary thing that makes people fear Makefiles is that they try
learning it by inspecting the output of automake/autoconf, cmake, or other
such systems. These machine-generated Makefiles are almost always awful to
look at, primarily because they have several dozen workarounds and least-
common-denominators for make implementations dating back to the 1980s.

A properly hand-tailored Makefile is a thing of beauty, and it is not
difficult.

~~~
digi_owl
And yet we keep producing such tool over and over.

Meson seems to be the latest fashion in Linux circles, it seems...

~~~
valarauca1
All build systems suck.

You are trying to solve a Turing complete problem in a simplest low impact way
as possible.

There is no good solution. Everyone will fall short in one way or another.
Hence a new build system. The wheel never stops.

~~~
blauditore
What do you mean by Turing-complete problems? Turing completeness is related
to programming languages, not problems.

Most build tools do have some kind of Turing-complete tooling built in with
their DSL. But I don't think that's an absolute necessity.

~~~
valarauca1
You are moving symbols based on the action of other symbols.

These symbols are artifacts/files. Elementary recursive Build-Scripts can be
Turing complete even if the overall language isn't.

------
bluejekyll
Make is awesome. I have always loved make, and got really good with some of
its magic. After switching to Java years ago, we collectively decided,
"platform independent tools are better", and then we used ant. Man was ant
bad, but hey! It was platform independent.

Then we started using maven, and man, maven is ridiculously complex,
especially adding custom tasks, but at least it was declarative. After getting
into Rust, I have to say, Cargo got the declarative build just right.

But then, for some basic scripts I decided to pick Make back up. And I
wondered, why did we move away from this? It's so simple and straightforward.
My suggestion, like others are saying, is keep it simple. Try and make
declarative files, without needing to customize to projects.

I do wish Make had a platform independent strict mode, because this is still
an issue if you want to support different Unixes and Windows.

p.s. I just thought of an interesting project. Something like oh-my-zsh for
common configs.

~~~
bhaak
How can Ant be bad? It's just Make in XML ... ;-)

But because it is Make written in Java with a XML syntax, it has inherently
the same problems.

Fun fact: They had for years a makefile rant on their home page which boilt
down to "Ant was invented because its developer couldn't make Makefiles work
as his editor didn't properly show tabs vs spaces":
[https://web.archive.org/web/20100203102803/http://ant.apache...](https://web.archive.org/web/20100203102803/http://ant.apache.org:80/)

~~~
AstralStorm
Ant is actually a different beast, it is more declarative than procedural
Makefile language.

Still ugly and terrible and limiting.

~~~
bhaak
How is it more declarative than the Makefile language?

You declare targets and dependencies and the steps within are target a
executed procedurally in sequence.

This sentence is 100% true for both Ant and Make.

Ugly, yes. It was developed during the times when everything had to be XML.
But also Makefiles don't win a beauty contest.

Terrible. Maybe, but not worse than trying to write portable Makefile for
something non-trivial (e.g. libraries).

Limiting? In what way? You are encouraged to use the supplied tasks which
cover a lot. Creating new tasks would be done in the language you programming.
You can still execute any arbitrary command if you really want to.

~~~
AstralStorm
In ant, to declare a target for any no direct code generator is a royal pain.
Even worse than in Make.

Ant is declarative in that you do not directly refer to files except in tasks
that directly handle files. You cannot say that phony task x depends on file
y.

------
raimue
By using pseudo targets only in the example and not real files, the article
misses the main point of targets and dependencies: target rules will only be
executed if the dependencies changed. make will compare the time of last
modification (mtime) on the filesystem to avoid unnecessary compilation. To
me, this is the most important advantage of a proper Makefile over a simple
shell script always executing lots of commands.

~~~
Touche
Don't you have to list every file though? Many of my projects have hundreds of
source files.

~~~
syncsynchalt
So my example (below in another comment), there's a solution using
$(wildcard).

------
rdtsc
Sneaky pro-tip - use Makefiles to parallelize jobs that have nothing to do
with building software. Then throw a -j16 or something at it and watch the
magic happen.

I was stuck on an old DoD redhat box and it didn't have gnu parallel or other
such things and co-worker suggested make. It was available and it did the job
nicely.

~~~
coredog64
Gnu xargs since circa 2009 has had a '-P' option that will let you execute
simple tasks in parallel.

~~~
rdtsc
I forgot why I couldn't use xargs -P there. If I had to guess ancient DoD
version of RHEL, like say RHEL 5. Note it was released in 2007 and xargs as
you mentioned had that since 2009.

Sometimes Redhat backport things but I just remember not finding anything
there and then being very happy to have discovered make. I think even with
xargs -P I would have gone with make anyway as it involved a few dependencies
and checking of file times.

------
syncsynchalt
Today's simple makefiles are the end result of lessons hard learned. You'd be
horrified to see what the output of imake looked like.

From memory here's a Makefile that serves most of my needs (use tabs):

    
    
      SOURCE=$(wildcard *.c)
      OBJS=$(patsubst %.c,%.o, $(SOURCE))
      CFLAGS=-Wall
      # define CFLAGS and LDFLAGS as necessary
    
      all: name_of_bin
    
      name_of_bin: $(OBJS)
          $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
    
      %.o: %.c
          $(CC) $(CFLAGS) -o $@ $^
    
      clean:
          rm -f *.o name_of_bin
    
      .PHONY: clean all

~~~
fiatjaf
This is totally incomprehensible to me.

~~~
qu4z-2
I don't really know what to tell you, there. Are you generally familiar with
the C build process, and bash?

The only "unusual" things I'm seeing there are the $@ (target) and $^
(dependencies) automatic variables, the wildcard and patsubst functions, which
have the description right in the title, and the general ``target:
dependencies \n\t command-list`` format of Makefile rules.

~~~
dmitriid
> The only "unusual" things I'm seeing

And then proceeds to list almost the entirety of the Makefile

------
martin_ky
Due to its versatility, Makefiles can be creatively used beyond building
software projects. Case in point: I used a very simple hand-crafted Makefile
[1] to drive massive Ansible deployment jobs (thousands of independently
deployed hosts) and work around several Ansible design deficiencies (inability
to run _whole playbooks_ in parallel - not just individual tasks, hangs when
deploying to hosts over unstable connection, etc.)

The principle was to create a make target and rule for every host. The rule
runs ansible-playbook for this single host only. Running the playbook for e.g.
4 hosts in parallel was as simple as running 'make -j4'. At the end of the
make rule, an empty file with the name of the host was created in the current
directory - this file was the target of the rule - it prevented running
Ansible for the same host again - kind of like Ansible retry file, only
better.

I realize that Ansible probably is not the best tool for this kind of job, but
this Makefile approach worked very well and was hacked together very quickly.

[1]
[https://gist.github.com/martinky/819ca4a9678dad554807b68705b...](https://gist.github.com/martinky/819ca4a9678dad554807b68705bedbcb)

------
qznc
I love Make for my small projects. It still could be better. Here is my list:

* Colorize errors

* Hide output unless the command fails

* Automatic help command which shows (non-file) targets

* Automatic clean command which deletes all intermediate files

* Hash-based update detection instead of mtime

* Changes in "Makefile" trigger rebuilds

* Parallel builds by default

* Handling multi-file outputs

* Continuous mode which watches the file system for changes and rebuilds automatically

I know of no build system which provides these features and is still simple
and generic. Tup is close, but it fails with LaTeX, because of the circular
dependencies (generates and reads aux file).

~~~
ejholmes
[https://github.com/ejholmes/walk](https://github.com/ejholmes/walk) can do
most of this FWIW.

~~~
qznc
Walk leaves most of this to the implementor of the Walkfile.

------
AceJohnny2
"Build systems are the bastard stepchild of every software project" \-- me a
years ago

I've work in embedded software for over a decade, and all projects have used
Make.

I have a love-hate relationship with Make. It's powerful and effective at what
it does, but its syntax is bad and it lacks good datastructures and some basic
functions that are useful when your project reaches several hundred files and
multiple outputs. In other words, it does not scale well.

Worth noting that JGC's Gnu Make Standard Library (GMSL) [1] appears to be a
solution for some of that, though I haven't applied it to our current project
yet.

Everyone ends up adding their own half-broken hacks to work around some of
Make's limitations. Most commonly, extracting header file dependency from C
files and integrating that into Make's dependency tree.

I've looked at alternative build systems. For blank-slate candidates, tup [2]
seemed like the most interesting for doing native dependency extraction and
leveraging Lua for its datastructures and functions (though I initially
rejected it due the the silliness of its front page.) djb's redo [3]
(implemented by apenwarr [4]) looked like another interesting concept, until
you realize that punting on Make's macro syntax to the shell means the tool is
only doing half the job: having a good language to specify your targets and
dependency is _actually most of the problem_.

Oh, and while I'm around I'll reiterate my biggest gripe with Make: it has two
mechanisms to keep "intermediate" files, .INTERMEDIATE and .PRECIOUS. The
first does not take wildcard arguments, the second does but it also keeps any
half-generated broken artifact if the build is interrupted, which is a great
way to _break your build_. Please can someone better than me add wildcard
support to .INTERMEDIATE.

[1] [http://gmsl.sourceforge.net](http://gmsl.sourceforge.net)

[2] [http://gittup.org/tup/](http://gittup.org/tup/) Also its creator, Mike
Shal, now works at Mozilla on their build system

[3] [http://cr.yp.to/redo.html](http://cr.yp.to/redo.html)

[4] [https://github.com/apenwarr/redo](https://github.com/apenwarr/redo)

~~~
joeyo
Tup is really great. I wish it were more widely used. Tup can effortlessly
handle a lot of situations that Make chokes on or requires deep magic to get
right. A good example is the clean handling of automatically generated header
files. This is all it takes to integrate protobufs into a Tupfile:

    
    
      : foreach *.proto |> !protoc |> %g.pb.cc | %g.pb.h
      : foreach *.pb.cc | *.pb.h |> !cpp |> %g.pb.o
    

The "|>" is the pipe operator and I've elided the definitions of the "!protoc"
and "!cpp" macros (but they're about what you'd expect). Tup detects whenever
a .proto file is changed and does the right thing. Getting this to work with
Make requires advanced tricks like .PHONY and .PRECIOUS.

------
rrmm
Makefiles are easy for small to medium sized projects with few configurations.
After that it seems like people throw up their hands and use autotools to deal
with all the recursive make file business.

Most attempts to improve build tools completely replace make rather than
adding features. I like the basic simplicity and the syntax, (the tab thing is
a bit annoying but easy enough to adapt to).

It'd be interesting to hear everyone's go to build tools.

~~~
wwwigham
> It'd be interesting to hear everyone's go to build tools.

A script written in whatever the primary language of the project is (usually
with some library support), ideally; to reduce the minimum required knowledge
to be a first time contributor to the project. Some kind of js build tool for
js projects, fake for f#, and so on. I don't want people to need to learn "the
one build tool DSL to rule them all" (be that makefile syntax, bazel rules, or
whatever) to contribute to a project's infrastructure, on top of the project's
primary language.

~~~
sevensor
So for C? Fortran? You'd have users of those languages use those languages to
write programs to do the build? But then, how would you keep them from
generalizing their good ideas about builds into some kind of... system?

------
wyldfire
> You've learned 90% of what you need to know about make.

That's probably in the ballpark, anyways.

The good (and horrible) stuff:

\- implicit rules

\- target specific variables

\- functions

\- includes

I find that with implicit rules and includes I can make really sane, 20-25
line makefiles that are not a nightmare to comprehend.

For a serious project of any scope, it's rare to use bare makefiles, though.
recursive make, autotools/m4, cmake, etc all rear their beautiful/ugly heads
soon enough.

But make is my go-to for a simple example/reproducible/portable test case.

~~~
gumby
> For a serious project of any scope, it's rare to use bare makefiles, though.
> recursive make, autotools/m4, cmake, etc all rear their beautiful/ugly heads
> soon enough.

If I didn't have to provide the option to build under Xcode or VS, I wouldn't
have to live in the hell that is Cmake.

I'd just use make.

~~~
tomjakubowski
cmake has improved so much since 3.0 that it's almost unrecognizable. Though
much of the cruft remains, thanks to backwards compatibility, and anything
involving list or string operations is still absolute hell, I dare say it's
actually pleasant to use, at least compared to 2.8.whatever-shipped-with-
ubuntu-precise.

Personally, I'm glad we use cmake at work simply because we have a diversity
of preferred build tools: some people like to work in XCode or Eclipse, others
like to use a text editor and make, others like to use a text editor and
ninja. While we all suffer a bit with cmake, we all benefit from its position
as a "metabuild" tool.

------
mauvehaus
I feel like any discussion of make is incomplete without a link to Recursive
Make Considered Harmful[0]. Whether you agree with the premise or not, it does
a nice job of introducing some advanced constructs that make supports and
provides a non-contrived context in which you might use them.

[0]
[http://aegis.sourceforge.net/auug97.pdf](http://aegis.sourceforge.net/auug97.pdf)

~~~
gergoerdi
Non-Recursive Make is also Considered Harmful:
[https://news.ycombinator.com/item?id=11441719](https://news.ycombinator.com/item?id=11441719)

In seriousness, the linked paper describes Shake, a Make replacement with two
party tricks: one, it is implemented as a DSL embedded in Haskell, thus giving
a nice way of programmatic rule generation; and two, it supports monadic
dependencies, unlike Make's applicative-only ones.

------
Animats
The trouble with "make" is that it's supposed to be driven by dependencies,
but in practice it's used as a scripting language. If the dependency stuff
worked, you would never need

    
    
       make clean; make
    

or

    
    
       touch

~~~
chubot
Yeah this is my pet peeve about how people use Makefiles. Make is supposed to
be a dependency-driven "dataflow" language, but over time it's evolved into
something like shell. And people tend to use it like shell.

The most glaring example is .PHONY targets, which are a hack and should just
be shell functions. In 'make <foo>', <foo> should be _data_ , not _code_.
'make test' doesn't make sense, but 'make bin/myprog' does.

I posted this link in another comment showing how Make, Shell, and Awk
overlap:

[http://www.oilshell.org/blog/2016/11/14.html](http://www.oilshell.org/blog/2016/11/14.html)

Here are some more comments on Make's confused execution model. It's sort of
functional/dataflow and sort of not. In practice you end up "stepping through"
an imperative program rather than reasoning about inputs and outputs like in a
functional program.

[https://news.ycombinator.com/item?id=14840696](https://news.ycombinator.com/item?id=14840696)

And at the end of this post, I talk a bit more about the functional model:

[http://www.oilshell.org/blog/2017/05/31.html](http://www.oilshell.org/blog/2017/05/31.html)

~~~
amirbehzad
What simple tool would you suggest to address those .PHONY-targets instead of
a Makefile?

~~~
chubot
I recommend using plain shell scripts. Each .PHONY target is a simple shell
function, and then the last line of the script is "$@", which means run
function $1 with parameters $2 to $n.

So instead of 'make test', I just use './test.sh all', or './test.sh foo'. The
test script can invoke make.

The idea is that 'dataflow' parts are done in Make, and the imperative parts
are done in shell. This works out fairly well if you're disciplined about it.
The only point of Makefiles is to support quick incremental builds. If there's
no incrementality, then you might as well use shell. (Note: whenever you use
Make, you're using shell by definition. There's no possibility of only using
Make. So I take Make out of the picture where it's not necessary.)

For example, all the instructions here are of the form 'foo.sh ACTION':

[https://github.com/oilshell/oil/wiki/Contributing](https://github.com/oilshell/oil/wiki/Contributing)

Pretty much every shell script in the repo uses "the argv dispatch pattern"...
I've been meaning to write a blog post about that pattern, i.e. using
functions with the last line as "$@".

[https://github.com/oilshell/oil](https://github.com/oilshell/oil)

~~~
bigmanwalter
Wow, I never knew about "$@" that's a super handy tip. Now I just need an
excuse to write a shell script :)

------
misnome
Almost every build system (where I think it isn't controversial to say make is
most often used) looks nice and simple with short, single-output examples to
demonstrate the basis of a system.

It's when you start having hundreds of sources, targets, external
dependencies, flags and special cases that it becomes hard to write sane,
understandable Makefiles, which it presumably _why_ people tend to use other
systems to generate makefiles.

So sure, understanding what make is, and how it works is probably important,
since it'll be around forever. But there are usually N better ways of
expressing a build system, nowadays.

------
nstart
So I saw this and thought why not give it a try. How hard could it be right?
My goal? Take my bash file that does just this (I started go just yesterday so
I might be doing cross compiling wrong :D) :

```

export GOPATH=$(pwd)

export PATH=$PATH:$GOPATH/bin

go install target/to/build

export GOOS=darwin

export GOARCH=amd64

go install target/to/build

```

which should be simple. Right? Set environment variables, run a command. Set
another environment variable, run a command.

45 minutes in and I haven't been able to quite figure it out just yet. I
definitely figured out how to write my build.sh files in less than 15 minutes
for sure when I started out.

~~~
JacksCracked
Each logical line of a makefile is executed in its own shell. This means that
variables cannot be set across lines and directory changes cannot be changed
across lines.

~~~
ComputerGuru
Just use the export command to set variables across lines.

------
pkkim
One important tip is that the commands under a target each run sequentially,
but in separate shells. So if you went to set env vars, cd, activate a Python
virtualenv, etc to affect the next command, you need to make them a single
command, like:

    
    
      target:
          cd ./dir; ./script.sh

~~~
vortico

        cd dir && \
            ./script.sh && \
            ./otherstuff.sh
    

handles errors, in case `dir` doesn't exist for example.

------
epx
Those who don't understand Make are condemned to reimplement it, poorly.

~~~
stormbeta
The closest thing I've seen that seem likes a real replacement is Bazel, but I
haven't tried using it for anything but a couple toy projects yet. The
concepts seem solid though, as expected from Google.

My main complaint about make is that relying on timestamps, while fast, is
error-prone and doesn't work with idempotent commands that might refresh the
timestamp without altering content.

------
bauerd
I remember trying to wrap my head around the monstrosity that is Webpack. Gave
up and used make, never looked back since

~~~
fiatjaf
Webpack is an horrible monster that makes the machine-generated Makefiles look
pretty.

------
flukus
Personal blog spam, I learned make recently too and discovered it was good for
high level languages as well, here is an example of building a c# project:
[http://flukus.github.io/rediscovering-
make.html](http://flukus.github.io/rediscovering-make.html) .

Now the blog itself is built with make: [http://flukus.github.io/building-a-
blog-engine.html](http://flukus.github.io/building-a-blog-engine.html)

~~~
nu11p01n73R
Love the simplicity of the design.

Is there any reason why you didn't include the date of publishing in the
pages. Or is it just me who looks for the dates on all the blogs that I read.

~~~
majewsky
I wouldn't call is "simplicity", but rather "absence" of a design. See the
arguments laid out in
[http://bettermotherfuckingwebsite.com/](http://bettermotherfuckingwebsite.com/)

------
DangerousPie
If you want all the greatness of Makefiles without the painful syntax I can
highly recommend Snakemake:
[https://snakemake.readthedocs.io/en/stable/](https://snakemake.readthedocs.io/en/stable/)

It has completely replaced Makefiles for me. It can be used to run shell
commands just like make, but the fact that it is written in Python allows you
to also run arbitrary Python code straight from the Makefile (Snakefile). So
now instead of writing a command-line interface for each of my Python scripts,
I can simply import the script in the Snakefile and call a function directly.

Eg.

    
    
      rule make_plot:
        input: data = "{name}.txt"
        output: plot = "{name}.png"
        run:
          import my_package
          my_package.plot(input['data'], output['plot'], name = wildcards['name'])
    

Another great feature is its integration with cluster engines like SGD/LSF,
which means it can automatically submit jobs to the cluster instead of running
them locally.

~~~
crdoconnor
Wow. This project has probably the worst example of the telescoping
constructor antipattern I've ever seen:

[https://snakemake.readthedocs.io/en/stable/api_reference/sna...](https://snakemake.readthedocs.io/en/stable/api_reference/snakemake.html)

~~~
todd8
It's Python, not Java, so it _can 't_ have the telescoping constructor anti-
pattern.

Admittedly, there are a _huge_ number of arguments to the snakefile
constructor, but they are optional named arguments making the constructor
safer and easier to use than Java's telescoping constructor and it
alternatives (Java Bean pattern or builder patterns). This application
apparently has many options or settings.

------
rcarmo
These days, most of my projects have a Makefile with four or five simple
commands that _just work_ regardless of the language, runtime or operating
system in use:

\- make deps to setup/update dependencies

\- make serve to start a local server

\- make test to run automated tests

\- make deploy to package/push to production

\- make clean to remove previously built containers/binaries/whatever

There are usually a bunch of other more specific commands or targets (like
dynamically defined targets to, say, scale-frontends-5 and other trickery),
but this way I can switch to any project and get it running without bothering
to lookup the npm/lein/Python incantation du jour.

Having sane, overridable (?=) defaults for environment variables is also
great, and makes it very easy to do stuff like FOOBAR=/opt/scratch make serve
for one-offs.

Dependency management is a much deeper and broader topic, but the usefulness
of Makefiles to act as a living document of how to actually run your stuff
(including documenting environment settings and build steps) shouldn't be
ignored.

(Edit: mention defaults)

~~~
MyPseudonym
There are other projects than just web apps you know.

~~~
rcarmo
I use the above for database imports, ETL and Azure template deployments too.
And who says "serve" launches an HTTP server? :)

------
rcthompson
For people who are more comfortable in Python, I highly recommend
Snakemake[1]. I use it for both big stuff like automating data analysis
workflows and small stuff like building my Resume PDF from LyX source.

[1]:
[https://snakemake.readthedocs.io/en/stable/](https://snakemake.readthedocs.io/en/stable/)

------
Joky
Make is fine for simple cases, but I'm working on a project that is based on
buildroot right now, and it is kind of a nightmare: make just does not provide
any good way _at this scale_ to keep track of what's going on and inspect /
understand what goes wrong. Especially in the context of a highly parallel
build with some dependencies are gonna get missing.

In general also all the implicit it has makes it hard to predict what can
happen. Again when you scale to support a project that would be 1) large and
2) wouldn't have a regular structure.

On another smaller scale: doing an incremental build of LLVM is _a lot_ faster
with Ninja compared to Make (crake-generated).

Make is great: just don't use it where it is not the best fit.

------
gtramont
Here's some tips I like to follow whenever writing Makefiles (I find them
joyful to write): [http://clarkgrubb.com/makefile-style-
guide](http://clarkgrubb.com/makefile-style-guide)

------
mschuster91
Please, don't ship your own Makefiles. Yes, autotools sucks - but there is one
thing that sucks more: no "make uninstall" target.

Good people do not ship software without a way to get rid of it, if needed.

------
rileytg
wow i've been feeling like not knowing make has been a major weakness of mine,
this article has finally tied all my learning together. i feel totally capable
of using make now. thank you.

~~~
sevensor
Make is great! Just remember to use tabs for indentation. Make is very picky
about that.

------
mauvehaus
Has anybody successfully used make to build java code? I realize there are any
number of other options (ant, maven, and gradle arguably being the most
popular).

In fact, I realize that the whole idea of using make is probably outright
foolish owing to the intertwined nature of the classpath (which expresses
runtime dependencies) and compile-time dependencies (which may not be
available in compiled form on the classpath) in Java. I'm merely curious if it
can be done.

~~~
agibsonccc
The problem with using make here is transitive dependency resolution. Many of
the new build systems have it "built in" now a days. Maybe make could wrap
something that does that? Either way...I wouldn't recommend it unless it
wrapped something that understood maven central.

------
zwischenzug
This is great, and needs saying.

Recently I wrote a similar blog about an alternative app pattern that uses
makefiles:

[https://zwischenzugs.wordpress.com/2017/08/07/a-non-cloud-
se...](https://zwischenzugs.wordpress.com/2017/08/07/a-non-cloud-serverless-
application-pattern-using-git-and-docker/)

~~~
majewsky
I guess that you're the owner of that blog, so I want to file a bug report:
There are ligatures in your code snippets, e.g. in "git diff", the "ff" is a
single character that's as wide as the "i" before it, which is really weird,
and also not what the terminal would do. So you might want to add this to your
CSS:

    
    
      pre { font-variant-ligatures: none }

------
fiatjaf
Makefiles are simple, but 99% of the existing Makefiles are computer-generated
incomprehensible blobs. I don't want that.

------
leastangle
I did not know people are afraid of Makefiles. Maybe a naïve question, but
what is so scary about make?

~~~
syncsynchalt
I think chungy is right: most people get their first experience with Makefiles
by trying to debug some automake monstrosity. When they write their own, they
assume they have to be just as complicated (or even worse, they'll copy/paste
from an autogenerated Makefile or use it as a base).

If I thought every Makefile had to be like that I'd write ./build.sh too.

------
user5994461
>>> Congratulations! You've learned 90% of what you need to know about

The next 90% will be to learn that Make breaks when having tabs and spaces in
the same file, and your developers all use slightly different editors that
will mix them up all the time.

~~~
vortico
Most editors that I've used switch to tabs mode when opening Makefile* files.
But yes, it's a weird restriction. I suppose it's a result of being one of the
first languages with syntactical indentation.

------
systemz
Instead of makefile I can recommend Taskfile
[https://hackernoon.com/introducing-the-
taskfile-5ddfe7ed83bd](https://hackernoon.com/introducing-the-
taskfile-5ddfe7ed83bd)

Simple to use without any magic.

------
quantos
I had written Non Recursive Makefile Boilerplate (nrmb) for C, which should
work in large projects with recursive directory structure. There is no need to
manually add source file names in makefile, it automaically do this. One
makefile compiles it all. Of course, it isn't perfect but it does the job and
you can modify it for your project. Here is the link

[https://github.com/quantos-unios/nrmb](https://github.com/quantos-unios/nrmb)

Have a look :)

------
knowsuchagency
Make is fine, but I think we have better tools nowadays to do the same things.

Even though it may not have been originally intended as such, I've found
Fabric
[http://docs.fabfile.org/en/1.13/tutorial.html](http://docs.fabfile.org/en/1.13/tutorial.html)
to be far far more powerful and intuitive as a means of creating CLI's (that
you can easily parametrize and test) around common tasks such as building
software.

------
bitwize
Or just use cmake and save yourself time, effort, and pain.

~~~
humanrebar
Yes. Among other things, a modern build system really needs a "help" command.

------
athenot
After using the various javascript build processes, I went back to good old
makefiles and the result is way simpler. I have a target to build the final
project with optimizations and a target to build a live-reload version of the
project, that watches for changes on disk and rebuilds the parts as needed
(thanks to watchify).

This works in my cases because I have browserify doing all the heavy lifting
with respect to dependency management.

------
ojosilva
Opinion poll. I'm writing a little automation language in YAML and I was
wondering if people prefer a dependency graph concept where tasks run parallel
by default, unless stated as dependency, or a sequential set of instructions
where tasks only run in parallel if explicitly "forked".

I'd say people would lean towards the former, but time and real world
experience has shown that sequential dominates everything else.

------
elnygren
I almost always roll a basic Makefile for even simple web projects. PHONY
commands like "make run" and "make test" in every project make context
switching a bit more easier.

While things like "npm start" are nice, not all projects are Node.js. In my
current startup we're gonna have standardised Makefiles in each project so its
easy to build, test, run, install any microservice locally :)

------
brian-armstrong
Using Cmake is so much nicer than make, and it's deeply cross-platform. Cmake
makes cross-compiling really easy, while with make you have to be careful and
preserve flags correctly. Much nicer to just include a cmake module that sets
up everything for you. Plus it can generate xcode and visual studio configs
for you. Doing make by hand just seems unncessary.

~~~
kyberias
I always felt that for small setups, makefiles are much more concise and easy
to understand. And more generic. Cmakefile.txt seems always really messy and
introduce concepts such as project and exes and dlls. But of course Cmake is
super-portable I know.

~~~
everheardofc
Except with make most small setups are at least 20-40 lines of nontrivial code
becaue you have to redo everything from scratch

~~~
slrz

      include std.mk
      
      TARGET=foo
      HFILES=bar.h
      OFILES=main.o foo.o baz.o
      
      include cmd.mk
    

This is what Makefiles tend to look like in some places.

------
baby
Other resources:

[https://gist.github.com/isaacs/62a2d1825d04437c6f08](https://gist.github.com/isaacs/62a2d1825d04437c6f08)

[https://learnxinyminutes.com/docs/make/](https://learnxinyminutes.com/docs/make/)

------
Crontab
I haven't ever had to mess with Makefiles, as I don't program anything other
the basic shell scripts, but I do remember reading articles in the past that
indicated Makefiles can be used for more than just programming.

For example: using Makefiles to automate static webpage creation and image
file conversion.

------
erAck
Take a look at the LibreOffice gbuild system, completely written in GNU make
"language". And then come back saying you're not afraid of make ;-)

Still, it probably would be much harder, if possible at all (doubted for
most), to achieve the same with any other tool mentioned here.

------
danso
Worth pointing to Mike Bostock's essay, _Why Use Make_ :
[https://bost.ocks.org/mike/make/](https://bost.ocks.org/mike/make/)

------
anilakar
Who needs makefiles when you have a build system, you might ask.

The truth: go see the configuration of a Jenkins project, and there's a high
chance that one of the lines there still says "make".

------
JelteF
> Add an @ sign to suppress output of the command that is executed.

This is the exact opposite. It supresses echoing the command that is being
executed. It's output is still shown like normal.

------
analognoise
Delphi/Pascal haven't used make files for...I don't know how long.

Shout out to FreePascal/Lazarus yet again!

------
mycat
So what is npm install of C/C++ ?

------
devdoomari
so I guess makefile is somewhat like gulp JS... but can I split a makefile
into multiple files?

~~~
cbcoutinho
Yes, you can split it up even into separate directories

------
callumlocke
I'm not scared of Makefiles, I just find them painful to work with.

------
leksak
I like Tup

------
vacri
One very important thing missing from this primer is that Make targets are not
'mini-scripts', even though they look like it. Every line is 'its own script'
in its own subshell - state is not passed between lines.

Make is scary because it's arcane and contains a lot of gotcha rules. I
avoided learning Make for a long time. I'm glad I did learn it in the end,
though I wouldn't call myself properly fluent in it yet. But there are a ton
of gotchas and historical artifacts in Make.

~~~
makapuf
> Every line is 'its own script' in its own subshell - state is not passed
> between lines. Unless you use the ONESHELL option.

------
whipoodle
I just always hated that you're supposed to write ".PHONY". The first time I
forgot to do that and it built a random file named after a build step I'd
scrap the whole thing.

------
eighthnate
Is that a rhetorical question? Is there anyone who is afraid of makefiles?
Makefiles exist to make your life easier.

