How to Compile Code in Linux
How to Compile Code in Linux Compiling code in Linux is a foundational skill for developers, system administrators, and open-source contributors. Unlike Windows or macOS, where many applications come as pre-built binaries, Linux often requires users to compile software from source code to ensure compatibility, optimize performance, or access the latest features not yet packaged by distribution mai
How to Compile Code in Linux
Compiling code in Linux is a foundational skill for developers, system administrators, and open-source contributors. Unlike Windows or macOS, where many applications come as pre-built binaries, Linux often requires users to compile software from source code to ensure compatibility, optimize performance, or access the latest features not yet packaged by distribution maintainers. This process transforms human-readable source code—written in languages like C, C++, or Rust—into machine-executable binaries that run directly on the operating system.
Understanding how to compile code in Linux empowers you to take full control of your software environment. Whether you're installing a niche utility, customizing a kernel module, or contributing to open-source projects, the ability to compile from source is indispensable. It also deepens your understanding of how software is built, linked, and executed—skills that enhance debugging, optimization, and security analysis.
This guide provides a comprehensive, step-by-step walkthrough of compiling code in Linux, covering everything from installing essential tools to troubleshooting common errors. You’ll learn best practices, explore real-world examples, and gain access to essential resources that will make you proficient in source compilation—no matter your experience level.
Step-by-Step Guide
Prerequisites: Setting Up Your Linux Environment
Before you begin compiling code, ensure your Linux system is properly configured. Most modern Linux distributions come with package managers that simplify tool installation. The first step is installing the essential build tools.
On Debian-based systems (Ubuntu, Linux Mint, etc.), run:
sudo apt update && sudo apt install build-essential
On Red Hat-based systems (Fedora, CentOS, RHEL), use:
sudo dnf groupinstall "Development Tools"
Or on older RHEL/CentOS versions:
sudo yum groupinstall "Development Tools"
On Arch Linux and derivatives:
sudo pacman -S base-devel
The build-essential package (or equivalent) includes:
- gcc – The GNU Compiler Collection for C and C++
- g++ – The C++ compiler
- make – A build automation tool
- libc6-dev – C library headers
- binutils – Essential binary utilities like ld (linker) and as (assembler)
Verify installation by checking the versions:
gcc --version
g++ --version
make --version
If these commands return version numbers, your system is ready. If not, revisit the installation steps or consult your distribution’s documentation.
Obtaining Source Code
Source code is typically distributed as compressed archives (.tar.gz, .tar.bz2, .zip) or via version control systems like Git. The most common sources are:
- Official project websites (e.g., nginx.org, apache.org)
- GitHub, GitLab, or Bitbucket repositories
- Package mirrors or FTP servers
For this guide, we’ll use the htop process viewer as a practical example. Download the latest source from its official GitHub releases page:
wget https://github.com/hishamhm/htop/archive/refs/tags/3.3.0.tar.gz
Extract the archive using:
tar -xzf 3.3.0.tar.gz
This creates a directory named htop-3.3.0. Navigate into it:
cd htop-3.3.0
Always inspect the contents of the extracted directory. Look for files like README, INSTALL, or configure. These files contain critical instructions for building the software.
Running the Configuration Script
Most open-source software uses the GNU Autotools build system, which relies on a script called configure. This script detects your system’s environment—available libraries, compiler features, paths—and generates a customized Makefile.
Run the configuration script:
./configure
If the script runs successfully, you’ll see output listing detected components. If it fails, the error message will indicate a missing dependency. Common errors include:
- configure: error: no acceptable C compiler found → gcc is not installed
- configure: error: libncurses not found → Missing development library
To resolve missing dependencies, search for packages using your package manager. For example, on Ubuntu:
apt search ncurses-dev
Then install the development package:
sudo apt install libncurses-dev
After installing missing dependencies, rerun ./configure.
For advanced users, the configure script supports options to customize the build. Common flags include:
- --prefix=/usr/local – Sets the installation directory (default is /usr/local)
- --enable-debug – Enables debug symbols for troubleshooting
- --disable-ipv6 – Disables optional features
Example with custom prefix:
./configure --prefix=/opt/htop
This installs htop into /opt/htop instead of the default location, useful for keeping custom builds isolated.
Building the Code with Make
Once configuration completes successfully, run the make command:
make
This step compiles the source code into object files and links them into a final executable. The process may take seconds or several minutes, depending on the project size and your system’s CPU and disk speed.
During compilation, you’ll see output showing which files are being processed. Each line typically begins with a compiler command like:
gcc -c -o htop.o htop.c
These lines indicate that the C compiler is translating source files (.c) into object files (.o). After all object files are compiled, the linker (ld) combines them into a single binary.
If errors occur during make, they are usually due to:
- Missing header files (install -dev packages)
- Incorrect compiler flags
- Version incompatibilities between libraries and source code
Read the error message carefully. For example:
error: ‘struct termios’ has no member named ‘c_line’
This suggests a kernel or library version mismatch. Search the error message online or check the project’s issue tracker for known fixes.
Installing the Compiled Program
After successful compilation, the executable is created in the build directory but not yet installed system-wide. To install it, run:
sudo make install
This command copies the binary, configuration files, and documentation to the directories specified during configuration (e.g., /usr/local/bin, /usr/local/share).
Verify the installation:
which htop
Should return:
/usr/local/bin/htop
Test the program:
htop
If it launches successfully, you’ve compiled and installed software from source!
Uninstalling Compiled Software
Unlike package managers, make install does not automatically track installed files. To uninstall, navigate back to the source directory and run:
sudo make uninstall
However, this only works if the project’s Makefile includes an uninstall target—which not all do. If make uninstall fails, you’ll need to manually remove files. Check the Makefile for installation paths or use:
make -n install
This simulates the install process without executing it, showing you exactly which files will be copied. Then remove them manually:
sudo rm /usr/local/bin/htop
sudo rm -r /usr/local/share/man/man1/htop.1
For future installations, consider using tools like checkinstall to create a .deb or .rpm package, which can be cleanly removed later.
Best Practices
Use a Separate Build Directory
Many developers make the mistake of compiling directly in the source directory. This can lead to clutter and make it difficult to clean up or switch between build configurations.
Instead, create a dedicated build directory:
mkdir build && cd build
../configure
make
This approach, known as “out-of-source” building, keeps source files pristine and allows you to maintain multiple build configurations (debug, release, cross-compile) from the same codebase.
Always Read Documentation
Before compiling, read the README and INSTALL files. They often contain crucial information about dependencies, known issues, or build flags specific to your platform.
Some projects require non-standard steps—for example, generating the configure script from autogen.sh if you’re building from a Git clone instead of a release tarball.
Use Version Control for Source Code
If you frequently compile from source, especially for development or customization, keep the source code under version control (Git). This allows you to track changes, revert to stable versions, and apply patches easily.
Example:
git clone https://github.com/hishamhm/htop.git
cd htop
git checkout v3.3.0
Using tagged releases ensures reproducibility and stability.
Install Dependencies Before Compiling
Always install all required development libraries before running configure. Missing libraries are the most common cause of build failures.
On Debian/Ubuntu, use:
apt install libssl-dev libncurses-dev libz-dev
On Fedora:
dnf install openssl-devel ncurses-devel zlib-devel
Use package search tools to find the correct -dev or -devel package name if unsure.
Avoid Running make as Root
Never run make as root. Compilation is a read-and-write process that should occur in your user space. Only use sudo for the final make install step.
Running make as root increases security risks. Malicious or buggy Makefiles could alter system files during compilation.
Use Checkinstall for Package Management
Instead of make install, consider using checkinstall to create a native package (.deb, .rpm) during installation:
sudo checkinstall
This tool monitors files installed by make install and packages them into a format your package manager recognizes. This allows you to uninstall later using:
sudo dpkg -r htop
or
sudo rpm -e htop
Install checkinstall via your package manager:
sudo apt install checkinstall
Keep a Build Log
For complex projects or troubleshooting, redirect output to a log file:
./configure > configure.log 2>&1
make > make.log 2>&1
sudo make install > install.log 2>&1
This creates a record of every step, useful for diagnosing failures or replicating builds on other systems.
Test Before Deployment
After compiling and installing, test the software thoroughly. Run its built-in tests if available:
make test
or
make check
These commands execute unit or integration tests to verify functionality. Skipping them may lead to unstable software in production environments.
Tools and Resources
Essential Build Tools
- gcc – GNU Compiler Collection for C, C++, Objective-C, Fortran, and more
- g++ – C++ frontend for gcc
- make – Automates compilation using Makefiles
- cmake – Modern cross-platform build system (used by many newer projects)
- ninja – Fast build system often used with CMake
- autotools – Legacy but still widely used (autoconf, automake, libtool)
- pkg-config – Helps locate library dependencies
- checkinstall – Creates installable packages from source
- strace – Debugs system calls during compilation
- ldd – Lists shared library dependencies of binaries
Package Managers and Dependency Resolution
Each Linux distribution has its own package manager:
- apt – Debian, Ubuntu, Linux Mint
- dnf – Fedora, RHEL 8+
- yum – RHEL 7 and older
- pacman – Arch Linux
- zypper – openSUSE
Always use your system’s package manager to install development libraries. For example:
apt search openssl-dev
Search for -dev (Debian) or -devel (RPM) packages, as these contain headers and static libraries needed for compilation.
Online Resources
- Linux From Scratch (LFS) – https://www.linuxfromscratch.org – Teaches building a Linux system from source, including detailed compilation guides.
- GNU Autotools Documentation – https://www.gnu.org/software/automake/manual/html_node/index.html – Official guide to configure scripts and Makefiles.
- CMake Documentation – https://cmake.org/documentation – Modern alternative to autotools.
- Stack Overflow – Search for “Linux compile [error message]” – Most compilation errors have been solved here.
- GitHub Issues – Check the issue tracker of the project you’re compiling. Many build problems are already documented and fixed.
Compiler Flags and Optimization
Advanced users can optimize compilation using flags passed to gcc/g++:
- -O2 – Optimizes for speed (recommended for production)
- -O3 – Aggressive optimization (may increase binary size)
- -g – Includes debug symbols for gdb
- -Wall – Enables all common warnings
- -Wextra – Enables additional warnings
- -std=c11 – Enforces C11 standard
Example with optimization and warnings:
CFLAGS="-O2 -Wall -Wextra" ./configure
These flags can be passed to configure scripts that respect CFLAGS and CXXFLAGS environment variables.
Cross-Compilation Tools
If you’re building software for a different architecture (e.g., compiling ARM code on x86), use cross-compilers:
arm-linux-gnueabihf-gcc
Install cross-compilation toolchains via your package manager:
sudo apt install gcc-arm-linux-gnueabihf
Then configure with:
./configure --host=arm-linux-gnueabihf
Real Examples
Example 1: Compiling Nginx from Source
Nginx is often compiled from source to enable custom modules or optimize for specific hardware.
Step 1: Install dependencies
sudo apt update
sudo apt install build-essential libpcre3-dev libssl-dev zlib1g-dev
Step 2: Download source
wget https://nginx.org/download/nginx-1.26.0.tar.gz
tar -xzf nginx-1.26.0.tar.gz
cd nginx-1.26.0
Step 3: Configure with custom options
./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_ssl_module --with-http_v2_module --with-http_gzip_static_module
Step 4: Compile and install
make
sudo make install
Step 5: Create a systemd service
Create /etc/systemd/system/nginx.service:
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=network.target
[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable nginx
sudo systemctl start nginx
Example 2: Compiling a Custom Kernel Module
Kernel modules are compiled against the current kernel headers.
Step 1: Install kernel headers
sudo apt install linux-headers-$(uname -r)
Step 2: Create a simple module (hello.c)
include <linux/init.h>
include <linux/module.h>
include <linux/kernel.h>
static int __init hello_init(void)
{
printk(KERN_INFO "Hello, Linux Kernel!\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "Goodbye, Linux Kernel!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple hello module");
Step 3: Create Makefile
obj-m += hello.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Step 4: Compile
make
Step 5: Load the module
sudo insmod hello.ko
dmesg | tail
You should see “Hello, Linux Kernel!” in the kernel log.
Unload it with:
sudo rmmod hello
Example 3: Compiling Rust Code
Rust uses Cargo, its own build system. Install Rust:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Then create a new project:
cargo new myapp
cd myapp
Build and run:
cargo build
cargo run
For release builds:
cargo build --release
Rust binaries are statically linked by default, making them portable across Linux systems.
FAQs
Why should I compile software from source instead of using a package manager?
Compiling from source gives you access to the latest features, allows you to enable or disable optional components, and ensures the software is optimized for your specific hardware. Package managers often provide stable, tested versions—but these may be outdated or lack specific patches or flags you need.
What should I do if ./configure fails with “No such file or directory”?
This usually means you’re trying to run configure on source code that hasn’t been prepared for Autotools—common when cloning from Git. Run ./autogen.sh or sh autogen.sh first. If that file doesn’t exist, consult the project’s README for build instructions.
Can I compile Windows software on Linux?
Not directly. Windows binaries (.exe) are incompatible with Linux. However, you can compile the source code of cross-platform software (e.g., using MinGW or Wine for Windows API emulation). For native Linux builds, ensure the code is written in portable languages like C, C++, or Go.
How do I know which libraries I need to install?
Look for error messages like “fatal error: openssl/ssl.h: No such file or directory.” This indicates you need the OpenSSL development package. Search for the header file name with your package manager: apt search ssl.h or dnf provides */ssl.h.
Is compiling software from source safe?
It is safe if you trust the source. Always download code from official project websites or verified repositories. Avoid compiling code from untrusted third-party sites. Review the source code before compilation if security is critical.
Why is my compiled program so large?
Debug symbols and unoptimized builds increase binary size. Use -O2 during compilation and strip symbols after install:
strip /usr/local/bin/myprogram
This reduces size significantly without affecting functionality.
Can I compile code on a headless server?
Yes. Compilation does not require a graphical interface. As long as you have the compiler, build tools, and dependencies installed, you can compile any software from the command line—even on remote servers via SSH.
What’s the difference between gcc and g++?
gcc is primarily for C code. g++ is for C++ and automatically links the C++ standard library. Use g++ when compiling C++ source files (.cpp, .cc). Using gcc on C++ code may result in linker errors.
How do I update a program I compiled from source?
Re-download the new source, reconfigure, recompile, and reinstall. If you used checkinstall, uninstall the old package first. Always backup configuration files before upgrading.
Conclusion
Compiling code in Linux is not just a technical skill—it’s a gateway to deeper control, customization, and understanding of how software works under the hood. From installing essential build tools to troubleshooting missing dependencies and optimizing compilation flags, this guide has equipped you with the knowledge to confidently compile software from source across any Linux distribution.
Remember: always read documentation, use version control, isolate your builds, and test thoroughly. Whether you’re installing a web server, building a kernel module, or contributing to open-source projects, the ability to compile from source ensures you’re never limited by pre-packaged binaries.
As you gain experience, you’ll begin to appreciate the elegance of the Unix philosophy: small, modular tools working together. The compiler, linker, and make system are among the most powerful of these tools. Master them, and you unlock the full potential of Linux as a development and production platform.
Keep experimenting. Build your own tools. Share your code. And never stop learning—the world of software compilation is vast, rewarding, and constantly evolving.