Rework Makefile API for binaries

Expose a higher level API to build binaries with make. The new
chaz_MakeBinary class allows to scan a directory for source files
and to pass separate compiler flags for each binary.

Improve building of shared libaries:

- Add compatibility version on Darwin (Mach-O).
- Build import library with GCC on Windows.
- Add soname with Sun CC (untested).

Remove the chaz_Library class.

Rework detection of the platform's make utility. We don't use this
information for now, so it's purely informative.
diff --git a/Makefile b/Makefile
index 8221cac..c5ffdd9 100644
--- a/Makefile
+++ b/Makefile
@@ -27,11 +27,11 @@
 
 TESTS= TestDirManip TestFuncMacro TestHeaders TestIntegers TestLargeFiles TestUnusedVars TestVariadicMacros
 
-OBJS= charmonize.o src/Charmonizer/Core/CFlags.o src/Charmonizer/Core/CLI.o src/Charmonizer/Core/Compiler.o src/Charmonizer/Core/ConfWriter.o src/Charmonizer/Core/ConfWriterC.o src/Charmonizer/Core/ConfWriterPerl.o src/Charmonizer/Core/ConfWriterPython.o src/Charmonizer/Core/ConfWriterRuby.o src/Charmonizer/Core/HeaderChecker.o src/Charmonizer/Core/Library.o src/Charmonizer/Core/Make.o src/Charmonizer/Core/OperatingSystem.o src/Charmonizer/Core/Util.o src/Charmonizer/Probe.o src/Charmonizer/Probe/AtomicOps.o src/Charmonizer/Probe/Booleans.o src/Charmonizer/Probe/BuildEnv.o src/Charmonizer/Probe/DirManip.o src/Charmonizer/Probe/Floats.o src/Charmonizer/Probe/FuncMacro.o src/Charmonizer/Probe/Headers.o src/Charmonizer/Probe/Integers.o src/Charmonizer/Probe/LargeFiles.o src/Charmonizer/Probe/Memory.o src/Charmonizer/Probe/RegularExpressions.o src/Charmonizer/Probe/Strings.o src/Charmonizer/Probe/SymbolVisibility.o src/Charmonizer/Probe/UnusedVars.o src/Charmonizer/Probe/VariadicMacros.o
+OBJS= charmonize.o src/Charmonizer/Core/CFlags.o src/Charmonizer/Core/CLI.o src/Charmonizer/Core/Compiler.o src/Charmonizer/Core/ConfWriter.o src/Charmonizer/Core/ConfWriterC.o src/Charmonizer/Core/ConfWriterPerl.o src/Charmonizer/Core/ConfWriterPython.o src/Charmonizer/Core/ConfWriterRuby.o src/Charmonizer/Core/HeaderChecker.o src/Charmonizer/Core/Make.o src/Charmonizer/Core/OperatingSystem.o src/Charmonizer/Core/Util.o src/Charmonizer/Probe.o src/Charmonizer/Probe/AtomicOps.o src/Charmonizer/Probe/Booleans.o src/Charmonizer/Probe/BuildEnv.o src/Charmonizer/Probe/DirManip.o src/Charmonizer/Probe/Floats.o src/Charmonizer/Probe/FuncMacro.o src/Charmonizer/Probe/Headers.o src/Charmonizer/Probe/Integers.o src/Charmonizer/Probe/LargeFiles.o src/Charmonizer/Probe/Memory.o src/Charmonizer/Probe/RegularExpressions.o src/Charmonizer/Probe/Strings.o src/Charmonizer/Probe/SymbolVisibility.o src/Charmonizer/Probe/UnusedVars.o src/Charmonizer/Probe/VariadicMacros.o
 
 TEST_OBJS= src/Charmonizer/Test.o src/Charmonizer/Test/TestDirManip.o src/Charmonizer/Test/TestFuncMacro.o src/Charmonizer/Test/TestHeaders.o src/Charmonizer/Test/TestIntegers.o src/Charmonizer/Test/TestLargeFiles.o src/Charmonizer/Test/TestUnusedVars.o src/Charmonizer/Test/TestVariadicMacros.o
 
