You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucy.apache.org by nw...@apache.org on 2016/07/06 19:55:15 UTC

[02/10] lucy git commit: Regenerate charmonizer.c

http://git-wip-us.apache.org/repos/asf/lucy/blob/c5e8d651/common/charmonizer.c
----------------------------------------------------------------------
diff --git a/common/charmonizer.c b/common/charmonizer.c
index 37d0a92..c60f7d3 100644
--- a/common/charmonizer.c
+++ b/common/charmonizer.c
@@ -36,61 +36,6 @@
 
 /***************************************************************************/
 
-#line 21 "src/Charmonizer/Core/Library.h"
-/* Charmonizer/Core/Library.h
- */
-
-#ifndef H_CHAZ_LIB
-#define H_CHAZ_LIB
-
-typedef struct chaz_Lib chaz_Lib;
-
-chaz_Lib*
-chaz_Lib_new_shared(const char *name, const char *version,
-                    const char *major_version);
-
-chaz_Lib*
-chaz_Lib_new_static(const char *name);
-
-void
-chaz_Lib_destroy(chaz_Lib *flags);
-
-const char*
-chaz_Lib_get_name(chaz_Lib *lib);
-
-const char*
-chaz_Lib_get_version(chaz_Lib *lib);
-
-const char*
-chaz_Lib_get_major_version(chaz_Lib *lib);
-
-int
-chaz_Lib_is_shared(chaz_Lib *lib);
-
-int
-chaz_Lib_is_static(chaz_Lib *lib);
-
-char*
-chaz_Lib_filename(chaz_Lib *lib);
-
-char*
-chaz_Lib_major_version_filename(chaz_Lib *lib);
-
-char*
-chaz_Lib_no_version_filename(chaz_Lib *lib);
-
-char*
-chaz_Lib_implib_filename(chaz_Lib *lib);
-
-char*
-chaz_Lib_export_filename(chaz_Lib *lib);
-
-#endif /* H_CHAZ_LIB */
-
-
-
-/***************************************************************************/
-
 #line 21 "src/Charmonizer/Core/CFlags.h"
 /* Charmonizer/Core/CFlags.h
  */
@@ -98,8 +43,6 @@ chaz_Lib_export_filename(chaz_Lib *lib);
 #ifndef H_CHAZ_CFLAGS
 #define H_CHAZ_CFLAGS
 
-/* #include "Charmonizer/Core/Library.h" */
-
 #define CHAZ_CFLAGS_STYLE_POSIX  1
 #define CHAZ_CFLAGS_STYLE_GNU    2
 #define CHAZ_CFLAGS_STYLE_MSVC   3
@@ -151,10 +94,9 @@ void
 chaz_CFlags_hide_symbols(chaz_CFlags *flags);
 
 void
-chaz_CFlags_link_shared_library(chaz_CFlags *flags);
-
-void
-chaz_CFlags_set_shared_library_version(chaz_CFlags *flags, chaz_Lib *lib);
+chaz_CFlags_link_shared_library(chaz_CFlags *flags, const char *basename,
+                                const char *version,
+                                const char *major_version);
 
 void
 chaz_CFlags_set_link_output(chaz_CFlags *flags, const char *filename);
@@ -163,10 +105,14 @@ void
 chaz_CFlags_add_library_path(chaz_CFlags *flags, const char *directory);
 
 void
-chaz_CFlags_add_library(chaz_CFlags *flags, chaz_Lib *lib);
+chaz_CFlags_add_shared_lib(chaz_CFlags *flags, const char *dir,
+                           const char *basename, const char *major_version);
+
+void
+chaz_CFlags_add_external_lib(chaz_CFlags *flags, const char *library);
 
 void
-chaz_CFlags_add_external_library(chaz_CFlags *flags, const char *library);
+chaz_CFlags_add_rpath(chaz_CFlags *flags, const char *path);
 
 void
 chaz_CFlags_enable_code_coverage(chaz_CFlags *flags);
@@ -402,6 +348,9 @@ chaz_CC_sun_c_version_num(void);
 int
 chaz_CC_is_cygwin(void);
 
+int
+chaz_CC_is_mingw(void);
+
 const char*
 chaz_CC_link_command(void);
 
@@ -420,6 +369,44 @@ chaz_CC_format_archiver_command(const char *target, const char *objects);
 char*
 chaz_CC_format_ranlib_command(const char *target);
 
+/** Returns the filename for a shared library.
+ *
+ * @param dir The target directory or NULL for the current directory.
+ * @param basename The name of the library without prefix and extension.
+ * @param version The library version.
+ */
+char*
+chaz_CC_shared_lib_filename(const char *dir, const char *basename,
+                            const char *version);
+
+/** Returns the filename for an import library.
+ *
+ * @param dir The target directory or NULL for the current directory.
+ * @param basename The name of the library without prefix and extension.
+ * @param version The library version.
+ */
+char*
+chaz_CC_import_lib_filename(const char *dir, const char *basename,
+                            const char *version);
+
+/** Returns the filename for an MSVC export file.
+ *
+ * @param dir The target directory or NULL for the current directory.
+ * @param basename The name of the library without prefix and extension.
+ * @param version The library version.
+ */
+char*
+chaz_CC_export_filename(const char *dir, const char *basename,
+                        const char *version);
+
+/** Returns the filename for a static library.
+ *
+ * @param dir The target directory or NULL for the current directory.
+ * @param basename The name of the library without prefix and extension.
+ */
+char*
+chaz_CC_static_lib_filename(const char *dir, const char *basename);
+
 #endif /* H_CHAZ_COMPILER */
 
 
