lucy-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nwelln...@apache.org
Subject [lucy-commits] [7/7] git commit: refs/heads/master - Regenerate charmonizer.c
Date Thu, 14 Feb 2013 18:54:18 GMT
Updated Branches:
  refs/heads/master c62326212 -> b75126099


Regenerate charmonizer.c


Project: http://git-wip-us.apache.org/repos/asf/lucy/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy/commit/b7512609
Tree: http://git-wip-us.apache.org/repos/asf/lucy/tree/b7512609
Diff: http://git-wip-us.apache.org/repos/asf/lucy/diff/b7512609

Branch: refs/heads/master
Commit: b7512609923e0f40f25b88763a46de8dd15d0311
Parents: 5267748
Author: Nick Wellnhofer <wellnhofer@aevum.de>
Authored: Thu Feb 14 19:29:23 2013 +0100
Committer: Nick Wellnhofer <wellnhofer@aevum.de>
Committed: Thu Feb 14 19:47:13 2013 +0100

----------------------------------------------------------------------
 clownfish/compiler/common/charmonizer.c | 1101 +++++++++++++++++++++++++-
 clownfish/runtime/common/charmonizer.c  |  953 ++++++++++++++++++++++-
 common/charmonizer.c                    |  959 ++++++++++++++++++++++-
 3 files changed, 2943 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy/blob/b7512609/clownfish/compiler/common/charmonizer.c
----------------------------------------------------------------------
diff --git a/clownfish/compiler/common/charmonizer.c b/clownfish/compiler/common/charmonizer.c
index 21bd6d1..486e3a1 100644
--- a/clownfish/compiler/common/charmonizer.c
+++ b/clownfish/compiler/common/charmonizer.c
@@ -93,6 +93,16 @@ chaz_CC_set_warnings_as_errors(const int flag);
 void
 chaz_CC_add_extra_cflags(const char *);
 
+/* Add include dir to extra cflags.
+ */
+void
+chaz_CC_add_include_dir(const char *dir);
+
+/* Set optimization level.
+ */
+void
+chaz_CC_set_optimization_level(const char *level);
+
 /* Accessor for the compiler executable's string representation.
  */
 const char*
@@ -109,13 +119,25 @@ const char*
 chaz_CC_get_extra_cflags(void);
 
 int
-chaz_CC_gcc_version_num();
+chaz_CC_gcc_version_num(void);
 
 const char*
-chaz_CC_gcc_version();
+chaz_CC_gcc_version(void);
 
 int
-chaz_CC_compiler_is_msvc(void);
+chaz_CC_msvc_version_num(void);
+
+const char*
+chaz_CC_link_command(void);
+
+const char*
+chaz_CC_link_flags(void);
+
+const char*
+chaz_CC_link_shared_obj_flag(void);
+
+const char*
+chaz_CC_link_output_flag(void);
 
 #endif /* H_CHAZ_COMPILER */
 
@@ -325,6 +347,162 @@ chaz_HeadCheck_contains_member(const char *struct_name, const char *member,
 
 /***************************************************************************/
 
+#line 21 "src/Charmonizer/Core/Make.h"
+/* Charmonizer/Core/Compiler.h
+ */
+
+#ifndef H_CHAZ_MAKE
+#define H_CHAZ_MAKE
+
+typedef struct chaz_MakeFile chaz_MakeFile;
+typedef struct chaz_MakeVar chaz_MakeVar;
+typedef struct chaz_MakeRule chaz_MakeRule;
+
+typedef void (*chaz_Make_list_files_callback_t)(char *file, void *context);
+
+/** Initialize the environment.
+ */
+void
+chaz_Make_init(void);
+
+/** Clean up the environment.
+ */
+void
+chaz_Make_clean_up(void);
+
+/** Return the name of the detected 'make' executable.
+ */
+const char*
+chaz_Make_get_make(void);
+
+/** Recursively list files in a directory. For every file a callback is called
+ * with the filename and a context variable.
+ *
+ * @param dir Directory to search in.
+ * @param ext File extension to search for.
+ * @param callback Callback to call for every matching file.
+ * @param context Context variable to pass to callback.
+ */
+void
+chaz_Make_list_files(const char *dir, const char *ext,
+                     chaz_Make_list_files_callback_t callback, void *context);
+
+/** MakeFile constructor.
+ */
+chaz_MakeFile*
+chaz_MakeFile_new();
+
+/** 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,
+                      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
+ * add prerequisites later.
+ * @return a MakeRule.
+ */
+chaz_MakeRule*
+chaz_MakeFile_add_rule(chaz_MakeFile *makefile, const char *target,
+                       const char *prereq);
+
+/** Add a file to the 'clean' target.
+ *
+ * @param makefile The makefile.
+ * @param target The filename.
+ */
+void
+chaz_MakeFile_add_to_cleanup(chaz_MakeFile *makefile, const char *target);
+
+/** Add a rule to link an executable. The executable will also be added to the
+ * list of files to clean.
+ *
+ * @param makefile The makefile.
+ * @param exe The name of the executable.
+ * @param objects The list of object files.
+ */
+void
+chaz_MakeFile_add_exe(chaz_MakeFile *makefile, const char *exe,
+                      const char *objects);
+
+/** Add a rule to link a shared object. The shared object will also be added to
+ * the list of files to clean.
+ *
+ * @param makefile The makefile.
+ * @param shared_obj The name of the shared object.
+ * @param objects The list of object files.
+ */
+void
+chaz_MakeFile_add_shared_obj(chaz_MakeFile *makefile, const char *shared_obj,
+                             const char *objects);
+
+/** Write the makefile to a file named 'Makefile' in the current directory.
+ *
+ * @param makefile The makefile.
+ */
+void
+chaz_MakeFile_write(chaz_MakeFile *makefile);
+
+/** 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);
+
+/** 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);
+
+/** 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);
+
+/** 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);
+
+/** 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_command_make(chaz_MakeRule *rule, const char *dir,
+                               const char *target);
+
+#endif /* H_CHAZ_MAKE */
+
+
+
+/***************************************************************************/
+
 #line 21 "src/Charmonizer/Core/OperatingSystem.h"
 /* Charmonizer/Core/OperatingSystem.h - abstract an operating system down to a few
  * variables.
@@ -333,6 +511,9 @@ chaz_HeadCheck_contains_member(const char *struct_name, const char *member,
 #ifndef H_CHAZ_OPER_SYS
 #define H_CHAZ_OPER_SYS
 
+#define CHAZ_OS_POSIX    1
+#define CHAZ_OS_CMD_EXE  2
+
 /* Safely remove a file named [name]. Needed because of Windows quirks.
  * Returns true on success, false on failure.
  */
@@ -358,6 +539,11 @@ chaz_OS_run_redirected(const char *command, const char *path);
 int
 chaz_OS_run_local_redirected(const char *command, const char *path);
 
+/* Run a command and return the output from stdout.
+ */
+char*
+chaz_OS_run_and_capture(const char *command, size_t *output_len);
+
 /* Attempt to create a directory.
  */
 void
@@ -368,6 +554,17 @@ chaz_OS_mkdir(const char *filepath);
 void
 chaz_OS_rmdir(const char *filepath);
 
+/* Return the operating system name.
+ */
+const char*
+chaz_OS_name(void);
+
+int
+chaz_OS_is_darwin(void);
+
+int
+chaz_OS_is_cygwin(void);
+
 /* Return the extension for an executable on this system.
  */
 const char*
@@ -378,11 +575,21 @@ chaz_OS_exe_ext(void);
 const char*
 chaz_OS_obj_ext(void);
 
+/* Return the extension for a shared object on this system.
+ */
+const char*
+chaz_OS_shared_obj_ext(void);
+
 /* Return the equivalent of /dev/null on this system.
  */
 const char*
 chaz_OS_dev_null(void);
 
+/* Return the shell type of this system.
+ */
+int
+chaz_OS_shell_type(void);
+
 /* Initialize the Charmonizer/Core/OperatingSystem module.
  */
 void
@@ -526,10 +733,10 @@ chaz_Probe_gcc_version_num(void);
 const char*
 chaz_Probe_gcc_version(void);
 
-/* Returns true if the compiler is MSVC.
+/* Return the integer version of MSVC defined by _MSC_VER
  */
 int
-chaz_Probe_compiler_is_msvc(void);
+chaz_Probe_msvc_version_num(void);
 
 #endif /* Include guard. */
 
@@ -1140,6 +1347,34 @@ chaz_CC_add_extra_cflags(const char *flags) {
     }
 }
 
