45.5 make Targets: all, install, clean, uninstall
Right, so you’ve run ./configure (or maybe cmake ., you rebel) and it didn’t explode. Congratulations. Now you’re staring at a Makefile and the only thing you know to type is make. That’ll work, about 70% of the time. But the other 30% of the time, you’ll be left wondering why the software installed somewhere deeply unhelpful, or why your disk space is mysteriously vanishing. Let’s fix that by understanding what you’re actually telling make to do.
When you just type make, you’re invoking the default target. By ancient, sacred convention, that default target is almost always named all. Its job is to compile all the necessary binaries, libraries, and other artifacts from the source code. It builds the whole thing, but it doesn’t put anything in its final resting place on your system. Think of it as assembling all the parts of a bookshelf in your garage.
# This is the kind of thing you'd see in a Makefile.
# The 'all' target lists what it needs to build (the dependencies).
all: myapp mylib.so
myapp: main.o utils.o
$(CC) -o myapp main.o utils.o
mylib.so: libstuff.o
$(CC) -shared -o mylib.so libstuff.o
# ... and so on with rules for the .o files
So you run make (which is make all) and it diligently compiles everything. Your project directory is now full of .o files, binaries, and that warm, fuzzy feeling of productivity.
install: The Point of No Return
Once you’ve built everything, you need to, you know, actually use the software. That’s what make install is for. This target takes all the lovely binaries you just built and copies them to their proper system-wide locations—like /usr/local/bin for executables and /usr/local/lib for libraries. This is the part where you move that assembled bookshelf from your garage into your living room.
The crucial thing to understand, and the part everyone messes up at least once, is that this step almost always requires sudo. Why? Because you’re writing to directories like /usr/local that are owned by root. If you just run make install without elevated privileges, it will fail spectacularly with a bunch of “Permission denied” errors.
# The classic rookie move (and its inevitable result)
$ make install
install: cannot create regular file '/usr/local/bin/myapp': Permission denied
make: *** [Makefile:42: install] Error 1
# The correct incantation
$ sudo make install
Password:
Installing myapp to /usr/local/bin...
And just like that, the software is on your system. But notice we used sudo only for the install step, not the all step. You should never run make all or just make as root. There’s no need, and it’s a security risk—you’d be executing arbitrary code from the internet as the superuser. Build as a user, install as root.
clean: The “I Want a Do-Over” Button
So you built it, but maybe you changed a compiler flag in the configure script, or you just want to free up a few hundred megabytes of disk space eaten by object files. Enter make clean. This target’s job is to obliterate everything that the all target created. It removes the .o files, the binaries, the temporary gunk—leaving you with a pristine source directory, just as it was before you ran make.
# Look at all this garbage
$ ls
main.c main.o myapp utils.c utils.o
# Execute order 66
$ make clean
rm -f *.o myapp
# Ah, much better
$ ls
main.c utils.c
It’s a fantastic best practice to run this before rebuilding if you’ve changed anything significant. It ensures you get a truly fresh build. Some projects have a distclean or mrproper (a delightful Linux kernel-ism) target that’s even more aggressive, wiping out files generated by ./configure too.
uninstall: The Designer’s Questionable Choice
Here’s where we get to the part that makes me sigh. You’d think make uninstall would be a standard, robust feature, right? It should meticulously remove every file that make install put on your system. In a perfect world, yes. In our world, it’s a crapshoot.
The problem is this: the uninstall target is only as good as the Makefile it’s written in. The developer has to manually write a rule that lists every single file to be deleted. If they forget to add a new config file to the list when they add it to the install target, make uninstall will leave it behind. Many projects, frankly, don’t bother to maintain this target properly.
# It might work perfectly...
$ sudo make uninstall
Removing /usr/local/bin/myapp
Removing /usr/local/share/man/man1/myapp.1.gz
# Or it might be a ghost, absent from the Makefile entirely
$ sudo make uninstall
make: *** No rule to make target 'uninstall'. Stop.
Because of this unreliability, my best practice is to always use a package manager (apt, brew, pacman) for software from your distro’s repositories. For source installs, the true expert move is to build a package for your system (a .deb, .rpm, or brew formula) and install that. Then you have a perfect manifest of what was installed and can uninstall it cleanly. But that’s a chapter for another day. For now, test make uninstall with a healthy dose of skepticism and follow up with a manual ls in the install directories.