You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucy.apache.org by nw...@apache.org on 2013/02/14 19:54:19 UTC

[lucy-commits] [4/7] git commit: refs/heads/master - Charmonizer module to generate Makefiles

Charmonizer module to generate Makefiles

Add code to detect the make 'utility' on the current system.

Add various functions to create Makefiles with variables and rules. Also
implement chaz_Make_list_files which recursively finds all files in a
directory with a given extension. It executes the shell command 'find' on
POSIX systems and 'dir' on Windows.


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

Branch: refs/heads/master
Commit: 214274312ca0a51a9c378a5b2725e5d49336f217
Parents: 97f3ac9
Author: Nick Wellnhofer <we...@aevum.de>
Authored: Thu Feb 14 19:18:15 2013 +0100
Committer: Nick Wellnhofer <we...@aevum.de>
Committed: Thu Feb 14 19:47:13 2013 +0100

----------------------------------------------------------------------
 charmonizer/buildbin/meld.pl                       |    1 +
 charmonizer/src/Charmonizer/Core/Compiler.c        |   67 ++
 charmonizer/src/Charmonizer/Core/Compiler.h        |   22 +
 charmonizer/src/Charmonizer/Core/Make.c            |  570 +++++++++++++++
 charmonizer/src/Charmonizer/Core/Make.h            |  176 +++++
 charmonizer/src/Charmonizer/Core/OperatingSystem.c |   73 ++-
 charmonizer/src/Charmonizer/Core/OperatingSystem.h |   29 +
 charmonizer/src/Charmonizer/Probe.c                |    2 +
 8 files changed, 936 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy/blob/21427431/charmonizer/buildbin/meld.pl
----------------------------------------------------------------------
diff --git a/charmonizer/buildbin/meld.pl b/charmonizer/buildbin/meld.pl
index 7df3260..71f177d 100755
--- a/charmonizer/buildbin/meld.pl
+++ b/charmonizer/buildbin/meld.pl
@@ -73,6 +73,7 @@ my @core = qw(
     ConfWriterPerl
     ConfWriterRuby
     HeaderChecker
+    Make
     OperatingSystem
     Util
 );

http://git-wip-us.apache.org/repos/asf/lucy/blob/21427431/charmonizer/src/Charmonizer/Core/Compiler.c
----------------------------------------------------------------------
diff --git a/charmonizer/src/Charmonizer/Core/Compiler.c b/charmonizer/src/Charmonizer/Core/Compiler.c
index 5136289..13bb0cd 100644
--- a/charmonizer/src/Charmonizer/Core/Compiler.c
+++ b/charmonizer/src/Charmonizer/Core/Compiler.c
@@ -342,6 +342,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;
@@ -374,3 +402,42 @@ 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 ";
+    }
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/21427431/charmonizer/src/Charmonizer/Core/Compiler.h
----------------------------------------------------------------------
diff --git a/charmonizer/src/Charmonizer/Core/Compiler.h b/charmonizer/src/Charmonizer/Core/Compiler.h
index 7504d40..69469e6 100644
--- a/charmonizer/src/Charmonizer/Core/Compiler.h
+++ b/charmonizer/src/Charmonizer/Core/Compiler.h
@@ -74,6 +74,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*
@@ -98,6 +108,18 @@ chaz_CC_gcc_version(void);
 int
 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);
+
 #ifdef __cplusplus
 }
 #endif

http://git-wip-us.apache.org/repos/asf/lucy/blob/21427431/charmonizer/src/Charmonizer/Core/Make.c
----------------------------------------------------------------------
diff --git a/charmonizer/src/Charmonizer/Core/Make.c b/charmonizer/src/Charmonizer/Core/Make.c
new file mode 100644
index 0000000..3470af1
--- /dev/null
+++ b/charmonizer/src/Charmonizer/Core/Make.c
@@ -0,0 +1,570 @@
+/* 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 <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);
+}
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/21427431/charmonizer/src/Charmonizer/Core/Make.h
----------------------------------------------------------------------
diff --git a/charmonizer/src/Charmonizer/Core/Make.h b/charmonizer/src/Charmonizer/Core/Make.h
new file mode 100644
index 0000000..430ae33
--- /dev/null
+++ b/charmonizer/src/Charmonizer/Core/Make.h
@@ -0,0 +1,176 @@
+/* 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/Compiler.h
+ */
+
+#ifndef H_CHAZ_MAKE
+#define H_CHAZ_MAKE
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_CHAZ_MAKE */
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/21427431/charmonizer/src/Charmonizer/Core/OperatingSystem.c
----------------------------------------------------------------------
diff --git a/charmonizer/src/Charmonizer/Core/OperatingSystem.c b/charmonizer/src/Charmonizer/Core/OperatingSystem.c
index 2593e2a..3a9975f 100644
--- a/charmonizer/src/Charmonizer/Core/OperatingSystem.c
+++ b/charmonizer/src/Charmonizer/Core/OperatingSystem.c
@@ -17,21 +17,25 @@
 #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) {
@@ -45,16 +49,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;
     }
@@ -65,6 +96,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;
 }
@@ -75,11 +121,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
@@ -153,6 +209,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;

http://git-wip-us.apache.org/repos/asf/lucy/blob/21427431/charmonizer/src/Charmonizer/Core/OperatingSystem.h
----------------------------------------------------------------------
diff --git a/charmonizer/src/Charmonizer/Core/OperatingSystem.h b/charmonizer/src/Charmonizer/Core/OperatingSystem.h
index fe936d6..490c1a0 100644
--- a/charmonizer/src/Charmonizer/Core/OperatingSystem.h
+++ b/charmonizer/src/Charmonizer/Core/OperatingSystem.h
@@ -25,6 +25,9 @@
 extern "C" {
 #endif
 
+#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.
  */
@@ -50,6 +53,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
@@ -60,6 +68,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*
@@ -70,11 +89,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

http://git-wip-us.apache.org/repos/asf/lucy/blob/21427431/charmonizer/src/Charmonizer/Probe.c
----------------------------------------------------------------------
diff --git a/charmonizer/src/Charmonizer/Probe.c b/charmonizer/src/Charmonizer/Probe.c
index c072e7e..c1475db 100644
--- a/charmonizer/src/Charmonizer/Probe.c
+++ b/charmonizer/src/Charmonizer/Probe.c
@@ -118,6 +118,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) {
@@ -147,6 +148,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"); }
 }