-HEADERS= src/Charmonizer/Core/CFlags.h src/Charmonizer/Core/CLI.h src/Charmonizer/Core/Compiler.h src/Charmonizer/Core/ConfWriter.h src/Charmonizer/Core/ConfWriterC.h src/Charmonizer/Core/ConfWriterPerl.h src/Charmonizer/Core/ConfWriterPython.h src/Charmonizer/Core/ConfWriterRuby.h src/Charmonizer/Core/Defines.h src/Charmonizer/Core/HeaderChecker.h src/Charmonizer/Core/Library.h src/Charmonizer/Core/Make.h src/Charmonizer/Core/OperatingSystem.h src/Charmonizer/Core/Util.h src/Charmonizer/Probe.h src/Charmonizer/Probe/AtomicOps.h src/Charmonizer/Probe/Booleans.h src/Charmonizer/Probe/BuildEnv.h src/Charmonizer/Probe/DirManip.h src/Charmonizer/Probe/Floats.h src/Charmonizer/Probe/FuncMacro.h src/Charmonizer/Probe/Headers.h src/Charmonizer/Probe/Integers.h src/Charmonizer/Probe/LargeFiles.h src/Charmonizer/Probe/Memory.h src/Charmonizer/Probe/RegularExpressions.h src/Charmonizer/Probe/Strings.h src/Charmonizer/Probe/SymbolVisibility.h src/Charmonizer/Probe/UnusedVars.h src/Charmonizer/Probe/VariadicMacros.h src/Charmonizer/Test.h
+HEADERS= src/Charmonizer/Core/CFlags.h src/Charmonizer/Core/CLI.h src/Charmonizer/Core/Compiler.h src/Charmonizer/Core/ConfWriter.h src/Charmonizer/Core/ConfWriterC.h src/Charmonizer/Core/ConfWriterPerl.h src/Charmonizer/Core/ConfWriterPython.h src/Charmonizer/Core/ConfWriterRuby.h src/Charmonizer/Core/Defines.h src/Charmonizer/Core/HeaderChecker.h src/Charmonizer/Core/Make.h src/Charmonizer/Core/OperatingSystem.h src/Charmonizer/Core/Util.h src/Charmonizer/Probe.h src/Charmonizer/Probe/AtomicOps.h src/Charmonizer/Probe/Booleans.h src/Charmonizer/Probe/BuildEnv.h src/Charmonizer/Probe/DirManip.h src/Charmonizer/Probe/Floats.h src/Charmonizer/Probe/FuncMacro.h src/Charmonizer/Probe/Headers.h src/Charmonizer/Probe/Integers.h src/Charmonizer/Probe/LargeFiles.h src/Charmonizer/Probe/Memory.h src/Charmonizer/Probe/RegularExpressions.h src/Charmonizer/Probe/Strings.h src/Charmonizer/Probe/SymbolVisibility.h src/Charmonizer/Probe/UnusedVars.h src/Charmonizer/Probe/VariadicMacros.h src/Charmonizer/Test.h
 
 CLEANABLE= $(OBJS) $(PROGNAME) $(CHARMONY_H) $(TEST_OBJS) $(TESTS) 
 
diff --git a/Makefile.MSVC b/Makefile.MSVC
index 155140c..ec9654a 100644
--- a/Makefile.MSVC
+++ b/Makefile.MSVC
@@ -27,11 +27,11 @@
 
 TESTS= TestDirManip.exe TestFuncMacro.exe TestHeaders.exe TestIntegers.exe TestLargeFiles.exe TestUnusedVars.exe TestVariadicMacros.exe
 
