{
    "archive_path": "archive/1783447154.234745",
    "base_url": "nuclear.mutantstargoat.com/articles/make",
    "basename": "",
    "bookmarked_date": "2026-07-07 17:59",
    "canonical": {
        "archive_org_path": "https://web.archive.org/web/nuclear.mutantstargoat.com/articles/make",
        "dom_path": "output.html",
        "favicon_path": "favicon.ico",
        "git_path": "git/",
        "google_favicon_path": "https://www.google.com/s2/favicons?domain=nuclear.mutantstargoat.com",
        "headers_path": "headers.json",
        "htmltotext_path": "htmltotext.txt",
        "index_path": "index.html",
        "media_path": "media/",
        "mercury_path": "mercury/content.html",
        "pdf_path": "output.pdf",
        "readability_path": "readability/content.html",
        "screenshot_path": "screenshot.png",
        "singlefile_path": "singlefile.html",
        "warc_path": "warc/",
        "wget_path": "nuclear.mutantstargoat.com/articles/make/index.html"
    },
    "domain": "nuclear.mutantstargoat.com",
    "extension": "",
    "hash": "1BZ3HDPWDMXC6Z8RD7MW",
    "history": {
        "archive_org": [
            {
                "cmd": [
                    "curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--head",
                    "--max-time",
                    "60",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.3 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.10.1 (x86_64-pc-linux-gnu)",
                    "https://web.archive.org/save/http://nuclear.mutantstargoat.com/articles/make/"
                ],
                "cmd_version": "curl 8.10.1 (x86_64-pc-linux-gnu)",
                "end_ts": "2026-07-07T18:07:01.440680+00:00",
                "index_texts": null,
                "output": "TimeoutExpired: Command '['curl', '--silent', '--location', '--compressed', '--head', '--max-time', '60', '--user-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.3 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.10.1 (x86_64-pc-linux-gnu)', 'https://web.archive.org/save/http://nuclear.mutantstargoat.com/articles/make/']' timed out after 60 seconds",
                "pwd": "/data/archive/1783447154.234745",
                "schema": "ArchiveResult",
                "start_ts": "2026-07-07T18:06:01.426671+00:00",
                "status": "failed"
            }
        ],
        "dom": [
            {
                "cmd": [
                    "/usr/bin/chromium-browser",
                    "--headless=new",
                    "--no-sandbox",
                    "--no-zygote",
                    "--disable-dev-shm-usage",
                    "--disable-software-rasterizer",
                    "--run-all-compositor-stages-before-draw",
                    "--hide-scrollbars",
                    "--window-size=1440,2000",
                    "--autoplay-policy=no-user-gesture-required",
                    "--no-first-run",
                    "--use-fake-ui-for-media-stream",
                    "--use-fake-device-for-media-stream",
                    "--disable-sync",
                    "--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.3 (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "--window-size=1440,2000",
                    "--dump-dom",
                    "http://nuclear.mutantstargoat.com/articles/make/"
                ],
                "cmd_version": "Chromium 131.0.6778.33",
                "end_ts": "2026-07-07T18:05:57.324198+00:00",
                "index_texts": null,
                "output": "output.html",
                "pwd": "/data/archive/1783447154.234745",
                "schema": "ArchiveResult",
                "start_ts": "2026-07-07T18:05:56.449153+00:00",
                "status": "succeeded"
            }
        ],
        "favicon": [
            {
                "cmd": [
                    "curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--max-time",
                    "60",
                    "--output",
                    "favicon.ico",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.3 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.10.1 (x86_64-pc-linux-gnu)",
                    "https://www.google.com/s2/favicons?domain=nuclear.mutantstargoat.com"
                ],
                "cmd_version": "curl 8.10.1 (x86_64-pc-linux-gnu)",
                "end_ts": "2026-07-07T18:05:46.600905+00:00",
                "index_texts": null,
                "output": "favicon.ico",
                "pwd": "/data/archive/1783447154.234745",
                "schema": "ArchiveResult",
                "start_ts": "2026-07-07T18:05:46.415591+00:00",
                "status": "succeeded"
            }
        ],
        "git": [],
        "headers": [
            {
                "cmd": [
                    "curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--head",
                    "--max-time",
                    "60",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.3 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.10.1 (x86_64-pc-linux-gnu)",
                    "http://nuclear.mutantstargoat.com/articles/make/"
                ],
                "cmd_version": "curl 8.10.1 (x86_64-pc-linux-gnu)",
                "end_ts": "2026-07-07T18:05:46.788549+00:00",
                "index_texts": null,
                "output": "headers.json",
                "pwd": "/data/archive/1783447154.234745",
                "schema": "ArchiveResult",
                "start_ts": "2026-07-07T18:05:46.603418+00:00",
                "status": "succeeded"
            }
        ],
        "htmltotext": [],
        "media": [
            {
                "cmd": [
                    "yt-dlp",
                    "--write-description",
                    "--write-info-json",
                    "--write-annotations",
                    "--write-thumbnail",
                    "--no-call-home",
                    "--write-sub",
                    "--write-auto-subs",
                    "--convert-subs=srt",
                    "--yes-playlist",
                    "--continue",
                    "--no-abort-on-error",
                    "--ignore-errors",
                    "--geo-bypass",
                    "--add-metadata",
                    "--format=(bv*+ba/b)[filesize<=750m][filesize_approx<=?750m]/(bv*+ba/b)",
                    "http://nuclear.mutantstargoat.com/articles/make/"
                ],
                "cmd_version": "2024.12.13",
                "end_ts": "2026-07-07T18:06:01.423571+00:00",
                "index_texts": [],
                "output": "media",
                "pwd": "/data/archive/1783447154.234745",
                "schema": "ArchiveResult",
                "start_ts": "2026-07-07T18:05:59.756329+00:00",
                "status": "succeeded"
            }
        ],
        "mercury": [
            {
                "cmd": [
                    "/app/node_modules/@postlight/parser/cli.js",
                    "http://nuclear.mutantstargoat.com/articles/make/"
                ],
                "cmd_version": "1.0.0",
                "end_ts": "2026-07-07T18:05:59.742058+00:00",
                "index_texts": null,
                "output": "mercury",
                "pwd": "/data/archive/1783447154.234745",
                "schema": "ArchiveResult",
                "start_ts": "2026-07-07T18:05:58.304905+00:00",
                "status": "succeeded"
            }
        ],
        "pdf": [
            {
                "cmd": [
                    "/usr/bin/chromium-browser",
                    "--headless=new",
                    "--no-sandbox",
                    "--no-zygote",
                    "--disable-dev-shm-usage",
                    "--disable-software-rasterizer",
                    "--run-all-compositor-stages-before-draw",
                    "--hide-scrollbars",
                    "--window-size=1440,2000",
                    "--autoplay-policy=no-user-gesture-required",
                    "--no-first-run",
                    "--use-fake-ui-for-media-stream",
                    "--use-fake-device-for-media-stream",
                    "--disable-sync",
                    "--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.3 (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "--window-size=1440,2000",
                    "--print-to-pdf",
                    "http://nuclear.mutantstargoat.com/articles/make/"
                ],
                "cmd_version": "Chromium 131.0.6778.33",
                "end_ts": "2026-07-07T18:05:55.224155+00:00",
                "index_texts": null,
                "output": "output.pdf",
                "pwd": "/data/archive/1783447154.234745",
                "schema": "ArchiveResult",
                "start_ts": "2026-07-07T18:05:54.088601+00:00",
                "status": "succeeded"
            }
        ],
        "readability": [
            {
                "cmd": [
                    "/app/node_modules/readability-extractor/readability-extractor",
                    "/tmp/tmpbljdz0wh",
                    "http://nuclear.mutantstargoat.com/articles/make/"
                ],
                "cmd_version": "0.0.11",
                "end_ts": "2026-07-07T18:05:58.302590+00:00",
                "index_texts": [
                    "Practical Makefiles, by example\n\n\n\n\nAuthor:\nJohn Tsiombikas\nContact:\nnuclear@mutantstargoat.com\nDate:\n9 Feb 2015\nLast update:2 June 2022\n\n\n\n\nContents\n\n1\u00a0\u00a0\u00a0Rationale\n2\u00a0\u00a0\u00a0Building C/C++ programs\n3\u00a0\u00a0\u00a0Enter make\n4\u00a0\u00a0\u00a0Simpler makefile\n5\u00a0\u00a0\u00a0A makefile for 99% of your programs\n6\u00a0\u00a0\u00a0Further improvements\n6.1\u00a0\u00a0\u00a0Multiple source directories\n6.2\u00a0\u00a0\u00a0Handling cross-platform differences\n6.3\u00a0\u00a0\u00a0Wildcard rules\n6.4\u00a0\u00a0\u00a0Automatic #include dependency tracking\n6.5\u00a0\u00a0\u00a0Improved automatic dependency tracking\n6.6\u00a0\u00a0\u00a0Building sub-projects\n\n\n7\u00a0\u00a0\u00a0Going the extra mile for release\n7.1\u00a0\u00a0\u00a0Writing install/uninstall rules\n7.2\u00a0\u00a0\u00a0Installing libraries\n7.3\u00a0\u00a0\u00a0Build options\n\n\n\n\n\n1\u00a0\u00a0\u00a0Rationale\nThe purpose of this document is to explain how to write practical makefiles for\nyour everyday hacks and projects. I want to illustrate, how easy it is to use\nmake for building your programs, and doing so, dispel the notion that resorting\nto big clunky graphical IDEs, or makefile generators such as autotools or cmake,\nis the way to focus on your code faster.\nThe purpose of this document is certainly not, to teach you how to write\nuser-friendly, all-encompassing, release-quality build systems. You can\ncertainly arrive to that by extending a simple makefile, and I'll give you a few\npointers about that sort of thing in the end, but I will not focus on complexity\nand completeness, but rather on simplicity and practicality.\nFinally, it's worth spelling out up front that when I say \"make\", I'm really\nreferring to the GNU implementation of make, which is packed with useful\nfeatures, not necessarily found in other make programs out there. It might be\ninteresting to write a follow-up article, providing similar examples for other\nmake implementations out there, such as BSD make, Watcom make, Borland make, or\nMicrosoft's nmake.  But I'll leave that for another time.\n\n\n2\u00a0\u00a0\u00a0Building C/C++ programs\nFirst let's consider an example C program, and how to build it without make.\nIn order to escape the trap of an oversimplified toy program, let's assume\nwe're making a small computer game. Our game is made up of the following\nsource and header files:\n\n\n\n\n\n\nmain.c\nentry point and system event loop\n\ngame.h & game.c\nevent handlers and game update/redraw loop\n\nplayer.h & player.c\nplayer state and input\n\nlevel.h & level.c\ngame world state\n\nenemy.h & enemy.c\nenemy state and AI\n\nrenderer.h & renderer.c\ngraphics algorithms / game drawing\n\nvecmath.h & vecmath.c\nvector & matrix math needed for simulation and rendering\n\nimage.h & image.c\nimage loading for textures\n\nmesh.h & mesh.c\n3D mesh data structures & loading\n\n\n\nAlso, our game depends on a number of 3rd party libraries:\n\n\n\n\n\n\nOpenGL\nLow level graphics library\n\nGLUT\nCross-platform OpenGL window setup & event handling\n\nlibpng\nPNG image file format reader/decoder\n\nzlib\nNeeded by libpng for lz77 decompression\n\n\n\nTo build this program we would first have to run the compiler on each\nsource file, generating a corresponding object code file:\ncc -c main.c\ncc -c game.c\ncc -c level.c\n...\n\nThen, we would have to feed all the object files to the linker (possibly\nstill using the C compiler front-end for convenience), instructing it to link\nall the libraries we need as well:\ncc -o mygame *.o -lGL -lglut -lpng -lz -lm\n\nObviously, such tediousness could be automated with a shell script.\nHowever, that would be a grossly sub-optimal solution, as it would recompile\nall source files from scratch, every time we try to build with it.\n\n\n3\u00a0\u00a0\u00a0Enter make\nMake is a much more elegant solution to our building problems. We create a file\nnamed Makefile, which contains a set of rules describing build products,\ntheir dependencies, and which commands are needed to build them. Then when we\nask make to build our program binary, it recursively traverses the dependency\ngraph to figure out which parts need to be re-created, based on the\nlast-modified timestamps of the target file, and its dependency files.\nWarning: do not run away terrified by the first makefile example. It's only\nmeant to illustrate how make works, so it's needlessly verbose. If you're easily\nscared, skip to section: \"A makefile for 99% of your programs\".\nBack to our game example, the binary mygame, obviously depends on\nmain.o (among other object files). main.o in turn, is created by\ncompiling main.c. So, let's define two make rules for creating these two\nfiles:\nmygame: main.o player.o enemy.o renderer.o vecmath.o image.o mesh.o\n    cc -o mygame main.o player.o enemy.o renderer.o vecmath.o image.o mesh.o -lGL -lglut -lpng -lz -lm\n\nmain.o: main.c\n    cc -c main.c\n\nplayer.o: player.c\n    cc -c player.c\n\n\n\nEssentially for each rule, the part before the colon is the filename we want to\nbuild when this rule is executed, the part after the colon is the list of\ndependencies, i.e. what is needed to create this rule's target. And finally, in\nsubsequent lines, we just type the commands needed to make this happen.\nNote that each line in the commands part of a rule needs to begin with a leading\ntab.  Spaces will not work, it really has to be a tab character, so pay extra\nattention to your text editor settings, about how it treats any tabs you enter.\nSo, if we modify main.c, and then type make (which means: build the\nfirst rule in the makefile), make will try to build the file named mygame.\nIn order to build it, make will have to use the declared dependency files, so it\nwill  execute each rule corresponding to those files in turn, starting from\nmain.o.\nTo build main.o, make sees that it needs main.c, which having been\nmodified more recently than main.o, is deemed out of date and must be\nre-created by executing the command specified in this rule.\nSimilarly, returning to the initial rule, since now main.o is\nnewer than the old mygame binary, the binary will be rebuilt by running the\nlinking command, after visiting each object file rule in turn and determining\nthat none of them needs rebuilding, since we didn't touch any of their\ndependencies.\n\n\n4\u00a0\u00a0\u00a0Simpler makefile\nObviously, having to type rules for each of our source files is tedious, and\nthankfully unnecessary. Make actually knows how to create object code from C\nsource files, so we can skip the object file rules, and also provides some handy\nvariables for referring to the target or dependency files in rule commands\nwithout having to re-type everything. So here is a slightly simpler makefile for\nthe same task:\nmyprog: main.o game.o level.o player.o enemy.o renderer.o vecmath.o image.o mesh.o\n    cc -o $@ $^ -lGL -lglut -lpng -lz -lm\n\n@ is a built-in make variable containing the target of each rule (myprog in\nthis case), and ^ is another built-in variable containing all the\ndependencies of each rule (so the list of object files in this case). Similarly,\nalthough not useful for this particular rule, there is also the < variable,\nwhich contains only the first element of the dependencies (so main.o if we\nused it in the rule above).\nWe substitute the value of any variable (built-in\nor not) by prepending a dollar sign to its name. However, one peculiarity of\nthe make syntax, as opposed to say the bourne shell syntax, is that only the\nfirst character following the dollar sign is considered to be the variable\nname. If we want to use longer names, we have to parenthesize the name before\napplying the dollar sign to extract its value. So for instance if we had\ndefined a variable: obj = main.o game.o level.o ... (etc) ...,\nthen to substitute its contents we have to use parentheses:\n$(obj). If we instead tried to use $obj then make\nwould try to expand a variable named \"o\", and then append the string \"bj\" to\nthe result.\n\n\n5\u00a0\u00a0\u00a0A makefile for 99% of your programs\nThere's one last thing bugging me in the last example. I hate having to type\nmanually the list of object files needed to build the program. Thankfully it\nturns out we don't need to do that either. See the following example:\nsrc = $(wildcard *.c)\nobj = $(src:.c=.o)\n\nLDFLAGS = -lGL -lglut -lpng -lz -lm\n\nmyprog: $(obj)\n    $(CC) -o $@ $^ $(LDFLAGS)\n\n.PHONY: clean\nclean:\n    rm -f $(obj) myprog\n\nThe first line of this example collects all source files in the current\ndirectory in the src variable. Then, the second line transforms the contents\nof the src variable, changing all file suffixes from .c to .o, thus\nconstructing the object file list we need.\nI also used a new variable called LDFLAGS for the list of libraries\nrequired during linking (LDFLAGS is conventionally used for this usage,\nwhile similarly CFLAGS and CXXFLAGS can be used to pass flags to the C\nand C++ compilers respectively).\nFinally, I added a new rule for cleaning up every target, in order to rebuild\nthe whole program from scratch. The clean rule is marked as phony, because\nits target is not an actual file that will be generated, but just an arbitrary\nname that we wish to use for executing this rule. In order to run any rule\nother than the first one, the user needs to pass the target name as a\ncommand-line argument to make. So, for instance, to clean everything in this\nexample, just type make clean.\n\n\n6\u00a0\u00a0\u00a0Further improvements\nThe makefile listed above is sufficient for most small programs you'll ever\nwrite. In this section, I'll go over a few improvements for larger projects.\n\n6.1\u00a0\u00a0\u00a0Multiple source directories\nLet's say you have some source files under src/, other source files under,\nsrc/engine, and some more source files under src/audio. Also, to make it\nmore interesting, let's assume that your code is a mix of C and C++ in any of\nthese directories. Here's a very simple modification which handles this\nsituation:\ncsrc = $(wildcard src/*.c) \\\n       $(wildcard src/engine/*.c) \\\n       $(wildcard src/audio/*.c)\nccsrc = $(wildcard src/*.cc) \\\n        $(wildcard src/engine/*.cc) \\\n        $(wildcard src/audio/*.cc)\nobj = $(csrc:.c=.o) $(ccsrc:.cc=.o)\n\nLDFLAGS = -lGL -lglut -lpng -lz -lm\n\nmygame: $(obj)\n    $(CXX) -o $@ $^ $(LDFLAGS)\n\nThe rest is exactly the same as previously. Note that I used the CXX\nbuilt-in variable, which defaults to c++ instead of cc, to invoke the\nlinker in the correct way for C++ programs, automatically linking libstdc++\nwith it. Also, backslashes at the end of lines serve to merge multiple lines\ntogether by escaping the newline character.\n\n\n6.2\u00a0\u00a0\u00a0Handling cross-platform differences\nSo far, in all the examples, I've linked OpenGL and GLUT by passing the -lGL\n-lglut flags to the linker. That's how reasonable UNIX systems do it, but what\nif we want to build our project on vaguely UNIX-like abominations like MacOSX,\nwhere you have to use the following command-line arguments: -framework OpenGL\n-framework GLUT (since OpenGL and GLUT they are \"frameworks\" and not just\nlibraries ...  *sigh*) ? Simple; use uname -s to figure out if we're\nbuilding on MacOSX, and modify the LDFLAGS variable accordingly:\nLDFLAGS = $(libgl) -lpng -lz -lm\n\n    libgl = -framework OpenGL -framework GLUT\n    libgl = -lGL -lglut\n\n\nThe order of defining libgl and defining LDFLAGS doesn't matter, as long\nas they're both before the first rule which uses either of them, since\nLDFLAGS won't be evaluated (and thus require substitution of the libgl\nvariable) until it's used in a rule.\nA slightly more elegant, but not exactly equivalent way of doing the above would\nbe the following instead:\nLDFLAGS = $(libgl_$(shell uname -s)) -lpng -lz -lm\n\nlibgl_Linux = -lGL -lglut\nlibgl_Darwin = -framework OpenGL -framework GLUT\n\nI said not exactly equivalent, because in this way we would have to enumerate\nall possible UNIX systems we'd like to support instead of just handling them in\nthe else part of the conditional statement.\n\n\n6.3\u00a0\u00a0\u00a0Wildcard rules\nAs I mentioned above, make knows how to compile C and C++ source files to create\nobject code, and you don't have to explicitly write rules of the form:\nmain.o: main.c\n    $(CC) $(CFLAGS) -o $@ -c $<\n\nBut let's say you want to compile code for a new language, which make knows\nnothing about. There is a mechanism to write generic rules, so that you won't\nhave to laboriously type specific rules for each file you wish to compile.\nIf you wish to inform make, on the generic form of rules for building object\nfiles out of source files in a fictitious foo language, which are commonly in\nfiles with a .foo suffix, and compiled by running fooc -c, you can write\nthe following wildcard rule:\n%.o: %.foo\n    fooc $(FOOFLAGS) -o $@ -c $<\n\nThen, whenever make needs to build xyzzy.o, and there is a file named\nxyzzy.foo, it will use that rule to compile it.\n\n\n6.4\u00a0\u00a0\u00a0Automatic #include dependency tracking\n\nUpdate: it turns out there's a better way to do this, make sure to also read\nthe next section for an improved version. Thanks to Dan Raymond for pointing\nit out.  I'm keeping this section in the document for posterity, and because\nI don't want to reformulate the explanations of how this works and why it's\nnecessary, which are still valid.\nThe built-in rule for compiling C source files, is similar to:\n%.o: %.c\n    $(CC) $(CFLAGS) -o $@ -c $<\n\nWhich means, that if we change foo.c, foo.o will be out of date and will\nbe rebuilt automatically. There are, however, usually many more dependencies\nthat need to be considered to determine if foo.o needs rebuilding, which\nfall through the cracks in this simple but generic scheme.\nSpecifically, if foo.c also includes foo.h and bar.h, then if we\nfail to rebuild foo.o when either of these header files change, will result\nin a potentially incorrect program, and depending on the nature of the\nmodifications, possibly memory corruption, and hopefully segmentation faults at\nruntime.\nFor simple hacks with a couple of source and header files, we can afford to just\nclean and rebuild any time we change header files significantly, for larger\nprograms however, we'd really like to have our build system track dependencies\ndue to header file inclusion too.\nMake can't possibly know what constitutes a dependency, and include a syntactic\nanalyzer to detect it for each and every language, which is why this doesn't\nhappen automatically. However with the help of our compiler's pre-processor, we\ncan create the necessary rules to do it.\nWe'll write a wildcard rule to generate makefile fragments with an explicit rule\nfor each source file in our project, which also includes header files in the\ndependency list. Then we'll instruct make to include these makefile fragments in\nour makefile as if we wrote them by hand. Any missing makefile fragments will be\nautomatically generated with our wildcard rule during this inclusion. Finally\nwe'll add a rule to clean all these dependency files (the makefile fragments).\nsrc = $(wildcard *.c)\nobj = $(src:.c=.o)\ndep = $(obj:.o=.d)  \nLDFLAGS = -lGL -lglut -lpng -lz -lm\n\nmygame: $(obj)\n    $(CC) -o $@ $^ $(LDFLAGS)\n\n\n%.d: %.c\n    @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@\n\n.PHONY: clean\nclean:\n    rm -f $(obj) mygame\n\n.PHONY: cleandep\ncleandep:\n    rm -f $(dep)\n\nThe added lines are marked with comments in the example above. To see exactly\nwhat the C preprocessor outputs when instructed to write dependency information,\ntry running the following in a file with a single include statement:\ncpp foo.c -MM -MT foo.o\n\nThat's right, it just outputs a single makefile rule! Which is exactly what we\nneed for make to track all the dependencies correctly.\n\n\n6.5\u00a0\u00a0\u00a0Improved automatic dependency tracking\nThe dependency generation mechanism described in the previous section, is\nslightly more complicated than necessary, and requires a separate (although\nautomatic) pass through the source files to generate the dependency files, which\nconsumes significant time on older machines or huge projects.\nThere's a way to instruct the compiler (GCC and clang supports this, more\ncompilers might do so too), to generate the necessary makefile fragment (.d\nfile) at the same time as it compiles each source file. This is done by\npassing the -MMD option to the compiler.\nThe absence of the .d files during the first make will not be detrimental,\nbecause at that point we're doing a full build anyway, and subsequent make\ninvocations will have the necessary makefile fragments included to aid in\ndeciding which source files need to be rebuilt because a header file has\nchanged.\nThis approach, apart from reducing initial build times, also simplifies our\nmakefile from the previous section slightly, since there's no need for a custom\n%.d: %.c rule for generating the dependency files:\nsrc = $(wildcard *.c)\nobj = $(src:.c=.o)\ndep = $(obj:.o=.d)  \nCFLAGS = -MMD     LDFLAGS = -lGL -lglut -lpng -lz -lm\n\nmygame: $(obj)\n    $(CC) -o $@ $^ $(LDFLAGS)\n\n\n.PHONY: clean\nclean:\n    rm -f $(obj) mygame\n\n.PHONY: cleandep\ncleandep:\n    rm -f $(dep)\n\nThe only changes from the previous example, is the removal of the wildcard rule,\nand the addition of -MMD in CFLAGS.\n\n\n6.6\u00a0\u00a0\u00a0Building sub-projects\nConsider the following case: our game has source files in src/, but we also\nneed to use an external library called libfoo, which is not a system-wide\ninstalled library, but one we want to carry with our source tree under\nlibs/foo. How would we integrate that in our build system?\nVery simply, instruct make to first build libfoo, by running make after\nchanging to the libs/foo directory, and then link it as usual:\nsrc = $(wildcard src/*.c)\nobj = $(src:.c=.o)\n\nLDFLAGS = -Llibs/foo -lfoo -lGL -lglut -lpng -lz -lm\n\nmygame: $(obj) libfoo\n    $(CC) -o $@ $^ $(LDFLAGS)\n\n.PHONY: clean\n    rm -f $(obj) mygame\n\n.PHONY: libfoo\nlibfoo:\n    $(MAKE) -C libs/foo\n\nBy adding the phony libfoo rule as a dependency to our binary linking rule,\nwe essentially force make to always change into libs/foo and run make before\nlinking our binary. If there is no need to actually rebuild libfoo because\nit has no changes, that makefile will do nothing and return immediately, so\nthere's no harm in trying every time.\nArbitrarily complex programs can be built by using a similar approach, to build\nvarious modules as separate libraries. So this little snippet really scales our\nsimple build system to easily handle huge projects as well."
                ],
                "output": "readability",
                "pwd": "/data/archive/1783447154.234745",
                "schema": "ArchiveResult",
                "start_ts": "2026-07-07T18:05:57.525478+00:00",
                "status": "succeeded"
            }
        ],
        "screenshot": [
            {
                "cmd": [
                    "/usr/bin/chromium-browser",
                    "--headless=new",
                    "--no-sandbox",
                    "--no-zygote",
                    "--disable-dev-shm-usage",
                    "--disable-software-rasterizer",
                    "--run-all-compositor-stages-before-draw",
                    "--hide-scrollbars",
                    "--window-size=1440,2000",
                    "--autoplay-policy=no-user-gesture-required",
                    "--no-first-run",
                    "--use-fake-ui-for-media-stream",
                    "--use-fake-device-for-media-stream",
                    "--disable-sync",
                    "--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.3 (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "--window-size=1440,2000",
                    "--screenshot",
                    "http://nuclear.mutantstargoat.com/articles/make/"
                ],
                "cmd_version": "Chromium 131.0.6778.33",
                "end_ts": "2026-07-07T18:05:56.446867+00:00",
                "index_texts": null,
                "output": "screenshot.png",
                "pwd": "/data/archive/1783447154.234745",
                "schema": "ArchiveResult",
                "start_ts": "2026-07-07T18:05:55.226731+00:00",
                "status": "succeeded"
            }
        ],
        "singlefile": [
            {
                "cmd": [
                    "/app/node_modules/single-file-cli/single-file",
                    "--browser-executable-path=chromium-browser",
                    "--browser-args=[\"--headless=new\", \"--no-sandbox\", \"--no-zygote\", \"--disable-dev-shm-usage\", \"--disable-software-rasterizer\", \"--run-all-compositor-stages-before-draw\", \"--hide-scrollbars\", \"--window-size=1440,2000\", \"--autoplay-policy=no-user-gesture-required\", \"--no-first-run\", \"--use-fake-ui-for-media-stream\", \"--use-fake-device-for-media-stream\", \"--disable-sync\", \"--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.3 (+https://github.com/ArchiveBox/ArchiveBox/)\", \"--window-size=1440,2000\"]",
                    "http://nuclear.mutantstargoat.com/articles/make/",
                    "singlefile.html"
                ],
                "cmd_version": "1.1.54",
                "end_ts": "2026-07-07T18:05:54.084628+00:00",
                "index_texts": null,
                "output": "singlefile.html",
                "pwd": "/data/archive/1783447154.234745",
                "schema": "ArchiveResult",
                "start_ts": "2026-07-07T18:05:46.790967+00:00",
                "status": "succeeded"
            }
        ],
        "title": [
            {
                "cmd": [
                    "curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--max-time",
                    "60",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.3 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.10.1 (x86_64-pc-linux-gnu)",
                    "http://nuclear.mutantstargoat.com/articles/make/"
                ],
                "cmd_version": "curl 8.10.1 (x86_64-pc-linux-gnu)",
                "end_ts": "2026-07-07T18:05:57.523726+00:00",
                "index_texts": null,
                "output": "Practical Makefiles, by example",
                "pwd": "/data/archive/1783447154.234745",
                "schema": "ArchiveResult",
                "start_ts": "2026-07-07T18:05:57.514791+00:00",
                "status": "succeeded"
            }
        ],
        "wget": [
            {
                "cmd": [
                    "wget",
                    "--no-verbose",
                    "--adjust-extension",
                    "--convert-links",
                    "--force-directories",
                    "--backup-converted",
                    "--span-hosts",
                    "--no-parent",
                    "-e",
                    "robots=off",
                    "--timeout=60",
                    "--restrict-file-names=windows",
                    "--warc-file=/data/archive/1783447154.234745/warc/1783447557",
                    "--page-requisites",
                    "--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.3 (+https://github.com/ArchiveBox/ArchiveBox/) wget/GNU Wget 1.21.3",
                    "--compression=auto",
                    "http://nuclear.mutantstargoat.com/articles/make/"
                ],
                "cmd_version": "GNU Wget 1.21.3",
                "end_ts": "2026-07-07T18:05:57.512368+00:00",
                "index_texts": null,
                "output": "nuclear.mutantstargoat.com/articles/make/index.html",
                "pwd": "/data/archive/1783447154.234745",
                "schema": "ArchiveResult",
                "start_ts": "2026-07-07T18:05:57.326342+00:00",
                "status": "succeeded"
            }
        ]
    },
    "icons": null,
    "is_archived": true,
    "is_static": false,
    "latest": {
        "archive_org": "TimeoutExpired: Command '['curl', '--silent', '--location', '--compressed', '--head', '--max-time', '60', '--user-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.3 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.10.1 (x86_64-pc-linux-gnu)', 'https://web.archive.org/save/http://nuclear.mutantstargoat.com/articles/make/']' timed out after 60 seconds",
        "dom": "output.html",
        "favicon": "favicon.ico",
        "git": null,
        "media": "media",
        "pdf": "output.pdf",
        "screenshot": "screenshot.png",
        "singlefile": "singlefile.html",
        "title": "Practical Makefiles, by example",
        "warc": null,
        "wget": "nuclear.mutantstargoat.com/articles/make/index.html"
    },
    "link_dir": "/data/archive/1783447154.234745",
    "newest_archive_date": "2026-07-07T18:06:01.426671+00:00",
    "num_failures": 1,
    "num_outputs": 11,
    "oldest_archive_date": "2026-07-07T18:05:46.415591+00:00",
    "path": "/articles/make/",
    "schema": "Link",
    "scheme": "http",
    "snapshot_id": "ccd87ea1-73f0-40e5-8327-5a3b068f9426",
    "sources": [
        "/data/sources/1783447153.649778-crawl-nuclear.mutantstargoat.com.txt"
    ],
    "tags": null,
    "tags_str": "",
    "timestamp": "1783447154.234745",
    "title": "Practical Makefiles, by example",
    "updated": "2026-07-07T18:05:46.415453+00:00",
    "updated_date": "2026-07-07 18:05",
    "url": "http://nuclear.mutantstargoat.com/articles/make/"
}