Building software from source

Listen to this lesson

Episode 12 · 53:06

This episode is a study companion for CompTIA Linux+ XK0-006 and is not produced by or endorsed by CompTIA.

Objective 2.4 · Services and User Management · 20% of the exam

Objective 2.4 lists this. Under Installation, update, and removal the objectives name two methods side by side: Repository and Source. Package management covers the first; this lesson covers the second. The objectives give it a single word, so expect it to be examined lightly and by recognition -- what ./configure does, why make install is the step that touches your system, where a source build belongs on disk -- rather than by having you compile anything under time pressure. It earns its place twice over, because the first package you need that nobody has built for you is a job problem long before it is an exam one.

Why this matters

Package managers cover the overwhelming majority of software, and you should use them whenever you can. But eventually you will need a version newer than your distribution ships, a program nobody has packaged, or software with a compile-time option turned off in the packaged build.

The classic incantation is ./configure && make && make install. Typing it is easy. Knowing what each step does — and why the last one should almost never go into /usr — is what stops you making a mess that no package manager can clean up.

The lesson

When a package is not in a repository

Before reaching for source, exhaust the cheaper options in order:

  1. The distribution's repositoriesdnf search / apt search
  2. A backports or extras repository — EPEL, Debian backports
  3. An official upstream repository — most large projects publish one
  4. A sandboxed build — Flatpak, Snap, AppImage
  5. A container — often the right answer for a server application

Building from source is the last option, not the first, because you take on responsibility for updating it forever. Nothing will tell you a security fix has been released; nothing will rebuild it when a library changes underneath. That maintenance burden is the real cost, and it is easy to underestimate.

Source tarballs

Source is distributed as a compressed archive — a tarball.

wget https://example.org/tool-2.4.1.tar.gz
wget https://example.org/tool-2.4.1.tar.gz.asc     # the signature
wget https://example.org/tool-2.4.1.tar.gz.sha256  # or a checksum

.tar.gz (or .tgz) is gzip-compressed; .tar.bz2 and .tar.xz use bzip2 and xz, with xz compressing hardest.

Verifying what you downloaded

Do this before extracting, not after. You are about to run a stranger's build script as yourself, and shortly afterwards as root.

Checksum — proves the file arrived intact. It does not prove who made it, since anyone who replaced the tarball could replace the checksum alongside it.

sha256sum -c tool-2.4.1.tar.gz.sha256

GPG signature — proves it was signed by a key you trust. This is the real check.

gpg --recv-keys 0xDEADBEEF          # fetch the project's key
gpg --verify tool-2.4.1.tar.gz.asc tool-2.4.1.tar.gz

A "Good signature" line still deserves a moment's thought: good from whom? Verify the key fingerprint against the project's website over HTTPS the first time you import it. After that, the signature genuinely means something.

This is precisely the protection your package manager gives you automatically and that you lose the moment you step outside it.

Extracting

tar -xzf tool-2.4.1.tar.gz    # gzip
tar -xjf tool-2.4.1.tar.bz2   # bzip2
tar -xJf tool-2.4.1.tar.xz    # xz
tar -xf  tool-2.4.1.tar.gz    # modern tar detects the compression
tar -tzf tool-2.4.1.tar.gz | head   # LIST before extracting
cd tool-2.4.1

-t lists rather than extracts, and it is worth the two seconds: a badly packaged tarball that spills fifty files into your current directory instead of its own subdirectory is a genuine nuisance.

Mnemonic for the flags: extract, zip, file.

Reading INSTALL and README

Every well-behaved project ships them, and skipping them is why builds fail.

ls
cat README
cat INSTALL

README describes what the software is and any unusual requirements. INSTALL gives the build steps and, crucially, the configure options — the flags that turn features on and off. That list is often the only reason you are building from source at all.

Build dependencies

You need a compiler and the development versions of every library the code uses. Development packages carry the header files, and they are separate packages from the runtime library — libssl3 versus libssl-dev.

# RPM family
dnf groupinstall "Development Tools"
dnf install openssl-devel zlib-devel

# Debian family
apt install build-essential
apt install libssl-dev zlib1g-dev
apt build-dep nginx          # everything needed to build the packaged version

apt build-dep is the shortcut worth knowing. If a package exists in the repositories and you are building a newer version of the same thing, it installs the entire build dependency set in one command.

Missing headers produce the most common and most confusing build failure: configure: error: OpenSSL not found when OpenSSL is plainly installed. It means the -devel/-dev package is missing, not the library.

./configure

./configure --help                     # every available option — read this
./configure                            # defaults: installs to /usr/local
./configure --prefix=/opt/tool-2.4.1   # somewhere of your own
./configure --enable-ssl --disable-docs