-OBJS= charmonize.obj src\Charmonizer\Core\CFlags.obj src\Charmonizer\Core\CLI.obj src\Charmonizer\Core\Compiler.obj src\Charmonizer\Core\ConfWriter.obj src\Charmonizer\Core\ConfWriterC.obj src\Charmonizer\Core\ConfWriterPerl.obj src\Charmonizer\Core\ConfWriterPython.obj src\Charmonizer\Core\ConfWriterRuby.obj src\Charmonizer\Core\HeaderChecker.obj src\Charmonizer\Core\Library.obj src\Charmonizer\Core\Make.obj src\Charmonizer\Core\OperatingSystem.obj src\Charmonizer\Core\Util.obj src\Charmonizer\Probe.obj src\Charmonizer\Probe\AtomicOps.obj src\Charmonizer\Probe\Booleans.obj src\Charmonizer\Probe\BuildEnv.obj src\Charmonizer\Probe\DirManip.obj src\Charmonizer\Probe\Floats.obj src\Charmonizer\Probe\FuncMacro.obj src\Charmonizer\Probe\Headers.obj src\Charmonizer\Probe\Integers.obj src\Charmonizer\Probe\LargeFiles.obj src\Charmonizer\Probe\Memory.obj src\Charmonizer\Probe\RegularExpressions.obj src\Charmonizer\Probe\Strings.obj src\Charmonizer\Probe\SymbolVisibility.obj src\Charmonizer\Probe\UnusedVars.obj src\Charmonizer\Probe\VariadicMacros.obj
+OBJS= charmonize.obj src\Charmonizer\Core\CFlags.obj src\Charmonizer\Core\CLI.obj src\Charmonizer\Core\Compiler.obj src\Charmonizer\Core\ConfWriter.obj src\Charmonizer\Core\ConfWriterC.obj src\Charmonizer\Core\ConfWriterPerl.obj src\Charmonizer\Core\ConfWriterPython.obj src\Charmonizer\Core\ConfWriterRuby.obj src\Charmonizer\Core\HeaderChecker.obj src\Charmonizer\Core\Make.obj src\Charmonizer\Core\OperatingSystem.obj src\Charmonizer\Core\Util.obj src\Charmonizer\Probe.obj src\Charmonizer\Probe\AtomicOps.obj src\Charmonizer\Probe\Booleans.obj src\Charmonizer\Probe\BuildEnv.obj src\Charmonizer\Probe\DirManip.obj src\Charmonizer\Probe\Floats.obj src\Charmonizer\Probe\FuncMacro.obj src\Charmonizer\Probe\Headers.obj src\Charmonizer\Probe\Integers.obj src\Charmonizer\Probe\LargeFiles.obj src\Charmonizer\Probe\Memory.obj src\Charmonizer\Probe\RegularExpressions.obj src\Charmonizer\Probe\Strings.obj src\Charmonizer\Probe\SymbolVisibility.obj src\Charmonizer\Probe\UnusedVars.obj src\Charmonizer\Probe\VariadicMacros.obj
 
 TEST_OBJS= src\Charmonizer\Test.obj src\Charmonizer\Test\TestDirManip.obj src\Charmonizer\Test\TestFuncMacro.obj src\Charmonizer\Test\TestHeaders.obj src\Charmonizer\Test\TestIntegers.obj src\Charmonizer\Test\TestLargeFiles.obj src\Charmonizer\Test\TestUnusedVars.obj src\Charmonizer\Test\TestVariadicMacros.obj
 
-HEADERS= src\Charmonizer\Core\CFlags.h src\Charmonizer\Core\CLI.h src\Charmonizer\Core\Compiler.h src\Charmonizer\Core\ConfWriter.h src\Charmonizer\Core\ConfWriterC.h src\Charmonizer\Core\ConfWriterPerl.h src\Charmonizer\Core\ConfWriterPython.h src\Charmonizer\Core\ConfWriterRuby.h src\Charmonizer\Core\Defines.h src\Charmonizer\Core\HeaderChecker.h src\Charmonizer\Core\Library.h src\Charmonizer\Core\Make.h src\Charmonizer\Core\OperatingSystem.h src\Charmonizer\Core\Util.h src\Charmonizer\Probe.h src\Charmonizer\Probe\AtomicOps.h src\Charmonizer\Probe\Booleans.h src\Charmonizer\Probe\BuildEnv.h src\Charmonizer\Probe\DirManip.h src\Charmonizer\Probe\Floats.h src\Charmonizer\Probe\FuncMacro.h src\Charmonizer\Probe\Headers.h src\Charmonizer\Probe\Integers.h src\Charmonizer\Probe\LargeFiles.h src\Charmonizer\Probe\Memory.h src\Charmonizer\Probe\RegularExpressions.h src\Charmonizer\Probe\Strings.h src\Charmonizer\Probe\SymbolVisibility.h src\Charmonizer\Probe\UnusedVars.h src\Charmonizer\Probe\VariadicMacros.h src\Charmonizer\Test.h
+HEADERS= src\Charmonizer\Core\CFlags.h src\Charmonizer\Core\CLI.h src\Charmonizer\Core\Compiler.h src\Charmonizer\Core\ConfWriter.h src\Charmonizer\Core\ConfWriterC.h src\Charmonizer\Core\ConfWriterPerl.h src\Charmonizer\Core\ConfWriterPython.h src\Charmonizer\Core\ConfWriterRuby.h src\Charmonizer\Core\Defines.h src\Charmonizer\Core\HeaderChecker.h src\Charmonizer\Core\Make.h src\Charmonizer\Core\OperatingSystem.h src\Charmonizer\Core\Util.h src\Charmonizer\Probe.h src\Charmonizer\Probe\AtomicOps.h src\Charmonizer\Probe\Booleans.h src\Charmonizer\Probe\BuildEnv.h src\Charmonizer\Probe\DirManip.h src\Charmonizer\Probe\Floats.h src\Charmonizer\Probe\FuncMacro.h src\Charmonizer\Probe\Headers.h src\Charmonizer\Probe\Integers.h src\Charmonizer\Probe\LargeFiles.h src\Charmonizer\Probe\Memory.h src\Charmonizer\Probe\RegularExpressions.h src\Charmonizer\Probe\Strings.h src\Charmonizer\Probe\SymbolVisibility.h src\Charmonizer\Probe\UnusedVars.h src\Charmonizer\Probe\VariadicMacros.h src\Charmonizer\Test.h
 
 CLEANABLE= $(OBJS) $(PROGNAME) $(CHARMONY_H) $(TEST_OBJS) $(TESTS) *.pdb
 
