{
  "@context": "https://schema.org",
  "@id": "https://aggr.aymericbeaumet.com/items/jvns-ca/2025-06-10-using-make-to-compile-c-programs-for-non-c-programmers/#webpage",
  "@type": [
    "WebPage",
    "ArchiveComponent"
  ],
  "articleBody": "I have never been a C programmer but every so often I need to compile a C/C++ program from source. This has been kind of a struggle for me: for a long time, my approach was basically “install the dependencies, run `make`, if it doesn’t work, either try to find a binary someone has compiled or give up”.\n\n“Hope someone else has compiled it” worked pretty well when I was running Linux but since I’ve been using a Mac for the last couple of years I’ve been running into more situations where I have to actually compile programs myself.\n\nSo let’s talk about what you might have to do to compile a C program! I’ll use a couple of examples of specific C programs I’ve compiled and talk about a few things that can go wrong. Here are three programs we’ll be talking about compiling:\n\n- [paperjam](https://mj.ucw.cz/sw/paperjam/)\n- [sqlite](https://www.sqlite.org/download.html)\n- [qf](https://git.causal.agency/src/tree/bin/qf.c) (a pager you can run to quickly open files from a search with `rg -n THING | qf`)\n\n### [step 1: install a C compiler](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-1-install-a-c-compiler)\n\nThis is pretty simple: on an Ubuntu system if I don’t already have a C compiler I’ll install one with:\n\n```\nsudo apt-get install build-essential\n```\n\nThis installs `gcc`, `g++`, and `make`. The situation on a Mac is more confusing but it’s something like “install xcode command line tools”.\n\n### [step 2: install the program’s dependencies](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-2-install-the-program-s-dependencies)\n\nUnlike some newer programming languages, C doesn’t have a dependency manager. So if a program has any dependencies, you need to hunt them down yourself. Thankfully because of this, C programmers usually keep their dependencies very minimal and often the dependencies will be available in whatever package manager you’re using.\n\nThere’s almost always a section explaining how to get the dependencies in the README, for example in [paperjam](https://mj.ucw.cz/sw/paperjam/)’s README, it says:\n\n> To compile PaperJam, you need the headers for the libqpdf and libpaper libraries (usually available as libqpdf-dev and libpaper-dev packages).\n\n> You may need `a2x` (found in [AsciiDoc](http://www.methods.co.nz/asciidoc/a2x.1.html)) for building manual pages.\n\nSo on a Debian-based system you can install the dependencies like this.\n\n```\nsudo apt install -y libqpdf-dev libpaper-dev\n```\n\nIf a README gives a name for a package (like `libqpdf-dev`), I’d basically always assume that they mean “in a Debian-based Linux distro”: if you’re on a Mac `brew install libqpdf-dev` will not work. I still have not 100% gotten the hang of developing on a Mac yet so I don’t have many tips there yet. I guess in this case it would be `brew install qpdf` if you’re using Homebrew.\n\n### [step 3: run `./configure` (if needed)](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-3-run-configure-if-needed)\n\nSome C programs come with a `Makefile` and some instead come with a script called `./configure`. For example, if you download [sqlite’s source code](https://www.sqlite.org/download.html), it has a `./configure` script in it instead of a Makefile.\n\nMy understanding of this `./configure` script is:\n\n1. You run it, it prints out a lot of somewhat inscrutable output, and then it either generates a `Makefile` or fails because you’re missing some dependency\n2. The `./configure` script is part of a system called [autotools](https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html) that I have never needed to learn anything about beyond “run it to generate a `Makefile`”.\n\nI think there might be some options you can pass to get the `./configure` script to produce a different `Makefile` but I have never done that.\n\n### [step 4: run `make`](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-4-run-make)\n\nThe next step is to run `make` to try to build a program. Some notes about `make`:\n\n- Sometimes you can run `make -j8` to parallelize the build and make it go faster\n- It usually prints out a million compiler warnings when compiling the program. I always just ignore them. I didn’t write the software! The compiler warnings are not my problem.\n\n### [compiler errors are often dependency problems](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#compiler-errors-are-often-dependency-problems)\n\nHere’s an error I got while compiling `paperjam` on my Mac:\n\n```\n/opt/homebrew/Cellar/qpdf/12.0.0/include/qpdf/InputSource.hh:85:19: error: function definition does not declare parameters\n   85 |     qpdf_offset_t last_offset{0};\n      |                   ^\n```\n\nOver the years I’ve learned it’s usually best not to overthink problems like this: if it’s talking about `qpdf`, there’s a good change it just means that I’ve done something wrong with how I’m including the `qpdf` dependency.\n\nNow let’s talk about some ways to get the `qpdf` dependency included in the right way.\n\n### [the world’s shortest introduction to the compiler and linker](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#the-world-s-shortest-introduction-to-the-compiler-and-linker)\n\nBefore we talk about how to fix dependency problems: building C programs is split into 2 steps:\n\n1. **Compiling** the code into **object files** (with `gcc` or `clang`)\n2. **Linking** those object files into a final binary (with `ld`)\n\nIt’s important to know this when building a C program because sometimes you need to pass the right flags to the compiler and linker to tell them where to find the dependencies for the program you’re compiling.\n\n### [`make` uses environment variables to configure the compiler and linker](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#make-uses-environment-variables-to-configure-the-compiler-and-linker)\n\nIf I run `make` on my Mac to install `paperjam`, I get this error:\n\n```\nc++ -o paperjam paperjam.o pdf-tools.o parse.o cmds.o pdf.o -lqpdf -lpaper\nld: library 'qpdf' not found\n```\n\nThis is not because `qpdf` is not installed on my system (it actually is!). But the compiler and linker don’t know how to *find* the `qpdf` library. To fix this, we need to:\n\n- pass `\"-I/opt/homebrew/include\"` to the compiler (to tell it where to find the header files)\n- pass `\"-L/opt/homebrew/lib -liconv\"` to the linker (to tell it where to find library files and to link in `iconv`)\n\nAnd we can get `make` to pass those extra parameters to the compiler and linker using environment variables! To see how this works: inside `paperjam`’s Makefile you can see a bunch of environment variables, like `LDLIBS` here:\n\n```\npaperjam: $(OBJS)\n\t$(LD) -o $@ $^ $(LDLIBS)\n```\n\nEverything you put into the `LDLIBS` environment variable gets passed to the linker (`ld`) as a command line argument.\n\n### [secret environment variable: `CPPFLAGS`](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#secret-environment-variable-cppflags)\n\n`Makefiles` sometimes define their own environment variables that they pass to the compiler/linker, but `make` also has a bunch of “implicit” environment variables which it will automatically pass to the C compiler and linker. There’s a [full list of implicit environment variables here](https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html#index-CFLAGS0), but one of them is `CPPFLAGS`, which gets automatically passed to the C compiler.\n\n(technically it would be more normal to use `CXXFLAGS` for this, but this particular `Makefile` hardcodes `CXXFLAGS` so setting `CPPFLAGS` was the only way I could find to set the compiler flags without editing the `Makefile`)\n\n As an aside: it took me a long time to realize how closely tied to C/C++ \\`make\\` is -- I used to think that \\`make\\` was just a general build system (and of course you can use it for anything!) but it has a lot of affordances for building C/C++ programs that it doesn't have for building any other kind of program.\n\n### [two ways to pass environment variables to `make`](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#two-ways-to-pass-environment-variables-to-make)\n\nI learned thanks to [@zwol](https://www.owlfolio.org/) that there are actually two ways to pass environment variables to `make`:\n\n1. `CXXFLAGS=xyz make` (the usual way)\n2. `make CXXFLAGS=xyz`\n\nThe difference between them is that `make CXXFLAGS=xyz` will override the value of `CXXFLAGS` set in the `Makefile` but `CXXFLAGS=xyz make` won’t.\n\nI’m not sure which way is the norm but I’m going to use the first way in this post.\n\n### [how to use `CPPFLAGS` and `LDLIBS` to fix this compiler error](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#how-to-use-cppflags-and-ldlibs-to-fix-this-compiler-error)\n\nNow that we’ve talked about how `CPPFLAGS` and `LDLIBS` get passed to the compiler and linker, here’s the final incantation that I used to get the program to build successfully!\n\n```\nCPPFLAGS=\"-I/opt/homebrew/include\" LDLIBS=\"-L/opt/homebrew/lib -liconv\" make paperjam\n```\n\nThis passes `-I/opt/homebrew/include` to the compiler and `-L/opt/homebrew/lib -liconv` to the linker.\n\nAlso I don’t want to pretend that I “magically” knew that those were the right arguments to pass, figuring them out involved a bunch of confused Googling that I skipped over in this post. I will say that:\n\n- the `-I` compiler flag tells the compiler which directory to find header files in, like `/opt/homebrew/include/qpdf/QPDF.hh`\n- the `-L` linker flag tells the linker which directory to find libraries in, like `/opt/homebrew/lib/libqpdf.a`\n- the `-l` linker flag tells the linker which libraries to link in, like `-liconv` means “link in the `iconv` library”, or `-lm` means “link `math`”\n\n### [tip: how to just build 1 specific file: `make $FILENAME`](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#tip-how-to-just-build-1-specific-file-make-filename)\n\nYesterday I discovered this cool tool called [qf](https://git.causal.agency/src/tree/bin/qf.c) which you can use to quickly open files from the output of `ripgrep`.\n\n`qf` is in a big directory of various tools, but I only wanted to compile `qf`. So I just compiled `qf`, like this:\n\n```\nmake qf\n```\n\nBasically if you know (or can guess) the output filename of the file you’re trying to build, you can tell `make` to just build that file by running `make $FILENAME`\n\n### [tip: you don’t need a Makefile](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#tip-you-don-t-need-a-makefile)\n\nI sometimes write 5-line C programs with no dependencies, and I just learned that if I have a file called `blah.c`, I can just compile it like this without creating a `Makefile`:\n\n```\nmake blah\n```\n\nIt gets automaticaly expanded to `cc -o blah blah.c`, which saves a bit of typing. I have no idea if I’m going to remember this (I might just keep typing `gcc -o blah blah.c` anyway) but it seems like a fun trick.\n\n### [tip: look at how other packaging systems built the same C program](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#tip-look-at-how-other-packaging-systems-built-the-same-c-program)\n\nIf you’re having trouble building a C program, maybe other people had problems building it too! Every Linux distribution has build files for every package that they build, so even if you can’t install packages from that distribution directly, maybe you can get tips from that Linux distro for how to build the package. Realizing this (thanks to my friend Dave) was a huge ah-ha moment for me.\n\nFor example, [this line from the nix package for `paperjam`](https://github.com/NixOS/nixpkgs/blob/405624e81a9b65378328accb0a11c3e5369e651c/pkgs/by-name/pa/paperjam/package.nix#L35) says:\n\n```\n  env.NIX_LDFLAGS = lib.optionalString stdenv.hostPlatform.isDarwin \"-liconv\";\n```\n\nThis is basically saying “pass the linker flag `-liconv` to build this on a Mac”, so that’s a clue we could use to build it.\n\nThat same file also says `env.NIX_CFLAGS_COMPILE = \"-DPOINTERHOLDER_TRANSITION=1\";`. I’m not sure what this means, but when I try to build the `paperjam` package I do get an error about something called a `PointerHolder`, so I guess that’s somehow related to the “PointerHolder transition”.\n\n### [step 5: installing the binary](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-5-installing-the-binary)\n\nOnce you’ve managed to compile the program, probably you want to install it somewhere! Some `Makefile`s have an `install` target that let you install the tool on your system with `make install`. I’m always a bit scared of this (where is it going to put the files? what if I want to uninstall them later?), so if I’m compiling a pretty simple program I’ll often just manually copy the binary to install it instead, like this:\n\n```\ncp qf ~/bin\n```\n\n### [step 6: maybe make your own package!](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-6-maybe-make-your-own-package)\n\nOnce I figured out how to do all of this, I realized that I could use my new `make` knowledge to contribute a `paperjam` package to Homebrew! Then I could just `brew install paperjam` on future systems.\n\nThe good thing is that even if the details of how all of the different packaging systems, they fundamentally all use C compilers and linkers.\n\n### [it can be useful to understand a little about C even if you’re not a C programmer](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#it-can-be-useful-to-understand-a-little-about-c-even-if-you-re-not-a-c-programmer)\n\nI think all of this is an interesting example of how it can useful to understand some basics of how C programs work (like “they have header files”) even if you’re never planning to write a nontrivial C program if your life.\n\nIt feels good to have some ability to compile C/C++ programs myself, even though I’m still not totally confident about all of the compiler and linker flags and I still plan to never learn anything about how autotools works other than “you run `./configure` to generate the `Makefile`”.\n\nTwo things I left out of this post:\n\n- `LD_LIBRARY_PATH / DYLD_LIBRARY_PATH` (which you use to tell the dynamic linker at runtime where to find dynamically linked files) because I can’t remember the last time I ran into an `LD_LIBRARY_PATH` issue and couldn’t find an example.\n- `pkg-config`, which I think is important but I don’t understand yet\n",
  "authors": [],
  "captured_at": "2026-09-19T21:30:23.395188495+00:00",
  "category": "programming",
  "content_html": "<p>I have never been a C programmer but every so often I need to compile a C/C++ program from source. This has been kind of a struggle for me: for a long time, my approach was basically “install the dependencies, run <code>make</code>, if it doesn’t work, either try to find a binary someone has compiled or give up”.</p>\n<p>“Hope someone else has compiled it” worked pretty well when I was running Linux but since I’ve been using a Mac for the last couple of years I’ve been running into more situations where I have to actually compile programs myself.</p>\n<p>So let’s talk about what you might have to do to compile a C program! I’ll use a couple of examples of specific C programs I’ve compiled and talk about a few things that can go wrong. Here are three programs we’ll be talking about compiling:</p>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://mj.ucw.cz/sw/paperjam/\">paperjam</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.sqlite.org/download.html\">sqlite</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://git.causal.agency/src/tree/bin/qf.c\">qf</a> (a pager you can run to quickly open files from a search with <code>rg -n THING | qf</code>)</li>\n</ul>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-1-install-a-c-compiler\">step 1: install a C compiler</a></h3>\n<p>This is pretty simple: on an Ubuntu system if I don’t already have a C compiler I’ll install one with:</p>\n<pre><code><span class=\"code-snippet\">sudo apt-get install build-essential\n</span></code></pre>\n<p>This installs <code>gcc</code>, <code>g++</code>, and <code>make</code>. The situation on a Mac is more confusing but it’s something like “install xcode command line tools”.</p>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-2-install-the-program-s-dependencies\">step 2: install the program’s dependencies</a></h3>\n<p>Unlike some newer programming languages, C doesn’t have a dependency manager. So if a program has any dependencies, you need to hunt them down yourself. Thankfully because of this, C programmers usually keep their dependencies very minimal and often the dependencies will be available in whatever package manager you’re using.</p>\n<p>There’s almost always a section explaining how to get the dependencies in the README, for example in <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://mj.ucw.cz/sw/paperjam/\">paperjam</a>’s README, it says:</p>\n<blockquote>\n<p>To compile PaperJam, you need the headers for the libqpdf and libpaper libraries (usually available as libqpdf-dev and libpaper-dev packages).</p>\n</blockquote>\n<blockquote>\n<p>You may need <code>a2x</code> (found in <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"http://www.methods.co.nz/asciidoc/a2x.1.html\">AsciiDoc</a>) for building manual pages.</p>\n</blockquote>\n<p>So on a Debian-based system you can install the dependencies like this.</p>\n<pre><code><span class=\"code-snippet\">sudo apt install -y libqpdf-dev libpaper-dev\n</span></code></pre>\n<p>If a README gives a name for a package (like <code>libqpdf-dev</code>), I’d basically always assume that they mean “in a Debian-based Linux distro”: if you’re on a Mac <code>brew install libqpdf-dev</code> will not work. I still have not 100% gotten the hang of developing on a Mac yet so I don’t have many tips there yet. I guess in this case it would be <code>brew install qpdf</code> if you’re using Homebrew.</p>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-3-run-configure-if-needed\">step 3: run <code>./configure</code> (if needed)</a></h3>\n<p>Some C programs come with a <code>Makefile</code> and some instead come with a script called <code>./configure</code>. For example, if you download <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.sqlite.org/download.html\">sqlite’s source code</a>, it has a <code>./configure</code> script in it instead of a Makefile.</p>\n<p>My understanding of this <code>./configure</code> script is:</p>\n<ol>\n<li>You run it, it prints out a lot of somewhat inscrutable output, and then it either generates a <code>Makefile</code> or fails because you’re missing some dependency</li>\n<li>The <code>./configure</code> script is part of a system called <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html\">autotools</a> that I have never needed to learn anything about beyond “run it to generate a <code>Makefile</code>”.</li>\n</ol>\n<p>I think there might be some options you can pass to get the <code>./configure</code> script to produce a different <code>Makefile</code> but I have never done that.</p>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-4-run-make\">step 4: run <code>make</code></a></h3>\n<p>The next step is to run <code>make</code> to try to build a program. Some notes about <code>make</code>:</p>\n<ul>\n<li>Sometimes you can run <code>make -j8</code> to parallelize the build and make it go faster</li>\n<li>It usually prints out a million compiler warnings when compiling the program. I always just ignore them. I didn’t write the software! The compiler warnings are not my problem.</li>\n</ul>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#compiler-errors-are-often-dependency-problems\">compiler errors are often dependency problems</a></h3>\n<p>Here’s an error I got while compiling <code>paperjam</code> on my Mac:</p>\n<pre><code><span class=\"code-snippet\">/opt/homebrew/Cellar/qpdf/12.0.0/include/qpdf/InputSource.hh:85:19: error: function definition does not declare parameters\n   85 |     qpdf_offset_t last_offset{0};\n      |                   ^\n</span></code></pre>\n<p>Over the years I’ve learned it’s usually best not to overthink problems like this: if it’s talking about <code>qpdf</code>, there’s a good change it just means that I’ve done something wrong with how I’m including the <code>qpdf</code> dependency.</p>\n<p>Now let’s talk about some ways to get the <code>qpdf</code> dependency included in the right way.</p>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#the-world-s-shortest-introduction-to-the-compiler-and-linker\">the world’s shortest introduction to the compiler and linker</a></h3>\n<p>Before we talk about how to fix dependency problems: building C programs is split into 2 steps:</p>\n<ol>\n<li><strong>Compiling</strong> the code into <strong>object files</strong> (with <code>gcc</code> or <code>clang</code>)</li>\n<li><strong>Linking</strong> those object files into a final binary (with <code>ld</code>)</li>\n</ol>\n<p>It’s important to know this when building a C program because sometimes you need to pass the right flags to the compiler and linker to tell them where to find the dependencies for the program you’re compiling.</p>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#make-uses-environment-variables-to-configure-the-compiler-and-linker\"><code>make</code> uses environment variables to configure the compiler and linker</a></h3>\n<p>If I run <code>make</code> on my Mac to install <code>paperjam</code>, I get this error:</p>\n<pre><code><span class=\"code-snippet\">c++ -o paperjam paperjam.o pdf-tools.o parse.o cmds.o pdf.o -lqpdf -lpaper\nld: library &#39;qpdf&#39; not found\n</span></code></pre>\n<p>This is not because <code>qpdf</code> is not installed on my system (it actually is!). But the compiler and linker don’t know how to <em>find</em> the <code>qpdf</code> library. To fix this, we need to:</p>\n<ul>\n<li>pass <code>&quot;-I/opt/homebrew/include&quot;</code> to the compiler (to tell it where to find the header files)</li>\n<li>pass <code>&quot;-L/opt/homebrew/lib -liconv&quot;</code> to the linker (to tell it where to find library files and to link in <code>iconv</code>)</li>\n</ul>\n<p>And we can get <code>make</code> to pass those extra parameters to the compiler and linker using environment variables! To see how this works: inside <code>paperjam</code>’s Makefile you can see a bunch of environment variables, like <code>LDLIBS</code> here:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"Makefile\"><span class=\"syntax-source syntax-makefile\"><span class=\"syntax-meta syntax-function syntax-makefile\"><span class=\"syntax-entity syntax-name syntax-function syntax-makefile\">paperjam</span></span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-makefile\">:</span> <span class=\"syntax-meta syntax-function syntax-arguments syntax-makefile\"><span class=\"syntax-string syntax-unquoted syntax-makefile\"><span class=\"syntax-variable syntax-parameter syntax-makefile\"><span class=\"syntax-keyword syntax-other syntax-block syntax-begin syntax-makefile\">$(</span>OBJS<span class=\"syntax-keyword syntax-other syntax-block syntax-end syntax-makefile\">)</span></span></span></span><span class=\"syntax-meta syntax-function syntax-body syntax-makefile\">\n</span><span class=\"syntax-meta syntax-function syntax-body syntax-makefile\">\t<span class=\"syntax-source syntax-shell\"><span class=\"syntax-variable syntax-parameter syntax-makefile\"><span class=\"syntax-keyword syntax-other syntax-block syntax-begin syntax-makefile\">$(</span>LD<span class=\"syntax-keyword syntax-other syntax-block syntax-end syntax-makefile\">)</span></span> <span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">-o</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> <span class=\"syntax-variable syntax-language syntax-automatic syntax-makefile\">$@</span> <span class=\"syntax-variable syntax-language syntax-automatic syntax-makefile\">$^</span> <span class=\"syntax-variable syntax-parameter syntax-makefile\"><span class=\"syntax-keyword syntax-other syntax-block syntax-begin syntax-makefile\">$(</span>LDLIBS<span class=\"syntax-keyword syntax-other syntax-block syntax-end syntax-makefile\">)</span></span></span></span>\n</span></span></span></code></pre>\n<p>Everything you put into the <code>LDLIBS</code> environment variable gets passed to the linker (<code>ld</code>) as a command line argument.</p>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#secret-environment-variable-cppflags\">secret environment variable: <code>CPPFLAGS</code></a></h3>\n<p><code>Makefiles</code> sometimes define their own environment variables that they pass to the compiler/linker, but <code>make</code> also has a bunch of “implicit” environment variables which it will automatically pass to the C compiler and linker. There’s a <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html#index-CFLAGS0\">full list of implicit environment variables here</a>, but one of them is <code>CPPFLAGS</code>, which gets automatically passed to the C compiler.</p>\n<p>(technically it would be more normal to use <code>CXXFLAGS</code> for this, but this particular <code>Makefile</code> hardcodes <code>CXXFLAGS</code> so setting <code>CPPFLAGS</code> was the only way I could find to set the compiler flags without editing the <code>Makefile</code>)</p>\n<p>As an aside: it took me a long time to realize how closely tied to C/C++ `make` is -- I used to think that `make` was just a general build system (and of course you can use it for anything!) but it has a lot of affordances for building C/C++ programs that it doesn't have for building any other kind of program.</p>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#two-ways-to-pass-environment-variables-to-make\">two ways to pass environment variables to <code>make</code></a></h3>\n<p>I learned thanks to <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.owlfolio.org/\">@zwol</a> that there are actually two ways to pass environment variables to <code>make</code>:</p>\n<ol>\n<li><code>CXXFLAGS=xyz make</code> (the usual way)</li>\n<li><code>make CXXFLAGS=xyz</code></li>\n</ol>\n<p>The difference between them is that <code>make CXXFLAGS=xyz</code> will override the value of <code>CXXFLAGS</code> set in the <code>Makefile</code> but <code>CXXFLAGS=xyz make</code> won’t.</p>\n<p>I’m not sure which way is the norm but I’m going to use the first way in this post.</p>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#how-to-use-cppflags-and-ldlibs-to-fix-this-compiler-error\">how to use <code>CPPFLAGS</code> and <code>LDLIBS</code> to fix this compiler error</a></h3>\n<p>Now that we’ve talked about how <code>CPPFLAGS</code> and <code>LDLIBS</code> get passed to the compiler and linker, here’s the final incantation that I used to get the program to build successfully!</p>\n<pre><code><span class=\"code-snippet\">CPPFLAGS=&quot;-I/opt/homebrew/include&quot; LDLIBS=&quot;-L/opt/homebrew/lib -liconv&quot; make paperjam\n</span></code></pre>\n<p>This passes <code>-I/opt/homebrew/include</code> to the compiler and <code>-L/opt/homebrew/lib -liconv</code> to the linker.</p>\n<p>Also I don’t want to pretend that I “magically” knew that those were the right arguments to pass, figuring them out involved a bunch of confused Googling that I skipped over in this post. I will say that:</p>\n<ul>\n<li>the <code>-I</code> compiler flag tells the compiler which directory to find header files in, like <code>/opt/homebrew/include/qpdf/QPDF.hh</code></li>\n<li>the <code>-L</code> linker flag tells the linker which directory to find libraries in, like <code>/opt/homebrew/lib/libqpdf.a</code></li>\n<li>the <code>-l</code> linker flag tells the linker which libraries to link in, like <code>-liconv</code> means “link in the <code>iconv</code> library”, or <code>-lm</code> means “link <code>math</code>”</li>\n</ul>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#tip-how-to-just-build-1-specific-file-make-filename\">tip: how to just build 1 specific file: <code>make $FILENAME</code></a></h3>\n<p>Yesterday I discovered this cool tool called <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://git.causal.agency/src/tree/bin/qf.c\">qf</a> which you can use to quickly open files from the output of <code>ripgrep</code>.</p>\n<p><code>qf</code> is in a big directory of various tools, but I only wanted to compile <code>qf</code>. So I just compiled <code>qf</code>, like this:</p>\n<pre><code><span class=\"code-snippet\">make qf\n</span></code></pre>\n<p>Basically if you know (or can guess) the output filename of the file you’re trying to build, you can tell <code>make</code> to just build that file by running <code>make $FILENAME</code></p>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#tip-you-don-t-need-a-makefile\">tip: you don’t need a Makefile</a></h3>\n<p>I sometimes write 5-line C programs with no dependencies, and I just learned that if I have a file called <code>blah.c</code>, I can just compile it like this without creating a <code>Makefile</code>:</p>\n<pre><code><span class=\"code-snippet\">make blah\n</span></code></pre>\n<p>It gets automaticaly expanded to <code>cc -o blah blah.c</code>, which saves a bit of typing. I have no idea if I’m going to remember this (I might just keep typing <code>gcc -o blah blah.c</code> anyway) but it seems like a fun trick.</p>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#tip-look-at-how-other-packaging-systems-built-the-same-c-program\">tip: look at how other packaging systems built the same C program</a></h3>\n<p>If you’re having trouble building a C program, maybe other people had problems building it too! Every Linux distribution has build files for every package that they build, so even if you can’t install packages from that distribution directly, maybe you can get tips from that Linux distro for how to build the package. Realizing this (thanks to my friend Dave) was a huge ah-ha moment for me.</p>\n<p>For example, <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/NixOS/nixpkgs/blob/405624e81a9b65378328accb0a11c3e5369e651c/pkgs/by-name/pa/paperjam/package.nix#L35\">this line from the nix package for <code>paperjam</code></a> says:</p>\n<pre><code><span class=\"code-snippet\">  env.NIX_LDFLAGS = lib.optionalString stdenv.hostPlatform.isDarwin &quot;-liconv&quot;;\n</span></code></pre>\n<p>This is basically saying “pass the linker flag <code>-liconv</code> to build this on a Mac”, so that’s a clue we could use to build it.</p>\n<p>That same file also says <code>env.NIX_CFLAGS_COMPILE = &quot;-DPOINTERHOLDER_TRANSITION=1&quot;;</code>. I’m not sure what this means, but when I try to build the <code>paperjam</code> package I do get an error about something called a <code>PointerHolder</code>, so I guess that’s somehow related to the “PointerHolder transition”.</p>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-5-installing-the-binary\">step 5: installing the binary</a></h3>\n<p>Once you’ve managed to compile the program, probably you want to install it somewhere! Some <code>Makefile</code>s have an <code>install</code> target that let you install the tool on your system with <code>make install</code>. I’m always a bit scared of this (where is it going to put the files? what if I want to uninstall them later?), so if I’m compiling a pretty simple program I’ll often just manually copy the binary to install it instead, like this:</p>\n<pre><code><span class=\"code-snippet\">cp qf ~/bin\n</span></code></pre>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-6-maybe-make-your-own-package\">step 6: maybe make your own package!</a></h3>\n<p>Once I figured out how to do all of this, I realized that I could use my new <code>make</code> knowledge to contribute a <code>paperjam</code> package to Homebrew! Then I could just <code>brew install paperjam</code> on future systems.</p>\n<p>The good thing is that even if the details of how all of the different packaging systems, they fundamentally all use C compilers and linkers.</p>\n<h3><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#it-can-be-useful-to-understand-a-little-about-c-even-if-you-re-not-a-c-programmer\">it can be useful to understand a little about C even if you’re not a C programmer</a></h3>\n<p>I think all of this is an interesting example of how it can useful to understand some basics of how C programs work (like “they have header files”) even if you’re never planning to write a nontrivial C program if your life.</p>\n<p>It feels good to have some ability to compile C/C++ programs myself, even though I’m still not totally confident about all of the compiler and linker flags and I still plan to never learn anything about how autotools works other than “you run <code>./configure</code> to generate the <code>Makefile</code>”.</p>\n<p>Two things I left out of this post:</p>\n<ul>\n<li><code>LD_LIBRARY_PATH / DYLD_LIBRARY_PATH</code> (which you use to tell the dynamic linker at runtime where to find dynamically linked files) because I can’t remember the last time I ran into an <code>LD_LIBRARY_PATH</code> issue and couldn’t find an example.</li>\n<li><code>pkg-config</code>, which I think is important but I don’t understand yet</li>\n</ul>\n",
  "content_markdown": "I have never been a C programmer but every so often I need to compile a C/C++ program from source. This has been kind of a struggle for me: for a long time, my approach was basically “install the dependencies, run `make`, if it doesn’t work, either try to find a binary someone has compiled or give up”.\n\n“Hope someone else has compiled it” worked pretty well when I was running Linux but since I’ve been using a Mac for the last couple of years I’ve been running into more situations where I have to actually compile programs myself.\n\nSo let’s talk about what you might have to do to compile a C program! I’ll use a couple of examples of specific C programs I’ve compiled and talk about a few things that can go wrong. Here are three programs we’ll be talking about compiling:\n\n- [paperjam](https://mj.ucw.cz/sw/paperjam/)\n- [sqlite](https://www.sqlite.org/download.html)\n- [qf](https://git.causal.agency/src/tree/bin/qf.c) (a pager you can run to quickly open files from a search with `rg -n THING | qf`)\n\n### [step 1: install a C compiler](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-1-install-a-c-compiler)\n\nThis is pretty simple: on an Ubuntu system if I don’t already have a C compiler I’ll install one with:\n\n```\nsudo apt-get install build-essential\n```\n\nThis installs `gcc`, `g++`, and `make`. The situation on a Mac is more confusing but it’s something like “install xcode command line tools”.\n\n### [step 2: install the program’s dependencies](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-2-install-the-program-s-dependencies)\n\nUnlike some newer programming languages, C doesn’t have a dependency manager. So if a program has any dependencies, you need to hunt them down yourself. Thankfully because of this, C programmers usually keep their dependencies very minimal and often the dependencies will be available in whatever package manager you’re using.\n\nThere’s almost always a section explaining how to get the dependencies in the README, for example in [paperjam](https://mj.ucw.cz/sw/paperjam/)’s README, it says:\n\n> To compile PaperJam, you need the headers for the libqpdf and libpaper libraries (usually available as libqpdf-dev and libpaper-dev packages).\n\n> You may need `a2x` (found in [AsciiDoc](http://www.methods.co.nz/asciidoc/a2x.1.html)) for building manual pages.\n\nSo on a Debian-based system you can install the dependencies like this.\n\n```\nsudo apt install -y libqpdf-dev libpaper-dev\n```\n\nIf a README gives a name for a package (like `libqpdf-dev`), I’d basically always assume that they mean “in a Debian-based Linux distro”: if you’re on a Mac `brew install libqpdf-dev` will not work. I still have not 100% gotten the hang of developing on a Mac yet so I don’t have many tips there yet. I guess in this case it would be `brew install qpdf` if you’re using Homebrew.\n\n### [step 3: run `./configure` (if needed)](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-3-run-configure-if-needed)\n\nSome C programs come with a `Makefile` and some instead come with a script called `./configure`. For example, if you download [sqlite’s source code](https://www.sqlite.org/download.html), it has a `./configure` script in it instead of a Makefile.\n\nMy understanding of this `./configure` script is:\n\n1. You run it, it prints out a lot of somewhat inscrutable output, and then it either generates a `Makefile` or fails because you’re missing some dependency\n2. The `./configure` script is part of a system called [autotools](https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html) that I have never needed to learn anything about beyond “run it to generate a `Makefile`”.\n\nI think there might be some options you can pass to get the `./configure` script to produce a different `Makefile` but I have never done that.\n\n### [step 4: run `make`](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-4-run-make)\n\nThe next step is to run `make` to try to build a program. Some notes about `make`:\n\n- Sometimes you can run `make -j8` to parallelize the build and make it go faster\n- It usually prints out a million compiler warnings when compiling the program. I always just ignore them. I didn’t write the software! The compiler warnings are not my problem.\n\n### [compiler errors are often dependency problems](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#compiler-errors-are-often-dependency-problems)\n\nHere’s an error I got while compiling `paperjam` on my Mac:\n\n```\n/opt/homebrew/Cellar/qpdf/12.0.0/include/qpdf/InputSource.hh:85:19: error: function definition does not declare parameters\n   85 |     qpdf_offset_t last_offset{0};\n      |                   ^\n```\n\nOver the years I’ve learned it’s usually best not to overthink problems like this: if it’s talking about `qpdf`, there’s a good change it just means that I’ve done something wrong with how I’m including the `qpdf` dependency.\n\nNow let’s talk about some ways to get the `qpdf` dependency included in the right way.\n\n### [the world’s shortest introduction to the compiler and linker](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#the-world-s-shortest-introduction-to-the-compiler-and-linker)\n\nBefore we talk about how to fix dependency problems: building C programs is split into 2 steps:\n\n1. **Compiling** the code into **object files** (with `gcc` or `clang`)\n2. **Linking** those object files into a final binary (with `ld`)\n\nIt’s important to know this when building a C program because sometimes you need to pass the right flags to the compiler and linker to tell them where to find the dependencies for the program you’re compiling.\n\n### [`make` uses environment variables to configure the compiler and linker](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#make-uses-environment-variables-to-configure-the-compiler-and-linker)\n\nIf I run `make` on my Mac to install `paperjam`, I get this error:\n\n```\nc++ -o paperjam paperjam.o pdf-tools.o parse.o cmds.o pdf.o -lqpdf -lpaper\nld: library 'qpdf' not found\n```\n\nThis is not because `qpdf` is not installed on my system (it actually is!). But the compiler and linker don’t know how to *find* the `qpdf` library. To fix this, we need to:\n\n- pass `\"-I/opt/homebrew/include\"` to the compiler (to tell it where to find the header files)\n- pass `\"-L/opt/homebrew/lib -liconv\"` to the linker (to tell it where to find library files and to link in `iconv`)\n\nAnd we can get `make` to pass those extra parameters to the compiler and linker using environment variables! To see how this works: inside `paperjam`’s Makefile you can see a bunch of environment variables, like `LDLIBS` here:\n\n```\npaperjam: $(OBJS)\n\t$(LD) -o $@ $^ $(LDLIBS)\n```\n\nEverything you put into the `LDLIBS` environment variable gets passed to the linker (`ld`) as a command line argument.\n\n### [secret environment variable: `CPPFLAGS`](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#secret-environment-variable-cppflags)\n\n`Makefiles` sometimes define their own environment variables that they pass to the compiler/linker, but `make` also has a bunch of “implicit” environment variables which it will automatically pass to the C compiler and linker. There’s a [full list of implicit environment variables here](https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html#index-CFLAGS0), but one of them is `CPPFLAGS`, which gets automatically passed to the C compiler.\n\n(technically it would be more normal to use `CXXFLAGS` for this, but this particular `Makefile` hardcodes `CXXFLAGS` so setting `CPPFLAGS` was the only way I could find to set the compiler flags without editing the `Makefile`)\n\n As an aside: it took me a long time to realize how closely tied to C/C++ \\`make\\` is -- I used to think that \\`make\\` was just a general build system (and of course you can use it for anything!) but it has a lot of affordances for building C/C++ programs that it doesn't have for building any other kind of program.\n\n### [two ways to pass environment variables to `make`](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#two-ways-to-pass-environment-variables-to-make)\n\nI learned thanks to [@zwol](https://www.owlfolio.org/) that there are actually two ways to pass environment variables to `make`:\n\n1. `CXXFLAGS=xyz make` (the usual way)\n2. `make CXXFLAGS=xyz`\n\nThe difference between them is that `make CXXFLAGS=xyz` will override the value of `CXXFLAGS` set in the `Makefile` but `CXXFLAGS=xyz make` won’t.\n\nI’m not sure which way is the norm but I’m going to use the first way in this post.\n\n### [how to use `CPPFLAGS` and `LDLIBS` to fix this compiler error](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#how-to-use-cppflags-and-ldlibs-to-fix-this-compiler-error)\n\nNow that we’ve talked about how `CPPFLAGS` and `LDLIBS` get passed to the compiler and linker, here’s the final incantation that I used to get the program to build successfully!\n\n```\nCPPFLAGS=\"-I/opt/homebrew/include\" LDLIBS=\"-L/opt/homebrew/lib -liconv\" make paperjam\n```\n\nThis passes `-I/opt/homebrew/include` to the compiler and `-L/opt/homebrew/lib -liconv` to the linker.\n\nAlso I don’t want to pretend that I “magically” knew that those were the right arguments to pass, figuring them out involved a bunch of confused Googling that I skipped over in this post. I will say that:\n\n- the `-I` compiler flag tells the compiler which directory to find header files in, like `/opt/homebrew/include/qpdf/QPDF.hh`\n- the `-L` linker flag tells the linker which directory to find libraries in, like `/opt/homebrew/lib/libqpdf.a`\n- the `-l` linker flag tells the linker which libraries to link in, like `-liconv` means “link in the `iconv` library”, or `-lm` means “link `math`”\n\n### [tip: how to just build 1 specific file: `make $FILENAME`](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#tip-how-to-just-build-1-specific-file-make-filename)\n\nYesterday I discovered this cool tool called [qf](https://git.causal.agency/src/tree/bin/qf.c) which you can use to quickly open files from the output of `ripgrep`.\n\n`qf` is in a big directory of various tools, but I only wanted to compile `qf`. So I just compiled `qf`, like this:\n\n```\nmake qf\n```\n\nBasically if you know (or can guess) the output filename of the file you’re trying to build, you can tell `make` to just build that file by running `make $FILENAME`\n\n### [tip: you don’t need a Makefile](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#tip-you-don-t-need-a-makefile)\n\nI sometimes write 5-line C programs with no dependencies, and I just learned that if I have a file called `blah.c`, I can just compile it like this without creating a `Makefile`:\n\n```\nmake blah\n```\n\nIt gets automaticaly expanded to `cc -o blah blah.c`, which saves a bit of typing. I have no idea if I’m going to remember this (I might just keep typing `gcc -o blah blah.c` anyway) but it seems like a fun trick.\n\n### [tip: look at how other packaging systems built the same C program](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#tip-look-at-how-other-packaging-systems-built-the-same-c-program)\n\nIf you’re having trouble building a C program, maybe other people had problems building it too! Every Linux distribution has build files for every package that they build, so even if you can’t install packages from that distribution directly, maybe you can get tips from that Linux distro for how to build the package. Realizing this (thanks to my friend Dave) was a huge ah-ha moment for me.\n\nFor example, [this line from the nix package for `paperjam`](https://github.com/NixOS/nixpkgs/blob/405624e81a9b65378328accb0a11c3e5369e651c/pkgs/by-name/pa/paperjam/package.nix#L35) says:\n\n```\n  env.NIX_LDFLAGS = lib.optionalString stdenv.hostPlatform.isDarwin \"-liconv\";\n```\n\nThis is basically saying “pass the linker flag `-liconv` to build this on a Mac”, so that’s a clue we could use to build it.\n\nThat same file also says `env.NIX_CFLAGS_COMPILE = \"-DPOINTERHOLDER_TRANSITION=1\";`. I’m not sure what this means, but when I try to build the `paperjam` package I do get an error about something called a `PointerHolder`, so I guess that’s somehow related to the “PointerHolder transition”.\n\n### [step 5: installing the binary](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-5-installing-the-binary)\n\nOnce you’ve managed to compile the program, probably you want to install it somewhere! Some `Makefile`s have an `install` target that let you install the tool on your system with `make install`. I’m always a bit scared of this (where is it going to put the files? what if I want to uninstall them later?), so if I’m compiling a pretty simple program I’ll often just manually copy the binary to install it instead, like this:\n\n```\ncp qf ~/bin\n```\n\n### [step 6: maybe make your own package!](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#step-6-maybe-make-your-own-package)\n\nOnce I figured out how to do all of this, I realized that I could use my new `make` knowledge to contribute a `paperjam` package to Homebrew! Then I could just `brew install paperjam` on future systems.\n\nThe good thing is that even if the details of how all of the different packaging systems, they fundamentally all use C compilers and linkers.\n\n### [it can be useful to understand a little about C even if you’re not a C programmer](https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/#it-can-be-useful-to-understand-a-little-about-c-even-if-you-re-not-a-c-programmer)\n\nI think all of this is an interesting example of how it can useful to understand some basics of how C programs work (like “they have header files”) even if you’re never planning to write a nontrivial C program if your life.\n\nIt feels good to have some ability to compile C/C++ programs myself, even though I’m still not totally confident about all of the compiler and linker flags and I still plan to never learn anything about how autotools works other than “you run `./configure` to generate the `Makefile`”.\n\nTwo things I left out of this post:\n\n- `LD_LIBRARY_PATH / DYLD_LIBRARY_PATH` (which you use to tell the dynamic linker at runtime where to find dynamically linked files) because I can’t remember the last time I ran into an `LD_LIBRARY_PATH` issue and couldn’t find an example.\n- `pkg-config`, which I think is important but I don’t understand yet\n",
  "dateCreated": "2026-09-19T21:30:23.395188495+00:00",
  "date_created": "2025-06-10T00:00:00+00:00",
  "date_modified": "2025-06-10T00:00:00+00:00",
  "date_published": "2025-06-10T00:00:00+00:00",
  "description": "I have never been a C programmer but every so often I need to compile a C/C++ program from source. This has been kind of a struggle for me: for a long time, my approach was basically “install the dependencies, run make, if it doesn’t work,…",
  "external_url": "https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/",
  "git": {
    "commit": "ebb0941c5c06a2ab2377342d607535262a3e969e",
    "path": "items/jvns-ca/2025/06/2025-06-10-using-make-to-compile-c-programs-for-non-c-programmers.md",
    "permalink": "https://github.com/aymericbeaumet/aggr-instance/blob/ebb0941c5c06a2ab2377342d607535262a3e969e/items/jvns-ca/2025/06/2025-06-10-using-make-to-compile-c-programs-for-non-c-programmers.md"
  },
  "id": "urn:aggr:item:00dae2af311ffa436f66661f4614f067e10fb439",
  "isBasedOn": {
    "@id": "https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/"
  },
  "mainEntity": {
    "@id": "https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/",
    "@type": "CreativeWork",
    "archivedAt": {
      "@id": "https://aggr.aymericbeaumet.com/items/jvns-ca/2025-06-10-using-make-to-compile-c-programs-for-non-c-programmers/#webpage"
    },
    "dateModified": "2025-06-10T00:00:00+00:00",
    "datePublished": "2025-06-10T00:00:00+00:00",
    "headline": "Using `make` to compile C programs (for non-C-programmers)",
    "timeRequired": "PT10M",
    "url": "https://jvns.ca/blog/2025/06/10/how-to-compile-a-c-program/",
    "wordCount": 2026
  },
  "name": "Archived snapshot: Using `make` to compile C programs (for non-C-programmers)",
  "preview": null,
  "reading_minutes": 10,
  "replicated_at": null,
  "source": "jvns-ca",
  "source_name": "Julia Evans",
  "summary": null,
  "tags": [],
  "temporalCoverage": "2026-09-19T21:30:23.395188495+00:00",
  "title": "Using `make` to compile C programs (for non-C-programmers)",
  "url": "https://aggr.aymericbeaumet.com/items/jvns-ca/2025-06-10-using-make-to-compile-c-programs-for-non-c-programmers/",
  "word_count": 2026
}