A Non-Programmer’s Guide to Compiling C Programs with Make

<h2>Introduction: Why Compiling C Programs Can Be Daunting</h2> <p>If you’re not a C developer, the prospect of compiling a C or C++ program from source can feel intimidating. For years, my approach was simple: install the dependencies, run <code>make</code>, and hope it works. If it didn’t, I’d search for a precompiled binary or give up. This strategy worked well on Linux, where binaries were common, but after switching to macOS, I found myself needing to compile programs more often. This article breaks down the process step by step, using real-world examples like <strong>paperjam</strong>, <strong>sqlite</strong>, and <strong>qf</strong> (a pager that opens files from search results with <code>rg -n THING | qf</code>).</p><figure style="margin:20px 0"><img src="https://picsum.photos/seed/3632587519/800/450" alt="A Non-Programmer’s Guide to Compiling C Programs with Make" style="width:100%;height:auto;border-radius:8px" loading="lazy"><figcaption style="font-size:12px;color:#666;margin-top:5px"></figcaption></figure> <h2 id="step1-install-a-c-compiler">Step 1: Install a C Compiler</h2> <p>Before you can compile anything, you need a C compiler and the <code>make</code> tool. On <strong>Ubuntu</strong> or other Debian-based systems, install the <code>build-essential</code> package:</p> <pre><code>sudo apt-get install build-essential</code></pre> <p>This installs <code>gcc</code>, <code>g++</code>, and <code>make</code>. On <strong>macOS</strong>, you’ll need the Xcode Command Line Tools, which you can install via Terminal:</p> <pre><code>xcode-select --install</code></pre> <p>If you use Homebrew, you can also install <code>gcc</code> directly, but the Command Line Tools are the standard route.</p> <h2 id="step2-install-dependencies">Step 2: Install the Program’s Dependencies</h2> <p>Unlike modern languages with built-in package managers, C programs require you to manually fetch dependencies. Thankfully, C projects tend to keep dependencies minimal, and most are available through your system’s package manager. The <strong>README</strong> file of a project usually explains what you need. For example, <strong>paperjam</strong>‘s README says:</p> <blockquote>To compile PaperJam, you need the headers for the libqpdf and libpaper libraries (usually available as libqpdf-dev and libpaper-dev packages). You may need a2x (found in AsciiDoc) for building manual pages.</blockquote> <p>On Debian-based systems, you’d run:</p> <pre><code>sudo apt install -y libqpdf-dev libpaper-dev</code></pre> <p>But be careful: package names given in READMEs often assume a Debian context. On macOS with Homebrew, use <code>brew install qpdf</code> (the <code>-dev</code> suffix isn’t used). For other distros, adapt accordingly.</p> <h2 id="step3-configure-script">Step 3: Run <code>./configure</code> (If Needed)</h2> <p>Some C projects come with a <code>Makefile</code> directly, while others provide a <code>./configure</code> script. For instance, <strong>sqlite</strong>‘s source includes a <code>./configure</code> script. When you run it, it checks for dependencies and generates a <code>Makefile</code> tailored to your system. If the script fails, it usually tells you what’s missing. The output can be verbose and cryptic, but the key is to look for error messages about missing headers or libraries.</p> <p>If the project already has a <code>Makefile</code>, you can skip this step and go directly to <code>make</code>. But if you see a <code>configure</code> script, run it first:</p> <pre><code>./configure make</code></pre> <h2 id="step4-compile-with-make">Step 4: Compile with <code>make</code></h2> <p>Once dependencies are installed and a <code>Makefile</code> exists (either provided or generated by <code>./configure</code>), running <code>make</code> is typically straightforward. This command reads the <code>Makefile</code> and builds the program. If the build fails, common issues include missing libraries, incorrect compiler flags, or version mismatches. For example, when compiling <strong>qf</strong>, ensure you have <code>ncurses</code> and <code>pkg-config</code>. If errors appear, check the output for clues—sometimes you’ll need to install additional development packages.</p> <h2 id="step5-install-the-program">Step 5: Install the Program (Optional)</h2> <p>After successful compilation, the binary is usually in the source directory. To install it system-wide, run <code>sudo make install</code>. This copies the executable to a directory like <code>/usr/local/bin</code>. However, you may prefer to keep the binary in your home directory or add the source folder to your PATH—especially if you’re just testing.</p> <h2 id="common-pitfalls-and-tips">Common Pitfalls and Tips</h2> <h3 id="missing-dependencies">Missing Dependencies</h3> <p>The most frequent problem is a missing <code>-dev</code> package for a library. Search for the library name plus <code>-dev</code> on Debian, or use <code>brew search</code> on macOS.</p> <h3 id="compiler-errors">Compiler Errors</h3> <p>If the compiler throws errors, they often point to specific missing functions or headers. Google the error—chances are someone else has solved it.</p> <h3 id="macos-specific-issues">macOS-Specific Issues</h3> <p>On macOS, <code>gcc</code> is often an alias for <code>clang</code>. If a program expects GNU extensions, you may need to install <code>gcc</code> via Homebrew and set <code>CC=gcc</code> before running <code>make</code>.</p> <h2 id="conclusion">Conclusion</h2> <p>Compiling C programs doesn’t have to be a black art. With a clear process—install a compiler, resolve dependencies, run <code>./configure</code> if present, then <code>make</code>—you can build most open-source tools from source. The examples of <strong>paperjam</strong>, <strong>sqlite</strong>, and <strong>qf</strong> show that the steps are similar across projects. Once you’ve done it a few times, the whole flow becomes second nature, and you’ll no longer have to rely on “hope someone else has compiled it.”</p>