diff --git a/Makefile.MinGW b/Makefile.MinGW
index 0c3104a..3cbad7f 100644
--- a/Makefile.MinGW
+++ b/Makefile.MinGW
@@ -27,11 +27,11 @@
 
 TESTS= TestDirManip.exe TestFuncMacro.exe TestHeaders.exe TestIntegers.exe TestLargeFiles.exe TestUnusedVars.exe TestVariadicMacros.exe
 
-OBJS= charmonize.o src\Charmonizer\Core\CFlags.o src\Charmonizer\Core\CLI.o src\Charmonizer\Core\Compiler.o src\Charmonizer\Core\ConfWriter.o src\Charmonizer\Core\ConfWriterC.o src\Charmonizer\Core\ConfWriterPerl.o src\Charmonizer\Core\ConfWriterPython.o src\Charmonizer\Core\ConfWriterRuby.o src\Charmonizer\Core\HeaderChecker.o src\Charmonizer\Core\Library.o src\Charmonizer\Core\Make.o src\Charmonizer\Core\OperatingSystem.o src\Charmonizer\Core\Util.o src\Charmonizer\Probe.o src\Charmonizer\Probe\AtomicOps.o src\Charmonizer\Probe\Booleans.o src\Charmonizer\Probe\BuildEnv.o src\Charmonizer\Probe\DirManip.o src\Charmonizer\Probe\Floats.o src\Charmonizer\Probe\FuncMacro.o src\Charmonizer\Probe\Headers.o src\Charmonizer\Probe\Integers.o src\Charmonizer\Probe\LargeFiles.o src\Charmonizer\Probe\Memory.o src\Charmonizer\Probe\RegularExpressions.o src\Charmonizer\Probe\Strings.o src\Charmonizer\Probe\SymbolVisibility.o src\Charmonizer\Probe\UnusedVars.o src\Charmonizer\Probe\VariadicMacros.o
+OBJS= charmonize.o src\Charmonizer\Core\CFlags.o src\Charmonizer\Core\CLI.o src\Charmonizer\Core\Compiler.o src\Charmonizer\Core\ConfWriter.o src\Charmonizer\Core\ConfWriterC.o src\Charmonizer\Core\ConfWriterPerl.o src\Charmonizer\Core\ConfWriterPython.o src\Charmonizer\Core\ConfWriterRuby.o src\Charmonizer\Core\HeaderChecker.o src\Charmonizer\Core\Make.o src\Charmonizer\Core\OperatingSystem.o src\Charmonizer\Core\Util.o src\Charmonizer\Probe.o src\Charmonizer\Probe\AtomicOps.o src\Charmonizer\Probe\Booleans.o src\Charmonizer\Probe\BuildEnv.o src\Charmonizer\Probe\DirManip.o src\Charmonizer\Probe\Floats.o src\Charmonizer\Probe\FuncMacro.o src\Charmonizer\Probe\Headers.o src\Charmonizer\Probe\Integers.o src\Charmonizer\Probe\LargeFiles.o src\Charmonizer\Probe\Memory.o src\Charmonizer\Probe\RegularExpressions.o src\Charmonizer\Probe\Strings.o src\Charmonizer\Probe\SymbolVisibility.o src\Charmonizer\Probe\UnusedVars.o src\Charmonizer\Probe\VariadicMacros.o
 
 TEST_OBJS= src\Charmonizer\Test.o src\Charmonizer\Test\TestDirManip.o src\Charmonizer\Test\TestFuncMacro.o src\Charmonizer\Test\TestHeaders.o src\Charmonizer\Test\TestIntegers.o src\Charmonizer\Test\TestLargeFiles.o src\Charmonizer\Test\TestUnusedVars.o src\Charmonizer\Test\TestVariadicMacros.o
 