+void
+chaz_CC_add_include_dir(const char *dir) {
+    size_t size = strlen(chaz_CC.include_flag) + strlen(dir) + 1;
+    char *flag = (char*)malloc(size);
+    sprintf(flag, "%s%s", chaz_CC.include_flag, dir);
+    chaz_CC_add_extra_cflags(flag);
+    free(flag);
+}
+
+void
+chaz_CC_set_optimization_level(const char *level) {
+    const char *opt_flag;
+    char *flag;
+    size_t size;
+
+    if (chaz_CC.intval__MSC_VER) {
+        opt_flag = "/O";
+    }
+    else {
+        opt_flag = "-O";
+    }
+    size = strlen(opt_flag) + strlen(level) + 1;
+    flag = (char*)malloc(size);
+    sprintf(flag, "%s%s", opt_flag, level);
+    chaz_CC_add_extra_cflags(flag);
+    free(flag);
+}
+
 const char*
 chaz_CC_get_cc(void) {
     return chaz_CC.cc_command;
@@ -1168,11 +1403,50 @@ chaz_CC_gcc_version(void) {
 }
 
 int
-chaz_CC_compiler_is_msvc(void) {
-    return !!chaz_CC.intval__MSC_VER;
+chaz_CC_msvc_version_num(void) {
+    return chaz_CC.intval__MSC_VER;
+}
+
+const char*
+chaz_CC_link_command() {
+    if (chaz_CC.intval__MSC_VER) {
+        return "link";
+    }
+    else {
+        return chaz_CC.cc_command;
+    }
+}
+
+const char*
+chaz_CC_link_flags() {
+    return "";
+}
+
+const char*
+chaz_CC_link_shared_obj_flag() {
+    if (chaz_CC.intval__MSC_VER) {
+        return "/DLL";
+    }
+    else if (chaz_OS_is_darwin()) {
+        return "-dynamiclib ";
+    }
+    else {
+        return "-shared";
+    }
+}
+
+const char*
+chaz_CC_link_output_flag() {
+    if (chaz_CC.intval__MSC_VER) {
+        return "/OUT:";
+    }
+    else {
+        return "-o ";
+    }
 }
 
 
+
 /***************************************************************************/
 
 #line 17 "src/Charmonizer/Core/ConfWriter.c"
@@ -2277,25 +2551,587 @@ chaz_HeadCheck_maybe_add_to_cache(const char *header_name, int exists) {
 
 /***************************************************************************/
 
+#line 17 "src/Charmonizer/Core/Make.c"
+#include <ctype.h>
+#include <string.h>
+/* #include "Charmonizer/Core/Make.h" */
+/* #include "Charmonizer/Core/Compiler.h" */
+/* #include "Charmonizer/Core/OperatingSystem.h" */
+/* #include "Charmonizer/Core/Util.h" */
+
+struct chaz_MakeVar {
+    char   *name;
+    char   *value;
+    size_t  num_elements;
+};
+
+struct chaz_MakeRule {
+    char *targets;
+    char *prereqs;
+    char *commands;
+};
+
+struct chaz_MakeFile {
+    chaz_MakeVar  **vars;
+    size_t          num_vars;
+    chaz_MakeRule **rules;
+    size_t          num_rules;
+    char          **cleanups;
+    size_t          num_cleanups;
+};
+
+/* Static vars. */
+static struct {
+    char *make_command;
+    int   is_gnu_make;
+    int   is_nmake;
+} chaz_Make = {
+    NULL,
+    0, 0
+};
+
+/* Detect make command.
+ *
+ * The argument list must be a NULL-terminated series of different spellings
+ * of `make`, which will be auditioned in the order they are supplied.  Here
+ * are several possibilities:
+ *
+ *      make
+ *      gmake
+ *      nmake
+ *      dmake
+ */
+static int
+chaz_Make_detect(const char *make1, ...);
+
+static int
+chaz_Make_audition(const char *make);
+
+void
+chaz_Make_init(void) {
+    const char *make;
+
+    chaz_Make_detect("make", "gmake", "nmake", "dmake", NULL);
+    make = chaz_Make.make_command;
+
+    if (make) {
+        if (strcmp(make, "make") == 0 || strcmp(make, "gmake") == 0) {
+            /* TODO: Add a feature test for GNU make. */
+            chaz_Make.is_gnu_make = 1;
+        }
+        else if (strcmp(make, "nmake") == 0) {
+            chaz_Make.is_nmake = 1;
+        }
+    }
+}
+
+void
+chaz_Make_clean_up(void) {
+    free(chaz_Make.make_command);
+}
+
+const char*
+chaz_Make_get_make(void) {
+    return chaz_Make.make_command;
+}
+
+static int
+chaz_Make_detect(const char *make1, ...) {
+    va_list args;
+    const char *candidate;
+    int found = 0;
+    const char makefile_content[] = "foo:\n\techo \"foo!\"\n";
+    chaz_Util_write_file("_charm_Makefile", makefile_content);
+
+    /* Audition candidates. */
+    found = chaz_Make_audition(make1);
+    va_start(args, make1);
+    while (!found && (NULL != (candidate = va_arg(args, const char*)))) {
+        found = chaz_Make_audition(candidate);
+    }
+    va_end(args);
+
+    chaz_Util_remove_and_verify("_charm_Makefile");
+
+    return found;
+}
+
+static int
+chaz_Make_audition(const char *make) {
+    int succeeded = 0;
+    const char pattern[] = "%s -f _charm_Makefile";
+    size_t size = strlen(make) + sizeof(pattern) + 10;
+    char *command = (char*)malloc(size);
+    sprintf(command, pattern, make);
+
+    chaz_Util_remove_and_verify("_charm_foo");
+    chaz_OS_run_redirected(command, "_charm_foo");
+    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!")) {
+            succeeded = 1;
+        }
+        free(content);
+    }
+    chaz_Util_remove_and_verify("_charm_foo");
+
+    if (succeeded) {
+        chaz_Make.make_command = chaz_Util_strdup(make);
+    }
+
+    free(command);
+    return succeeded;
+}
+
+chaz_MakeFile*
+chaz_MakeFile_new() {
+    chaz_MakeFile *makefile = (chaz_MakeFile*)malloc(sizeof(chaz_MakeFile));
+
+    makefile->vars = (chaz_MakeVar**)malloc(sizeof(chaz_MakeVar*));
+    makefile->vars[0] = NULL;
+    makefile->num_vars = 0;
+
+    makefile->rules = (chaz_MakeRule**)malloc(sizeof(chaz_MakeRule*));
+    makefile->rules[0] = NULL;
+    makefile->num_rules = 0;
+
+    makefile->cleanups = (char**)malloc(sizeof(char*));
+    makefile->cleanups[0] = NULL;
+    makefile->num_cleanups = 0;
+
+    return makefile;
+}
+
+chaz_MakeVar*
+chaz_MakeFile_add_var(chaz_MakeFile *makefile, 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;
+
+    var->name         = chaz_Util_strdup(name);
+    var->value        = chaz_Util_strdup("");
+    var->num_elements = 0;
+
+    if (value) { chaz_MakeVar_append(var, value); }
+
+    vars = (chaz_MakeVar**)realloc(vars,
+                                   (num_vars + 1) * sizeof(chaz_MakeVar*));
+    vars[num_vars-1] = var;
+    vars[num_vars]   = NULL;
+    makefile->vars = vars;
+    makefile->num_vars = num_vars;
+
+    return var;
+}
+
+chaz_MakeRule*
+chaz_MakeFile_add_rule(chaz_MakeFile *makefile, const char *target,
+                       const char *prereq) {
+    chaz_MakeRule  *rule      = (chaz_MakeRule*)malloc(sizeof(chaz_MakeRule));
+    chaz_MakeRule **rules     = makefile->rules;
+    size_t          num_rules = makefile->num_rules + 1;
+
+    rule->targets  = NULL;
+    rule->prereqs  = NULL;
+    rule->commands = NULL;
+
+    if (target) { chaz_MakeRule_add_target(rule, target); }
+    if (prereq) { chaz_MakeRule_add_prereq(rule, prereq); }
+
+    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;
+
+    return rule;
+}
+
+void
+chaz_MakeFile_add_to_cleanup(chaz_MakeFile *makefile, const char *target) {
+    char    *cleanup      = chaz_Util_strdup(target);
+    char   **cleanups     = makefile->cleanups;
+    size_t   num_cleanups = makefile->num_cleanups + 1;
+
+    cleanups = (char**)realloc(cleanups, (num_cleanups + 1) * sizeof(char*));
+    cleanups[num_cleanups-1] = cleanup;
+    cleanups[num_cleanups]   = NULL;
+    makefile->cleanups = cleanups;
+    makefile->num_cleanups = num_cleanups;
+}
+
+void
+chaz_MakeFile_add_exe(chaz_MakeFile *makefile, const char *exe,
+                      const char *objects) {
+    const char    *pattern     = "%s %s %s %s%s";
+    const char    *link        = chaz_CC_link_command();
+    const char    *link_flags  = chaz_CC_link_flags();
+    const char    *output_flag = chaz_CC_link_output_flag();
+    chaz_MakeRule *rule;
+    char          *command;
+    size_t         size;
+
+    rule = chaz_MakeFile_add_rule(makefile, exe, objects);
+
+    size = strlen(pattern)
+           + strlen(link)
+           + strlen(link_flags)
+           + strlen(objects)
+           + strlen(output_flag)
+           + strlen(exe)
+           + 50;
+    command = (char*)malloc(size);
+    sprintf(command, pattern, link, link_flags, objects, output_flag, exe);
+    chaz_MakeRule_add_command(rule, command);
+
+    chaz_MakeFile_add_to_cleanup(makefile, exe);
+}
+
+void
+chaz_MakeFile_add_shared_obj(chaz_MakeFile *makefile, const char *shared_obj,
+                             const char *objects) {
+    const char    *pattern     = "%s %s %s %s %s%s";
+    const char    *link        = chaz_CC_link_command();
+    const char    *shobj_flags = chaz_CC_link_shared_obj_flag();
+    const char    *link_flags  = chaz_CC_link_flags();
+    const char    *output_flag = chaz_CC_link_output_flag();
+    chaz_MakeRule *rule;
+    char          *command;
+    size_t         size;
+
+    rule = chaz_MakeFile_add_rule(makefile, shared_obj, objects);
+
+    size = strlen(pattern)
+           + strlen(link)
+           + strlen(shobj_flags)
+           + strlen(link_flags)
+           + strlen(objects)
+           + strlen(output_flag)
+           + strlen(shared_obj)
+           + 50;
+    command = (char*)malloc(size);
+    sprintf(command, pattern, link, shobj_flags, link_flags, objects,
+            output_flag, shared_obj);
+    chaz_MakeRule_add_command(rule, command);
+
+    chaz_MakeFile_add_to_cleanup(makefile, shared_obj);
+}
+
+void
+chaz_MakeFile_write(chaz_MakeFile *makefile) {
+    int     shell_type = chaz_OS_shell_type();
+    FILE   *file;
+    size_t  i;
+
+    file = fopen("Makefile", "w");
+    if (!file) {
+        chaz_Util_die("Can't open Makefile\n");
+    }
+
+    for (i = 0; makefile->vars[i]; i++) {
+        chaz_MakeVar *var = makefile->vars[i];
+        fprintf(file, "%s = %s\n", var->name, var->value);
+    }
+    fprintf(file, "\n");
+
+    for (i = 0; makefile->rules[i]; i++) {
+        chaz_MakeRule *rule = makefile->rules[i];
+        fprintf(file, "%s :", rule->targets);
+        if (rule->prereqs) {
+            fprintf(file, " %s", rule->prereqs);
+        }
+        fprintf(file, "\n");
+        if (rule->commands) {
+            fprintf(file, "%s", rule->commands);
+        }
+        fprintf(file, "\n");
+    }
+
+    if (makefile->cleanups[0]) {
+        if (shell_type == CHAZ_OS_POSIX) {
+            fprintf(file, "clean :\n\trm -f");
+            for (i = 0; makefile->cleanups[i]; i++) {
+                const char *cleanup = makefile->cleanups[i];
+                fprintf(file, " \\\n\t    %s", cleanup);
+            }
+            fprintf(file, "\n\n");
+        }
+        else if (shell_type == CHAZ_OS_CMD_EXE) {
+            fprintf(file, "clean :\n");
+            for (i = 0; makefile->cleanups[i]; i++) {
+                const char *cleanup = makefile->cleanups[i];
+                fprintf(file, "\tfor %%i in (%s) do @if exist %%i del /f %%i\n",
+                        cleanup);
+            }
+            fprintf(file, "\n");
+        }
+        else {
+            chaz_Util_die("Unsupported shell type: %d", shell_type);
+        }
+    }
+
+    fprintf(file, "distclean : clean\n");
+    if (shell_type == CHAZ_OS_POSIX) {
+        fprintf(file, "\trm -f charmonizer$(EXE_EXT) charmony.h Makefile\n\n");
+    }
+    else if (shell_type == CHAZ_OS_CMD_EXE) {
+        fprintf(file,
+            "\tfor %%i in (charmonizer$(EXE_EXT) charmonizer$(OBJ_EXT)"
+            " charmony.h Makefile) do @if exist %%i del /f %%i\n\n");
+    }
+    else {
+        chaz_Util_die("Unsupported shell type: %d", shell_type);
+    }
+
+    if (chaz_Make.is_nmake) {
+        /* Inference rule for .c files. */
+        fprintf(file, ".c.obj :\n");
+        if (chaz_CC_msvc_version_num()) {
+            fprintf(file, "\t$(CC) $(CFLAGS) /c $< /Fo$@\n\n");
+        }
+        else {
+            fprintf(file, "\t$(CC) $(CFLAGS) -c $< -o $@\n\n");
+        }
+    }
+
+    fclose(file);
+}
+
+void
+chaz_MakeVar_append(chaz_MakeVar *var, const char *element) {
+    char *value;
+
+    if (element[0] == '\0') { return; }
+
+    if (var->num_elements == 0) {
+        value = chaz_Util_strdup(element);
+    }
+    else {
+        value = (char*)malloc(strlen(var->value) + strlen(element) + 20);
+
+        if (var->num_elements == 1) {
+            sprintf(value, "\\\n    %s \\\n    %s", var->value, element);
+        }
+        else {
+            sprintf(value, "%s \\\n    %s", var->value, element);
+        }
+    }
+
+    free(var->value);
+    var->value = value;
+    var->num_elements++;
+}
+
+void
+chaz_MakeRule_add_target(chaz_MakeRule *rule, const char *target) {
+    char *targets;
+
+    if (!rule->targets) {
+        targets = chaz_Util_strdup(target);
+    }
+    else {
+        targets = (char*)malloc(strlen(rule->targets) + strlen(target) + 20);
+        sprintf(targets, "%s %s", rule->targets, target);
+        free(rule->targets);
+    }
+
+    rule->targets = targets;
+}
+
+void
+chaz_MakeRule_add_prereq(chaz_MakeRule *rule, const char *prereq) {
+    char *prereqs;
+
+    if (!rule->prereqs) {
+        prereqs = chaz_Util_strdup(prereq);
+    }
+    else {
+        prereqs = (char*)malloc(strlen(rule->prereqs) + strlen(prereq) + 20);
+        sprintf(prereqs, "%s %s", rule->prereqs, prereq);
+        free(rule->prereqs);
+    }
+
+    rule->prereqs = prereqs;
+}
+
+void
+chaz_MakeRule_add_command(chaz_MakeRule *rule, const char *command) {
+    char *commands;
+
+    if (!rule->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);
+    }
+
+    rule->commands = commands;
+}
+
+void
+chaz_MakeRule_add_command_make(chaz_MakeRule *rule, const char *dir,
+                               const char *target) {
+    const char *make = chaz_Make.make_command;
+    char *command;
+
+    if (chaz_Make.is_gnu_make) {
+        if (!target) {
+            size_t size = strlen(dir) + 20;
+            command = (char*)malloc(size);
+            sprintf(command, "$(MAKE) -C %s", dir);
+        }
+        else {
+            size_t size = strlen(dir) + strlen(target) + 20;
+            command = (char*)malloc(size);
+            sprintf(command, "$(MAKE) -C %s %s", dir, target);
+        }
+        chaz_MakeRule_add_command(rule, command);
+        free(command);
+    }
+    else if (chaz_Make.is_nmake) {
+        command = (char*)malloc(strlen(dir) + 20);
+        sprintf(command, "cd %s", dir);
+        chaz_MakeRule_add_command(rule, command);
+        free(command);
+
+        if (!target) {
+            chaz_MakeRule_add_command(rule, "$(MAKE)");
+        }
+        else {
+            size_t size = strlen(target) + 20;
+            command = (char*)malloc(size);
+            sprintf(command, "$(MAKE) %s", target);
+            chaz_MakeRule_add_command(rule, command);
+            free(command);
+        }
+
+        chaz_MakeRule_add_command(rule, "cd $(MAKEDIR)");
+    }
+    else {
+        chaz_Util_die("Couldn't find a supported 'make' utility.");
+    }
+}
+
+void
+chaz_Make_list_files(const char *dir, const char *ext,
+                     chaz_Make_list_files_callback_t callback, void *context) {
+    int         shell_type = chaz_OS_shell_type();
+    const char *pattern;
+    char       *command;
+    char       *list;
+    char       *prefix;
+    char       *file;
+    size_t      command_size;
+    size_t      list_len;
+    size_t      prefix_len;
+
+    /* List files using shell. */
+
+    if (shell_type == CHAZ_OS_POSIX) {
+        pattern = "find %s -name '*.%s' -type f";
+    }
+    else if (shell_type == CHAZ_OS_CMD_EXE) {
+        pattern = "dir %s\\*.%s /s /b /a-d";
+    }
+    else {
+        chaz_Util_die("Unknown shell type %d", shell_type);
+    }
+
+    command_size = strlen(pattern) + strlen(dir) + strlen(ext) + 10;
+    command = (char*)malloc(command_size);
+    sprintf(command, pattern, dir, ext);
+    list = chaz_OS_run_and_capture(command, &list_len);
+    free(command);
+    if (!list) {
+        chaz_Util_die("Failed to list files in '%s'", dir);
+    }
+    list[list_len-1] = 0;
+
+    /* Find directory prefix to strip from files */
+
+    if (shell_type == CHAZ_OS_POSIX) {
+        prefix_len = strlen(dir);
+        prefix = (char*)malloc(prefix_len + 2);
+        memcpy(prefix, dir, prefix_len);
+        prefix[prefix_len++] = '/';
+        prefix[prefix_len]   = '\0';
+    }
+    else {
+        char   *output;
+        size_t  output_len;
+
+        /* 'dir /s' returns absolute paths, so we have to find the absolute
+         * path of the directory. This is done by using the variable
+         * substitution feature of the 'for' command.
+         */
+        pattern = "for %%I in (%s) do @echo %%~fI";
+        command_size = strlen(pattern) + strlen(dir) + 10;
+        command = (char*)malloc(command_size);
+        sprintf(command, pattern, dir);
+        output = chaz_OS_run_and_capture(command, &output_len);
+        free(command);
+        if (!output) { chaz_Util_die("Failed to find absolute path"); }
+
+        /* Strip whitespace from end of output. */
+        for (prefix_len = output_len; prefix_len > 0; --prefix_len) {
+            if (!isspace(output[prefix_len-1])) { break; }
+        }
+        prefix = (char*)malloc(prefix_len + 2);
+        memcpy(prefix, output, prefix_len);
+        prefix[prefix_len++] = '\\';
+        prefix[prefix_len]   = '\0';
+        free(output);
+    }
+
+    /* Iterate file list and invoke callback. */
+
+    for (file = strtok(list, "\r\n"); file; file = strtok(NULL, "\r\n")) {
+        if (strlen(file) <= prefix_len
+            || memcmp(file, prefix, prefix_len) != 0
+           ) {
+            chaz_Util_die("Expected prefix '%s' for file name '%s'", prefix,
+                          file);
+        }
+
+        callback(file + prefix_len, context);
+    }
+
+    free(prefix);
+    free(list);
+}
+
+
+/***************************************************************************/
+
 #line 17 "src/Charmonizer/Core/OperatingSystem.c"
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
+#include <ctype.h>
 
 /* #include "Charmonizer/Core/Compiler.h" */
 /* #include "Charmonizer/Core/Util.h" */
 /* #include "Charmonizer/Core/ConfWriter.h" */
 /* #include "Charmonizer/Core/OperatingSystem.h" */
 
