Building from Source
Building from Source#
Concepts#
Why Build from Source?#
Occasionally you need to compile software yourself:
- The package is not available in any repository, Snap, or Flatpak
- You need a newer version than what is available
- You need to enable specific compile-time options
- You are developing or patching the software
- You are autistic
Building from source means downloading the program’s source code and compiling it into a binary executable on your machine.
Prerequisites: Build Tools#
Before compiling anything, install the essential build tools:
sudo apt install -y build-essential
This installs:
gcc/g++— the C/C++ compilersmake— the build automation toollibc-dev— C library headersdpkg-dev— Debian package development tools
Many programs also need additional libraries. These are called build dependencies and vary by program.
The Standard Build Process#
Most C/C++ projects follow the same three-step process:
# 1. Configure — check your system, detect libraries, set options
./configure
# 2. Compile — build the binaries from source code
make
# 3. Install — copy binaries to system directories
sudo make install
Step 1: Download and Extract#
Source code is usually distributed as a compressed archive (tarball):
# Download
wget https://example.com/program-1.2.3.tar.gz
# Extract
tar -xzf program-1.2.3.tar.gz
# Enter the source directory
cd program-1.2.3/
Common archive formats:
| Extension | Extract Command |
|---|---|
.tar.gz or .tgz |
tar -xzf file.tar.gz |
.tar.bz2 |
tar -xjf file.tar.bz2 |
.tar.xz |
tar -xJf file.tar.xz |
.zip |
unzip file.zip |
Step 2: Configure#
./configure
The configure script:
- Checks that your system has the required compilers and libraries
- Detects system-specific settings (architecture, paths)
- Generates a
Makefiletailored to your system
Common options:
./configure --prefix=/usr/local # install to /usr/local (default)
./configure --prefix=/opt/myprogram # install to a custom location
./configure --enable-feature # enable optional feature
./configure --disable-feature # disable optional feature
./configure --with-library=/path # specify library location
./configure --help # show all options
If configure fails, it will tell you what is missing. Common fix:
# Missing library "libfoo"? Install its development package:
sudo apt install libfoo-dev
# Then rerun ./configure
Development packages (ending in -dev) contain header files and libraries needed for compilation but not for running the program.
Step 3: Compile#
make
This compiles the source code. It can take a while for large projects. To speed it up, use multiple CPU cores:
make -j$(nproc)
# nproc returns the number of CPU cores
Step 4: Install#
sudo make install
This copies the compiled files to the system (default: /usr/local/bin, /usr/local/lib, /usr/local/share).
The /usr/local Prefix#
By default, make install puts files in /usr/local/:
| Directory | Content |
|---|---|
/usr/local/bin/ |
Executable programs |
/usr/local/lib/ |
Libraries |
/usr/local/share/ |
Documentation, data files |
/usr/local/include/ |
Header files |
/usr/local/etc/ |
Configuration files |
This keeps manually installed software separate from APT-managed software (which lives in /usr/). This is intentional — it prevents conflicts.
The Problem with make install#
make install copies files directly to your system with no tracking. The package manager (apt/dpkg) does not know about these files. This means:
dpkg -S /usr/local/bin/myprogramwill not find it- There is no clean way to uninstall (some Makefiles support
sudo make uninstall, many do not) - APT cannot track or update it
checkinstall — A Better Approach#
checkinstall wraps make install and creates a .deb package that dpkg tracks:
sudo apt install -y checkinstall
# Instead of: sudo make install
# Use:
sudo checkinstall --pkgname=myprogram --pkgversion=1.2.3 --default
Benefits:
- Creates a
.debpackage and installs it viadpkg - You can uninstall with
sudo apt remove myprogram dpkg -L myprogramshows all installed files
CMake-Based Projects#
Some projects use CMake instead of ./configure:
sudo apt install -y cmake
mkdir build && cd build
cmake ..
make -j$(nproc)
sudo make install
CMake is more modern and supports out-of-source builds (keeping the build files separate from the source).
Other Build Systems#
| Build System | Typical Commands | Language |
|---|---|---|
make / configure |
./configure && make && sudo make install |
C/C++ |
| CMake | cmake .. && make && sudo make install |
C/C++ |
| Meson | meson setup build && ninja -C build && sudo ninja -C build install |
C/C++/Rust |
| Python | pip install . or pip install package |
Python |
| Rust | cargo build --release |
Rust |
| Go | go build |
Go |
Cleaning Up Build Artifacts#
# Remove compiled objects (in the source directory)
make clean
# Remove everything configure generated
make distclean
# Remove the source directory entirely when done
cd ..
rm -rf program-1.2.3/
Lab#
Exercise 1: Install Build Tools#
# Install the essential build toolchain
sudo apt install -y build-essential
# Verify
gcc --version
make --version
Exercise 2: Build a Simple C Program#
Before building a real project, let’s compile a C program from scratch to understand the process:
mkdir -p ~/lab/build
cd ~/lab/build
# Write a simple C program
cat > hello.c << 'EOF'
#include <stdio.h>
int main() {
printf("Hello from a compiled C program!\n");
printf("Compiled on this machine with GCC.\n");
return 0;
}
EOF
# Compile it
gcc -o hello hello.c
# Run it
./hello
# See the file type
file hello
# ELF 64-bit LSB pie executable...
Exercise 3: Build with a Makefile#
cd ~/lab/build
# Create a Makefile
cat > Makefile << 'EOF'
CC = gcc
CFLAGS = -Wall -O2
PREFIX = /usr/local
hello: hello.c
$(CC) $(CFLAGS) -o hello hello.c
install: hello
install -m 755 hello $(PREFIX)/bin/hello
uninstall:
rm -f $(PREFIX)/bin/hello
clean:
rm -f hello
EOF
# Build using make
make
# Run
./hello
# Clean
make clean
ls hello # should be gone
# Rebuild
make
Exercise 4: Build a Real Project (htop from Source)#
cd ~/lab/build
# Install build dependencies for htop
sudo apt install -y libncursesw5-dev autoconf automake
# Download htop source
apt source htop 2>/dev/null || (
wget https://github.com/htop-dev/htop/releases/download/3.3.0/htop-3.3.0.tar.xz
tar -xJf htop-3.3.0.tar.xz
cd htop-3.3.0
)
# If using apt source:
cd htop-*/
# Configure
./autogen.sh 2>/dev/null # some projects need this first
./configure --prefix=/usr/local
# Compile (using all CPU cores)
make -j$(nproc)
# Test the binary (without installing)
./htop
# Press q to quit
# Do NOT install system-wide for this exercise
# In a real scenario: sudo make install
# Or better: sudo checkinstall
Exercise 5: Use checkinstall (Optional)#
# Install checkinstall
sudo apt install -y checkinstall
# In the htop source directory (from Exercise 4):
# Instead of sudo make install:
sudo checkinstall --pkgname=htop-custom --pkgversion=3.3.0 --default
# Now it's tracked by dpkg
dpkg -l htop-custom
dpkg -L htop-custom
# Uninstall cleanly
sudo apt remove htop-custom
Exercise 6: Clean Up#
cd ~
rm -rf ~/lab/build
Review#
1. What are the three standard steps to build software from source?
./configure (detect system settings and dependencies), make (compile the source code), sudo make install (install the compiled files to the system).
2. What does the `./configure` script do?
It checks your system for required compilers, libraries, and headers. It detects architecture-specific settings and generates a Makefile customized for your system. If it fails, it tells you which dependencies are missing.
3. What is a `-dev` package and when do you need it?
A -dev (development) package contains header files (.h) and library files needed at compile time. You need them when building software from source — they are not needed for running pre-built packages.
4. Why does `make install` default to `/usr/local/`?
To keep manually compiled software separate from packages managed by apt/dpkg (which install to /usr/). This prevents conflicts between the two.
5. What is the problem with `sudo make install`?
It installs files directly without any tracking. The package manager does not know about them, making it hard to uninstall or update. There is no reliable way to know which files belong to the program.
6. How does `checkinstall` improve on `make install`?
checkinstall creates a .deb package from the installation, registers it with dpkg, and then installs it. This means you can uninstall cleanly with apt remove, and dpkg -L shows all installed files.
7. How do you speed up compilation on a multi-core system?
Use make -j$(nproc). The -j flag tells make to run multiple jobs in parallel. $(nproc) expands to the number of CPU cores available.
Previous: Snap, Flatpak, and AppImage | Next: nano