-HEADERS= src\Charmonizer\Core\CFlags.h src\Charmonizer\Core\CLI.h src\Charmonizer\Core\Compiler.h src\Charmonizer\Core\ConfWriter.h src\Charmonizer\Core\ConfWriterC.h src\Charmonizer\Core\ConfWriterPerl.h src\Charmonizer\Core\ConfWriterPython.h src\Charmonizer\Core\ConfWriterRuby.h src\Charmonizer\Core\Defines.h src\Charmonizer\Core\HeaderChecker.h src\Charmonizer\Core\Library.h src\Charmonizer\Core\Make.h src\Charmonizer\Core\OperatingSystem.h src\Charmonizer\Core\Util.h src\Charmonizer\Probe.h src\Charmonizer\Probe\AtomicOps.h src\Charmonizer\Probe\Booleans.h src\Charmonizer\Probe\BuildEnv.h src\Charmonizer\Probe\DirManip.h src\Charmonizer\Probe\Floats.h src\Charmonizer\Probe\FuncMacro.h src\Charmonizer\Probe\Headers.h src\Charmonizer\Probe\Integers.h src\Charmonizer\Probe\LargeFiles.h src\Charmonizer\Probe\Memory.h src\Charmonizer\Probe\RegularExpressions.h src\Charmonizer\Probe\Strings.h src\Charmonizer\Probe\SymbolVisibility.h src\Charmonizer\Probe\UnusedVars.h src\Charmonizer\Probe\VariadicMacros.h src\Charmonizer\Test.h
+HEADERS= src\Charmonizer\Core\CFlags.h src\Charmonizer\Core\CLI.h src\Charmonizer\Core\Compiler.h src\Charmonizer\Core\ConfWriter.h src\Charmonizer\Core\ConfWriterC.h src\Charmonizer\Core\ConfWriterPerl.h src\Charmonizer\Core\ConfWriterPython.h src\Charmonizer\Core\ConfWriterRuby.h src\Charmonizer\Core\Defines.h src\Charmonizer\Core\HeaderChecker.h src\Charmonizer\Core\Make.h src\Charmonizer\Core\OperatingSystem.h src\Charmonizer\Core\Util.h src\Charmonizer\Probe.h src\Charmonizer\Probe\AtomicOps.h src\Charmonizer\Probe\Booleans.h src\Charmonizer\Probe\BuildEnv.h src\Charmonizer\Probe\DirManip.h src\Charmonizer\Probe\Floats.h src\Charmonizer\Probe\FuncMacro.h src\Charmonizer\Probe\Headers.h src\Charmonizer\Probe\Integers.h src\Charmonizer\Probe\LargeFiles.h src\Charmonizer\Probe\Memory.h src\Charmonizer\Probe\RegularExpressions.h src\Charmonizer\Probe\Strings.h src\Charmonizer\Probe\SymbolVisibility.h src\Charmonizer\Probe\UnusedVars.h src\Charmonizer\Probe\VariadicMacros.h src\Charmonizer\Test.h
 
 CLEANABLE= $(OBJS) $(PROGNAME) $(CHARMONY_H) $(TEST_OBJS) $(TESTS) 
 
diff --git a/buildbin/meld.pl b/buildbin/meld.pl
index 48a5885..b4db46c 100755
--- a/buildbin/meld.pl
+++ b/buildbin/meld.pl
@@ -67,7 +67,6 @@
 }
 
 my @core = qw(
-    Library
     CFlags
     CLI
     Compiler
diff --git a/src/Charmonizer/Core/CFlags.c b/src/Charmonizer/Core/CFlags.c
index 1c177b5..a1f4684 100644
--- a/src/Charmonizer/Core/CFlags.c
+++ b/src/Charmonizer/Core/CFlags.c
@@ -20,7 +20,6 @@
 #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;
@@ -244,55 +243,44 @@
 }
 
 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);
     }
 }
 
@@ -333,20 +321,22 @@
 }
 
 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);