+#define CHAZ_OS_TARGET_PATH  "_charmonizer_target"
+#define CHAZ_OS_NAME_MAX     31
+
 static struct {
+    char name[CHAZ_OS_NAME_MAX+1];
     char dev_null[20];
     char exe_ext[5];
     char obj_ext[5];
+    char shared_obj_ext[7];
     char local_command_start[3];
     int  shell_type;
-} chaz_OS = { "", "", "", "", 0 };
-#define CHAZ_OS_POSIX    1
-#define CHAZ_OS_CMD_EXE  2
+} chaz_OS = { "", "", "", "", "", "", 0 };
 
 void
 chaz_OS_init(void) {
@@ -2309,16 +3145,43 @@ chaz_OS_init(void) {
 
     /* Detect shell based on whether the bitbucket is "/dev/null" or "nul". */
     if (chaz_Util_can_open_file("/dev/null")) {
+        char   *uname;
+        size_t  uname_len;
+        size_t i;
+
+        chaz_OS.shell_type = CHAZ_OS_POSIX;
+
+        /* Detect Unix name. */
+        uname = chaz_OS_run_and_capture("uname", &uname_len);
+        for (i = 0; i < CHAZ_OS_NAME_MAX && i < uname_len; i++) {
+            char c = uname[i];
+            if (!c || isspace(c)) { break; }
+            chaz_OS.name[i] = tolower(c);
+        }
+        if (i > 0) { chaz_OS.name[i] = '\0'; }
+        else       { strcpy(chaz_OS.name, "unknown_unix"); }
+        free(uname);
+
         strcpy(chaz_OS.dev_null, "/dev/null");
         strcpy(chaz_OS.exe_ext, "");
         strcpy(chaz_OS.obj_ext, ".o");
+        if (memcmp(chaz_OS.name, "darwin", 6) == 0) {
+            strcpy(chaz_OS.shared_obj_ext, ".dylib");
+        }
+        else if (memcmp(chaz_OS.name, "cygwin", 6) == 0) {
+            strcpy(chaz_OS.shared_obj_ext, ".dll");
+        }
+        else {
+            strcpy(chaz_OS.shared_obj_ext, ".so");
+        }
         strcpy(chaz_OS.local_command_start, "./");
-        chaz_OS.shell_type = CHAZ_OS_POSIX;
     }
     else if (chaz_Util_can_open_file("nul")) {
+        strcpy(chaz_OS.name, "windows");
         strcpy(chaz_OS.dev_null, "nul");
         strcpy(chaz_OS.exe_ext, ".exe");
         strcpy(chaz_OS.obj_ext, ".obj");
+        strcpy(chaz_OS.shared_obj_ext, ".dll");
         strcpy(chaz_OS.local_command_start, ".\\");
         chaz_OS.shell_type = CHAZ_OS_CMD_EXE;
     }
@@ -2329,6 +3192,21 @@ chaz_OS_init(void) {
 }
 
 const char*
+chaz_OS_name(void) {
+    return chaz_OS.name;
+}
+
+int
+chaz_OS_is_darwin(void) {
+    return memcmp(chaz_OS.name, "darwin", 6) == 0;
+}
+
+int
+chaz_OS_is_cygwin(void) {
+    return memcmp(chaz_OS.name, "cygwin", 6) == 0;
+}
+
+const char*
 chaz_OS_exe_ext(void) {
     return chaz_OS.exe_ext;
 }
@@ -2339,11 +3217,21 @@ chaz_OS_obj_ext(void) {
 }
 
 const char*
+chaz_OS_shared_obj_ext(void) {
+    return chaz_OS.shared_obj_ext;
+}
+
+const char*
 chaz_OS_dev_null(void) {
     return chaz_OS.dev_null;
 }
 
 int
+chaz_OS_shell_type(void) {
+    return chaz_OS.shell_type;
+}
+
+int
 chaz_OS_remove(const char *name) {
     /*
      * On Windows it can happen that another process, typically a
@@ -2417,6 +3305,15 @@ chaz_OS_run_redirected(const char *command, const char *path) {
     return retval;
 }
 
+char*
+chaz_OS_run_and_capture(const char *command, size_t *output_len) {
+    char *output;
+    chaz_OS_run_redirected(command, CHAZ_OS_TARGET_PATH);
+    output = chaz_Util_slurp_file(CHAZ_OS_TARGET_PATH, output_len);
+    chaz_Util_remove_and_verify(CHAZ_OS_TARGET_PATH);
+    return output;
+}
+
 void
 chaz_OS_mkdir(const char *filepath) {
     char *command = NULL;
@@ -2719,6 +3616,7 @@ chaz_Probe_init(struct chaz_CLIArgs *args) {
     chaz_CC_init(args->cc, args->cflags);
     chaz_ConfWriter_init();
     chaz_HeadCheck_init();
+    chaz_Make_init();
 
     /* Enable output. */
     if (args->charmony_h) {
@@ -2748,6 +3646,7 @@ chaz_Probe_clean_up(void) {
     /* Dispatch various clean up routines. */
     chaz_ConfWriter_clean_up();
     chaz_CC_clean_up();
+    chaz_Make_clean_up();
 
     if (chaz_Util_verbosity) { printf("Cleanup complete.\n"); }
 }
@@ -2763,8 +3662,8 @@ chaz_Probe_gcc_version(void) {
 }
 
 int
-chaz_Probe_compiler_is_msvc(void) {
-    return chaz_CC_compiler_is_msvc();
+chaz_Probe_msvc_version_num(void) {
+    return chaz_CC_msvc_version_num();
 }
 
 /***************************************************************************/
@@ -3737,6 +4636,17 @@ chaz_Strings_probe_c99_snprintf(void) {
 /* #include "Charmonizer/Probe.h" */
 /* #include "Charmonizer/Probe/Integers.h" */
 
+#if defined(_WIN32) && !defined(__CYGWIN__)
+  #define DIR_SEP "\\"
+#else
+  #define DIR_SEP "/"
+#endif
+
+typedef struct SourceFileContext {
+    chaz_MakeVar *common_objs;
+    chaz_MakeVar *test_cfc_objs;
+} SourceFileContext;
+
 static void
 S_add_compiler_flags(struct chaz_CLIArgs *args) {
     if (chaz_Probe_gcc_version_num()) {
@@ -3753,17 +4663,160 @@ S_add_compiler_flags(struct chaz_CLIArgs *args) {
         /* Tell GCC explicitly to run with maximum options. */
         chaz_CC_add_extra_cflags("-std=gnu99 -D_GNU_SOURCE");
     }
-    else if (chaz_Probe_compiler_is_msvc()) {
+    else if (chaz_Probe_msvc_version_num()) {
         /* Compile as C++ under MSVC. */
-        chaz_CC_add_extra_cflags("-TP");
+        chaz_CC_add_extra_cflags("/TP");
 
         /* Thwart stupid warnings. */
-        chaz_CC_add_extra_cflags("-D_CRT_SECURE_NO_WARNINGS");
-        chaz_CC_add_extra_cflags("-D_SCL_SECURE_NO_WARNINGS");
+        chaz_CC_add_extra_cflags("/D_CRT_SECURE_NO_WARNINGS");
+        chaz_CC_add_extra_cflags("/D_SCL_SECURE_NO_WARNINGS");
+
+        if (chaz_Probe_msvc_version_num() < 1300) {
+            /* Redefine 'for' to fix broken 'for' scoping under MSVC6. */
+            chaz_CC_add_extra_cflags("/Dfor=\"if(0);else for\"");
+        }
+    }
+}
+
+static void
+S_source_file_callback(char *file, void *context) {
+    SourceFileContext *sfc = (SourceFileContext*)context;
+    size_t file_len = strlen(file);
+    size_t obj_file_size;
+    const char *pattern;
+    char *obj_file;
+
+    if (strcmp(file, "CFCParseHeader.c") == 0) { return; }
+
+    /* Strip extension */
+    if (file_len <= 2 || memcmp(file + file_len - 2, ".c", 2) != 0) {
+        chaz_Util_warn("Unexpected source file name: %s", file);
+        return;
+    }
+    file[file_len-2] = '\0';
+
+    pattern = "$(SRC_DIR)" DIR_SEP "%s$(OBJ_EXT)";
+    obj_file_size = strlen(pattern) + file_len + 10;
+    obj_file = (char*)malloc(obj_file_size);
+    sprintf(obj_file, pattern, file);
+
+    if (strlen(file) >= 7 && memcmp(file, "CFCTest", 7) == 0) {
+        chaz_MakeVar_append(sfc->test_cfc_objs, obj_file);
+    }
+    else {
+        chaz_MakeVar_append(sfc->common_objs, obj_file);
+    }
+
+    free(obj_file);
+}
+
+static void
+S_write_makefile() {
+    SourceFileContext sfc;
+
+    const char *base_dir  = "..";
+    const char *lemon_dir = ".." DIR_SEP ".." DIR_SEP ".." DIR_SEP "lemon";
+    const char *exe_ext   = chaz_OS_exe_ext();
+    const char *obj_ext   = chaz_OS_obj_ext();
+
+    const char *parse_header_y = "$(SRC_DIR)" DIR_SEP "CFCParseHeader.y";
+    const char *parse_header_h = "$(SRC_DIR)" DIR_SEP "CFCParseHeader.h";
+    const char *parse_header_c = "$(SRC_DIR)" DIR_SEP "CFCParseHeader.c";
+
+    char *src_dir;
+    char *scratch;
+
+    chaz_MakeFile *makefile;
+    chaz_MakeVar  *var;
+    chaz_MakeRule *rule;
+
+    printf("Creating Makefile...\n");
+
+    src_dir = (char*)malloc(strlen(base_dir) + 20);
+    sprintf(src_dir, "%s" DIR_SEP "src", base_dir);
+
+    makefile = chaz_MakeFile_new();
+
+    /* Variables */
 
-        /* Redefine 'for' to fix broken 'for' scoping under MSVC6. */
-        chaz_CC_add_extra_cflags("-Dfor=\"if(0);else for\"");
+    chaz_MakeFile_add_var(makefile, "SRC_DIR", src_dir);
+    scratch = (char*)malloc(strlen(base_dir) + 20);
+    sprintf(scratch, "%s" DIR_SEP "include", base_dir);
+    chaz_MakeFile_add_var(makefile, "INCLUDE_DIR", scratch);
+    free(scratch);
+    chaz_MakeFile_add_var(makefile, "LEMON_DIR", lemon_dir);
+
+    chaz_MakeFile_add_var(makefile, "EXE_EXT", exe_ext);
+    chaz_MakeFile_add_var(makefile, "OBJ_EXT", obj_ext);
+
+    chaz_MakeFile_add_var(makefile, "CC", chaz_CC_get_cc());
+
+    if (chaz_CC_msvc_version_num()) {
+        chaz_CC_add_extra_cflags("/nologo");
     }
+    chaz_CC_set_optimization_level("2");
+    chaz_CC_add_include_dir(".");
+    chaz_CC_add_include_dir("$(INCLUDE_DIR)");
+    chaz_CC_add_include_dir("$(SRC_DIR)");
+
+    var = chaz_MakeFile_add_var(makefile, "CFLAGS", NULL);
+    chaz_MakeVar_append(var, chaz_CC_get_cflags());
+    chaz_MakeVar_append(var, chaz_CC_get_extra_cflags());
+
+    chaz_MakeFile_add_var(makefile, "LEMON_OBJS",
+                          "$(LEMON_DIR)" DIR_SEP "lemon$(OBJ_EXT)");
+
+    sfc.common_objs   = chaz_MakeFile_add_var(makefile, "COMMON_OBJS", NULL);
+    sfc.test_cfc_objs = chaz_MakeFile_add_var(makefile, "TEST_CFC_OBJS", NULL);
+
+    chaz_Make_list_files(src_dir, "c", S_source_file_callback, &sfc);
+
+    chaz_MakeVar_append(sfc.common_objs,
+                        "$(SRC_DIR)" DIR_SEP "CFCParseHeader$(OBJ_EXT)");
+    chaz_MakeVar_append(sfc.test_cfc_objs, "t" DIR_SEP "test_cfc$(OBJ_EXT)");
+
+    chaz_MakeFile_add_var(makefile, "CFC_OBJS", "cfc$(OBJ_EXT)");
+
+    chaz_MakeFile_add_var(makefile, "LEMON_EXE",
+                          "$(LEMON_DIR)" DIR_SEP "lemon$(EXE_EXT)");
+    chaz_MakeFile_add_var(makefile, "CFC_EXE", "cfc$(EXE_EXT)");
+    chaz_MakeFile_add_var(makefile, "TEST_CFC_EXE",
+                          "t" DIR_SEP "test_cfc$(EXE_EXT)");
+
+    /* Rules */
+
+    chaz_MakeFile_add_rule(makefile, "all", "$(CFC_EXE)");
+
+    chaz_MakeFile_add_exe(makefile, "$(LEMON_EXE)", "$(LEMON_OBJS)");
+
+    rule = chaz_MakeFile_add_rule(makefile, parse_header_c, NULL);
+    chaz_MakeRule_add_prereq(rule, "$(LEMON_EXE)");
+    chaz_MakeRule_add_prereq(rule, parse_header_y);
+    scratch = (char*)malloc(strlen(parse_header_y) + 20);
+    sprintf(scratch, "$(LEMON_EXE) -q %s", parse_header_y);
+    chaz_MakeRule_add_command(rule, scratch);
+    free(scratch);
+
+    chaz_MakeFile_add_rule(makefile, "$(COMMON_OBJS)", parse_header_c);
+
+    chaz_MakeFile_add_exe(makefile, "$(CFC_EXE)",
+                          "$(COMMON_OBJS) $(CFC_OBJS)");
+    chaz_MakeFile_add_exe(makefile, "$(TEST_CFC_EXE)",
+                          "$(COMMON_OBJS) $(TEST_CFC_OBJS)");
+
+    rule = chaz_MakeFile_add_rule(makefile, "test", "$(TEST_CFC_EXE)");
+    chaz_MakeRule_add_command(rule, "$(TEST_CFC_EXE)");
+
+    chaz_MakeFile_add_to_cleanup(makefile, "$(LEMON_OBJS)");
+    chaz_MakeFile_add_to_cleanup(makefile, "$(COMMON_OBJS)");
+    chaz_MakeFile_add_to_cleanup(makefile, "$(CFC_OBJS)");
+    chaz_MakeFile_add_to_cleanup(makefile, "$(TEST_CFC_OBJS)");
+    chaz_MakeFile_add_to_cleanup(makefile, parse_header_h);
+    chaz_MakeFile_add_to_cleanup(makefile, parse_header_c);
+
+    chaz_MakeFile_write(makefile);
+
+    free(src_dir);
 }
 
 int main(int argc, const char **argv) {
@@ -3785,6 +4838,16 @@ int main(int argc, const char **argv) {
     chaz_Integers_run();
     chaz_Strings_run();
 
+    {
+        int i;
+        for (i = 0; i < argc; i++) {
+            if (strncmp(argv[i], "--enable-makefile", 17) == 0) {
+                S_write_makefile();
+                break;
+            }
+        }
+    }
+
     /* Clean up. */
     chaz_Probe_clean_up();
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/b7512609/clownfish/runtime/common/charmonizer.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/common/charmonizer.c b/clownfish/runtime/common/charmonizer.c
index fa9db46..534a4dd 100644
--- a/clownfish/runtime/common/charmonizer.c
+++ b/clownfish/runtime/common/charmonizer.c
@@ -93,6 +93,16 @@ chaz_CC_set_warnings_as_errors(const int flag);
 void
 chaz_CC_add_extra_cflags(const char *);
 
+/* Add include dir to extra cflags.
+ */
+void
+chaz_CC_add_include_dir(const char *dir);
+
+/* Set optimization level.
+ */
+void
+chaz_CC_set_optimization_level(const char *level);
+
 /* Accessor for the compiler executable's string representation.
  */
 const char*
@@ -109,13 +119,25 @@ const char*
 chaz_CC_get_extra_cflags(void);
 
 int
-chaz_CC_gcc_version_num();
+chaz_CC_gcc_version_num(void);
 
 const char*
-chaz_CC_gcc_version();
+chaz_CC_gcc_version(void);
 
 int
-chaz_CC_compiler_is_msvc(void);
+chaz_CC_msvc_version_num(void);
+
+const char*
+chaz_CC_link_command(void);
+
+const char*
+chaz_CC_link_flags(void);
+
+const char*
+chaz_CC_link_shared_obj_flag(void);
+
+const char*
+chaz_CC_link_output_flag(void);
 
 #endif /* H_CHAZ_COMPILER */
 
@@ -325,6 +347,162 @@ chaz_HeadCheck_contains_member(const char *struct_name, const char *member,
 
 /***************************************************************************/
 
+#line 21 "src/Charmonizer/Core/Make.h"
+/* Charmonizer/Core/Compiler.h
+ */
+
+#ifndef H_CHAZ_MAKE
+#define H_CHAZ_MAKE
+
+typedef struct chaz_MakeFile chaz_MakeFile;
+typedef struct chaz_MakeVar chaz_MakeVar;
+typedef struct chaz_MakeRule chaz_MakeRule;
+
+typedef void (*chaz_Make_list_files_callback_t)(char *file, void *context);
+
+/** Initialize the environment.
+ */
+void
+chaz_Make_init(void);
+
+/** Clean up the environment.
+ */
+void
+chaz_Make_clean_up(void);
+
+/** Return the name of the detected 'make' executable.
+ */
+const char*
+chaz_Make_get_make(void);
+
+/** Recursively list files in a directory. For every file a callback is called
+ * with the filename and a context variable.
+ *
+ * @param dir Directory to search in.
+ * @param ext File extension to search for.
+ * @param callback Callback to call for every matching file.
+ * @param context Context variable to pass to callback.
+ */
+void
+chaz_Make_list_files(const char *dir, const char *ext,
+                     chaz_Make_list_files_callback_t callback, void *context);
+
+/** MakeFile constructor.
+ */
+chaz_MakeFile*
+chaz_MakeFile_new();
+
+/** 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,
+                      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
+ * add prerequisites later.
+ * @return a MakeRule.
+ */
+chaz_MakeRule*
+chaz_MakeFile_add_rule(chaz_MakeFile *makefile, const char *target,
+                       const char *prereq);
+
+/** Add a file to the 'clean' target.
+ *
+ * @param makefile The makefile.
+ * @param target The filename.
+ */
+void
+chaz_MakeFile_add_to_cleanup(chaz_MakeFile *makefile, const char *target);
+
+/** Add a rule to link an executable. The executable will also be added to the
+ * list of files to clean.
+ *
+ * @param makefile The makefile.
+ * @param exe The name of the executable.
+ * @param objects The list of object files.
+ */
+void
+chaz_MakeFile_add_exe(chaz_MakeFile *makefile, const char *exe,
+                      const char *objects);
+
+/** Add a rule to link a shared object. The shared object will also be added to
+ * the list of files to clean.
+ *
+ * @param makefile The makefile.
+ * @param shared_obj The name of the shared object.
+ * @param objects The list of object files.
+ */
+void
+chaz_MakeFile_add_shared_obj(chaz_MakeFile *makefile, const char *shared_obj,
+                             const char *objects);
+
+/** Write the makefile to a file named 'Makefile' in the current directory.
+ *
+ * @param makefile The makefile.
+ */
+void
+chaz_MakeFile_write(chaz_MakeFile *makefile);
+
+/** 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);
+
+/** 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);
+
+/** 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);
+
+/** 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);
+
+/** 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_command_make(chaz_MakeRule *rule, const char *dir,
+                               const char *target);
+
+#endif /* H_CHAZ_MAKE */
+
+
+
+/***************************************************************************/
+
 #line 21 "src/Charmonizer/Core/OperatingSystem.h"
 /* Charmonizer/Core/OperatingSystem.h - abstract an operating system down to a few
  * variables.
@@ -333,6 +511,9 @@ chaz_HeadCheck_contains_member(const char *struct_name, const char *member,
 #ifndef H_CHAZ_OPER_SYS
 #define H_CHAZ_OPER_SYS
 
+#define CHAZ_OS_POSIX    1
+#define CHAZ_OS_CMD_EXE  2
+
 /* Safely remove a file named [name]. Needed because of Windows quirks.
  * Returns true on success, false on failure.
  */
@@ -358,6 +539,11 @@ chaz_OS_run_redirected(const char *command, const char *path);
 int
 chaz_OS_run_local_redirected(const char *command, const char *path);
 
+/* Run a command and return the output from stdout.
+ */
+char*
+chaz_OS_run_and_capture(const char *command, size_t *output_len);
+
 /* Attempt to create a directory.
  */
 void
@@ -368,6 +554,17 @@ chaz_OS_mkdir(const char *filepath);
 void
 chaz_OS_rmdir(const char *filepath);
 
+/* Return the operating system name.
+ */
+const char*
+chaz_OS_name(void);
+
+int
+chaz_OS_is_darwin(void);
+
+int
+chaz_OS_is_cygwin(void);
+
 /* Return the extension for an executable on this system.
  */
 const char*
@@ -378,11 +575,21 @@ chaz_OS_exe_ext(void);
 const char*
 chaz_OS_obj_ext(void);
 
+/* Return the extension for a shared object on this system.
+ */
+const char*
+chaz_OS_shared_obj_ext(void);
+
 /* Return the equivalent of /dev/null on this system.
  */
 const char*
 chaz_OS_dev_null(void);
 
+/* Return the shell type of this system.
+ */
+int
+chaz_OS_shell_type(void);
+
 /* Initialize the Charmonizer/Core/OperatingSystem module.
  */
 void
@@ -526,10 +733,10 @@ chaz_Probe_gcc_version_num(void);
 const char*
 chaz_Probe_gcc_version(void);
 
-/* Returns true if the compiler is MSVC.
+/* Return the integer version of MSVC defined by _MSC_VER
  */
 int
-chaz_Probe_compiler_is_msvc(void);
+chaz_Probe_msvc_version_num(void);
 
 #endif /* Include guard. */
 
@@ -1422,6 +1629,34 @@ chaz_CC_add_extra_cflags(const char *flags) {
     }
 }
 
+void
+chaz_CC_add_include_dir(const char *dir) {
+    size_t size = strlen(chaz_CC.include_flag) + strlen(dir) + 1;
+    char *flag = (char*)malloc(size);
+    sprintf(flag, "%s%s", chaz_CC.include_flag, dir);
+    chaz_CC_add_extra_cflags(flag);
+    free(flag);
+}
+
+void
+chaz_CC_set_optimization_level(const char *level) {
+    const char *opt_flag;
+    char *flag;
+    size_t size;
+
+    if (chaz_CC.intval__MSC_VER) {
+        opt_flag = "/O";
+    }
+    else {
+        opt_flag = "-O";
+    }
+    size = strlen(opt_flag) + strlen(level) + 1;
+    flag = (char*)malloc(size);
+    sprintf(flag, "%s%s", opt_flag, level);
+    chaz_CC_add_extra_cflags(flag);
+    free(flag);
+}
+
 const char*
 chaz_CC_get_cc(void) {
     return chaz_CC.cc_command;
@@ -1450,11 +1685,50 @@ chaz_CC_gcc_version(void) {
 }
 
 int
-chaz_CC_compiler_is_msvc(void) {
-    return !!chaz_CC.intval__MSC_VER;
+chaz_CC_msvc_version_num(void) {
+    return chaz_CC.intval__MSC_VER;
+}
+
+const char*
+chaz_CC_link_command() {
+    if (chaz_CC.intval__MSC_VER) {
+        return "link";
+    }
+    else {
+        return chaz_CC.cc_command;
+    }
+}
+
+const char*
+chaz_CC_link_flags() {
+    return "";
+}
+
+const char*
+chaz_CC_link_shared_obj_flag() {
+    if (chaz_CC.intval__MSC_VER) {
+        return "/DLL";
+    }
+    else if (chaz_OS_is_darwin()) {
+        return "-dynamiclib ";
+    }
+    else {
+        return "-shared";
+    }
+}
+
+const char*
+chaz_CC_link_output_flag() {
+    if (chaz_CC.intval__MSC_VER) {
+        return "/OUT:";
+    }
+    else {
+        return "-o ";
+    }
 }
 
 
+
 /***************************************************************************/
 
 #line 17 "src/Charmonizer/Core/ConfWriter.c"
@@ -2559,25 +2833,587 @@ chaz_HeadCheck_maybe_add_to_cache(const char *header_name, int exists) {
 
 /***************************************************************************/
 
+#line 17 "src/Charmonizer/Core/Make.c"
+#include <ctype.h>
+#include <string.h>
+/* #include "Charmonizer/Core/Make.h" */
+/* #include "Charmonizer/Core/Compiler.h" */
+/* #include "Charmonizer/Core/OperatingSystem.h" */
+/* #include "Charmonizer/Core/Util.h" */
+
+struct chaz_MakeVar {
+    char   *name;
+    char   *value;
+    size_t  num_elements;
+};
+
+struct chaz_MakeRule {
+    char *targets;
+    char *prereqs;
+    char *commands;
+};
+
+struct chaz_MakeFile {
+    chaz_MakeVar  **vars;
+    size_t          num_vars;
+    chaz_MakeRule **rules;
+    size_t          num_rules;
+    char          **cleanups;
+    size_t          num_cleanups;
+};
+
+/* Static vars. */
+static struct {
+    char *make_command;
+    int   is_gnu_make;
+    int   is_nmake;
+} chaz_Make = {
+    NULL,
+    0, 0
+};
+
+/* Detect make command.
+ *
+ * The argument list must be a NULL-terminated series of different spellings
+ * of `make`, which will be auditioned in the order they are supplied.  Here
+ * are several possibilities:
+ *
+ *      make
+ *      gmake
+ *      nmake
+ *      dmake
+ */
+static int
+chaz_Make_detect(const char *make1, ...);
+
+static int
+chaz_Make_audition(const char *make);
+
+void
+chaz_Make_init(void) {
+    const char *make;
+
+    chaz_Make_detect("make", "gmake", "nmake", "dmake", NULL);
+    make = chaz_Make.make_command;
+
+    if (make) {
+        if (strcmp(make, "make") == 0 || strcmp(make, "gmake") == 0) {
+            /* TODO: Add a feature test for GNU make. */
+            chaz_Make.is_gnu_make = 1;
+        }
+        else if (strcmp(make, "nmake") == 0) {
+            chaz_Make.is_nmake = 1;
+        }
+    }
+}
+
+void
+chaz_Make_clean_up(void) {
+    free(chaz_Make.make_command);
+}
+
+const char*
+chaz_Make_get_make(void) {
+    return chaz_Make.make_command;
+}
+
+static int
+chaz_Make_detect(const char *make1, ...) {
+    va_list args;
+    const char *candidate;
+    int found = 0;
+    const char makefile_content[] = "foo:\n\techo \"foo!\"\n";
+    chaz_Util_write_file("_charm_Makefile", makefile_content);
+
+    /* Audition candidates. */
+    found = chaz_Make_audition(make1);
+    va_start(args, make1);
+    while (!found && (NULL != (candidate = va_arg(args, const char*)))) {
+        found = chaz_Make_audition(candidate);
+    }
+    va_end(args);
+
+    chaz_Util_remove_and_verify("_charm_Makefile");
+
+    return found;
+}
+
+static int
+chaz_Make_audition(const char *make) {
+    int succeeded = 0;
+    const char pattern[] = "%s -f _charm_Makefile";
+    size_t size = strlen(make) + sizeof(pattern) + 10;
+    char *command = (char*)malloc(size);
+    sprintf(command, pattern, make);
+
+    chaz_Util_remove_and_verify("_charm_foo");
+    chaz_OS_run_redirected(command, "_charm_foo");
+    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!")) {
+            succeeded = 1;
+        }
+        free(content);
+    }
+    chaz_Util_remove_and_verify("_charm_foo");
+
+    if (succeeded) {
+        chaz_Make.make_command = chaz_Util_strdup(make);
+    }
+
+    free(command);
+    return succeeded;
+}
+
+chaz_MakeFile*
+chaz_MakeFile_new() {
+    chaz_MakeFile *makefile = (chaz_MakeFile*)malloc(sizeof(chaz_MakeFile));
+
+    makefile->vars = (chaz_MakeVar**)malloc(sizeof(chaz_MakeVar*));
+    makefile->vars[0] = NULL;
+    makefile->num_vars = 0;
+
+    makefile->rules = (chaz_MakeRule**)malloc(sizeof(chaz_MakeRule*));
+    makefile->rules[0] = NULL;
+    makefile->num_rules = 0;
+
+    makefile->cleanups = (char**)malloc(sizeof(char*));
+    makefile->cleanups[0] = NULL;
+    makefile->num_cleanups = 0;
+
+    return makefile;
+}
+
+chaz_MakeVar*
+chaz_MakeFile_add_var(chaz_MakeFile *makefile, 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;
+
+    var->name         = chaz_Util_strdup(name);
+    var->value        = chaz_Util_strdup("");
+    var->num_elements = 0;
+
+    if (value) { chaz_MakeVar_append(var, value); }
+
+    vars = (chaz_MakeVar**)realloc(vars,
+                                   (num_vars + 1) * sizeof(chaz_MakeVar*));
+    vars[num_vars-1] = var;
+    vars[num_vars]   = NULL;
+    makefile->vars = vars;
+    makefile->num_vars = num_vars;
+
+    return var;
+}
+
+chaz_MakeRule*
+chaz_MakeFile_add_rule(chaz_MakeFile *makefile, const char *target,
+                       const char *prereq) {
+    chaz_MakeRule  *rule      = (chaz_MakeRule*)malloc(sizeof(chaz_MakeRule));
+    chaz_MakeRule **rules     = makefile->rules;
+    size_t          num_rules = makefile->num_rules + 1;
+
+    rule->targets  = NULL;
+    rule->prereqs  = NULL;
+    rule->commands = NULL;
+
+    if (target) { chaz_MakeRule_add_target(rule, target); }
+    if (prereq) { chaz_MakeRule_add_prereq(rule, prereq); }
+
+    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;
+
+    return rule;
+}
+
+void
+chaz_MakeFile_add_to_cleanup(chaz_MakeFile *makefile, const char *target) {
+    char    *cleanup      = chaz_Util_strdup(target);
+    char   **cleanups     = makefile->cleanups;
+    size_t   num_cleanups = makefile->num_cleanups + 1;
+
+    cleanups = (char**)realloc(cleanups, (num_cleanups + 1) * sizeof(char*));
+    cleanups[num_cleanups-1] = cleanup;
+    cleanups[num_cleanups]   = NULL;
+    makefile->cleanups = cleanups;
+    makefile->num_cleanups = num_cleanups;
+}
+
+void
+chaz_MakeFile_add_exe(chaz_MakeFile *makefile, const char *exe,
+                      const char *objects) {
+    const char    *pattern     = "%s %s %s %s%s";
+    const char    *link        = chaz_CC_link_command();
+    const char    *link_flags  = chaz_CC_link_flags();
+    const char    *output_flag = chaz_CC_link_output_flag();
+    chaz_MakeRule *rule;
+    char          *command;
+    size_t         size;
+
+    rule = chaz_MakeFile_add_rule(makefile, exe, objects);
+
+    size = strlen(pattern)
+           + strlen(link)
+           + strlen(link_flags)
+           + strlen(objects)
+           + strlen(output_flag)
+           + strlen(exe)
+           + 50;
+    command = (char*)malloc(size);
+    sprintf(command, pattern, link, link_flags, objects, output_flag, exe);
+    chaz_MakeRule_add_command(rule, command);
+
+    chaz_MakeFile_add_to_cleanup(makefile, exe);
+}
+
+void
+chaz_MakeFile_add_shared_obj(chaz_MakeFile *makefile, const char *shared_obj,
+                             const char *objects) {
+    const char    *pattern     = "%s %s %s %s %s%s";
+    const char    *link        = chaz_CC_link_command();
+    const char    *shobj_flags = chaz_CC_link_shared_obj_flag();
+    const char    *link_flags  = chaz_CC_link_flags();
+    const char    *output_flag = chaz_CC_link_output_flag();
+    chaz_MakeRule *rule;
+    char          *command;
+    size_t         size;
+
+    rule = chaz_MakeFile_add_rule(makefile, shared_obj, objects);
+
+    size = strlen(pattern)
+           + strlen(link)
+           + strlen(shobj_flags)
+           + strlen(link_flags)
+           + strlen(objects)
+           + strlen(output_flag)
+           + strlen(shared_obj)
+           + 50;
+    command = (char*)malloc(size);
+    sprintf(command, pattern, link, shobj_flags, link_flags, objects,
+            output_flag, shared_obj);
+    chaz_MakeRule_add_command(rule, command);
+
+    chaz_MakeFile_add_to_cleanup(makefile, shared_obj);
+}
+
+void
+chaz_MakeFile_write(chaz_MakeFile *makefile) {
+    int     shell_type = chaz_OS_shell_type();
+    FILE   *file;
+    size_t  i;
+
+    file = fopen("Makefile", "w");
+    if (!file) {
+        chaz_Util_die("Can't open Makefile\n");
+    }
+
+    for (i = 0; makefile->vars[i]; i++) {
+        chaz_MakeVar *var = makefile->vars[i];
+        fprintf(file, "%s = %s\n", var->name, var->value);
+    }
+    fprintf(file, "\n");
+
+    for (i = 0; makefile->rules[i]; i++) {
+        chaz_MakeRule *rule = makefile->rules[i];
+        fprintf(file, "%s :", rule->targets);
+        if (rule->prereqs) {
+            fprintf(file, " %s", rule->prereqs);
+        }
+        fprintf(file, "\n");
+        if (rule->commands) {
+            fprintf(file, "%s", rule->commands);
+        }
+        fprintf(file, "\n");
+    }
+
+    if (makefile->cleanups[0]) {
+        if (shell_type == CHAZ_OS_POSIX) {
+            fprintf(file, "clean :\n\trm -f");
+            for (i = 0; makefile->cleanups[i]; i++) {
+                const char *cleanup = makefile->cleanups[i];
+                fprintf(file, " \\\n\t    %s", cleanup);
+            }
+            fprintf(file, "\n\n");
+        }
+        else if (shell_type == CHAZ_OS_CMD_EXE) {
+            fprintf(file, "clean :\n");
+            for (i = 0; makefile->cleanups[i]; i++) {
+                const char *cleanup = makefile->cleanups[i];
+                fprintf(file, "\tfor %%i in (%s) do @if exist %%i del /f %%i\n",
+                        cleanup);
+            }
+            fprintf(file, "\n");
+        }
+        else {
+            chaz_Util_die("Unsupported shell type: %d", shell_type);
+        }
+    }
+
+    fprintf(file, "distclean : clean\n");
+    if (shell_type == CHAZ_OS_POSIX) {
+        fprintf(file, "\trm -f charmonizer$(EXE_EXT) charmony.h Makefile\n\n");
+    }
+    else if (shell_type == CHAZ_OS_CMD_EXE) {
+        fprintf(file,
+            "\tfor %%i in (charmonizer$(EXE_EXT) charmonizer$(OBJ_EXT)"
+            " charmony.h Makefile) do @if exist %%i del /f %%i\n\n");
+    }
+    else {
+        chaz_Util_die("Unsupported shell type: %d", shell_type);
+    }
+
+    if (chaz_Make.is_nmake) {
+        /* Inference rule for .c files. */
+        fprintf(file, ".c.obj :\n");
+        if (chaz_CC_msvc_version_num()) {
+            fprintf(file, "\t$(CC) $(CFLAGS) /c $< /Fo$@\n\n");
+        }
+        else {
+            fprintf(file, "\t$(CC) $(CFLAGS) -c $< -o $@\n\n");
+        }
+    }
+
+    fclose(file);
+}
+
+void
+chaz_MakeVar_append(chaz_MakeVar *var, const char *element) {
+    char *value;
+
+    if (element[0] == '\0') { return; }
+
+    if (var->num_elements == 0) {
+        value = chaz_Util_strdup(element);
+    }
+    else {
+        value = (char*)malloc(strlen(var->value) + strlen(element) + 20);
+
+        if (var->num_elements == 1) {
+            sprintf(value, "\\\n    %s \\\n    %s", var->value, element);
+        }
+        else {
+            sprintf(value, "%s \\\n    %s", var->value, element);
+        }
+    }
+
+    free(var->value);
+    var->value = value;
+    var->num_elements++;
+}
+
+void
+chaz_MakeRule_add_target(chaz_MakeRule *rule, const char *target) {
+    char *targets;
+
+    if (!rule->targets) {
+        targets = chaz_Util_strdup(target);
+    }
+    else {
+        targets = (char*)malloc(strlen(rule->targets) + strlen(target) + 20);
+        sprintf(targets, "%s %s", rule->targets, target);
+        free(rule->targets);
+    }
+
+    rule->targets = targets;
+}
+
+void
+chaz_MakeRule_add_prereq(chaz_MakeRule *rule, const char *prereq) {
+    char *prereqs;
+
+    if (!rule->prereqs) {
+        prereqs = chaz_Util_strdup(prereq);
+    }
+    else {
+        prereqs = (char*)malloc(strlen(rule->prereqs) + strlen(prereq) + 20);
+        sprintf(prereqs, "%s %s", rule->prereqs, prereq);
+        free(rule->prereqs);
+    }
+
+    rule->prereqs = prereqs;
+}
+
+void
+chaz_MakeRule_add_command(chaz_MakeRule *rule, const char *command) {
+    char *commands;
+
+    if (!rule->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);
+    }
+
+    rule->commands = commands;
+}
+
+void
+chaz_MakeRule_add_command_make(chaz_MakeRule *rule, const char *dir,
+                               const char *target) {
+    const char *make = chaz_Make.make_command;
+    char *command;
+
+    if (chaz_Make.is_gnu_make) {
+        if (!target) {
+            size_t size = strlen(dir) + 20;
+            command = (char*)malloc(size);
+            sprintf(command, "$(MAKE) -C %s", dir);
+        }
+        else {
+            size_t size = strlen(dir) + strlen(target) + 20;
+            command = (char*)malloc(size);
+            sprintf(command, "$(MAKE) -C %s %s", dir, target);
+        }
+        chaz_MakeRule_add_command(rule, command);
+        free(command);
+    }
+    else if (chaz_Make.is_nmake) {
+        command = (char*)malloc(strlen(dir) + 20);
+        sprintf(command, "cd %s", dir);
+        chaz_MakeRule_add_command(rule, command);
+        free(command);
+
+        if (!target) {
+            chaz_MakeRule_add_command(rule, "$(MAKE)");
+        }
+        else {
+            size_t size = strlen(target) + 20;
+            command = (char*)malloc(size);
+            sprintf(command, "$(MAKE) %s", target);
+            chaz_MakeRule_add_command(rule, command);
+            free(command);
+        }
+
+        chaz_MakeRule_add_command(rule, "cd $(MAKEDIR)");
+    }
+    else {
+        chaz_Util_die("Couldn't find a supported 'make' utility.");
+    }
+}
+
+void
+chaz_Make_list_files(const char *dir, const char *ext,
+                     chaz_Make_list_files_callback_t callback, void *context) {
+    int         shell_type = chaz_OS_shell_type();
+    const char *pattern;
+    char       *command;
+    char       *list;
+    char       *prefix;
+    char       *file;
+    size_t      command_size;
+    size_t      list_len;
+    size_t      prefix_len;
+
+    /* List files using shell. */
+
+    if (shell_type == CHAZ_OS_POSIX) {
+        pattern = "find %s -name '*.%s' -type f";
+    }
+    else if (shell_type == CHAZ_OS_CMD_EXE) {
+        pattern = "dir %s\\*.%s /s /b /a-d";
+    }
+    else {
+        chaz_Util_die("Unknown shell type %d", shell_type);
+    }
+
+    command_size = strlen(pattern) + strlen(dir) + strlen(ext) + 10;
+    command = (char*)malloc(command_size);
+    sprintf(command, pattern, dir, ext);
+    list = chaz_OS_run_and_capture(command, &list_len);
+    free(command);
+    if (!list) {
+        chaz_Util_die("Failed to list files in '%s'", dir);
+    }
+    list[list_len-1] = 0;
+
+    /* Find directory prefix to strip from files */
+
+    if (shell_type == CHAZ_OS_POSIX) {
+        prefix_len = strlen(dir);
+        prefix = (char*)malloc(prefix_len + 2);
+        memcpy(prefix, dir, prefix_len);
+        prefix[prefix_len++] = '/';
+        prefix[prefix_len]   = '\0';
+    }
+    else {
+        char   *output;
+        size_t  output_len;
+
+        /* 'dir /s' returns absolute paths, so we have to find the absolute
+         * path of the directory. This is done by using the variable
+         * substitution feature of the 'for' command.
+         */
+        pattern = "for %%I in (%s) do @echo %%~fI";
+        command_size = strlen(pattern) + strlen(dir) + 10;
+        command = (char*)malloc(command_size);
+        sprintf(command, pattern, dir);
+        output = chaz_OS_run_and_capture(command, &output_len);
+        free(command);
+        if (!output) { chaz_Util_die("Failed to find absolute path"); }
+
+        /* Strip whitespace from end of output. */
+        for (prefix_len = output_len; prefix_len > 0; --prefix_len) {
+            if (!isspace(output[prefix_len-1])) { break; }
+        }
+        prefix = (char*)malloc(prefix_len + 2);
+        memcpy(prefix, output, prefix_len);
+        prefix[prefix_len++] = '\\';
+        prefix[prefix_len]   = '\0';
+        free(output);
+    }
+
+    /* Iterate file list and invoke callback. */
+
+    for (file = strtok(list, "\r\n"); file; file = strtok(NULL, "\r\n")) {
+        if (strlen(file) <= prefix_len
+            || memcmp(file, prefix, prefix_len) != 0
+           ) {
+            chaz_Util_die("Expected prefix '%s' for file name '%s'", prefix,
+                          file);
+        }
+
+        callback(file + prefix_len, context);
+    }
+
+    free(prefix);
+    free(list);
+}
+
+
+/***************************************************************************/
+
 #line 17 "src/Charmonizer/Core/OperatingSystem.c"
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
+#include <ctype.h>
 
 /* #include "Charmonizer/Core/Compiler.h" */
 /* #include "Charmonizer/Core/Util.h" */
 /* #include "Charmonizer/Core/ConfWriter.h" */
 /* #include "Charmonizer/Core/OperatingSystem.h" */
 
+#define CHAZ_OS_TARGET_PATH  "_charmonizer_target"
+#define CHAZ_OS_NAME_MAX     31
+
 static struct {
+    char name[CHAZ_OS_NAME_MAX+1];
     char dev_null[20];
     char exe_ext[5];
     char obj_ext[5];
+    char shared_obj_ext[7];
     char local_command_start[3];
     int  shell_type;
-} chaz_OS = { "", "", "", "", 0 };
-#define CHAZ_OS_POSIX    1
-#define CHAZ_OS_CMD_EXE  2
+} chaz_OS = { "", "", "", "", "", "", 0 };
 
 void
 chaz_OS_init(void) {
@@ -2591,16 +3427,43 @@ chaz_OS_init(void) {
 
     /* Detect shell based on whether the bitbucket is "/dev/null" or "nul". */
     if (chaz_Util_can_open_file("/dev/null")) {
+        char   *uname;
+        size_t  uname_len;
+        size_t i;
+
+        chaz_OS.shell_type = CHAZ_OS_POSIX;
+
+        /* Detect Unix name. */
+        uname = chaz_OS_run_and_capture("uname", &uname_len);
+        for (i = 0; i < CHAZ_OS_NAME_MAX && i < uname_len; i++) {
+            char c = uname[i];
+            if (!c || isspace(c)) { break; }
+            chaz_OS.name[i] = tolower(c);
+        }
+        if (i > 0) { chaz_OS.name[i] = '\0'; }
+        else       { strcpy(chaz_OS.name, "unknown_unix"); }
+        free(uname);
+
         strcpy(chaz_OS.dev_null, "/dev/null");
         strcpy(chaz_OS.exe_ext, "");
         strcpy(chaz_OS.obj_ext, ".o");
+        if (memcmp(chaz_OS.name, "darwin", 6) == 0) {
+            strcpy(chaz_OS.shared_obj_ext, ".dylib");
+        }
+        else if (memcmp(chaz_OS.name, "cygwin", 6) == 0) {
+            strcpy(chaz_OS.shared_obj_ext, ".dll");
+        }
+        else {
+            strcpy(chaz_OS.shared_obj_ext, ".so");
+        }
         strcpy(chaz_OS.local_command_start, "./");
-        chaz_OS.shell_type = CHAZ_OS_POSIX;
     }
     else if (chaz_Util_can_open_file("nul")) {
+        strcpy(chaz_OS.name, "windows");
         strcpy(chaz_OS.dev_null, "nul");
         strcpy(chaz_OS.exe_ext, ".exe");
         strcpy(chaz_OS.obj_ext, ".obj");
+        strcpy(chaz_OS.shared_obj_ext, ".dll");
         strcpy(chaz_OS.local_command_start, ".\\");
         chaz_OS.shell_type = CHAZ_OS_CMD_EXE;
     }
@@ -2611,6 +3474,21 @@ chaz_OS_init(void) {
 }
 
 const char*
+chaz_OS_name(void) {
+    return chaz_OS.name;
+}
+
+int
+chaz_OS_is_darwin(void) {
+    return memcmp(chaz_OS.name, "darwin", 6) == 0;
+}
+
+int
+chaz_OS_is_cygwin(void) {
+    return memcmp(chaz_OS.name, "cygwin", 6) == 0;
+}
+
+const char*
 chaz_OS_exe_ext(void) {
     return chaz_OS.exe_ext;
 }
@@ -2621,11 +3499,21 @@ chaz_OS_obj_ext(void) {
 }
 
 const char*
+chaz_OS_shared_obj_ext(void) {
+    return chaz_OS.shared_obj_ext;
+}
+
+const char*
 chaz_OS_dev_null(void) {
     return chaz_OS.dev_null;
 }
 
 int
+chaz_OS_shell_type(void) {
+    return chaz_OS.shell_type;
+}
+
+int
 chaz_OS_remove(const char *name) {
     /*
      * On Windows it can happen that another process, typically a
@@ -2699,6 +3587,15 @@ chaz_OS_run_redirected(const char *command, const char *path) {
     return retval;
 }
 
+char*
+chaz_OS_run_and_capture(const char *command, size_t *output_len) {
+    char *output;
+    chaz_OS_run_redirected(command, CHAZ_OS_TARGET_PATH);
+    output = chaz_Util_slurp_file(CHAZ_OS_TARGET_PATH, output_len);
+    chaz_Util_remove_and_verify(CHAZ_OS_TARGET_PATH);
+    return output;
+}
+
 void
 chaz_OS_mkdir(const char *filepath) {
     char *command = NULL;
@@ -3001,6 +3898,7 @@ chaz_Probe_init(struct chaz_CLIArgs *args) {
     chaz_CC_init(args->cc, args->cflags);
     chaz_ConfWriter_init();
     chaz_HeadCheck_init();
+    chaz_Make_init();
 
     /* Enable output. */
     if (args->charmony_h) {
@@ -3030,6 +3928,7 @@ chaz_Probe_clean_up(void) {
     /* Dispatch various clean up routines. */
     chaz_ConfWriter_clean_up();
     chaz_CC_clean_up();
+    chaz_Make_clean_up();
 
     if (chaz_Util_verbosity) { printf("Cleanup complete.\n"); }
 }
@@ -3045,8 +3944,8 @@ chaz_Probe_gcc_version(void) {
 }
 
 int
-chaz_Probe_compiler_is_msvc(void) {
-    return chaz_CC_compiler_is_msvc();
+chaz_Probe_msvc_version_num(void) {
+    return chaz_CC_msvc_version_num();
 }
 
 /***************************************************************************/
@@ -4889,17 +5788,6 @@ S_add_compiler_flags(struct chaz_CLIArgs *args) {
         /* Only core source files require this -- not our headers and
          * autogenerated files. */
         chaz_CC_add_extra_cflags("-std=gnu99 -D_GNU_SOURCE");
-    }
-    else if (chaz_Probe_compiler_is_msvc()) {
-        /* Compile as C++ under MSVC. */
-        chaz_CC_add_extra_cflags("/TP");
-
-        /* Thwart stupid warnings. */
-        chaz_CC_add_extra_cflags("-D_CRT_SECURE_NO_WARNINGS");
-        chaz_CC_add_extra_cflags("-D_SCL_SECURE_NO_WARNINGS");
-
-        /* Redefine 'for' to fix broken 'for' scoping under MSVC6. */
-        chaz_CC_add_extra_cflags("-Dfor=\"if(0);else for\"");
 
         /* When compiling for Perl bindings, define HAS_BOOL so that the Perl
          * headers don't redefine 'bool' in conflict with C++.
@@ -4911,6 +5799,23 @@ S_add_compiler_flags(struct chaz_CLIArgs *args) {
             chaz_CC_add_extra_cflags("-DHAS_BOOL");
         }
     }
+    else if (chaz_Probe_msvc_version_num()) {
+        /* Compile as C++ under MSVC. */
+        chaz_CC_add_extra_cflags("/TP");
+
+        /* Thwart stupid warnings. */
+        chaz_CC_add_extra_cflags("/D_CRT_SECURE_NO_WARNINGS");
+        chaz_CC_add_extra_cflags("/D_SCL_SECURE_NO_WARNINGS");
+
+        if (chaz_Probe_msvc_version_num() < 1300) {
+            /* Redefine 'for' to fix broken 'for' scoping under MSVC6. */
+            chaz_CC_add_extra_cflags("/Dfor=\"if(0);else for\"");
+        }
+
+        if (args->charmony_pm) {
+            chaz_CC_add_extra_cflags("/DHAS_BOOL");
+        }
+    }
 }
 
 int main(int argc, const char **argv) {


Mime
View raw message