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/06 23:08:26 UTC

[lucy-commits] [3/4] git commit: refs/heads/c-bindings-cfc - Initial version of cfc command line tool

Initial version of cfc command line tool


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

Branch: refs/heads/c-bindings-cfc
Commit: e65019ccb4fbf0e55c18623aed10a13891ea2224
Parents: f5bc246
Author: Nick Wellnhofer <we...@aevum.de>
Authored: Thu Nov 22 23:17:21 2012 +0100
Committer: Nick Wellnhofer <we...@aevum.de>
Committed: Wed Feb 6 23:03:54 2013 +0100

----------------------------------------------------------------------
 clownfish/compiler/c/.gitignore                    |    5 +
 clownfish/compiler/c/cfc.c                         |  202 +++++++++++++++
 clownfish/compiler/c/configure                     |   47 ++++
 clownfish/compiler/c/t/cfext/Animal/Rottweiler.cfh |   31 +++
 clownfish/compiler/c/t/cfsource/Animal.cfh         |   19 ++
 clownfish/compiler/c/t/cfsource/Animal.cfp         |    4 +
 clownfish/compiler/c/t/cfsource/Animal/Dog.cfh     |   28 ++
 clownfish/compiler/c/t/cfsource/Animal/Util.cfh    |   23 ++
 clownfish/compiler/c/t/lcov.sh                     |    9 +
 clownfish/compiler/c/t/test_cfc.c                  |   31 +++
 clownfish/compiler/common/charmonizer.main         |  148 +++++++++++
 11 files changed, 547 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy/blob/e65019cc/clownfish/compiler/c/.gitignore
----------------------------------------------------------------------
diff --git a/clownfish/compiler/c/.gitignore b/clownfish/compiler/c/.gitignore
new file mode 100644
index 0000000..b5fef9b
--- /dev/null
+++ b/clownfish/compiler/c/.gitignore
@@ -0,0 +1,5 @@
+/Makefile
+/cfc
+/charmonizer
+/charmony.h
+/t/test_cfc