diff --git a/src/Charmonizer/Core/CFlags.h b/src/Charmonizer/Core/CFlags.h
index 901eb45..ec699df 100644
--- a/src/Charmonizer/Core/CFlags.h
+++ b/src/Charmonizer/Core/CFlags.h
@@ -24,8 +24,6 @@
 extern "C" {
 #endif
 
-#include "Charmonizer/Core/Library.h"
-
 #define CHAZ_CFLAGS_STYLE_POSIX  1
 #define CHAZ_CFLAGS_STYLE_GNU    2
 #define CHAZ_CFLAGS_STYLE_MSVC   3
@@ -77,10 +75,9 @@
 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);
@@ -89,10 +86,11 @@
 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_library(chaz_CFlags *flags, const char *library);
+chaz_CFlags_add_external_lib(chaz_CFlags *flags, const char *library);
 
 void
 chaz_CFlags_enable_code_coverage(chaz_CFlags *flags);
diff --git a/src/Charmonizer/Core/Compiler.c b/src/Charmonizer/Core/Compiler.c
index dd4fa6c..83177f2 100644
--- a/src/Charmonizer/Core/Compiler.c
+++ b/src/Charmonizer/Core/Compiler.c
@@ -37,6 +37,13 @@
 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"
@@ -75,7 +82,6 @@
 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"); }
 
@@ -566,7 +572,8 @@
         /* 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;
     }
@@ -583,3 +590,85 @@
     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);
+    }
+}
+
diff --git a/src/Charmonizer/Core/Compiler.h b/src/Charmonizer/Core/Compiler.h
index 6361af7..8190123 100644
--- a/src/Charmonizer/Core/Compiler.h
+++ b/src/Charmonizer/Core/Compiler.h
@@ -169,6 +169,44 @@
 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);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/Charmonizer/Core/Library.c b/src/Charmonizer/Core/Library.c
deleted file mode 100644
index d1e0cb6..0000000
--- a/src/Charmonizer/Core/Library.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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";
-    }
-}
-
-
diff --git a/src/Charmonizer/Core/Library.h b/src/Charmonizer/Core/Library.h
deleted file mode 100644
index 80fff83..0000000
--- a/src/Charmonizer/Core/Library.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* Charmonizer/Core/Library.h
- */
-
-#ifndef H_CHAZ_LIB
-#define H_CHAZ_LIB
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-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);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* H_CHAZ_LIB */
-
-
diff --git a/src/Charmonizer/Core/Make.c b/src/Charmonizer/Core/Make.c
index 8e8699b..23350f8 100644
--- a/src/Charmonizer/Core/Make.c
+++ b/src/Charmonizer/Core/Make.c
@@ -18,10 +18,15 @@
 #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;
@@ -34,13 +39,34 @@
     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. */
@@ -69,6 +95,31 @@
 static int
 chaz_Make_audition(const char *make);
 
+static void
+chaz_MakeFile_finish_exe(chaz_MakeFile *self, chaz_MakeBinary *binary);
+
+static void
+chaz_MakeFile_finish_shared_lib(chaz_MakeFile *self, chaz_MakeBinary *binary);
+
+static void
+chaz_MakeFile_finish_static_lib(chaz_MakeFile *self, chaz_MakeBinary *binary);
+
+static chaz_MakeBinary*
+chaz_MakeFile_add_binary(chaz_MakeFile *self, int type, const char *dir,
+                         const char *basename, const char *target);
+
+static void
+chaz_MakeFile_write_binary_rules(chaz_MakeFile *self, chaz_MakeBinary *binary,
+                                 FILE *out);
+
+static void
+chaz_MakeFile_write_object_rules(char **sources, const char *command,
+                                 FILE *out);
+
+static void
+chaz_MakeFile_write_pattern_rules(char **dirs, const char *command,
+                                  FILE *out);
+
 static chaz_MakeRule*
 S_new_rule(const char *target, const char *prereq);
 
@@ -78,19 +129,48 @@
 static void
 S_write_rule(chaz_MakeRule *rule, FILE *out);
 
