Skip to content
ALB Wiki

Diff — Building from Source

Comparing rev 1 → rev 2

{{infobox
| title = Building from Source
| chapter = Linux Handbook, chapter 16 — tarballs, configure/make/install, build systems, patching
| core | The ./configure && make && make install liturgy — and what each step does
| systems | autotools · cmake · meson/ninja · plain makefiles
| skills | configure flags · DESTDIR/prefix · patches · out-of-tree builds
| next | Inside a Linux system — what a built system consists of
}}
**Building from source is the original Linux distribution mechanism** — before package managers, you got software as a tarball, configured it for your system, compiled it, and installed it. Every LFS chapter is this ritual repeated; every BLFS package, every `git clone` project, every ancient tarball in an archive.org listing runs the same liturgy. This chapter teaches the liturgy and the build systems that automate it — the universal grammar of "source → installed".
## The liturgy — three sacred steps
```sh
tar -xf package-1.0.tar.xz
cd package-1.0
./configure --prefix=/usr      # adapt the build to THIS system
make -j$(nproc)                # compile
make check                     # optional testsuite
sudo make install              # install
```
**`configure`** probes the system — compiler present? headers? libraries? — and generates the Makefiles. **`make`** builds per the Makefile. **`make install`** copies binaries, headers, libraries, docs into `--prefix`. Three steps; a hundred years of software follows them.
## configure — the system's interrogation
```sh
./configure --help             # every package's custom options
./configure --prefix=/usr \    # WHERE it installs — the big one
            --sysconfdir=/etc \# config location
            --localstatedir=/var \
            --disable-static \  # don't ship .a libraries
            --enable-shared \
            --with-foo --without-bar   # feature toggles
```
`--prefix` decides *where everything lands*: `/usr` (system), `/usr/local` (local additions, the classic default), `/opt/package` (self-contained). The LFS prefix dance — `/tools`, `/usr`, `DESTDIR` — is this flag doing deep work.
**`make DESTDIR=$STAGING install`** — the packager's trick: install *believing* prefix is `/usr` but landing in `$STAGING/usr` — how packages get built for one root and assembled for another.
## The other build systems
| System | Liturgy | Found in |
| --- | --- | --- |
| **autotools** | `./configure && make && make install` | The classic — most tarballs |
| **cmake** | `cmake -B build -DCMAKE_INSTALL_PREFIX=/usr && cmake --build build` | KDE, modern C++ |
| **meson** | `meson setup build --prefix=/usr && ninja -C build` | GNOME, systemd-era |
| **plain make** | `make && make install` | Minimal projects |
| **cargo/go/npm** | the language's own | Rust, Go, Node projects |
cmake's `-B build` creates an **out-of-tree build dir** — the modern way; source tree stays pristine. Meson+ninja is the fastest. Autotools remains the most common on old-school tarballs — and the deepest to understand: `configure.ac` → `autoconf` → `configure` → `Makefile.in` → `make`.
## What configure actually detects
```sh
./configure ... 2>&1 | tee config.log     # keep the log — failures live there
tail -20 config.log                        # when it fails, the answer's here
```
`configure` compiles test programs — "checking for gcc... yes" means it ran a hello-world; "checking for foo.h... no" means a test compile failed. The fix is almost always a **missing -dev/-devel package**: `libfoo-dev` supplies the headers a build needs that the runtime `libfoo` doesn't. Debian's `-dev`, Red Hat's `-devel`, Slackware ships headers always — the host difference.
## Patches — the surgical fixes
```sh
patch -p1 < fix.patch          # apply — -p1 strips the a/ b/ prefixes
patch -R -p1 < fix.patch       # reverse — the undo
diff -u old.c new.c > my.patch # create one
diff -Naur old/ new/ > big.patch  # whole trees
```
LFS's build applies dozens of small patches — FHS placements, security fixes, cross-build fixes — each a surgical diff that upstream hasn't folded in yet.
+
## The configure internals — what it actually runs
+
+
```sh
+
./configure --help | less               # every option the package ships
+
cat config.log                          # the probe transcript — failures live here
+
less config.status                      # the record of what was detected
+
cat config.h                            # the generated feature macros
+
```
+
+
`configure` is a generated shell script — thousands of small test-compiles: "checking for gcc... yes" means it ran a hello-world; "checking foo.h usability... no" means a test compile failed and the config.log shows the *compiler's actual error*. `config.status --recheck` reruns; `config.status` regenerates the Makefiles.
+
+
## The DESTDIR/prefix grammar — the packager's two knobs
+
+
```sh
+
./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var
+
make
+
make install                        # writes to /usr, /etc — REAL
+
# vs:
+
make DESTDIR=/tmp/stage install     # writes to /tmp/stage/usr — STAGED
+
```
+
+
`--prefix` = where the software *believes* it lives (baked into paths); `DESTDIR` = where it *actually lands* (the packager's staging). LFS uses both — `--prefix=/usr` + `DESTDIR=$LFS` = "believe /usr, land in $LFS/usr" — exactly where the chroot needs it. The `--sysconfdir`/`--localstatedir` pair move `/etc` and `/var` away from `/usr/etc`/`/usr/var` (the classic wrong defaults).
+
+
## The meson + cmake grammar
+
+
```sh
+
# meson — the modern fast one
+
meson setup build --prefix=/usr --buildtype=release -Dfeature=enabled
+
ninja -C build
+
ninja -C build test
+
DESTDIR=/tmp/stage ninja -C build install
+
meson configure build -Doption=value   # reconfigure
+
+
# cmake — the heavyweight
+
cmake -B build -DCMAKE_BUILD_TYPE=Release \
+
      -DCMAKE_INSTALL_PREFIX=/usr \
+
      -DBUILD_TESTING=OFF -DWITH_X=ON
+
cmake --build build -j$(nproc)
+
cmake --install build --prefix /tmp/stage/usr
+
ctest --test-dir build                 # the tests
+
```
+
+
Both honor `DESTDIR`; meson's `-D` toggles the package's own options (`meson_options.txt` lists them); cmake's `-DWITH_*`/`-DBUILD_*` the same.
+
## Dependencies — the real work
The tarball itself is the easy half; the dependencies are the job:
```sh
./configure   # missing dep = the error is your answer
apt build-dep package        # Debian's dep-fetcher — installs every build-dep
ldd binary | grep 'not found' # runtime gaps
pkg-config --modversion foo  # is it visible to builds?
```
`apt build-dep`, `dnf builddep`, Slackware's "install everything" — each distro's answer to "install the deps this needs". On LFS, dependency management *is* the book's ordering — the build sequence is topological.
## Version sanity and the release hygiene
```sh
make V=1                      # verbose builds — the actual commands
./configure --version         # sometimes tells you what's expected
cat README INSTALL NEWS       # the three files always worth a skim
```
And the trust layer: `gpg --verify package.tar.xz.sig package.tar.xz`, `md5sum`/sha256 checks — the sources are the system's DNA; verify before compiling. This handbook's LFS chapters verify checksums before the first configure — chapter 2 makes it law.
## The install aftermath
```sh
make install DESTDIR=$D       # staged install — packager's form
make install                  # real install — needs root for /usr
ldconfig                      # refresh the .so registry after new libs
command -v newtool            # did it land in PATH?
make uninstall                # often supported — often doesn't exist
```
`checkinstall`/`make install` into `/usr/local` keeps hand-built software quarantined from the package manager's `/usr` — the classic hygiene.
## Where source builds go wrong
| Symptom | Cause |
| --- | --- |
| `configure: foo.h not found` | Missing `-dev`/`devel` package |
| `config.log` cryptic failure | Read the last 20 lines — always |
| Installed but `command not found` | `--prefix` landed outside PATH — `/usr/local/bin`? |
| `libtool` version fights | The tarball's ltmain vs the host's — `autoreconf -fi` |
| Build works, install fails on perms | `sudo make install` — or DESTDIR staging |
| Old build residue breaks rebuild | `make distclean` — or re-extract fresh |
| `make` fails only parallel | Racy Makefile — `-j1` |
| .la files breaking downstream | `rm` the `.la` files — libtool's legacy hazard |
## Exercises
1. Build a real tarball — `tar`, `configure`, `make`, DESTDIR-staged install into `/tmp/stage`.
2. `tee` a configure log; find the exact probe that failed for a missing lib.
3. `apt build-dep` or `dnf builddep` something — see the dep list as a set.
4. Write and apply a patch — `diff -u`, `patch -p1`, then `patch -R` it back.
5. Compare a cmake and an autotools build side by side — the different liturgies.
## See also
- [[C toolchain]] · [[LFS Cross-Toolchain]] · [[LFS System Packages]] · [[Linux]]
[[Category:Linux]]
[[Category:Handbook]]
[[Category:Compilers]]