http://git-wip-us.apache.org/repos/asf/lucy/blob/e65019cc/clownfish/compiler/c/cfc.c
----------------------------------------------------------------------
diff --git a/clownfish/compiler/c/cfc.c b/clownfish/compiler/c/cfc.c
new file mode 100644
index 0000000..9fa27f8
--- /dev/null
+++ b/clownfish/compiler/c/cfc.c
@@ -0,0 +1,202 @@
+/* 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 "CFCBase.h"
+#include "CFCBindCore.h"
+#include "CFCC.h"
+#include "CFCHierarchy.h"
+#include "CFCUtil.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct CFCArgs {
+    char  *dest;
+    int    num_source_dirs;
+    char **source_dirs;
+    int    num_include_dirs;
+    char **include_dirs;
+    char  *header_filename;
+    char  *footer_filename;
+};
+typedef struct CFCArgs CFCArgs;
+
+static int
+S_parse_string_argument(const char *arg, const char *name, char **result) {
+    size_t arg_len  = strlen(arg);
+    size_t name_len = strlen(name);
+
+    if (arg_len < name_len
+        || memcmp(arg, name, name_len) != 0
+        || arg[name_len] != '='
+       ) {
+        return 0;
+    }
+
+    if (*result != NULL) {
+        fprintf(stderr, "Duplicate %s argument\n", name);
+        exit(EXIT_FAILURE);
+    }
+    *result = CFCUtil_strdup(arg + name_len + 1);
+
+    return 1;
+}
+
+static int
+S_parse_string_array_argument(const char *arg, const char *name,
+                              int *num_results, char ***results) {
+    size_t   arg_len  = strlen(arg);
+    size_t   name_len = strlen(name);
+    int      new_num_results;
+    char   **new_results;
+
+    if (arg_len < name_len
+        || memcmp(arg, name, name_len) != 0
+        || arg[name_len] != '='
+       ) {
+        return 0;
+    }
+
+    new_num_results = *num_results + 1;
+    new_results     = (char **)REALLOCATE(*results,
+                              (new_num_results + 1) * sizeof(char *));
+    new_results[new_num_results-1] = CFCUtil_strdup(arg + name_len + 1);
+    new_results[new_num_results]   = NULL;
+    *num_results = new_num_results;
+    *results     = new_results;
+
+    return 1;
+}
+
+/* Parse command line arguments. */
+static void
+S_parse_arguments(int argc, char **argv, CFCArgs *args) {
+    int i;
+
+    memset(args, 0, sizeof(CFCArgs));
+    args->source_dirs     = (char **)MALLOCATE(sizeof(char *));
+    args->source_dirs[0]  = NULL;
+    args->include_dirs    = (char **)MALLOCATE(sizeof(char *));
+    args->include_dirs[0] = NULL;
+
+    for (i = 1; i < argc; i++) {
+        char *arg = argv[i];
+
+        if (S_parse_string_argument(arg, "--dest", &args->dest)) {
+            continue;
+        }
+        if (S_parse_string_argument(arg, "--header", &args->header_filename)) {
+            continue;
+        }
+        if (S_parse_string_argument(arg, "--footer", &args->footer_filename)) {
+            continue;
+        }
+        if (S_parse_string_array_argument(arg, "--source",
+                                          &args->num_source_dirs,
+                                          &args->source_dirs)
+           ) {
+            continue;
+        }
+        if (S_parse_string_array_argument(arg, "--include",
+                                          &args->num_include_dirs,
+                                          &args->include_dirs)
+           ) {
+            continue;
+        }
+
+        fprintf(stderr, "Invalid argument '%s'\n", arg);
+        exit(EXIT_FAILURE);
+    }
+
+    if (!args->dest) {
+        fprintf(stderr, "Mandatory argument --dest missing\n");
+        exit(EXIT_FAILURE);
+    }
+}
+
+static void S_free_arguments(CFCArgs *args) {
+    int i;
+
+    if (args->dest)            { FREEMEM(args->dest); }
+    if (args->header_filename) { FREEMEM(args->header_filename); }
+    if (args->footer_filename) { FREEMEM(args->footer_filename); }
+
+    for (i = 0; args->source_dirs[i]; ++i) {
+        FREEMEM(args->source_dirs[i]);
+    }
+    FREEMEM(args->source_dirs);
+
+    for (i = 0; args->include_dirs[i]; ++i) {
+        FREEMEM(args->include_dirs[i]);
+    }
+    FREEMEM(args->include_dirs);
+}
+
+int
+main(int argc, char **argv) {
+    int           i;
+    size_t        file_len;
+    CFCArgs       args;
+    CFCHierarchy *hierarchy;
+    CFCBindCore  *core_binding;
+    CFCC         *c_binding;
+    char         *header = NULL;
+    char         *footer = NULL;
+
+    S_parse_arguments(argc, argv, &args);
+
+    hierarchy = CFCHierarchy_new(args.dest);
+
+    for (i = 0; args.source_dirs[i]; ++i) {
+        CFCHierarchy_add_source_dir(hierarchy, args.source_dirs[i]);
+    }
+    for (i = 0; args.include_dirs[i]; ++i) {
+        CFCHierarchy_add_source_dir(hierarchy, args.include_dirs[i]);
+    }
+
+    CFCHierarchy_build(hierarchy);
+
+    if (args.header_filename) {
+        header = CFCUtil_slurp_text(args.header_filename, &file_len);
+    }
+    else {
+        header = CFCUtil_strdup("");
+    }
+    if (args.footer_filename) {
+        footer = CFCUtil_slurp_text(args.footer_filename, &file_len);
+    }
+    else {
+        footer = CFCUtil_strdup("");
+    }
+
+    core_binding = CFCBindCore_new(hierarchy, header, footer);
+    CFCBindCore_write_all_modified(core_binding, 0);
+
+    c_binding = CFCC_new(hierarchy, header, footer);
+    CFCC_write_callbacks(c_binding);
+
+    CFCBase_decref((CFCBase*)c_binding);
+    CFCBase_decref((CFCBase*)core_binding);
+    CFCBase_decref((CFCBase*)hierarchy);
+    FREEMEM(header);
+    FREEMEM(footer);
+
+    S_free_arguments(&args);
+
+    return EXIT_SUCCESS;
+}
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/e65019cc/clownfish/compiler/c/configure
----------------------------------------------------------------------
diff --git a/clownfish/compiler/c/configure b/clownfish/compiler/c/configure
new file mode 100755
index 0000000..c8618ce
--- /dev/null
+++ b/clownfish/compiler/c/configure
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+# 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.
+
+probe_clang() { clang -v; }
+probe_gcc()   { gcc -v; }
+
+if [ -z "$CC" ]; then
+    case $(uname) in
+        Darwin*) compilers="clang gcc";;
+        *)       compilers="gcc clang";;
+    esac
+
+    for compiler in $compilers; do
+        if probe_$compiler >/dev/null 2>&1; then
+            CC=$compiler
+            break
+        fi
+    done
+
+    if [ -z "$CC" ]; then
+        CC=cc
+    fi
+fi
+
+echo "Using C compiler '$CC'"
+
+command="$CC ../common/charmonizer.c -o charmonizer"
+echo $command
+$command || exit
+
+echo Running charmonizer
+./charmonizer --cc=$CC --enable-c --enable-makefile $@
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/e65019cc/clownfish/compiler/c/t/cfext/Animal/Rottweiler.cfh
----------------------------------------------------------------------
diff --git a/clownfish/compiler/c/t/cfext/Animal/Rottweiler.cfh b/clownfish/compiler/c/t/cfext/Animal/Rottweiler.cfh
new file mode 100644
index 0000000..8de5e24
--- /dev/null
+++ b/clownfish/compiler/c/t/cfext/Animal/Rottweiler.cfh
@@ -0,0 +1,31 @@
+/* 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.
+ */
+
+parcel Animal;
+
+class Animal::Rottweiler inherits Animal::Dog {
+    public inert incremented Rottweiler*
+    new();
+
+    public inert Rottweiler*
+    init(Rottweiler *self);
+
+    public void
+    Bark(Rottweiler *self);
+
+    public void
+    Bite(Rottweiler *self);
+}