+static void
+chaz_MakeBinary_destroy(chaz_MakeBinary *self);
+
+static void
+chaz_MakeBinary_list_files_callback(const char *dir, char *file,
+                                    void *context);
+static void
+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*
+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");
+            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 = chaz_Make_detect("make", "gmake", "dmake",
+                                         "mingw32-make", NULL);
+        }
+        else if (chaz_Make.shell_type = CHAZ_OS_CMD_EXE) {
+            succeeded = chaz_Make_detect("nmake", "dmake", "mingw32-make",
+                                         NULL);
+        }
+
+        if (!succeeded) {
             chaz_Util_warn("No working make utility found");
         }
         else if (chaz_Util_verbosity) {
@@ -119,7 +199,7 @@
     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. */
@@ -145,15 +225,8 @@
     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);
     }
@@ -169,51 +242,52 @@
 
 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_new_rule("clean", NULL);
+    self->distclean = S_new_rule("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; makefile->rules[i]; i++) {
-        S_destroy_rule(makefile->rules[i]);
+    for (i = 0; self->rules[i]; i++) {
+        S_destroy_rule(self->rules[i]);
     }
-    free(makefile->rules);
+    free(self->rules);
 
-    S_destroy_rule(makefile->clean);
-    S_destroy_rule(makefile->distclean);
+    for (i = 0; self->binaries[i]; i++) {
+        chaz_MakeBinary_destroy(self->binaries[i]);
+    }
+    free(self->binaries);
 
-    free(makefile);
+    S_destroy_rule(self->clean);
+    S_destroy_rule(self->distclean);
+
+    free(self);
 }
 
 chaz_MakeVar*
@@ -266,196 +340,249 @@
     return makefile->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;
+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;
 
-    rule = chaz_MakeFile_add_rule(makefile, exe, sources);
-
-    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 = 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;
-
-    rule = chaz_MakeFile_add_rule(makefile, exe, sources);
-
-    if (cflags) {
-        cflags_string = chaz_CFlags_get_string(cflags);
-    }
-    if (chaz_CC_msvc_version_num()) {
-        chaz_CFlags_append(local_flags, "/nologo");
-    }
-    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. */
-
-    chaz_CFlags_destroy(local_flags);
-    free(command);
-    return rule;
-}
-
-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
+chaz_MakeFile_finish_exe(chaz_MakeFile *self, chaz_MakeBinary *binary) {
     const char *link = chaz_CC_link_command();
-    const char *link_flags_string = "";
-    const char *local_flags_string;
-    int binfmt = chaz_CC_binary_format();
-    chaz_MakeRule *rule;
-    char *filename;
+    const char *link_flags_string;
     char *command;
 
-    filename = chaz_Lib_filename(lib);
-    rule = chaz_MakeFile_add_rule(makefile, filename, sources);
+    (void)self;
 
-    if (link_flags) {
-        link_flags_string = chaz_CFlags_get_string(link_flags);
+    /* 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);
+    }
+    else {
+        target = chaz_CC_shared_lib_filename(dir, basename, version);
     }
 
-    if (chaz_CC_msvc_version_num()) {
-        chaz_CFlags_append(local_flags, "/nologo");
-    }
-    chaz_CFlags_link_shared_library(local_flags);
+    binary = 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_compile_shared_library(binary->compile_flags);
+    chaz_CFlags_link_shared_library(binary->link_flags, basename, version,
+                                    major_version);
+
+    free(target);
+    return binary;
+}
+
+void
+chaz_MakeFile_finish_shared_lib(chaz_MakeFile *self, chaz_MakeBinary *binary) {
+    const char *link = chaz_CC_link_command();
+    const char *link_flags_string;
+    int binfmt = chaz_CC_binary_format();
+    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;
+
     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);
+    chaz_CFlags_set_link_output(binary->link_flags, "$@");
+    link_flags_string = chaz_CFlags_get_string(binary->link_flags);
+
+    command = chaz_Util_join(" ", link, binary->dollar_var, link_flags_string,
+                             NULL);
+    chaz_MakeRule_add_command(binary->rule, command);
     free(command);
 
-    chaz_MakeRule_add_rm_command(makefile->clean, filename);
-
     /* 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
+        = 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
+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*
+static chaz_MakeBinary*
+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_new_rule(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 *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);
-
-    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);
-
-    chaz_CFlags_destroy(cflags);
-    free(lemon_c);
-    free(lemon_exe);
-    return rule;
+    chaz_MakeBinary *exe = chaz_MakeFile_add_exe(makefile, dir, "lemon");
+    chaz_MakeBinary_add_src_file(exe, dir, "lemon.c");
+    return exe;
 }
 
 chaz_MakeRule*
@@ -482,49 +609,6 @@
     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) {
     FILE   *out;
@@ -545,6 +629,10 @@
         S_write_rule(makefile->rules[i], out);
     }
 
+    for (i = 0; makefile->binaries[i]; i++) {
+        chaz_MakeFile_write_binary_rules(makefile, makefile->binaries[i], out);
+    }
+
     S_write_rule(makefile->clean, out);
     S_write_rule(makefile->distclean, out);
 
@@ -561,6 +649,122 @@
     fclose(out);
 }
 
+static void
+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->link_flags, "/nologo");
+    }
+
+    switch (binary->type) {
+        case CHAZ_MAKEBINARY_EXE:
+            chaz_MakeFile_finish_exe(self, binary);
+            break;
+        case CHAZ_MAKEBINARY_STATIC_LIB:
+            chaz_MakeFile_finish_static_lib(self, binary);
+            break;
+        case CHAZ_MAKEBINARY_SHARED_LIB:
+            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_write_rule(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.
+             */
+            chaz_MakeFile_write_object_rules(binary->sources, cflags, out);
+        }
+        else {
+            /* Write a pattern rule for each directory. */
+            chaz_MakeFile_write_pattern_rules(binary->dirs, cflags, out);
+            /* Write a rule for each object added with add_src_file. */
+            chaz_MakeFile_write_object_rules(binary->single_sources, cflags,
+                                             out);
+        }
+    }
+}
+
+static void
+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 = chaz_MakeBinary_obj_path(source);
+        chaz_MakeRule *rule;
+        char *command;
+
+        if (obj_path == NULL) { continue; }
+
+        rule = S_new_rule(obj_path, source);
+        command = chaz_Util_join(" ", "$(CC) $(CFLAGS)", cflags, source,
+                                 output_cflags_string, NULL);
+        chaz_MakeRule_add_command(rule, command);
+        S_write_rule(rule, out);
+
+        free(command);
+        S_destroy_rule(rule);
+        free(obj_path);
+    }
+
+    chaz_CFlags_destroy(output_cflags);
+}
+
+static void
+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_new_rule(target, prereq);
+
+        chaz_MakeRule_add_command(rule, command);
+        S_write_rule(rule, out);
+
+        S_destroy_rule(rule);
+        free(prereq);
+        free(target);
+    }
+
+    free(command);
+    chaz_CFlags_destroy(output_cflags);
+}
+
 void
 chaz_MakeVar_append(chaz_MakeVar *var, const char *element) {
     char *value;
@@ -791,6 +995,170 @@
     }
 }
 