configure is a script that inspects your system — which compiler, which libraries, which headers — and generates a Makefile tailored to it. It fails loudly when something is missing, and the last twenty lines of config.log say exactly what.

--prefix is the most important flag on the line. It decides where everything lands.

make and make install

make                    # compile. This is the slow step
make -j$(nproc)         # use every core
make check              # run the project's test suite, if it has one
sudo make install       # copy the built files into --prefix

make compiles in your build directory and touches nothing else, so it is safe to run as yourself. make install copies the results into the system, which is why it needs privilege.

Install to /usr/local, not /usr

The filesystem hierarchy reserves /usr/local for software installed by the local administrator. Your distribution's package manager never writes there, so nothing you build can collide with a packaged file.

/usr        managed by the package manager  — do not install here
/usr/local  yours                            — the default --prefix, and correct
/opt        self-contained third-party applications, often versioned

Install into /usr and you overwrite files a package owns. The package manager does not know, so the next update either reverts your build without warning or fails on a conflict. Both are unpleasant, and neither is easy to diagnose months later.

Using --prefix=/opt/tool-2.4.1 goes further: everything for that version sits in one directory you can delete outright, and you can install several versions side by side.

Why not to build as root

Run ./configure and make as an ordinary user. Only make install needs sudo, and only because it writes outside your home directory.

The reason is simple: a build runs thousands of lines of the project's own scripts. A mistake in one of them — a bad path, an over-enthusiastic clean target — is a nuisance under your account and a catastrophe as root. There is a genuine, well-known class of bug where a build script deletes a directory it should not, and the only thing standing between that and your system is not having granted it permission.

The habit costs nothing and occasionally saves everything.

Removing a source install

This is where the maintenance burden becomes concrete: there is no package database, so nothing knows what was installed.

# From the original build directory, if you still have it:
sudo make uninstall        # only if the project provides the target — many do not

# Otherwise, if you used a dedicated prefix:
sudo rm -rf /opt/tool-2.4.1

Keep the build directory until you are sure you will not need to uninstall. Better, use a versioned --prefix from the start so removal is one rm -rf of a directory that contains nothing else.

The tool checkinstall is worth knowing: it wraps make install and produces a real .deb or .rpm, so your package manager tracks the result and can remove it properly. It does not work for every project, but when it works it turns a permanent liability into an ordinary package.

What to take away

  • Build from source last, not first, and understand that you own its updates forever.
  • Verify the signature, not just the checksum. A checksum alongside a tampered tarball proves nothing.
  • Missing -dev/-devel headers cause the confusing "library not found" error when the library is installed.
  • ./configure --help lists the options, and those options are usually the reason you are here.
  • Install into /usr/local or a versioned /opt prefix. Never /usr.
  • Build as yourself; use sudo only for make install.
  • Plan the uninstall before you install, because nothing is tracking it.

Practise what you just read

1. You have downloaded a tarball together with its SHA256 checksum from the same mirror. What does verifying the checksum prove?

Select one

  1. Only that the download was not corrupted in transit
  2. That the archive was produced by the project's maintainers
  3. That the archive matches the version in the distribution's repository
  4. That the archive contains no malicious code
Show answer

A. A checksum detects corruption, nothing more. Anyone who could replace the tarball on a mirror could replace the checksum file beside it, so the two agreeing proves only that they agree. Verifying a GPG signature over the checksum file against the project's published key is what establishes origin -- and that key must come from somewhere other than the same mirror.

2. configure fails with "openssl development headers not found", although openssl is installed and working. What is missing?

Select one

  1. The PKG_CONFIG_PATH variable, which must point at /usr/bin
  2. A symbolic link from /usr/lib to /usr/local/lib
  3. The openssl binary's execute permission
  4. The openssl-devel or libssl-dev package containing the header files
Show answer

D. Distributions split libraries into a runtime package and a development package. The runtime holds the shared object that programs load; the -devel or -dev package holds the header files and the .pc metadata that compiling against it requires. This produces the reliably confusing message that a library is not found when it is plainly installed and in use.

3. Into which prefix should locally compiled software normally be installed?

Select one

  1. /bin, alongside the core system commands
  2. /usr/local, or a versioned directory under /opt
  3. /usr, so it is found on the default PATH
  4. The user's home directory, with a symlink into /usr/bin
Show answer

B. The Filesystem Hierarchy Standard reserves /usr for the package manager and /usr/local for locally built software, precisely so the two cannot collide. Installing into /usr means a package update can overwrite your build, or your build can overwrite a packaged file the database still believes is intact. A versioned /opt prefix is the other good answer, because it makes removal trivial.

5 more questions on this objective are part of the full course.

Practise the full question bank in the exam simulator

Hands-on labs

All hands-on labs