http://git-wip-us.apache.org/repos/asf/lucy/blob/e65019cc/clownfish/compiler/c/t/cfsource/Animal.cfh
----------------------------------------------------------------------
diff --git a/clownfish/compiler/c/t/cfsource/Animal.cfh b/clownfish/compiler/c/t/cfsource/Animal.cfh
new file mode 100644
index 0000000..a4d689a
--- /dev/null
+++ b/clownfish/compiler/c/t/cfsource/Animal.cfh
@@ -0,0 +1,19 @@
+/* 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.
+ */
+
+parcel Animal;
+
+abstract class Animal { }

http://git-wip-us.apache.org/repos/asf/lucy/blob/e65019cc/clownfish/compiler/c/t/cfsource/Animal.cfp
----------------------------------------------------------------------
diff --git a/clownfish/compiler/c/t/cfsource/Animal.cfp b/clownfish/compiler/c/t/cfsource/Animal.cfp
new file mode 100644
index 0000000..e2b5ab5
--- /dev/null
+++ b/clownfish/compiler/c/t/cfsource/Animal.cfp
@@ -0,0 +1,4 @@
+{
+    "name": "Animal",
+    "version": "v0.1.0"
+}

http://git-wip-us.apache.org/repos/asf/lucy/blob/e65019cc/clownfish/compiler/c/t/cfsource/Animal/Dog.cfh
----------------------------------------------------------------------
diff --git a/clownfish/compiler/c/t/cfsource/Animal/Dog.cfh b/clownfish/compiler/c/t/cfsource/Animal/Dog.cfh
new file mode 100644
index 0000000..6d54baa
--- /dev/null
+++ b/clownfish/compiler/c/t/cfsource/Animal/Dog.cfh
@@ -0,0 +1,28 @@
+/* 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.
+ */
+
+parcel Animal;
+
+class Animal::Dog inherits Animal {
+    public inert incremented Dog*
+    new();
+
+    public inert Dog*
+    init(Dog *self);
+
+    public void
+    Bark(Dog *self);
+}

