| | {{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]] |
| | |