{"id":116,"date":"2026-10-12T08:31:20","date_gmt":"2026-10-12T08:31:20","guid":{"rendered":"http:\/\/www.scmgalaxy.com\/tutorials\/2009\/01\/29\/makefile\/"},"modified":"2026-10-12T08:31:20","modified_gmt":"2026-10-12T08:31:20","slug":"makefile","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/makefile\/","title":{"rendered":"Makefile Guide for C Libraries: Build Static and Shared Targets Cleanly"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">This guide explains a classic non-recursive <code>Makefile<\/code> for a C project that builds both a static library (<code>.a<\/code>) and a shared library (<code>.so<\/code>). You will learn what each target does, how <code>make<\/code> executes the workflow, and how to modernize the file while keeping its original intent.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Originally published on 2009-01-29; fully revised and updated.<\/em><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2009\/01\/makefile.png\" alt=\"makefile example from original post\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">What the original Makefile is doing<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The original post shows an older, single-file build style: source files are listed directly, object files are derived from that list, and two library targets are built from the same objects. This is still a valid approach for small C projects.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript language-makefile\">CC = gcc\nCFLAGS = -Wall -O2\nLFLAGS +=\n\nSRCS = cas.c \\\n       client.c \\\n       hashtable_itr.c\nOBJS = $(SRCS:.c=.o)\n\n<span class=\"hljs-attr\">all<\/span>: libmarquis.a libmarquis.so\n\n<span class=\"hljs-attr\">clean<\/span>:\n\\trm -f libmarquis.a libmarquis.so *.o\n\nlibmarquis.a: $(OBJS)\n\\tar -r $@ $(OBJS)\n\nlibmarquis.so: $(OBJS)\n\\t$(CC) -shared $(CFLAGS) -fPIC -Wl,-soname,libmarquis.so -o $@ $(OBJS)\n\n.c.o:\n\\t$(CC) $(CFLAGS) -c $&lt;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>Important:<\/strong> recipe lines in a Makefile must begin with a real tab character. If tabs are converted to spaces, builds fail with a \u201cmissing separator\u201d error.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Build workflow at a glance<\/h2>\n\n\n\n<pre class=\"mermaid\">\nflowchart TD\n    A[Run make all] --> B[Compile each C file into object file]\n    B --> C[Archive static library libmarquis.a]\n    B --> D[Link shared library libmarquis.so]\n    C --> E[Artifacts ready]\n    D --> E\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Static vs shared library targets<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Target<\/th><th>Output<\/th><th>Command style<\/th><th>Typical use<\/th><\/tr><\/thead><tbody><tr><td><code>libmarquis.a<\/code><\/td><td>Static archive<\/td><td><code>ar -r ...<\/code><\/td><td>Link code into binaries at build time<\/td><\/tr><tr><td><code>libmarquis.so<\/code><\/td><td>Shared object<\/td><td><code>gcc -shared ...<\/code><\/td><td>Load library dynamically at runtime<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Useful commands while developing<\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php language-bash\"><span class=\"hljs-comment\"># Build both outputs (default target)<\/span>\nmake\n\n<span class=\"hljs-comment\"># Build only the static library<\/span>\nmake libmarquis.a\n\n<span class=\"hljs-comment\"># Build only the shared library<\/span>\nmake libmarquis.so\n\n<span class=\"hljs-comment\"># Show commands without executing them<\/span>\nmake -n\n\n<span class=\"hljs-comment\"># Force rebuild of targets<\/span>\nmake -B\n\n<span class=\"hljs-comment\"># Remove generated artifacts<\/span>\nmake clean<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Modernized template with the same goals<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The following version keeps the same intent, but uses a pattern rule, marks phony targets, and tracks header dependencies automatically.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript language-makefile\">CC ?= gcc\nAR ?= ar\nCFLAGS ?= -Wall -O2\nCPPFLAGS ?=\nLDFLAGS ?=\n\nSRCS := cas.c client.c hashtable_itr.c\n<span class=\"hljs-attr\">OBJS<\/span> := $(SRCS:.c=.o)\n<span class=\"hljs-attr\">DEPS<\/span> := $(OBJS:.o=.d)\n\n.PHONY: all clean\n<span class=\"hljs-attr\">all<\/span>: libmarquis.a libmarquis.so\n\nlibmarquis.a: $(OBJS)\n\\t$(AR) rcs $@ $(OBJS)\n\nlibmarquis.so: $(OBJS)\n\\t$(CC) -shared -Wl,-soname,libmarquis.so -o $@ $(OBJS) $(LDFLAGS)\n\n%.o: %.c\n\\t$(CC) $(CPPFLAGS) $(CFLAGS) -fPIC -MMD -MP -c $&lt; -o $@\n\n<span class=\"hljs-attr\">clean<\/span>:\n\\trm -f $(OBJS) $(DEPS) libmarquis.a libmarquis.so\n\n-include $(DEPS)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Best practices<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Keep target names predictable (<code>all<\/code>, <code>clean<\/code>, and output names).<\/li><li>Use <code>.PHONY<\/code> for non-file targets to avoid accidental conflicts.<\/li><li>Prefer pattern rules (<code>%.o: %.c<\/code>) over older suffix rules when maintaining or extending builds.<\/li><li>Generate and include dependency files so header changes trigger correct rebuilds.<\/li><li>Treat linker options (like <code>-Wl,-soname,...<\/code>) as platform-specific and validate them in your CI environment.<\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>This revised guide explains a classic C Makefile that builds both static and shared libraries. It breaks down each target, shows the build workflow, and provides a safer modern template you can adapt.<\/p>\n","protected":false},"author":1,"featured_media":3983,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[2],"tags":[2876,2971,2963,2967,2968,2965,2964,2969,2970,2966],"class_list":["post-116","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorised","tag-build-tool","tag-c-intro-project","tag-makefile","tag-makefile-example","tag-makefile-guide","tag-makefile-instruction","tag-makefile-overview","tag-makefile-tutorial","tag-simple-makefile","tag-what-is-makefile"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/116","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=116"}],"version-history":[{"count":3,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/116\/revisions"}],"predecessor-version":[{"id":78965,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/116\/revisions\/78965"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media\/3983"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=116"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=116"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=116"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}