http://git-wip-us.apache.org/repos/asf/lucy/blob/e65019cc/clownfish/compiler/c/t/cfsource/Animal/Util.cfh
----------------------------------------------------------------------
diff --git a/clownfish/compiler/c/t/cfsource/Animal/Util.cfh b/clownfish/compiler/c/t/cfsource/Animal/Util.cfh
new file mode 100644
index 0000000..f5688a5
--- /dev/null
+++ b/clownfish/compiler/c/t/cfsource/Animal/Util.cfh
@@ -0,0 +1,23 @@
+/* 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.
+ */
+
+parcel Animal;
+
+inert class Animal::Util {
+    inert void
+    groom(Animal *animal);
+}
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/e65019cc/clownfish/compiler/c/t/lcov.sh
----------------------------------------------------------------------
diff --git a/clownfish/compiler/c/t/lcov.sh b/clownfish/compiler/c/t/lcov.sh
new file mode 100755
index 0000000..3411f55
--- /dev/null
+++ b/clownfish/compiler/c/t/lcov.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+set -ex
+
+lcov --zerocounters --directory ..
+t/test_cfc
+lcov --capture --directory .. --rc lcov_branch_coverage=1 --output-file cfc.info
+genhtml --branch-coverage --output-directory coverage cfc.info
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/e65019cc/clownfish/compiler/c/t/test_cfc.c
----------------------------------------------------------------------
diff --git a/clownfish/compiler/c/t/test_cfc.c b/clownfish/compiler/c/t/test_cfc.c
new file mode 100644
index 0000000..66fa90a
--- /dev/null
+++ b/clownfish/compiler/c/t/test_cfc.c
@@ -0,0 +1,31 @@
+/* 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 <stdlib.h>
+
+#include "CFCBase.h"
+#include "CFCTest.h"
+
+int
+main() {
+    CFCTest *test = CFCTest_new("clownfish");
+    CFCTest_run_all(test);
+    int pass = CFCTest_finish(test);
+    CFCBase_decref((CFCBase*)test);
+
+    return pass ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/e65019cc/clownfish/compiler/common/charmonizer.main
----------------------------------------------------------------------
diff --git a/clownfish/compiler/common/charmonizer.main b/clownfish/compiler/common/charmonizer.main
index d083f57..702312d 100644
--- a/clownfish/compiler/common/charmonizer.main
+++ b/clownfish/compiler/common/charmonizer.main
@@ -23,6 +23,13 @@
 #include "Charmonizer/Probe.h"
 #include "Charmonizer/Probe/Integers.h"
 
+#define DIR_SEP "/"
+
+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()) {
@@ -52,6 +59,137 @@ S_add_compiler_flags(struct chaz_CLIArgs *args) {
     }
 }
 
+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 */
+
+    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, "LEMON_EXE",
+                          "$(LEMON_DIR)" DIR_SEP "lemon$(EXE_EXT)");
+
+    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_MakeVar_append(var, "-I.");
+    chaz_MakeVar_append(var, "-I$(INCLUDE_DIR)");
+    chaz_MakeVar_append(var, "-I$(SRC_DIR)");
+
+    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, "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)");
+
+    rule = chaz_MakeFile_add_rule(makefile, "$(LEMON_EXE)", NULL);
+    chaz_MakeRule_add_command_make(rule, "$(LEMON_DIR)", NULL);
+
+    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, "$(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) {
     /* Initialize. */
     {
@@ -71,6 +209,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();