@@ -657,11 +644,11 @@ chaz_HeadCheck_contains_member(const char *struct_name, const char *member,
 #define H_CHAZ_MAKE
 
 /* #include "Charmonizer/Core/CFlags.h" */
-/* #include "Charmonizer/Core/Library.h" */
 
 typedef struct chaz_MakeFile chaz_MakeFile;
 typedef struct chaz_MakeVar chaz_MakeVar;
 typedef struct chaz_MakeRule chaz_MakeRule;
+typedef struct chaz_MakeBinary chaz_MakeBinary;
 
 typedef void (*chaz_Make_list_files_callback_t)(const char *dir, char *file,
                                                 void *context);
@@ -708,23 +695,21 @@ chaz_MakeFile_new();
 /** MakeFile destructor.
  */
 void
-chaz_MakeFile_destroy(chaz_MakeFile *makefile);
+chaz_MakeFile_destroy(chaz_MakeFile *self);
 
 /** Add a variable to a makefile.
  *
- * @param makefile The makefile.
  * @param name Name of the variable.
  * @param value Value of the variable. Can be NULL if you want add content
  * later.
  * @return a MakeVar.
  */
 chaz_MakeVar*
-chaz_MakeFile_add_var(chaz_MakeFile *makefile, const char *name,
+chaz_MakeFile_add_var(chaz_MakeFile *self, const char *name,
                       const char *value);
 
 /** Add a rule to a makefile.
  *
- * @param makefile The makefile.
  * @param target The first target of the rule. Can be NULL if you want to add
  * targets later.
  * @param prereq The first prerequisite of the rule. Can be NULL if you want to
@@ -732,173 +717,174 @@ chaz_MakeFile_add_var(chaz_MakeFile *makefile, const char *name,
  * @return a MakeRule.
  */
 chaz_MakeRule*
-chaz_MakeFile_add_rule(chaz_MakeFile *makefile, const char *target,
+chaz_MakeFile_add_rule(chaz_MakeFile *self, const char *target,
                        const char *prereq);
 
 /** Return the rule for the 'clean' target.
- *
- * @param makefile The makefile.
  */
 chaz_MakeRule*
-chaz_MakeFile_clean_rule(chaz_MakeFile *makefile);
+chaz_MakeFile_clean_rule(chaz_MakeFile *self);
 
 /** Return the rule for the 'distclean' target.
- *
- * @param makefile The makefile.
  */
 chaz_MakeRule*
-chaz_MakeFile_distclean_rule(chaz_MakeFile *makefile);
+chaz_MakeFile_distclean_rule(chaz_MakeFile *self);
 
-/** Add a rule to link an executable. The executable will also be added to the
- * list of files to clean.
+/** Add an executable. Returns a chaz_MakeBinary object.
  *
- * @param makefile The makefile.
- * @param exe The name of the executable.
- * @param sources The list of source files.
- * @param link_flags Additional link flags.
+ * @param dir The target directory or NULL for the current directory.
+ * @param basename The name of the executable without extension.
  */
-chaz_MakeRule*
-chaz_MakeFile_add_exe(chaz_MakeFile *makefile, const char *exe,
-                      const char *sources, chaz_CFlags *link_flags);
+chaz_MakeBinary*
+chaz_MakeFile_add_exe(chaz_MakeFile *self, const char *dir,
+                      const char *basename);
 
-/** Add a rule to compile and link an executable. The executable will also be
- * added to the list of files to clean.
+/** Add a shared library. The library will be built in the current directory.
+ * Returns a chaz_MakeBinary object.
  *
- * @param makefile The makefile.
- * @param exe The name of the executable.
- * @param sources The list of source files.
- * @param cflags Additional compiler flags.
- */
-chaz_MakeRule*
-chaz_MakeFile_add_compiled_exe(chaz_MakeFile *makefile, const char *exe,
-                               const char *sources, chaz_CFlags *cflags);
-
-/** Add a rule to link a shared library. The shared library will also be added
- * to the list of files to clean.
+ * @param dir The target directory or NULL for the current directory.
+ * @param basename The name of the library without prefix and extension.
+ * @param version The version of the library.
+ * @param major_version The major version of the library.
+ */
+chaz_MakeBinary*
+chaz_MakeFile_add_shared_lib(chaz_MakeFile *self, const char *dir,
+                             const char *basename, const char *version,
+                             const char *major_version);
+
+/** Add a static library. The library will be built in the current directory.
+ * Returns a chaz_MakeBinary object.
  *
- * @param makefile The makefile.
- * @param lib The shared library.
- * @param sources The list of source files.
- * @param link_flags Additional link flags.
+ * @param dir The target directory or NULL for the current directory.
+ * @param basename The name of the library without prefix and extension.
  */
-chaz_MakeRule*
-chaz_MakeFile_add_shared_lib(chaz_MakeFile *makefile, chaz_Lib *lib,
-                             const char *sources, chaz_CFlags *link_flags);
-
-/** Add a rule to create a static library. The static library will also be added
- * to the list of files to clean.
- *
- * @param makefile The makefile.
- * @param lib The static library.
- * @param objects The list of object files to be archived.
- */
-chaz_MakeRule*
-chaz_MakeFile_add_static_lib(chaz_MakeFile *makefile, chaz_Lib *lib,
-                             const char *objects);
+chaz_MakeBinary*
+chaz_MakeFile_add_static_lib(chaz_MakeFile *self, const char *dir,
+                             const char *basename);
 
 /** Add a rule to build the lemon parser generator.
  *
- * @param makefile The makefile.
  * @param dir The lemon directory.
  */
-chaz_MakeRule*
-chaz_MakeFile_add_lemon_exe(chaz_MakeFile *makefile, const char *dir);
+chaz_MakeBinary*
+chaz_MakeFile_add_lemon_exe(chaz_MakeFile *self, const char *dir);
 
 /** Add a rule for a lemon grammar.
  *
- * @param makefile The makefile.
  * @param base_name The filename of the grammar without extension.
  */
 chaz_MakeRule*
-chaz_MakeFile_add_lemon_grammar(chaz_MakeFile *makefile,
-                                const char *base_name);
-
-/** Override compiler flags for a single object file.
- *
- * @param makefile The makefile.
- * @param obj The object file.
- * @param cflags Compiler flags.
- */
-chaz_MakeRule*
-chaz_MakeFile_override_cflags(chaz_MakeFile *makefile, const char *obj,
-                              chaz_CFlags *cflags);
+chaz_MakeFile_add_lemon_grammar(chaz_MakeFile *self, const char *base_name);
 
 /** Write the makefile to a file named 'Makefile' in the current directory.
- *
- * @param makefile The makefile.
  */
 void
-chaz_MakeFile_write(chaz_MakeFile *makefile);
+chaz_MakeFile_write(chaz_MakeFile *self);
 
 /** Append content to a makefile variable. The new content will be separated
  * from the existing content with whitespace.
  *
- * @param var The variable.
  * @param element The additional content.
  */
 void
-chaz_MakeVar_append(chaz_MakeVar *var, const char *element);
+chaz_MakeVar_append(chaz_MakeVar *self, const char *element);
 
 /** Add another target to a makefile rule.
  *
- * @param rule The rule.
  * @param target The additional rule.
  */
 void
-chaz_MakeRule_add_target(chaz_MakeRule *rule, const char *target);
+chaz_MakeRule_add_target(chaz_MakeRule *self, const char *target);
 
 /** Add another prerequisite to a makefile rule.
  *
- * @param rule The rule.
  * @param prereq The additional prerequisite.
  */
 void
-chaz_MakeRule_add_prereq(chaz_MakeRule *rule, const char *prereq);
+chaz_MakeRule_add_prereq(chaz_MakeRule *self, const char *prereq);
 
 /** Add a command to a rule.
  *
- * @param rule The rule.
  * @param command The additional command.
  */
 void
-chaz_MakeRule_add_command(chaz_MakeRule *rule, const char *command);
+chaz_MakeRule_add_command(chaz_MakeRule *self, const char *command);
 
 /** Add a command to be executed with a special runtime library path.
  *
- * @param rule The rule.
  * @param command The additional command.
  * @param ... NULL-terminated list of library directories.
  */
 void
-chaz_MakeRule_add_command_with_libpath(chaz_MakeRule *rule,
+chaz_MakeRule_add_command_with_libpath(chaz_MakeRule *self,
                                        const char *command, ...);
 
 /** Add a command to remove one or more files.
  *
- * @param rule The rule.
  * @param files The list of files.
  */
 void
-chaz_MakeRule_add_rm_command(chaz_MakeRule *rule, const char *files);
+chaz_MakeRule_add_rm_command(chaz_MakeRule *self, const char *files);
 
 /** Add a command to remove one or more directories.
  *
- * @param rule The rule.
  * @param dirs The list of directories.
  */
 void
-chaz_MakeRule_add_recursive_rm_command(chaz_MakeRule *rule, const char *dirs);
+chaz_MakeRule_add_recursive_rm_command(chaz_MakeRule *self, const char *dirs);
 
 /** Add one or more commands to call another makefile recursively.
  *
- * @param rule The rule.
  * @param dir The directory in which to call the makefile.
  * @param target The target to call. Pass NULL for the default target.
  */
 void
-chaz_MakeRule_add_make_command(chaz_MakeRule *rule, const char *dir,
+chaz_MakeRule_add_make_command(chaz_MakeRule *self, const char *dir,
                                const char *target);
 
+/** Add a source file for the binary.
+ *
+ * @param dir The source directory or NULL for the current directory.
+ * @param filename The filename.
+ */
+void
+chaz_MakeBinary_add_src_file(chaz_MakeBinary *self, const char *dir,
+                             const char *filename);
+
+/** Add all .c files in a directory as sources for the binary.
+ *
+ * @param path The path to the directory.
+ */
+void
+chaz_MakeBinary_add_src_dir(chaz_MakeBinary *self, const char *path);
+
+/** Add a prerequisite to the make rule of the binary.
+ *
+ * @param prereq The prerequisite.
+ */
+void
+chaz_MakeBinary_add_prereq(chaz_MakeBinary *self, const char *prereq);
+
+/** Return a list of all objects separated by space.
+ */
+char*
+chaz_MakeBinary_obj_string(chaz_MakeBinary *self);
+
+/** Accessor for target.
+ */
+const char*
+chaz_MakeBinary_get_target(chaz_MakeBinary *self);
+
+/** Accessor for compile flags.
+ */
+chaz_CFlags*
+chaz_MakeBinary_get_compile_flags(chaz_MakeBinary *self);
+
+/** Accessor for link flags.
+ */
+chaz_CFlags*
+chaz_MakeBinary_get_link_flags(chaz_MakeBinary *self);
+
 #endif /* H_CHAZ_MAKE */
 
 
@@ -1705,168 +1691,6 @@ void chaz_VariadicMacros_run(void);
 
 /***************************************************************************/
 
-#line 17 "src/Charmonizer/Core/Library.c"
-#include <string.h>
-#include <stdlib.h>
-/* #include "Charmonizer/Core/Library.h" */
-/* #include "Charmonizer/Core/Compiler.h" */
-/* #include "Charmonizer/Core/Util.h" */
-/* #include "Charmonizer/Core/OperatingSystem.h" */
-
-struct chaz_Lib {
-    char *name;
-    char *version;
-    char *major_version;
-    int   is_static;
-    int   is_shared;
-};
-
-static char*
-S_build_filename(chaz_Lib *lib, const char *version, const char *ext);
-
-static const char*
-S_get_prefix(chaz_Lib *lib);
-
-chaz_Lib*
-chaz_Lib_new_shared(const char *name, const char *version,
-                    const char *major_version) {
-    chaz_Lib *lib = (chaz_Lib*)malloc(sizeof(chaz_Lib));
-    lib->name          = chaz_Util_strdup(name);
-    lib->version       = chaz_Util_strdup(version);
-    lib->major_version = chaz_Util_strdup(major_version);
-    lib->is_shared = 1;
-    lib->is_static = 0;
-    return lib;
-}
-
-chaz_Lib*
-chaz_Lib_new_static(const char *name) {
-    chaz_Lib *lib = (chaz_Lib*)malloc(sizeof(chaz_Lib));
-    lib->name          = chaz_Util_strdup(name);
-    lib->version       = NULL;
-    lib->major_version = NULL;
-    lib->is_shared = 0;
-    lib->is_static = 1;
-    return lib;
-}
-
-void
-chaz_Lib_destroy(chaz_Lib *lib) {
-    free(lib->name);
-    free(lib->version);
-    free(lib->major_version);
-    free(lib);
-}
-
-const char*
-chaz_Lib_get_name(chaz_Lib *lib) {
-    return lib->name;
-}
-
-const char*
-chaz_Lib_get_version(chaz_Lib *lib) {
-    return lib->version;
-}
-
-const char*
-chaz_Lib_get_major_version(chaz_Lib *lib) {
-    return lib->major_version;
-}
-
-int
-chaz_Lib_is_shared (chaz_Lib *lib) {
-    return lib->is_shared;
-}
-
-int
-chaz_Lib_is_static (chaz_Lib *lib) {
-    return lib->is_static;
-}
-
-char*
-chaz_Lib_filename(chaz_Lib *lib) {
-    if (lib->is_static) {
-        return chaz_Lib_no_version_filename(lib);
-    }
-    else {
-        const char *ext = chaz_CC_shared_lib_ext();
-        if (chaz_CC_binary_format() == CHAZ_CC_BINFMT_PE) {
-            return S_build_filename(lib, lib->major_version, ext);
-        }
-        else {
-            return S_build_filename(lib, lib->version, ext);
-        }
-    }
-}
-
-char*
-chaz_Lib_major_version_filename(chaz_Lib *lib) {
-    if (lib->is_static) {
-        return chaz_Lib_no_version_filename(lib);
-    }
-    else {
-        const char *ext = chaz_CC_shared_lib_ext();
-        return S_build_filename(lib, lib->major_version, ext);
-    }
-}
-
-char*
-chaz_Lib_no_version_filename(chaz_Lib *lib) {
-    const char *prefix = S_get_prefix(lib);
-    const char *ext = lib->is_shared
-                      ? chaz_CC_shared_lib_ext()
-                      : chaz_CC_static_lib_ext();
-    return chaz_Util_join("", prefix, lib->name, ext, NULL);
-}
-
-char*
-chaz_Lib_implib_filename(chaz_Lib *lib) {
-    const char *ext = chaz_CC_import_lib_ext();
-    return S_build_filename(lib, lib->major_version, ext);
-}
-
-char*
-chaz_Lib_export_filename(chaz_Lib *lib) {
-    return S_build_filename(lib, lib->major_version, ".exp");
-}
-
-static char*
-S_build_filename(chaz_Lib *lib, const char *version, const char *ext) {
-    const char *prefix = S_get_prefix(lib);
-    int binary_format = chaz_CC_binary_format();
-
-    if (binary_format == CHAZ_CC_BINFMT_PE) {
-        return chaz_Util_join("", prefix, lib->name, "-", version, ext, NULL);
-    }
-    else if (binary_format == CHAZ_CC_BINFMT_MACHO) {
-        return chaz_Util_join("", prefix, lib->name, ".", version, ext, NULL);
-    }
-    else if (binary_format == CHAZ_CC_BINFMT_ELF) {
-        return chaz_Util_join("", prefix, lib->name, ext, ".", version, NULL);
-    }
-    else {
-        chaz_Util_die("Unsupported binary format");
-        return NULL;
-    }
-}
-
-static const char*
-S_get_prefix(chaz_Lib *lib) {
-    if (chaz_CC_msvc_version_num()) {
-        return "";
-    }
-    else if (chaz_CC_is_cygwin()) {
-        return lib->is_static ? "lib" : "cyg";
-    }
-    else {
-        return "lib";
-    }
-}
-
-
-
-/***************************************************************************/
-
 #line 17 "src/Charmonizer/Core/CFlags.c"
 #include <string.h>
 #include <stdlib.h>
@@ -1874,7 +1698,6 @@ S_get_prefix(chaz_Lib *lib) {
 /* #include "Charmonizer/Core/Compiler.h" */
 /* #include "Charmonizer/Core/Util.h" */
 /* #include "Charmonizer/Core/OperatingSystem.h" */
-/* #include "Charmonizer/Core/Library.h" */
 
 struct chaz_CFlags {
     int   style;
@@ -2098,55 +1921,44 @@ chaz_CFlags_hide_symbols(chaz_CFlags *flags) {
 }
 
 void
-chaz_CFlags_link_shared_library(chaz_CFlags *flags) {
-    const char *string;
+chaz_CFlags_link_shared_library(chaz_CFlags *flags, const char *basename,
+                                const char *version,
+                                const char *major_version) {
+    char *string = NULL;
+
     if (flags->style == CHAZ_CFLAGS_STYLE_MSVC) {
-        string = "/DLL";
+        string = chaz_Util_strdup("/DLL");
     }
     else if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
-        if (chaz_CC_binary_format() == CHAZ_CC_BINFMT_MACHO) {
-            string = "-dynamiclib";
+        int binary_format = chaz_CC_binary_format();
+
+        if (binary_format == CHAZ_CC_BINFMT_MACHO) {
+            string = chaz_Util_join(" ", "-dynamiclib", "-current_version",
+                                    version, "-compatibility_version",
+                                    major_version, NULL);
         }
-        else {
-            string = "-shared";
+        else if (binary_format == CHAZ_CC_BINFMT_ELF) {
+            string = chaz_Util_join("", "-shared -Wl,-soname,lib", basename,
+                                    ".so.", major_version, NULL);
+        }
+        else if (binary_format == CHAZ_CC_BINFMT_PE) {
+            string = chaz_Util_join("", "-shared -Wl,--out-implib,lib",
+                                    basename, "-", major_version, ".dll.a",
+                                    NULL);
         }
     }
     else if (flags->style == CHAZ_CFLAGS_STYLE_SUN_C) {
-        string = "-G";
+        string = chaz_Util_join("", "-G -h lib", basename, ".so.",
+                                major_version, NULL);
     }
     else {
         chaz_Util_die("Don't know how to link a shared library with '%s'",
                       chaz_CC_get_cc());
     }
-    chaz_CFlags_append(flags, string);
-}
-
-void
-chaz_CFlags_set_shared_library_version(chaz_CFlags *flags, chaz_Lib *lib) {
-    if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
-        int binary_format = chaz_CC_binary_format();
 
-        if (binary_format == CHAZ_CC_BINFMT_MACHO) {
-            const char *version = chaz_Lib_get_version(lib);
-            char *string
-                = chaz_Util_join(" ", "-current_version", version, NULL);
-            chaz_CFlags_append(flags, string);
-            free(string);
-        }
-        else if (binary_format == CHAZ_CC_BINFMT_ELF) {
-            char *soname = chaz_Lib_major_version_filename(lib);
-            char *string = chaz_Util_join("", "-Wl,-soname,", soname, NULL);
-            chaz_CFlags_append(flags, string);
-            free(string);
-            free(soname);
-        }
-    }
-    else if (flags->style == CHAZ_CFLAGS_STYLE_SUN_C) {
-        char *soname = chaz_Lib_major_version_filename(lib);
-        char *string = chaz_Util_join(" ", "-h", soname, NULL);
+    if (string) {
         chaz_CFlags_append(flags, string);
         free(string);
-        free(soname);
     }
 }
 
@@ -2187,20 +1999,22 @@ chaz_CFlags_add_library_path(chaz_CFlags *flags, const char *directory) {
 }
 
 void
-chaz_CFlags_add_library(chaz_CFlags *flags, chaz_Lib *lib) {
+chaz_CFlags_add_shared_lib(chaz_CFlags *flags, const char *dir,
+                           const char *basename, const char *major_version) {
+    int binfmt = chaz_CC_binary_format();
     char *filename;
-    if (flags->style == CHAZ_CFLAGS_STYLE_MSVC) {
-        filename = chaz_Lib_implib_filename(lib);
+    if (binfmt == CHAZ_CC_BINFMT_PE) {
+        filename = chaz_CC_import_lib_filename(dir, basename, major_version);
     }
     else {
-        filename = chaz_Lib_filename(lib);
+        filename = chaz_CC_shared_lib_filename(dir, basename, major_version);
     }
     chaz_CFlags_append(flags, filename);
     free(filename);
 }
 
 void
-chaz_CFlags_add_external_library(chaz_CFlags *flags, const char *library) {
+chaz_CFlags_add_external_lib(chaz_CFlags *flags, const char *library) {
     char *string;
     if (flags->style == CHAZ_CFLAGS_STYLE_MSVC) {
         string = chaz_Util_join("", library, ".lib", NULL);
@@ -2213,6 +2027,27 @@ chaz_CFlags_add_external_library(chaz_CFlags *flags, const char *library) {
 }
 
 void
+chaz_CFlags_add_rpath(chaz_CFlags *flags, const char *path) {
+    char *string;
+
+    if (chaz_CC_binary_format() != CHAZ_CC_BINFMT_ELF) { return; }
+
+    if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
+        string = chaz_Util_join("", "-Wl,-rpath,", path, NULL);
+    }
+    else if (flags->style == CHAZ_CFLAGS_STYLE_SUN_C) {
+        string = chaz_Util_join(" ", "-R", path, NULL);
+    }
+    else {
+        chaz_Util_die("Don't know how to set rpath with '%s'",
+                      chaz_CC_get_cc());
+    }
+
+    chaz_CFlags_append(flags, string);
+    free(string);
+}
+
+void
 chaz_CFlags_enable_code_coverage(chaz_CFlags *flags) {
     if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
         chaz_CFlags_append(flags, "--coverage");
@@ -2256,6 +2091,7 @@ struct chaz_CLI {
 static void
 S_chaz_CLI_error(chaz_CLI *self, const char *pattern, ...) {
     va_list ap;
+    (void)self;
     if (chaz_Util_verbosity > 0) {
         va_start(ap, pattern);
         vfprintf(stderr, pattern, ap);
@@ -2349,7 +2185,7 @@ S_chaz_CLI_rebuild_help(chaz_CLI *self) {
 }
 
 static chaz_CLIOption*
-S_find_opt(chaz_CLI *self, const char *name) {
+S_chaz_CLI_find_opt(chaz_CLI *self, const char *name) {
     int i;
     for (i = 0; i < self->num_opts; i++) {
         chaz_CLIOption *opt = &self->opts[i];
@@ -2445,7 +2281,7 @@ chaz_CLI_register(chaz_CLI *self, const char *name, const char *help,
 
 int
 chaz_CLI_set(chaz_CLI *self, const char *name, const char *value) {
-    chaz_CLIOption *opt = S_find_opt(self, name);
+    chaz_CLIOption *opt = S_chaz_CLI_find_opt(self, name);
     if (opt == NULL) {
         S_chaz_CLI_error(self, "Attempt to set unknown option: '%s'", name);
         return 0;
@@ -2473,7 +2309,7 @@ chaz_CLI_set(chaz_CLI *self, const char *name, const char *value) {
 
 int
 chaz_CLI_unset(chaz_CLI *self, const char *name) {
-    chaz_CLIOption *opt = S_find_opt(self, name);
+    chaz_CLIOption *opt = S_chaz_CLI_find_opt(self, name);
     if (opt == NULL) {
         S_chaz_CLI_error(self, "Attempt to unset unknown option: '%s'", name);
         return 0;
@@ -2486,7 +2322,7 @@ chaz_CLI_unset(chaz_CLI *self, const char *name) {
 
 int
 chaz_CLI_defined(chaz_CLI *self, const char *name) {
-    chaz_CLIOption *opt = S_find_opt(self, name);
+    chaz_CLIOption *opt = S_chaz_CLI_find_opt(self, name);
     if (opt == NULL) {
         S_chaz_CLI_error(self, "Inquiry for unknown option: '%s'", name);
         return 0;
@@ -2496,7 +2332,7 @@ chaz_CLI_defined(chaz_CLI *self, const char *name) {
 
 long
 chaz_CLI_longval(chaz_CLI *self, const char *name) {
-    chaz_CLIOption *opt = S_find_opt(self, name);
+    chaz_CLIOption *opt = S_chaz_CLI_find_opt(self, name);
     if (opt == NULL) {
         S_chaz_CLI_error(self, "Longval request for unknown option: '%s'",
                          name);
@@ -2510,7 +2346,7 @@ chaz_CLI_longval(chaz_CLI *self, const char *name) {
 
 const char*
 chaz_CLI_strval(chaz_CLI *self, const char *name) {
-    chaz_CLIOption *opt = S_find_opt(self, name);
+    chaz_CLIOption *opt = S_chaz_CLI_find_opt(self, name);
     if (opt == NULL) {
         S_chaz_CLI_error(self, "Strval request for unknown option: '%s'",
                          name);
@@ -2571,7 +2407,7 @@ chaz_CLI_parse(chaz_CLI *self, int argc, const char *argv[]) {
 
         if (value == NULL && i + 1 < argc) {
             /* Support both '--opt=val' and '--opt val' styles. */
-            chaz_CLIOption *opt = S_find_opt(self, name);
+            chaz_CLIOption *opt = S_chaz_CLI_find_opt(self, name);
             if (opt == NULL) {
                 S_chaz_CLI_error(self, "Attempt to set unknown option: '%s'",
                                  name);
@@ -2631,6 +2467,13 @@ chaz_CC_eval_macro(const char *macro);
 static void
 chaz_CC_detect_known_compilers(void);
 
+/** Build a library filename from its components.
+ */
+static char*
+chaz_CC_build_lib_filename(const char *dir, const char *prefix,
+                           const char *basename, const char *version,
+                           const char *ext);
+
 /* Temporary files. */
 #define CHAZ_CC_TRY_SOURCE_PATH  "_charmonizer_try.c"
 #define CHAZ_CC_TRY_BASENAME     "_charmonizer_try"
@@ -2656,12 +2499,13 @@ static struct {
     int       intval___clang__;
     int       intval___SUNPRO_C;
     int       is_cygwin;
+    int       is_mingw;
     chaz_CFlags *extra_cflags;
     chaz_CFlags *temp_cflags;
 } chaz_CC = {
     NULL, NULL, NULL,
     "", "", "", "", "", "",
-    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     NULL, NULL
 };
 
@@ -2669,7 +2513,6 @@ void
 chaz_CC_init(const char *compiler_command, const char *compiler_flags) {
     const char *code = "int main() { return 0; }\n";
     int compile_succeeded = 0;
-    int retval            = -1;
 
     if (chaz_Util_verbosity) { printf("Creating compiler object...\n"); }
 
@@ -2771,6 +2614,9 @@ chaz_CC_init(const char *compiler_command, const char *compiler_flags) {
         if (chaz_CC_has_macro("__CYGWIN__")) {
             chaz_CC.is_cygwin = 1;
         }
+        if (chaz_CC_has_macro("__MINGW32__")) {
+            chaz_CC.is_mingw = 1;
+        }
     }
     else {
         chaz_Util_die("Failed to detect binary format");
@@ -3144,6 +2990,11 @@ chaz_CC_is_cygwin(void) {
     return chaz_CC.is_cygwin;
 }
 
+int
+chaz_CC_is_mingw(void) {
+    return chaz_CC.is_mingw;
+}
+
 const char*
 chaz_CC_link_command() {
     if (chaz_CC.intval__MSC_VER) {
@@ -3160,7 +3011,8 @@ chaz_CC_format_archiver_command(const char *target, const char *objects) {
         /* TODO: Write `objects` to a temporary file in order to avoid
          * exceeding line length limits. */
         char *out = chaz_Util_join("", "/OUT:", target, NULL);
-        char *command = chaz_Util_join(" ", "lib", "/NOLOGO", out, NULL);
+        char *command = chaz_Util_join(" ", "lib", "/NOLOGO", objects, out,
+                                       NULL);
         free(out);
         return command;
     }
@@ -3177,6 +3029,88 @@ chaz_CC_format_ranlib_command(const char *target) {
     return chaz_Util_join(" ", "ranlib", target, NULL);
 }
 
+char*
+chaz_CC_shared_lib_filename(const char *dir, const char *basename,
+                            const char *version) {
+    /* Cygwin uses a "cyg" prefix for shared libraries. */
+    const char *prefix = chaz_CC_msvc_version_num()
+                         ? ""
+                         : chaz_CC_is_cygwin() ? "cyg" : "lib";
+    return chaz_CC_build_lib_filename(dir, prefix, basename, version,
+                                      chaz_CC.shared_lib_ext);
+}
+
+char*
+chaz_CC_import_lib_filename(const char *dir, const char *basename,
+                            const char *version) {
+    const char *prefix = chaz_CC_msvc_version_num() ? "" : "lib";
+    return chaz_CC_build_lib_filename(dir, prefix, basename, version,
+                                      chaz_CC.import_lib_ext);
+}
+
+char*
+chaz_CC_export_filename(const char *dir, const char *basename,
+                        const char *version) {
+    /* Only for MSVC. */
+    return chaz_CC_build_lib_filename(dir, "", basename, version, ".exp");
+}
+
+static char*
+chaz_CC_build_lib_filename(const char *dir, const char *prefix,
+                           const char *basename, const char *version,
+                           const char *ext) {
+    char *suffix;
+    char *retval;
+
+    if (version == NULL) {
+        suffix = chaz_Util_strdup(ext);
+    }
+    else {
+        int binary_format = chaz_CC_binary_format();
+
+        if (binary_format == CHAZ_CC_BINFMT_PE) {
+            suffix = chaz_Util_join("", "-", version, ext, NULL);
+        }
+        else if (binary_format == CHAZ_CC_BINFMT_MACHO) {
+            suffix = chaz_Util_join("", ".", version, ext, NULL);
+        }
+        else if (binary_format == CHAZ_CC_BINFMT_ELF) {
+            suffix = chaz_Util_join("", ext, ".", version, NULL);
+        }
+        else {
+            chaz_Util_die("Unsupported binary format");
+            return NULL;
+        }
+    }
+
+    if (dir == NULL || strcmp(dir, ".") == 0) {
+        retval = chaz_Util_join("", prefix, basename, suffix, NULL);
+    }
+    else {
+        const char *dir_sep = chaz_OS_dir_sep();
+        retval = chaz_Util_join("", dir, dir_sep, prefix, basename, suffix,
+                                NULL);
+    }
+
+    free(suffix);
+    return retval;
+}
+
+char*
+chaz_CC_static_lib_filename(const char *dir, const char *basename) {
+    const char *prefix = chaz_CC_msvc_version_num() ? "" : "lib";
+
+    if (dir == NULL || strcmp(dir, ".") == 0) {
+        return chaz_Util_join("", prefix, basename, chaz_CC.static_lib_ext,
+                              NULL);
+    }
+    else {
+        const char *dir_sep = chaz_OS_dir_sep();
+        return chaz_Util_join("", dir, dir_sep, prefix, basename,
+                              chaz_CC.static_lib_ext, NULL);
+    }
+}
+
 
 /***************************************************************************/
 
@@ -3329,7 +3263,7 @@ static struct {
     chaz_ConfElem *defs;
     size_t         def_cap;
     size_t         def_count;
-} chaz_ConfWriterC = { NULL, NULL, 0, 0 };
+} chaz_ConfWriterC = { NULL, NULL, NULL, 0, 0 };
 static chaz_ConfWriter CWC_conf_writer;
 
 /* Open the charmony.h file handle.  Print supplied text to it, if non-null.
@@ -4566,10 +4500,15 @@ chaz_HeadCheck_maybe_add_to_cache(const char *header_name, int exists) {
 #include <stdlib.h>
 #include <string.h>
 /* #include "Charmonizer/Core/Make.h" */
+/* #include "Charmonizer/Core/CFlags.h" */
 /* #include "Charmonizer/Core/Compiler.h" */
 /* #include "Charmonizer/Core/OperatingSystem.h" */
 /* #include "Charmonizer/Core/Util.h" */
 
+#define CHAZ_MAKEBINARY_EXE         1
+#define CHAZ_MAKEBINARY_STATIC_LIB  2
+#define CHAZ_MAKEBINARY_SHARED_LIB  3
+
 struct chaz_MakeVar {
     char   *name;
     char   *value;
@@ -4582,13 +4521,34 @@ struct chaz_MakeRule {
     char *commands;
 };
 
+struct chaz_MakeBinary {
+    int             type;
+    char           *target_dir;
+    char           *basename;
+    char           *version;
+    char           *major_version;
+    char          **sources;  /* List of all sources. */
+    size_t          num_sources;
+    char          **single_sources;  /* Only sources from add_src_file. */
+    size_t          num_single_sources;
+    char          **dirs;
+    size_t          num_dirs;
+    chaz_MakeVar   *obj_var;  /* Owned by MakeFile. */
+    char           *dollar_var;
+    chaz_MakeRule  *rule;  /* Not added to MakeFile, owned by MakeBinary. */
+    chaz_CFlags    *compile_flags;
+    chaz_CFlags    *link_flags;
+};
+
 struct chaz_MakeFile {
-    chaz_MakeVar  **vars;
-    size_t          num_vars;
-    chaz_MakeRule **rules;
-    size_t          num_rules;
-    chaz_MakeRule  *clean;
-    chaz_MakeRule  *distclean;
+    chaz_MakeVar    **vars;
+    size_t            num_vars;
+    chaz_MakeRule   **rules;
+    size_t            num_rules;
+    chaz_MakeRule    *clean;
+    chaz_MakeRule    *distclean;
+    chaz_MakeBinary **binaries;
+    size_t            num_binaries;
 };
 
 /* Static vars. */
@@ -4612,33 +4572,89 @@ static struct {
  *      dmake
  */
 static int
-chaz_Make_detect(const char *make1, ...);
+S_chaz_Make_detect(const char *make1, ...);
 
 static int
-chaz_Make_audition(const char *make);
+S_chaz_Make_audition(const char *make);
+
+static void
+S_chaz_MakeFile_finish_exe(chaz_MakeFile *self, chaz_MakeBinary *binary);
+
+static void
+S_chaz_MakeFile_finish_shared_lib(chaz_MakeFile *self,
+                                  chaz_MakeBinary *binary);
+
+static void
+S_chaz_MakeFile_finish_static_lib(chaz_MakeFile *self,
+                                  chaz_MakeBinary *binary);
+
+static chaz_MakeBinary*
+S_chaz_MakeFile_add_binary(chaz_MakeFile *self, int type, const char *dir,
+                           const char *basename, const char *target);
+
+static void
+S_chaz_MakeFile_write_binary_rules(chaz_MakeFile *self,
+                                   chaz_MakeBinary *binary, FILE *out);
+
+static void
+S_chaz_MakeFile_write_object_rules(char **sources, const char *command,
+                                   FILE *out);
+
+static void
+S_chaz_MakeFile_write_pattern_rules(char **dirs, const char *command,
+                                    FILE *out);
 
 static chaz_MakeRule*
-S_new_rule(const char *target, const char *prereq);
+S_chaz_MakeRule_new(const char *target, const char *prereq);
+
+static void
+S_chaz_MakeRule_destroy(chaz_MakeRule *self);
+
+static void
+S_chaz_MakeRule_write(chaz_MakeRule *self, FILE *out);
 
 static void
-S_destroy_rule(chaz_MakeRule *rule);
+S_chaz_MakeBinary_destroy(chaz_MakeBinary *self);
 
 static void
-S_write_rule(chaz_MakeRule *rule, FILE *out);
+S_chaz_MakeBinary_list_files_callback(const char *dir, char *file,
+                                      void *context);
+static void
+S_chaz_MakeBinary_do_add_src_file(chaz_MakeBinary *self, const char *path);
+
+/** Return the path to the object file for a source file.
+ *
+ * @param path The path to the source file.
+ */
+static char*
+S_chaz_MakeBinary_obj_path(const char *src_path);
 
 void
 chaz_Make_init(const char *make_command) {
     chaz_Make.shell_type = chaz_OS_shell_type();
 
     if (make_command) {
-        if (!chaz_Make_detect(make_command, NULL)) {
-            chaz_Util_warn("Make utility '%s' doesn't appear to work");
+        if (!S_chaz_Make_detect(make_command, NULL)) {
+            chaz_Util_warn("Make utility '%s' doesn't appear to work",
+                           make_command);
         }
     }
     else {
-        if (!chaz_Make_detect("make", "gmake", "nmake", "dmake",
-                              "mingw32-make", "mingw64-make", NULL)
-           ) {
+        int succeeded = 0;
+
+        /* mingw32-make seems to try to run commands under both cmd.exe
+         * and sh.exe. Not sure about dmake.
+         */
+        if (chaz_Make.shell_type == CHAZ_OS_POSIX) {
+            succeeded = S_chaz_Make_detect("make", "gmake", "dmake",
+                                           "mingw32-make", NULL);
+        }
+        else if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
+            succeeded = S_chaz_Make_detect("nmake", "dmake", "mingw32-make",
+                                           NULL);
+        }
+
+        if (!succeeded) {
             chaz_Util_warn("No working make utility found");
         }
         else if (chaz_Util_verbosity) {
@@ -4663,18 +4679,18 @@ chaz_Make_shell_type(void) {
 }
 
 static int
-chaz_Make_detect(const char *make1, ...) {
+S_chaz_Make_detect(const char *make1, ...) {
     va_list args;
     const char *candidate;
     int found = 0;
-    const char makefile_content[] = "foo:\n\techo foo\\^bar\n";
+    const char makefile_content[] = "foo:\n\t@echo 643490c943525d19\n";
     chaz_Util_write_file("_charm_Makefile", makefile_content);
 
     /* Audition candidates. */
-    found = chaz_Make_audition(make1);
+    found = S_chaz_Make_audition(make1);
     va_start(args, make1);
     while (!found && (NULL != (candidate = va_arg(args, const char*)))) {
-        found = chaz_Make_audition(candidate);
+        found = S_chaz_Make_audition(candidate);
     }
     va_end(args);
 
@@ -4684,7 +4700,7 @@ chaz_Make_detect(const char *make1, ...) {
 }
 
 static int
-chaz_Make_audition(const char *make) {
+S_chaz_Make_audition(const char *make) {
     int succeeded = 0;
     char *command = chaz_Util_join(" ", make, "-f", "_charm_Makefile", NULL);
 
@@ -4693,15 +4709,8 @@ chaz_Make_audition(const char *make) {
     if (chaz_Util_can_open_file("_charm_foo")) {
         size_t len;
         char *content = chaz_Util_slurp_file("_charm_foo", &len);
-        if (NULL != strstr(content, "foo\\bar")) {
-            if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
-                succeeded = 1;
-            }
-        }
-        else if (NULL != strstr(content, "foo^bar")) {
-            if (chaz_Make.shell_type == CHAZ_OS_POSIX) {
-                succeeded = 1;
-            }
+        if (content != NULL && strstr(content, "643490c943525d19") != NULL) {
+            succeeded = 1;
         }
         free(content);
     }
@@ -4717,59 +4726,60 @@ chaz_Make_audition(const char *make) {
 
 chaz_MakeFile*
 chaz_MakeFile_new() {
-    chaz_MakeFile *makefile = (chaz_MakeFile*)malloc(sizeof(chaz_MakeFile));
+    chaz_MakeFile *self = (chaz_MakeFile*)calloc(1, sizeof(chaz_MakeFile));
     const char    *exe_ext  = chaz_CC_exe_ext();
     const char    *obj_ext  = chaz_CC_obj_ext();
     char *generated;
 
-    makefile->vars = (chaz_MakeVar**)malloc(sizeof(chaz_MakeVar*));
-    makefile->vars[0] = NULL;
-    makefile->num_vars = 0;
+    self->vars     = (chaz_MakeVar**)calloc(1, sizeof(chaz_MakeVar*));
+    self->rules    = (chaz_MakeRule**)calloc(1, sizeof(chaz_MakeRule*));
+    self->binaries = (chaz_MakeBinary**)calloc(1, sizeof(chaz_MakeBinary*));
 
-    makefile->rules = (chaz_MakeRule**)malloc(sizeof(chaz_MakeRule*));
-    makefile->rules[0] = NULL;
-    makefile->num_rules = 0;
-
-    makefile->clean     = S_new_rule("clean", NULL);
-    makefile->distclean = S_new_rule("distclean", "clean");
+    self->clean     = S_chaz_MakeRule_new("clean", NULL);
+    self->distclean = S_chaz_MakeRule_new("distclean", "clean");
 
     generated = chaz_Util_join("", "charmonizer", exe_ext, " charmonizer",
                                obj_ext, " charmony.h Makefile", NULL);
-    chaz_MakeRule_add_rm_command(makefile->distclean, generated);
+    chaz_MakeRule_add_rm_command(self->distclean, generated);
 
     free(generated);
-    return makefile;
+    return self;
 }
 
 void
-chaz_MakeFile_destroy(chaz_MakeFile *makefile) {
+chaz_MakeFile_destroy(chaz_MakeFile *self) {
     size_t i;
 
-    for (i = 0; makefile->vars[i]; i++) {
-        chaz_MakeVar *var = makefile->vars[i];
+    for (i = 0; self->vars[i]; i++) {
+        chaz_MakeVar *var = self->vars[i];
         free(var->name);
         free(var->value);
         free(var);
     }
-    free(makefile->vars);
+    free(self->vars);
+
+    for (i = 0; self->rules[i]; i++) {
+        S_chaz_MakeRule_destroy(self->rules[i]);
+    }
+    free(self->rules);
 
-    for (i = 0; makefile->rules[i]; i++) {
-        S_destroy_rule(makefile->rules[i]);
+    for (i = 0; self->binaries[i]; i++) {
+        S_chaz_MakeBinary_destroy(self->binaries[i]);
     }
-    free(makefile->rules);
+    free(self->binaries);
 
-    S_destroy_rule(makefile->clean);
-    S_destroy_rule(makefile->distclean);
+    S_chaz_MakeRule_destroy(self->clean);
+    S_chaz_MakeRule_destroy(self->distclean);
 
-    free(makefile);
+    free(self);
 }
 
 chaz_MakeVar*
-chaz_MakeFile_add_var(chaz_MakeFile *makefile, const char *name,
+chaz_MakeFile_add_var(chaz_MakeFile *self, const char *name,
                       const char *value) {
     chaz_MakeVar  *var      = (chaz_MakeVar*)malloc(sizeof(chaz_MakeVar));
-    chaz_MakeVar **vars     = makefile->vars;
-    size_t         num_vars = makefile->num_vars + 1;
+    chaz_MakeVar **vars     = self->vars;
+    size_t         num_vars = self->num_vars + 1;
 
     var->name         = chaz_Util_strdup(name);
     var->value        = chaz_Util_strdup("");
@@ -4781,241 +4791,303 @@ chaz_MakeFile_add_var(chaz_MakeFile *makefile, const char *name,
                                    (num_vars + 1) * sizeof(chaz_MakeVar*));
     vars[num_vars-1] = var;
     vars[num_vars]   = NULL;
-    makefile->vars = vars;
-    makefile->num_vars = num_vars;
+    self->vars = vars;
+    self->num_vars = num_vars;
 
     return var;
 }
 
 chaz_MakeRule*
-chaz_MakeFile_add_rule(chaz_MakeFile *makefile, const char *target,
+chaz_MakeFile_add_rule(chaz_MakeFile *self, const char *target,
                        const char *prereq) {
-    chaz_MakeRule  *rule      = S_new_rule(target, prereq);
-    chaz_MakeRule **rules     = makefile->rules;
-    size_t          num_rules = makefile->num_rules + 1;
+    chaz_MakeRule  *rule      = S_chaz_MakeRule_new(target, prereq);
+    chaz_MakeRule **rules     = self->rules;
+    size_t          num_rules = self->num_rules + 1;
 
     rules = (chaz_MakeRule**)realloc(rules,
                                      (num_rules + 1) * sizeof(chaz_MakeRule*));
     rules[num_rules-1] = rule;
     rules[num_rules]   = NULL;
-    makefile->rules = rules;
-    makefile->num_rules = num_rules;
+    self->rules = rules;
+    self->num_rules = num_rules;
 
     return rule;
 }
 
 chaz_MakeRule*
-chaz_MakeFile_clean_rule(chaz_MakeFile *makefile) {
-    return makefile->clean;
+chaz_MakeFile_clean_rule(chaz_MakeFile *self) {
+    return self->clean;
 }
 
 chaz_MakeRule*
-chaz_MakeFile_distclean_rule(chaz_MakeFile *makefile) {
-    return makefile->distclean;
+chaz_MakeFile_distclean_rule(chaz_MakeFile *self) {
+    return self->distclean;
 }
 
-chaz_MakeRule*
-chaz_MakeFile_add_exe(chaz_MakeFile *makefile, const char *exe,
-                      const char *sources, chaz_CFlags *link_flags) {
-    chaz_CFlags   *local_flags  = chaz_CC_new_cflags();
-    const char    *link         = chaz_CC_link_command();
-    const char    *link_flags_string = "";
-    const char    *local_flags_string;
-    chaz_MakeRule *rule;
-    char          *command;
-
-    rule = chaz_MakeFile_add_rule(makefile, exe, sources);
+chaz_MakeBinary*
+chaz_MakeFile_add_exe(chaz_MakeFile *self, const char *dir,
+                      const char *basename) {
+    const char *exe_ext = chaz_CC_exe_ext();
+    char *target;
+    chaz_MakeBinary *binary;
 
-    if (link_flags) {
-        link_flags_string = chaz_CFlags_get_string(link_flags);
+    if (dir == NULL || strcmp(dir, ".") == 0) {
+        target = chaz_Util_join("", basename, exe_ext, NULL);
     }
-    if (chaz_CC_msvc_version_num()) {
-        chaz_CFlags_append(local_flags, "/nologo");
+    else {
+        const char *dir_sep = chaz_OS_dir_sep();
+        target = chaz_Util_join("", dir, dir_sep, basename, exe_ext, NULL);
     }
-    chaz_CFlags_set_link_output(local_flags, exe);
-    local_flags_string = chaz_CFlags_get_string(local_flags);
-    command = chaz_Util_join(" ", link, sources, link_flags_string,
-                             local_flags_string, NULL);
-    chaz_MakeRule_add_command(rule, command);
 
-    chaz_MakeRule_add_rm_command(makefile->clean, exe);
+    binary = S_chaz_MakeFile_add_binary(self, CHAZ_MAKEBINARY_EXE, dir,
+                                        basename, target);
 
-    chaz_CFlags_destroy(local_flags);
-    free(command);
-    return rule;
+    free(target);
+    return binary;
 }
 
-chaz_MakeRule*
-chaz_MakeFile_add_compiled_exe(chaz_MakeFile *makefile, const char *exe,
-                               const char *sources, chaz_CFlags *cflags) {
-    chaz_CFlags   *local_flags   = chaz_CC_new_cflags();
-    const char    *cc            = chaz_CC_get_cc();
-    const char    *cflags_string = "";
-    const char    *local_flags_string;
-    chaz_MakeRule *rule;
-    char          *command;
+void
+S_chaz_MakeFile_finish_exe(chaz_MakeFile *self, chaz_MakeBinary *binary) {
+    const char *link = chaz_CC_link_command();
+    const char *link_flags_string;
+    char *command;
 
-    rule = chaz_MakeFile_add_rule(makefile, exe, sources);
+    (void)self;
 
-    if (cflags) {
-        cflags_string = chaz_CFlags_get_string(cflags);
+    /* This is destructive but shouldn't be a problem since a Makefile
+     * is only written once.
+     */
+    chaz_CFlags_set_link_output(binary->link_flags, "$@");
+    link_flags_string = chaz_CFlags_get_string(binary->link_flags);
+
+    /* Objects in dollar var must come before flags since flags may
+     * contain libraries.
+     */
+    command = chaz_Util_join(" ", link, binary->dollar_var, link_flags_string,
+                             NULL);
+    chaz_MakeRule_add_command(binary->rule, command);
+    free(command);
+}
+
+chaz_MakeBinary*
+chaz_MakeFile_add_shared_lib(chaz_MakeFile *self, const char *dir,
+                             const char *basename, const char *version,
+                             const char *major_version) {
+    int binary_format = chaz_CC_binary_format();
+    char *target;
+    chaz_MakeBinary *binary;
+
+    if (binary_format == CHAZ_CC_BINFMT_PE) {
+        target = chaz_CC_shared_lib_filename(dir, basename, major_version);
     }
-    if (chaz_CC_msvc_version_num()) {
-        chaz_CFlags_append(local_flags, "/nologo");
+    else {
+        target = chaz_CC_shared_lib_filename(dir, basename, version);
     }
-    chaz_CFlags_set_output_exe(local_flags, exe);
-    local_flags_string = chaz_CFlags_get_string(local_flags);
-    command = chaz_Util_join(" ", cc, sources, cflags_string,
-                             local_flags_string, NULL);
-    chaz_MakeRule_add_command(rule, command);
 
-    chaz_MakeRule_add_rm_command(makefile->clean, exe);
-    /* TODO: Clean .obj file on Windows. */
+    binary = S_chaz_MakeFile_add_binary(self, CHAZ_MAKEBINARY_SHARED_LIB, dir,
+                                        basename, target);
+    binary->version       = chaz_Util_strdup(version);
+    binary->major_version = chaz_Util_strdup(major_version);
 
-    chaz_CFlags_destroy(local_flags);
-    free(command);
-    return rule;
+    chaz_CFlags_compile_shared_library(binary->compile_flags);
+    chaz_CFlags_link_shared_library(binary->link_flags, basename, version,
+                                    major_version);
+
+    free(target);
+    return binary;
 }
 
-chaz_MakeRule*
-chaz_MakeFile_add_shared_lib(chaz_MakeFile *makefile, chaz_Lib *lib,
-                             const char *sources, chaz_CFlags *link_flags) {
-    chaz_CFlags *local_flags = chaz_CC_new_cflags();
+void
+S_chaz_MakeFile_finish_shared_lib(chaz_MakeFile *self,
+                                  chaz_MakeBinary *binary) {
     const char *link = chaz_CC_link_command();
-    const char *link_flags_string = "";
-    const char *local_flags_string;
+    const char *link_flags_string;
     int binfmt = chaz_CC_binary_format();
-    chaz_MakeRule *rule;
-    char *filename;
+    char *no_v_name
+        = chaz_CC_shared_lib_filename(binary->target_dir, binary->basename,
+                                      NULL);
+    char *major_v_name
+        = chaz_CC_shared_lib_filename(binary->target_dir, binary->basename,
+                                      binary->major_version);
     char *command;
 
-    filename = chaz_Lib_filename(lib);
-    rule = chaz_MakeFile_add_rule(makefile, filename, sources);
-
-    if (link_flags) {
-        link_flags_string = chaz_CFlags_get_string(link_flags);
-    }
-
-    if (chaz_CC_msvc_version_num()) {
-        chaz_CFlags_append(local_flags, "/nologo");
-    }
-    chaz_CFlags_link_shared_library(local_flags);
     if (binfmt == CHAZ_CC_BINFMT_MACHO) {
-        /* Set temporary install name with full path on Darwin. */
         const char *dir_sep = chaz_OS_dir_sep();
-        char *major_v_name = chaz_Lib_major_version_filename(lib);
-        char *install_name = chaz_Util_join("", "-install_name $(CURDIR)",
-                                            dir_sep, major_v_name, NULL);
-        chaz_CFlags_append(local_flags, install_name);
-        free(major_v_name);
+        char *install_name;
+
+        /* Set temporary install name with full path on Darwin. */
+        install_name = chaz_Util_join("", "-install_name $(CURDIR)", dir_sep,
+                                      major_v_name, NULL);
+        chaz_CFlags_append(binary->link_flags, install_name);
         free(install_name);
     }
-    chaz_CFlags_set_shared_library_version(local_flags, lib);
-    chaz_CFlags_set_link_output(local_flags, filename);
-    local_flags_string = chaz_CFlags_get_string(local_flags);
 
-    command = chaz_Util_join(" ", link, sources, link_flags_string,
-                             local_flags_string, NULL);
-    chaz_MakeRule_add_command(rule, command);
-    free(command);
+    chaz_CFlags_set_link_output(binary->link_flags, "$@");
+    link_flags_string = chaz_CFlags_get_string(binary->link_flags);
 
-    chaz_MakeRule_add_rm_command(makefile->clean, filename);
+    command = chaz_Util_join(" ", link, binary->dollar_var, link_flags_string,
+                             NULL);
+    chaz_MakeRule_add_command(binary->rule, command);
+    free(command);
 
     /* Add symlinks. */
     if (binfmt == CHAZ_CC_BINFMT_ELF || binfmt == CHAZ_CC_BINFMT_MACHO) {
-        char *major_v_name = chaz_Lib_major_version_filename(lib);
-        char *no_v_name    = chaz_Lib_no_version_filename(lib);
-
-        command = chaz_Util_join(" ", "ln -sf", filename, major_v_name, NULL);
-        chaz_MakeRule_add_command(rule, command);
+        command = chaz_Util_join(" ", "ln -sf", binary->rule->targets,
+                                 major_v_name, NULL);
+        chaz_MakeRule_add_command(binary->rule, command);
         free(command);
 
         if (binfmt == CHAZ_CC_BINFMT_MACHO) {
-            command = chaz_Util_join(" ", "ln -sf", filename, no_v_name,
-                                     NULL);
+            command = chaz_Util_join(" ", "ln -sf", binary->rule->targets,
+                                     no_v_name, NULL);
         }
         else {
             command = chaz_Util_join(" ", "ln -sf", major_v_name, no_v_name,
                                      NULL);
         }
-        chaz_MakeRule_add_command(rule, command);
+        chaz_MakeRule_add_command(binary->rule, command);
         free(command);
 
-        chaz_MakeRule_add_rm_command(makefile->clean, major_v_name);
-        chaz_MakeRule_add_rm_command(makefile->clean, no_v_name);
+        chaz_MakeRule_add_rm_command(self->clean, major_v_name);
+        chaz_MakeRule_add_rm_command(self->clean, no_v_name);
+    }
 
-        free(major_v_name);
-        free(no_v_name);
+    if (binfmt == CHAZ_CC_BINFMT_PE) {
+        /* Remove import library. */
+        char *filename
+            = chaz_CC_import_lib_filename(binary->target_dir, binary->basename,
+                                          binary->major_version);
+        chaz_MakeRule_add_rm_command(self->clean, filename);
+        free(filename);
     }
 
     if (chaz_CC_msvc_version_num()) {
-        /* Remove import library and export file under MSVC. */
-        char *lib_filename = chaz_Lib_implib_filename(lib);
-        char *exp_filename = chaz_Lib_export_filename(lib);
-        chaz_MakeRule_add_rm_command(makefile->clean, lib_filename);
-        chaz_MakeRule_add_rm_command(makefile->clean, exp_filename);
-        free(lib_filename);
-        free(exp_filename);
+        /* Remove export file. */
+        char *filename
+            = chaz_CC_export_filename(binary->target_dir, binary->basename,
+                                      binary->major_version);
+        chaz_MakeRule_add_rm_command(self->clean, filename);
+        free(filename);
     }
 
-    chaz_CFlags_destroy(local_flags);
-    free(filename);
-    return rule;
+    free(major_v_name);
+    free(no_v_name);
 }
 
-chaz_MakeRule*
-chaz_MakeFile_add_static_lib(chaz_MakeFile *makefile, chaz_Lib *lib,
-                             const char *objects) {
-    chaz_MakeRule *rule;
-    char          *filename;
-    char          *command;
+chaz_MakeBinary*
+chaz_MakeFile_add_static_lib(chaz_MakeFile *self, const char *dir,
+                             const char *basename) {
+    char *target = chaz_CC_static_lib_filename(dir, basename);
+    chaz_MakeBinary *binary
+        = S_chaz_MakeFile_add_binary(self, CHAZ_MAKEBINARY_STATIC_LIB, dir,
+                                     basename, target);
 
-    filename = chaz_Lib_filename(lib);
-    rule = chaz_MakeFile_add_rule(makefile, filename, objects);
-    command = chaz_CC_format_archiver_command(filename, objects);
-    chaz_MakeRule_add_command(rule, command);
+    free(target);
+    return binary;
+}
+
+static void
+S_chaz_MakeFile_finish_static_lib(chaz_MakeFile *self,
+                                  chaz_MakeBinary *binary) {
+    char *command;
+
+    (void)self;
+
+    command = chaz_CC_format_archiver_command("$@", binary->dollar_var);
+    chaz_MakeRule_add_command(binary->rule, command);
     free(command);
-    command = chaz_CC_format_ranlib_command(filename);
+
+    command = chaz_CC_format_ranlib_command("$@");
     if (command) {
-        chaz_MakeRule_add_command(rule, command);
+        chaz_MakeRule_add_command(binary->rule, command);
         free(command);
     }
-    chaz_MakeRule_add_rm_command(makefile->clean, filename);
-
-    free(filename);
-    return rule;
 }
 
-chaz_MakeRule*
-chaz_MakeFile_add_lemon_exe(chaz_MakeFile *makefile, const char *dir) {
-    chaz_CFlags   *cflags = chaz_CC_new_cflags();
-    chaz_MakeRule *rule;
-    const char *dir_sep = chaz_OS_dir_sep();
-    const char *exe_ext = chaz_CC_exe_ext();
-    char *lemon_exe = chaz_Util_join("", dir, dir_sep, "lemon", exe_ext, NULL);
-    char *lemon_c   = chaz_Util_join(dir_sep, dir, "lemon.c", NULL);
+static chaz_MakeBinary*
+S_chaz_MakeFile_add_binary(chaz_MakeFile *self, int type, const char *dir,
+                           const char *basename, const char *target) {
+    chaz_MakeBinary *binary
+        = (chaz_MakeBinary*)calloc(1, sizeof(chaz_MakeBinary));
+    const char *suffix;
+    char *uc_basename = chaz_Util_strdup(basename);
+    char *binary_var_name;
+    char *obj_var_name;
+    char *dollar_var;
+    size_t i;
+    size_t num_binaries;
+    size_t alloc_size;
+    chaz_MakeBinary **binaries;
+
+    switch (type) {
+        case CHAZ_MAKEBINARY_EXE:        suffix = "EXE";        break;
+        case CHAZ_MAKEBINARY_STATIC_LIB: suffix = "STATIC_LIB"; break;
+        case CHAZ_MAKEBINARY_SHARED_LIB: suffix = "SHARED_LIB"; break;
+        default:
+            chaz_Util_die("Unknown binary type %d", type);
+            return NULL;
+    }
+
+    for (i = 0; uc_basename[i] != '\0'; i++) {
+        uc_basename[i] = toupper((unsigned char)uc_basename[i]);
+    }
+
+    binary_var_name = chaz_Util_join("_", uc_basename, suffix, NULL);
+    obj_var_name    = chaz_Util_join("_", uc_basename, suffix, "OBJS", NULL);
+    dollar_var      = chaz_Util_join("", "$(", obj_var_name, ")", NULL);
+
+    chaz_MakeFile_add_var(self, binary_var_name, target);
+
+    binary->type           = type;
+    binary->target_dir     = dir ? chaz_Util_strdup(dir) : NULL;
+    binary->basename       = chaz_Util_strdup(basename);
+    binary->obj_var        = chaz_MakeFile_add_var(self, obj_var_name, NULL);
+    binary->dollar_var     = dollar_var;
+    binary->rule           = S_chaz_MakeRule_new(target, dollar_var);
+    binary->sources        = (char**)calloc(1, sizeof(char*));
+    binary->single_sources = (char**)calloc(1, sizeof(char*));
+    binary->dirs           = (char**)calloc(1, sizeof(char*));
+    binary->compile_flags  = chaz_CC_new_cflags();
+    binary->link_flags     = chaz_CC_new_cflags();
+
+    num_binaries = self->num_binaries;
+    alloc_size   = (num_binaries + 2) * sizeof(chaz_MakeBinary*);
+    binaries     = (chaz_MakeBinary**)realloc(self->binaries, alloc_size);
+    binaries[num_binaries]   = binary;
+    binaries[num_binaries+1] = NULL;
+    self->binaries     = binaries;
+    self->num_binaries = num_binaries + 1;
+
+    free(uc_basename);
+    free(obj_var_name);
+    free(binary_var_name);
+    return binary;
+}
+
+chaz_MakeBinary*
+chaz_MakeFile_add_lemon_exe(chaz_MakeFile *self, const char *dir) {
+    chaz_MakeBinary *exe = chaz_MakeFile_add_exe(self, dir, "lemon");
+    chaz_MakeBinary_add_src_file(exe, dir, "lemon.c");
 
-    chaz_CFlags_enable_optimization(cflags);
-    chaz_MakeFile_add_var(makefile, "LEMON_EXE", lemon_exe);
-    rule = chaz_MakeFile_add_compiled_exe(makefile, "$(LEMON_EXE)", lemon_c,
-                                          cflags);
+    if (chaz_CC_gcc_version_num()) {
+        chaz_CFlags *cflags = chaz_MakeBinary_get_compile_flags(exe);
+        chaz_CFlags_append(cflags, "-Wno-pedantic -Wno-sign-compare"
+                           " -Wno-unused-parameter");
+    }
 
-    chaz_CFlags_destroy(cflags);
-    free(lemon_c);
-    free(lemon_exe);
-    return rule;
+    return exe;
 }
 
 chaz_MakeRule*
-chaz_MakeFile_add_lemon_grammar(chaz_MakeFile *makefile,
+chaz_MakeFile_add_lemon_grammar(chaz_MakeFile *self,
                                 const char *base_name) {
     char *c_file  = chaz_Util_join(".", base_name, "c", NULL);
     char *h_file  = chaz_Util_join(".", base_name, "h", NULL);
     char *y_file  = chaz_Util_join(".", base_name, "y", NULL);
     char *command = chaz_Util_join(" ", "$(LEMON_EXE) -q", y_file, NULL);
 
-    chaz_MakeRule *rule = chaz_MakeFile_add_rule(makefile, c_file, y_file);
-    chaz_MakeRule *clean_rule = chaz_MakeFile_clean_rule(makefile);
+    chaz_MakeRule *rule = chaz_MakeFile_add_rule(self, c_file, y_file);
+    chaz_MakeRule *clean_rule = chaz_MakeFile_clean_rule(self);
 
     chaz_MakeRule_add_prereq(rule, "$(LEMON_EXE)");
     chaz_MakeRule_add_command(rule, command);
@@ -5030,51 +5102,8 @@ chaz_MakeFile_add_lemon_grammar(chaz_MakeFile *makefile,
     return rule;
 }
 
-chaz_MakeRule*
-chaz_MakeFile_override_cflags(chaz_MakeFile *makefile, const char *obj,
-                              chaz_CFlags *cflags) {
-    const char *obj_ext       = chaz_CC_obj_ext();
-    const char *cflags_string = chaz_CFlags_get_string(cflags);
-    size_t obj_ext_len = strlen(obj_ext);
-    size_t obj_len     = strlen(obj);
-    size_t base_len;
-    char *src;
-    char *command;
-    chaz_MakeRule *rule;
-
-    if (obj_len <= obj_ext_len) {
-       chaz_Util_die("Invalid object file: %s", obj);
-    }
-
-    base_len = obj_len - obj_ext_len;
-
-    if (strcmp(obj + base_len, obj_ext) != 0) {
-       chaz_Util_die("Invalid object file: %s", obj);
-    }
-
-    src = malloc(base_len + sizeof(".c"));
-    memcpy(src, obj, base_len);
-    memcpy(src + base_len, ".c", sizeof(".c"));
-
-    rule = chaz_MakeFile_add_rule(makefile, obj, src);
-    if (chaz_CC_msvc_version_num()) {
-        command = chaz_Util_join(" ", "$(CC) /nologo", cflags_string, "/c",
-                                 src, "/Fo$@", NULL);
-    }
-    else {
-        command = chaz_Util_join(" ", "$(CC)", cflags_string, "-c", src,
-                                 "-o $@", NULL);
-    }
-    chaz_MakeRule_add_command(rule, command);
-
-    free(command);
-    free(src);
-
-    return rule;
-}
-
 void
-chaz_MakeFile_write(chaz_MakeFile *makefile) {
+chaz_MakeFile_write(chaz_MakeFile *self) {
     FILE   *out;
     size_t  i;
 
@@ -5083,18 +5112,27 @@ chaz_MakeFile_write(chaz_MakeFile *makefile) {
         chaz_Util_die("Can't open Makefile\n");
     }
 
-    for (i = 0; makefile->vars[i]; i++) {
-        chaz_MakeVar *var = makefile->vars[i];
+    if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
+        /* Make sure that mingw32-make uses the cmd.exe shell. */
+        fprintf(out, "SHELL = cmd\n");
+    }
+
+    for (i = 0; self->vars[i]; i++) {
+        chaz_MakeVar *var = self->vars[i];
         fprintf(out, "%s = %s\n", var->name, var->value);
     }
     fprintf(out, "\n");
 
-    for (i = 0; makefile->rules[i]; i++) {
-        S_write_rule(makefile->rules[i], out);
+    for (i = 0; self->rules[i]; i++) {
+        S_chaz_MakeRule_write(self->rules[i], out);
+    }
+
+    for (i = 0; self->binaries[i]; i++) {
+        S_chaz_MakeFile_write_binary_rules(self, self->binaries[i], out);
     }
 
-    S_write_rule(makefile->clean, out);
-    S_write_rule(makefile->distclean, out);
+    S_chaz_MakeRule_write(self->clean, out);
+    S_chaz_MakeRule_write(self->distclean, out);
 
     /* Suffix rule for .c files. */
     if (chaz_CC_msvc_version_num()) {
@@ -5109,33 +5147,151 @@ chaz_MakeFile_write(chaz_MakeFile *makefile) {
     fclose(out);
 }
 
+static void
+S_chaz_MakeFile_write_binary_rules(chaz_MakeFile *self,
+                                   chaz_MakeBinary *binary, FILE *out) {
+    const char *cflags;
+
+    if (chaz_CC_msvc_version_num()) {
+        chaz_CFlags_append(binary->compile_flags, "/nologo");
+        chaz_CFlags_append(binary->link_flags, "/nologo");
+    }
+
+    switch (binary->type) {
+        case CHAZ_MAKEBINARY_EXE:
+            S_chaz_MakeFile_finish_exe(self, binary);
+            break;
+        case CHAZ_MAKEBINARY_STATIC_LIB:
+            S_chaz_MakeFile_finish_static_lib(self, binary);
+            break;
+        case CHAZ_MAKEBINARY_SHARED_LIB:
+            S_chaz_MakeFile_finish_shared_lib(self, binary);
+            break;
+        default:
+            chaz_Util_die("Invalid binary type: %d", binary->type);
+            return;
+    }
+
+    chaz_MakeRule_add_rm_command(self->clean, binary->rule->targets);
+    chaz_MakeRule_add_rm_command(self->clean, binary->dollar_var);
+
+    S_chaz_MakeRule_write(binary->rule, out);
+
+    cflags = chaz_CFlags_get_string(binary->compile_flags);
+
+    /* Write rules to compile with custom flags. */
+    if (cflags[0] != '\0') {
+        if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
+            /* Write a rule for each object file. This is needed for nmake
+             * which doesn't support pattern rules but also for mingw32-make
+             * which has problems with pattern rules and backslash directory
+             * separators.
+             */
+            S_chaz_MakeFile_write_object_rules(binary->sources, cflags, out);
+        }
+        else {
+            /* Write a pattern rule for each directory. */
+            S_chaz_MakeFile_write_pattern_rules(binary->dirs, cflags, out);
+            /* Write a rule for each object added with add_src_file. */
+            S_chaz_MakeFile_write_object_rules(binary->single_sources, cflags,
+                                               out);
+        }
+    }
+}
+
+static void
+S_chaz_MakeFile_write_object_rules(char **sources, const char *cflags,
+                                   FILE *out) {
+    chaz_CFlags *output_cflags = chaz_CC_new_cflags();
+    const char *output_cflags_string;
+    size_t i;
+
+    chaz_CFlags_set_output_obj(output_cflags, "$@");
+    output_cflags_string = chaz_CFlags_get_string(output_cflags);
+
+    for (i = 0; sources[i]; i++) {
+        const char *source = sources[i];
+        char *obj_path = S_chaz_MakeBinary_obj_path(source);
+        chaz_MakeRule *rule;
+        char *command;
+
+        if (obj_path == NULL) { continue; }
+
+        rule = S_chaz_MakeRule_new(obj_path, source);
+        command = chaz_Util_join(" ", "$(CC) $(CFLAGS)", cflags, source,
+                                 output_cflags_string, NULL);
+        chaz_MakeRule_add_command(rule, command);
+        S_chaz_MakeRule_write(rule, out);
+
+        free(command);
+        S_chaz_MakeRule_destroy(rule);
+        free(obj_path);
+    }
+
+    chaz_CFlags_destroy(output_cflags);
+}
+
+static void
+S_chaz_MakeFile_write_pattern_rules(char **dirs, const char *cflags,
+                                    FILE *out) {
+    const char *obj_ext = chaz_CC_obj_ext();
+    const char *dir_sep = chaz_OS_dir_sep();
+    chaz_CFlags *output_cflags = chaz_CC_new_cflags();
+    const char *output_cflags_string;
+    char *command;
+    size_t i;
+
+    chaz_CFlags_set_output_obj(output_cflags, "$@");
+    output_cflags_string = chaz_CFlags_get_string(output_cflags);
+    command  = chaz_Util_join(" ", "$(CC) $(CFLAGS)", cflags, "$<",
+                              output_cflags_string, NULL);
+
+    for (i = 0; dirs[i]; i++) {
+        const char *dir = dirs[i];
+        char *target = chaz_Util_join("", dir, dir_sep, "%", obj_ext,
+                                      NULL);
+        char *prereq = chaz_Util_join("", dir, dir_sep, "%.c", NULL);
+        chaz_MakeRule *rule = S_chaz_MakeRule_new(target, prereq);
+
+        chaz_MakeRule_add_command(rule, command);
+        S_chaz_MakeRule_write(rule, out);
+
+        S_chaz_MakeRule_destroy(rule);
+        free(prereq);
+        free(target);
+    }
+
+    free(command);
+    chaz_CFlags_destroy(output_cflags);
+}
+
 void
-chaz_MakeVar_append(chaz_MakeVar *var, const char *element) {
+chaz_MakeVar_append(chaz_MakeVar *self, const char *element) {
     char *value;
 
     if (element[0] == '\0') { return; }
 
-    if (var->num_elements == 0) {
+    if (self->num_elements == 0) {
         value = chaz_Util_strdup(element);
     }
     else {
-        value = (char*)malloc(strlen(var->value) + strlen(element) + 20);
+        value = (char*)malloc(strlen(self->value) + strlen(element) + 20);
 
-        if (var->num_elements == 1) {
-            sprintf(value, "\\\n    %s \\\n    %s", var->value, element);
+        if (self->num_elements == 1) {
+            sprintf(value, "\\\n    %s \\\n    %s", self->value, element);
         }
         else {
-            sprintf(value, "%s \\\n    %s", var->value, element);
+            sprintf(value, "%s \\\n    %s", self->value, element);
         }
     }
 
-    free(var->value);
-    var->value = value;
-    var->num_elements++;
+    free(self->value);
+    self->value = value;
+    self->num_elements++;
 }
 
 static chaz_MakeRule*
-S_new_rule(const char *target, const char *prereq) {
+S_chaz_MakeRule_new(const char *target, const char *prereq) {
     chaz_MakeRule *rule = (chaz_MakeRule*)malloc(sizeof(chaz_MakeRule));
 
     rule->targets  = NULL;
@@ -5149,75 +5305,75 @@ S_new_rule(const char *target, const char *prereq) {
 }
 
 static void
-S_destroy_rule(chaz_MakeRule *rule) {
-    if (rule->targets)  { free(rule->targets); }
-    if (rule->prereqs)  { free(rule->prereqs); }
-    if (rule->commands) { free(rule->commands); }
-    free(rule);
+S_chaz_MakeRule_destroy(chaz_MakeRule *self) {
+    if (self->targets)  { free(self->targets); }
+    if (self->prereqs)  { free(self->prereqs); }
+    if (self->commands) { free(self->commands); }
+    free(self);
 }
 
 static void
-S_write_rule(chaz_MakeRule *rule, FILE *out) {
-    fprintf(out, "%s :", rule->targets);
-    if (rule->prereqs) {
-        fprintf(out, " %s", rule->prereqs);
+S_chaz_MakeRule_write(chaz_MakeRule *self, FILE *out) {
+    fprintf(out, "%s :", self->targets);
+    if (self->prereqs) {
+        fprintf(out, " %s", self->prereqs);
     }
     fprintf(out, "\n");
-    if (rule->commands) {
-        fprintf(out, "%s", rule->commands);
+    if (self->commands) {
+        fprintf(out, "%s", self->commands);
     }
     fprintf(out, "\n");
 }
 
 void
-chaz_MakeRule_add_target(chaz_MakeRule *rule, const char *target) {
+chaz_MakeRule_add_target(chaz_MakeRule *self, const char *target) {
     char *targets;
 
-    if (!rule->targets) {
+    if (!self->targets) {
         targets = chaz_Util_strdup(target);
     }
     else {
-        targets = chaz_Util_join(" ", rule->targets, target, NULL);
-        free(rule->targets);
+        targets = chaz_Util_join(" ", self->targets, target, NULL);
+        free(self->targets);
     }
 
-    rule->targets = targets;
+    self->targets = targets;
 }
 
 void
-chaz_MakeRule_add_prereq(chaz_MakeRule *rule, const char *prereq) {
+chaz_MakeRule_add_prereq(chaz_MakeRule *self, const char *prereq) {
     char *prereqs;
 
-    if (!rule->prereqs) {
+    if (!self->prereqs) {
         prereqs = chaz_Util_strdup(prereq);
     }
     else {
-        prereqs = chaz_Util_join(" ", rule->prereqs, prereq, NULL);
-        free(rule->prereqs);
+        prereqs = chaz_Util_join(" ", self->prereqs, prereq, NULL);
+        free(self->prereqs);
     }
 
-    rule->prereqs = prereqs;
+    self->prereqs = prereqs;
 }
 
 void
-chaz_MakeRule_add_command(chaz_MakeRule *rule, const char *command) {
+chaz_MakeRule_add_command(chaz_MakeRule *self, const char *command) {
     char *commands;
 
-    if (!rule->commands) {
+    if (!self->commands) {
         commands = (char*)malloc(strlen(command) + 20);
         sprintf(commands, "\t%s\n", command);
     }
     else {
-        commands = (char*)malloc(strlen(rule->commands) + strlen(command) + 20);
-        sprintf(commands, "%s\t%s\n", rule->commands, command);
-        free(rule->commands);
+        commands = (char*)malloc(strlen(self->commands) + strlen(command) + 20);
+        sprintf(commands, "%s\t%s\n", self->commands, command);
+        free(self->commands);
     }
 
-    rule->commands = commands;
+    self->commands = commands;
 }
 
 void
-chaz_MakeRule_add_command_with_libpath(chaz_MakeRule *rule,
+chaz_MakeRule_add_command_with_libpath(chaz_MakeRule *self,
                                        const char *command, ...) {
     va_list args;
     char *path        = NULL;
@@ -5264,12 +5420,12 @@ chaz_MakeRule_add_command_with_libpath(chaz_MakeRule *rule,
         lib_command = chaz_Util_strdup(command);
     }
 
-    chaz_MakeRule_add_command(rule, lib_command);
+    chaz_MakeRule_add_command(self, lib_command);
     free(lib_command);
 }
 
 void
-chaz_MakeRule_add_rm_command(chaz_MakeRule *rule, const char *files) {
+chaz_MakeRule_add_rm_command(chaz_MakeRule *self, const char *files) {
     char *command;
 
     if (chaz_Make.shell_type == CHAZ_OS_POSIX) {
@@ -5283,12 +5439,12 @@ chaz_MakeRule_add_rm_command(chaz_MakeRule *rule, const char *files) {
         chaz_Util_die("Unsupported shell type: %d", chaz_Make.shell_type);
     }
 
-    chaz_MakeRule_add_command(rule, command);
+    chaz_MakeRule_add_command(self, command);
     free(command);
 }
 
 void
-chaz_MakeRule_add_recursive_rm_command(chaz_MakeRule *rule, const char *dirs) {
+chaz_MakeRule_add_recursive_rm_command(chaz_MakeRule *self, const char *dirs) {
     char *command;
 
     if (chaz_Make.shell_type == CHAZ_OS_POSIX) {
@@ -5302,12 +5458,12 @@ chaz_MakeRule_add_recursive_rm_command(chaz_MakeRule *rule, const char *dirs) {
         chaz_Util_die("Unsupported shell type: %d", chaz_Make.shell_type);
     }
 
-    chaz_MakeRule_add_command(rule, command);
+    chaz_MakeRule_add_command(self, command);
     free(command);
 }
 
 void
-chaz_MakeRule_add_make_command(chaz_MakeRule *rule, const char *dir,
+chaz_MakeRule_add_make_command(chaz_MakeRule *self, const char *dir,
                                const char *target) {
     char *command;
 
@@ -5319,7 +5475,7 @@ chaz_MakeRule_add_make_command(chaz_MakeRule *rule, const char *dir,
             command = chaz_Util_join("", "(cd ", dir, " && $(MAKE) ", target,
                                      ")", NULL);
         }
-        chaz_MakeRule_add_command(rule, command);
+        chaz_MakeRule_add_command(self, command);
         free(command);
     }
     else if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
@@ -5331,7 +5487,7 @@ chaz_MakeRule_add_make_command(chaz_MakeRule *rule, const char *dir,
             command = chaz_Util_join(" ", "pushd", dir, "&& $(MAKE)", target,
                                      "&& popd", NULL);
         }
-        chaz_MakeRule_add_command(rule, command);
+        chaz_MakeRule_add_command(self, command);
         free(command);
     }
     else {
@@ -5339,6 +5495,170 @@ chaz_MakeRule_add_make_command(chaz_MakeRule *rule, const char *dir,
     }
 }
 
+static void
+S_chaz_MakeBinary_destroy(chaz_MakeBinary *self) {
+    size_t i;
+
+    free(self->target_dir);
+    free(self->basename);
+    free(self->version);
+    free(self->major_version);
+    free(self->dollar_var);
+    S_chaz_MakeRule_destroy(self->rule);
+
+    for (i = 0; i < self->num_sources; i++) {
+        free(self->sources[i]);
+    }
+    free(self->sources);
+    for (i = 0; i < self->num_single_sources; i++) {
+        free(self->single_sources[i]);
+    }
+    free(self->single_sources);
+    for (i = 0; i < self->num_dirs; i++) {
+        free(self->dirs[i]);
+    }
+    free(self->dirs);
+
+    chaz_CFlags_destroy(self->compile_flags);
+    chaz_CFlags_destroy(self->link_flags);
+
+    free(self);
+}
+
+void
+chaz_MakeBinary_add_src_file(chaz_MakeBinary *self, const char *dir,
+                             const char *filename) {
+    size_t num_sources = self->num_single_sources;
+    size_t alloc_size  = (num_sources + 2) * sizeof(char*);
+    char **sources = (char**)realloc(self->single_sources, alloc_size);
+    char *path;
+
+    if (dir == NULL || strcmp(dir, ".") == 0) {
+        path = chaz_Util_strdup(filename);
+    }
+    else {
+        const char *dir_sep = chaz_OS_dir_sep();
+        path = chaz_Util_join(dir_sep, dir, filename, NULL);
+    }
+
+    /* Add to single_sources. */
+    sources[num_sources]     = path;
+    sources[num_sources+1]   = NULL;
+    self->single_sources     = sources;
+    self->num_single_sources = num_sources + 1;
+
+    S_chaz_MakeBinary_do_add_src_file(self, path);
+}
+
+void
+chaz_MakeBinary_add_src_dir(chaz_MakeBinary *self, const char *path) {
+    size_t num_dirs = self->num_dirs;
+    char **dirs = (char**)realloc(self->dirs, (num_dirs + 2) * sizeof(char*));
+
+    dirs[num_dirs]   = chaz_Util_strdup(path);
+    dirs[num_dirs+1] = NULL;
+    self->dirs     = dirs;
+    self->num_dirs = num_dirs + 1;
+
+    chaz_Make_list_files(path, "c", S_chaz_MakeBinary_list_files_callback,
+                         self);
+}
+
+static void
+S_chaz_MakeBinary_list_files_callback(const char *dir, char *file,
+                                      void *context) {
+    const char *dir_sep = chaz_OS_dir_sep();
+    char *path = chaz_Util_join(dir_sep, dir, file, NULL);
+
+    S_chaz_MakeBinary_do_add_src_file((chaz_MakeBinary*)context, path);
+    free(path);
+}
+
+static void
+S_chaz_MakeBinary_do_add_src_file(chaz_MakeBinary *self, const char *path) {
+    size_t num_sources = self->num_sources;
+    size_t alloc_size  = (num_sources + 2) * sizeof(char*);
+    char **sources = (char**)realloc(self->sources, alloc_size);
+    char *obj_path;
+
+    sources[num_sources]   = chaz_Util_strdup(path);
+    sources[num_sources+1] = NULL;
+    self->sources     = sources;
+    self->num_sources = num_sources + 1;
+
+    obj_path = S_chaz_MakeBinary_obj_path(path);
+    if (obj_path == NULL) {
+        chaz_Util_warn("Invalid source filename: %s", path);
+    }
+    else {
+        chaz_MakeVar_append(self->obj_var, obj_path);
+        free(obj_path);
+    }
+}
+
+static char*
+S_chaz_MakeBinary_obj_path(const char *src_path) {
+    const char *dir_sep = chaz_OS_dir_sep();
+    const char *obj_ext = chaz_CC_obj_ext();
+    size_t obj_ext_len = strlen(obj_ext);
+    size_t i = strlen(src_path);
+    char *retval;
+
+    while (i > 0) {
+        i -= 1;
+        if (src_path[i] == dir_sep[0]) { return NULL; }
+        if (src_path[i] == '.')        { break; }
+    }
+
+    if (src_path[i] != '.') { return NULL; }
+
+    retval = (char*)malloc(i + obj_ext_len + 1);
+    memcpy(retval, src_path, i);
+    memcpy(retval + i, obj_ext, obj_ext_len + 1);
+
+    return retval;
+}
+
+void
+chaz_MakeBinary_add_prereq(chaz_MakeBinary *self, const char *prereq) {
+    chaz_MakeRule_add_prereq(self->rule, prereq);
+}
+
+char*
+chaz_MakeBinary_obj_string(chaz_MakeBinary *self) {
+    char *retval = chaz_Util_strdup("");
+    size_t i;
+
+    for (i = 0; i < self->num_sources; i++) {
+        const char *sep = retval[0] == '\0' ? "" : " ";
+        char *obj_path = S_chaz_MakeBinary_obj_path(self->sources[i]);
+        char *tmp;
+
+        if (obj_path == NULL) { continue; }
+
+        tmp = chaz_Util_join("", retval, sep, obj_path, NULL);
+        free(retval);
+        retval = tmp;
+    }
+
+    return retval;
+}
+
+const char*
+chaz_MakeBinary_get_target(chaz_MakeBinary *self) {
+    return self->rule->targets;
+}
+
+chaz_CFlags*
+chaz_MakeBinary_get_compile_flags(chaz_MakeBinary *self) {
+    return self->compile_flags;
+}
+
+chaz_CFlags*
+chaz_MakeBinary_get_link_flags(chaz_MakeBinary *self) {
+    return self->link_flags;
+}
+
 void
 chaz_Make_list_files(const char *dir, const char *ext,
                      chaz_Make_list_files_callback_t callback, void *context) {
@@ -5500,6 +5820,9 @@ chaz_OS_init(void) {
             chaz_OS.shell_type = CHAZ_OS_CMD_EXE;
             chaz_OS.run_sh_via_cmd_exe = 0;
         }
+
+        /* Redirection is always run through cmd.exe. */
+        strcpy(chaz_OS.dev_null, "nul");
     }
     else if (output_len >= 7 && memcmp(output, "foo^bar", 7) == 0) {
         /* Escape character is backslash. */
@@ -5507,17 +5830,16 @@ chaz_OS_init(void) {
             printf("Detected POSIX shell\n");
         }
         chaz_OS.shell_type = CHAZ_OS_POSIX;
+        strcpy(chaz_OS.dev_null, "/dev/null");
     }
 
     if (chaz_OS.shell_type == CHAZ_OS_CMD_EXE) {
         strcpy(chaz_OS.dir_sep, "\\");
-        strcpy(chaz_OS.dev_null, "nul");
         /* Empty string should work, too. */
         strcpy(chaz_OS.local_command_start, ".\\");
     }
     else if (chaz_OS.shell_type == CHAZ_OS_POSIX) {
         strcpy(chaz_OS.dir_sep, "/");
-        strcpy(chaz_OS.dev_null, "/dev/null");
         strcpy(chaz_OS.local_command_start, "./");
     }
     else {
@@ -6529,7 +6851,7 @@ chaz_Floats_math_library(void) {
         return NULL;
     }
 
-    chaz_CFlags_add_external_library(temp_cflags, "m");
+    chaz_CFlags_add_external_lib(temp_cflags, "m");
     output = chaz_CC_capture_output(sqrt_code, &output_len);
     chaz_CFlags_clear(temp_cflags);
 
@@ -7481,6 +7803,12 @@ chaz_Integers_run(void) {
     chaz_ConfWriter_start_module("IntegerFormatStrings");
 
     if (has_inttypes) {
+        if (chaz_CC_is_mingw()) {
+            /* Suppress warnings about undefined inline function `llabs`
+             * under MinGW.
+             */
+            chaz_ConfWriter_add_sys_include("stdlib.h");
+        }
         chaz_ConfWriter_add_sys_include("inttypes.h");
     }
 
@@ -8319,8 +8647,9 @@ chaz_VariadicMacros_run(void) {
 /* #include "Charmonizer/Core/ConfWriterRuby.h" */
 
 typedef struct lucy_MakeFile {
-    chaz_MakeFile *makefile;
-    chaz_CLI      *cli;
+    chaz_CLI        *cli;
+    chaz_MakeFile   *makefile;
+    chaz_MakeBinary *lib;
 
     /* Directories. */
     const char *base_dir;
@@ -8335,21 +8664,15 @@ typedef struct lucy_MakeFile {
     char       *snowstop_dir;
     char       *ucd_dir;
     char       *utf8proc_dir;
+    char       *json_dir;
 
     /* Files. */
     char        *autogen_target;
     const char **autogen_src_files;
-    char        *json_parser;
 
     /* Clownfish library. */
     char       *cfish_lib_dir;
     const char *cfish_lib_name;
-
-    /* Lucy libraries. */
-    chaz_Lib *shared_lib;
-    chaz_Lib *static_lib;
-    char     *shared_lib_filename;
-    char     *static_lib_filename;
 } lucy_MakeFile;
 
 typedef struct SourceFileContext {
@@ -8378,9 +8701,6 @@ static void
 lucy_MakeFile_write_c_test_rules(lucy_MakeFile *self);
 
 static void
-S_c_file_callback(const char *dir, char *file, void *context);
-
-static void
 S_cfh_file_callback(const char *dir, char *file, void *context);
 
 static int
@@ -8455,11 +8775,6 @@ int main(int argc, const char **argv) {
     if (chaz_CLI_defined(cli, "enable-makefile")) {
         lucy_MakeFile *mf = lucy_MakeFile_new(cli);
         lucy_MakeFile_write(mf);
-        /* Export filenames. */
-        chaz_ConfWriter_add_def("SHARED_LIB_FILENAME",
-                                mf->shared_lib_filename);
-        chaz_ConfWriter_add_def("STATIC_LIB_FILENAME",
-                                mf->static_lib_filename);
         lucy_MakeFile_destroy(mf);
     }
 
@@ -8510,9 +8825,6 @@ S_add_compiler_flags(struct chaz_CLI *cli) {
         }
     }
 
-    chaz_CFlags_add_define(extra_cflags, "CFP_LUCY", NULL);
-    chaz_CFlags_add_define(extra_cflags, "CFP_TESTLUCY", NULL);
-
     chaz_CFlags_hide_symbols(extra_cflags);
 }
 
@@ -8523,8 +8835,9 @@ lucy_MakeFile_new(chaz_CLI *cli) {
 
     lucy_MakeFile *self = malloc(sizeof(lucy_MakeFile));
 
-    self->makefile = chaz_MakeFile_new();
     self->cli      = cli;
+    self->makefile = chaz_MakeFile_new();
+    self->lib      = NULL;
 
     /* Initialize directories. */
     self->base_dir = "..";
@@ -8557,31 +8870,31 @@ lucy_MakeFile_new(chaz_CLI *cli) {
     self->utf8proc_dir
         = chaz_Util_join(dir_sep, self->modules_dir, "unicode", "utf8proc",
                          NULL);
+    self->json_dir
+        = chaz_Util_join(dir_sep, self->core_dir, "Lucy", "Util", "Json",
+                         NULL);
 
     /* Initialize file names. */
     if (chaz_CLI_defined(cli, "enable-perl")) {
         static const char *perl_autogen_src_files[] = {
-            "boot",
-            "callbacks",
-            "lucy_parcel",
-            "testlucy_parcel",
+            "boot.c",
+            "callbacks.c",
+            "lucy_parcel.c",
+            "testlucy_parcel.c",
             NULL
         };
         self->autogen_src_files = perl_autogen_src_files;
     }
     else {
         static const char *c_autogen_src_files[] = {
-            "lucy_parcel",
-            "testlucy_parcel",
+            "lucy_parcel.c",
+            "testlucy_parcel.c",
             NULL
         };
         self->autogen_src_files = c_autogen_src_files;
     }
     self->autogen_target
         = chaz_Util_join(dir_sep, "autogen", "hierarchy.json", NULL);
-    self->json_parser
-        = chaz_Util_join(dir_sep, self->core_dir, "Lucy", "Util", "Json",
-                         "JsonParser", NULL);
 
     /* Clownfish library. */
     if (cfish_prefix) {
@@ -8598,13 +8911,6 @@ lucy_MakeFile_new(chaz_CLI *cli) {
         self->cfish_lib_name = "cfish";
     }
 
-    /* Lucy libraries. */
-    self->shared_lib = chaz_Lib_new_shared("lucy", lucy_version,
-                                           lucy_major_version);
-    self->static_lib = chaz_Lib_new_static("lucy");
-    self->shared_lib_filename = chaz_Lib_filename(self->shared_lib);
-    self->static_lib_filename = chaz_Lib_filename(self->static_lib);
-
     return self;
 }
 
@@ -8622,34 +8928,27 @@ lucy_MakeFile_destroy(lucy_MakeFile *self) {
     free(self->snowstop_dir);
     free(self->ucd_dir);
     free(self->utf8proc_dir);
+    free(self->json_dir);
 
     free(self->autogen_target);
-    free(self->json_parser);
 
     free(self->cfish_lib_dir);
 
-    chaz_Lib_destroy(self->shared_lib);
-    chaz_Lib_destroy(self->static_lib);
-    free(self->shared_lib_filename);
-    free(self->static_lib_filename);
-
     free(self);
 }
 
 static void
 lucy_MakeFile_write(lucy_MakeFile *self) {
-    SourceFileContext sfc;
-
     const char *dir_sep  = chaz_OS_dir_sep();
-    const char *obj_ext  = chaz_CC_obj_ext();
+    const char *host     = chaz_CLI_strval(self->cli, "host");
     const char *math_lib = chaz_Floats_math_library();
 
     chaz_MakeVar  *var;
     chaz_MakeRule *rule;
-    chaz_MakeRule *clean_rule;
 
     chaz_CFlags *extra_cflags = chaz_CC_get_extra_cflags();
     chaz_CFlags *makefile_cflags;
+    chaz_CFlags *compile_flags;
     chaz_CFlags *link_flags;
 
     char *scratch;
@@ -8670,7 +8969,6 @@ lucy_MakeFile_write(lucy_MakeFile *self) {
     chaz_CFlags_enable_optimization(makefile_cflags);
     chaz_CFlags_enable_debugging(makefile_cflags);
     chaz_CFlags_disable_strict_aliasing(makefile_cflags);
-    chaz_CFlags_compile_shared_library(makefile_cflags);
     if (chaz_CLI_defined(self->cli, "enable-coverage")) {
         chaz_CFlags_enable_code_coverage(makefile_cflags);
     }
@@ -8689,105 +8987,96 @@ lucy_MakeFile_write(lucy_MakeFile *self) {
 
     chaz_CFlags_destroy(makefile_cflags);
 
-    /* Object files */
+    /* Binary. */
 
-    var = chaz_MakeFile_add_var(self->makefile, "LUCY_OBJS", NULL);
-    sfc.var = var;
+    if (strcmp(host, "c") == 0 || strcmp(host, "perl") == 0) {
+        chaz_MakeFile_add_rule(self->makefile, "all", "$(LUCY_SHARED_LIB)");
 
-    chaz_Make_list_files(self->host_src_dir, "c", S_c_file_callback, &sfc);
-    chaz_Make_list_files(self->core_dir,     "c", S_c_file_callback, &sfc);
-    chaz_Make_list_files(self->snowstem_dir, "c", S_c_file_callback, &sfc);
-    chaz_Make_list_files(self->snowstop_dir, "c", S_c_file_callback, &sfc);
-    chaz_Make_list_files(self->utf8proc_dir, "c", S_c_file_callback, &sfc);
+        self->lib
+            = chaz_MakeFile_add_shared_lib(self->makefile, NULL, "lucy",
+                                           lucy_version, lucy_major_version);
+        rule = chaz_MakeFile_add_rule(self->makefile,
+                                      "$(LUCY_SHARED_LIB_OBJS)",
+                                      self->autogen_target);
+        /*
+         * The dependency is actually on JsonParser.h, but make doesn't cope
+         * well with multiple output files.
+         */
+        scratch = chaz_Util_join(dir_sep, self->json_dir, "JsonParser.c",
+                                 NULL);
+        chaz_MakeRule_add_prereq(rule, scratch);
+        free(scratch);
 
-    scratch = chaz_Util_join("", self->json_parser, obj_ext, NULL);
-    chaz_MakeVar_append(var, scratch);
-    free(scratch);
+        link_flags = chaz_MakeBinary_get_link_flags(self->lib);
+        chaz_CFlags_enable_debugging(link_flags);
+        if (self->cfish_lib_dir) {
+            chaz_CFlags_add_library_path(link_flags, self->cfish_lib_dir);
+        }
+        if (math_lib) {
+            chaz_CFlags_add_external_lib(link_flags, math_lib);
+        }
+        chaz_CFlags_add_external_lib(link_flags, self->cfish_lib_name);
+        if (chaz_HeadCheck_check_header("pcre.h")) {
+            chaz_CFlags_add_external_lib(link_flags, "pcre");
+        }
+        if (chaz_CLI_defined(self->cli, "enable-coverage")) {
+            chaz_CFlags_enable_code_coverage(link_flags);
+        }
+    }
+    else {
+        chaz_MakeFile_add_rule(self->makefile, "static", "$(LUCY_STATIC_LIB)");
 
-    for (i = 0; self->autogen_src_files[i] != NULL; ++i) {
-        char *path = chaz_Util_join("", self->autogen_src_dir, dir_sep,
-                                    self->autogen_src_files[i], obj_ext, NULL);
-        chaz_MakeVar_append(var, path);
-        free(path);
+        self->lib
+            = chaz_MakeFile_add_static_lib(self->makefile, NULL, "lucy");
+        rule = chaz_MakeFile_add_rule(self->ma

<TRUNCATED>