+static void
+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_destroy_rule(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;
+
+    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", chaz_MakeBinary_list_files_callback,
+                         self);
+}
+
+static void
+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);
+
+    chaz_MakeBinary_do_add_src_file((chaz_MakeBinary*)context, path);
+    free(path);
+}
+
+static void
+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 = 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*
+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 = 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) {
diff --git a/src/Charmonizer/Core/Make.h b/src/Charmonizer/Core/Make.h
index 25e99ea..f2ab064 100644
--- a/src/Charmonizer/Core/Make.h
+++ b/src/Charmonizer/Core/Make.h
@@ -25,11 +25,11 @@
 #endif
 
 #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);
@@ -117,59 +117,44 @@
 chaz_MakeRule*
 chaz_MakeFile_distclean_rule(chaz_MakeFile *makefile);
 
-/** 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.
+ * @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_MakeRule*
-chaz_MakeFile_add_compiled_exe(chaz_MakeFile *makefile, const char *exe,
-                               const char *sources, chaz_CFlags *cflags);
+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 rule to link a shared library. The shared library will also be added
- * to the list of files to clean.
+/** 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_MakeBinary*
 chaz_MakeFile_add_lemon_exe(chaz_MakeFile *makefile, const char *dir);
 
 /** Add a rule for a lemon grammar.
@@ -181,16 +166,6 @@
 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);
-
 /** Write the makefile to a file named 'Makefile' in the current directory.
  *
  * @param makefile The makefile.
@@ -267,6 +242,49 @@
 chaz_MakeRule_add_make_command(chaz_MakeRule *rule, 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);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/Charmonizer/Probe/Floats.c b/src/Charmonizer/Probe/Floats.c
index 609bee8..e2f8d41 100644
--- a/src/Charmonizer/Probe/Floats.c
+++ b/src/Charmonizer/Probe/Floats.c
@@ -90,7 +90,7 @@
         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);