Beispiel #1
0
void
CFCC_write_man_pages(CFCC *self) {
    CFCHierarchy  *hierarchy = self->hierarchy;
    CFCClass     **ordered   = CFCHierarchy_ordered_classes(hierarchy);

    size_t num_classes = 0;
    for (size_t i = 0; ordered[i] != NULL; i++) {
        CFCClass *klass = ordered[i];
        if (!CFCClass_included(klass)) { ++num_classes; }
    }
    char **man_pages = (char**)CALLOCATE(num_classes, sizeof(char*));

    // Generate man pages, but don't write.  That way, if there's an error
    // while generating the pages, we leak memory but don't clutter up the file 
    // system.
    for (size_t i = 0, j = 0; ordered[i] != NULL; i++) {
        CFCClass *klass = ordered[i];
        if (CFCClass_included(klass)) { continue; }

        char *man_page = CFCCMan_create_man_page(klass);
        man_pages[j++] = man_page;
    }

    const char *dest = CFCHierarchy_get_dest(hierarchy);
    char *man3_path
        = CFCUtil_sprintf("%s" CHY_DIR_SEP "man" CHY_DIR_SEP "man3", dest);
    if (!CFCUtil_is_dir(man3_path)) {
        CFCUtil_make_path(man3_path);
        if (!CFCUtil_is_dir(man3_path)) {
            CFCUtil_die("Can't make path %s", man3_path);
        }
    }

    // Write out any man pages that have changed.
    for (size_t i = 0, j = 0; ordered[i] != NULL; i++) {
        CFCClass *klass = ordered[i];
        if (CFCClass_included(klass)) { continue; }

        char *raw_man_page = man_pages[j++];
        if (!raw_man_page) { continue; }
        char *man_page = CFCUtil_sprintf("%s%s%s", self->man_header,
                                         raw_man_page, self->man_footer);

        const char *full_struct_sym = CFCClass_full_struct_sym(klass);
        char *filename = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s.3", man3_path,
                                         full_struct_sym);
        CFCUtil_write_if_changed(filename, man_page, strlen(man_page));
        FREEMEM(filename);
        FREEMEM(man_page);
        FREEMEM(raw_man_page);
    }

    FREEMEM(man3_path);
    FREEMEM(man_pages);
    FREEMEM(ordered);
}
static void
S_run_basic_tests(CFCTest *test) {
    CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST);
    STR_EQ(test, CFCHierarchy_get_dest(hierarchy), T_CFDEST, "get_dest");
    STR_EQ(test, CFCHierarchy_get_include_dest(hierarchy), T_CFDEST_INCLUDE,
           "get_include_dest");
    STR_EQ(test, CFCHierarchy_get_source_dest(hierarchy), T_CFDEST_SOURCE,
           "get_source_dest");

    CFCHierarchy_add_source_dir(hierarchy, T_CFBASE);
    const char **source_dirs = CFCHierarchy_get_source_dirs(hierarchy);
    STR_EQ(test, source_dirs[0], T_CFBASE, "source_dirs[0]");
    OK(test, source_dirs[1] == NULL, "source_dirs[1]");

    CFCHierarchy_build(hierarchy);

    CFCFile **files  = CFCHierarchy_files(hierarchy);
    CFCFile  *animal = NULL;
    CFCFile  *dog    = NULL;
    CFCFile  *util   = NULL;
    for (int i = 0; i < 3; ++i) {
        CFCFile *file = files[i];
        OK(test, file != NULL, "files[%d]", i);
        OK(test, !CFCFile_get_modified(file), "start off not modified");

        CFCBase **blocks = CFCFile_blocks(file);
        for (int j = 0; blocks[j]; ++j) {
            CFCBase *block = blocks[j];
            const char *cfc_class_name = CFCBase_get_cfc_class(block);
            if (strcmp(cfc_class_name, "Clownfish::CFC::Model::Class") == 0) {
                CFCClass *klass = (CFCClass*)block;
                const char *class_name = CFCClass_get_name(klass);
                if (strcmp(class_name, "Animal") == 0) {
                    animal = file;
                }
                else if (strcmp(class_name, "Animal::Dog") == 0) {
                    dog = file;
                }
                else if (strcmp(class_name, "Animal::Util") == 0) {
                    util = file;
                }
            }
        }
    }
    OK(test, files[3] == NULL, "recursed and found all three files");

    {
        CFCClass **ordered_classes = CFCHierarchy_ordered_classes(hierarchy);
        OK(test, ordered_classes[0] != NULL, "ordered_classes[0]");
        OK(test, ordered_classes[1] != NULL, "ordered_classes[1]");
        OK(test, ordered_classes[2] != NULL, "ordered_classes[2]");
        OK(test, ordered_classes[3] != NULL, "ordered_classes[3]");
        OK(test, ordered_classes[4] == NULL, "all classes");
        FREEMEM(ordered_classes);
    }

    // Generate fake C files, with times set to two seconds ago.
    time_t now       = time(NULL);
    time_t past_time = now - 2;
    static const char *const h_paths[] = {
        T_CFDEST_INCLUDE CHY_DIR_SEP "Animal.h",
        T_CFDEST_INCLUDE CHY_DIR_SEP "Animal" CHY_DIR_SEP "Dog.h",
        T_CFDEST_INCLUDE CHY_DIR_SEP "Animal" CHY_DIR_SEP "Util.h"
    };
    OK(test, CFCUtil_make_path(T_CFDEST_INCLUDE CHY_DIR_SEP "Animal"),
       "make_path");
    for (int i = 0; i < 3; ++i) {
        const char *h_path  = h_paths[i];
        const char *content = "#include <stdio.h>\n";
        CFCUtil_write_file(h_path, content, strlen(content));
        CFCTest_set_file_times(h_path, past_time);
    }

    char *cfh_path = CFCFile_cfh_path(animal, T_CFBASE);
    CFCTest_set_file_times(cfh_path, now);
    FREEMEM(cfh_path);

    CFCHierarchy_propagate_modified(hierarchy, 0);

    OK(test, CFCFile_get_modified(animal), "Animal modified");
    OK(test, CFCFile_get_modified(dog),
       "Parent's modification propagates to child's file");
    OK(test, !CFCFile_get_modified(util),
       "Modification doesn't propagate to inert class");

    for (int i = 0; i < 3; ++i) {
        remove(h_paths[i]);
    }
    rmdir(T_CFDEST_INCLUDE CHY_DIR_SEP "Animal");
    rmdir(T_CFDEST_INCLUDE);
    rmdir(T_CFDEST_SOURCE);
    rmdir(T_CFDEST);

    CFCBase_decref((CFCBase*)hierarchy);
    CFCClass_clear_registry();
    CFCParcel_reap_singletons();
}
Beispiel #3
0
void
CFCBindFile_write_h(CFCFile *file, const char *dest, const char *header,
                    const char *footer) {
    CFCUTIL_NULL_CHECK(file);
    CFCUTIL_NULL_CHECK(dest);
    CFCUTIL_NULL_CHECK(header);
    CFCUTIL_NULL_CHECK(footer);

    // Make directories.
    char *h_path = CFCFile_h_path(file, dest);
    char *h_dir  = CFCUtil_strdup(h_path);
    for (size_t len = strlen(h_dir); len--;) {
        if (h_dir[len] == CHY_DIR_SEP_CHAR) {
            h_dir[len] = 0;
            break;
        }
    }
    if (!CFCUtil_is_dir(h_dir)) {
        CFCUtil_make_path(h_dir);
        if (!CFCUtil_is_dir(h_dir)) {
            CFCUtil_die("Can't make path %s", h_dir);
        }
    }
    FREEMEM(h_dir);

    // Create the include-guard strings.
    const char *include_guard_start = CFCFile_guard_start(file);
    const char *include_guard_close = CFCFile_guard_close(file);

    // Aggregate block content.
    char *content = CFCUtil_strdup("");
    CFCBase **blocks = CFCFile_blocks(file);
    for (int i = 0; blocks[i] != NULL; i++) {
        const char *cfc_class = CFCBase_get_cfc_class(blocks[i]);

        if (strcmp(cfc_class, "Clownfish::CFC::Model::Parcel") == 0) {
            CFCParcel *parcel = (CFCParcel*)blocks[i];
            const char *prefix = CFCParcel_get_prefix(parcel);
            content = CFCUtil_cat(content, "#include \"", prefix,
                                  "parcel.h\"\n\n", NULL);
        }
        else if (strcmp(cfc_class, "Clownfish::CFC::Model::Class") == 0) {
            CFCBindClass *class_binding
                = CFCBindClass_new((CFCClass*)blocks[i]);
            char *c_header = CFCBindClass_to_c_header(class_binding);
            content = CFCUtil_cat(content, c_header, "\n", NULL);
            FREEMEM(c_header);
            CFCBase_decref((CFCBase*)class_binding);
        }
        else if (strcmp(cfc_class, "Clownfish::CFC::Model::CBlock") == 0) {
            const char *block_contents 
                = CFCCBlock_get_contents((CFCCBlock*)blocks[i]);
            content = CFCUtil_cat(content, block_contents, "\n", NULL);
        }
        else {
            CFCUtil_die("Unexpected class: %s", cfc_class);
        }
    }

    char pattern[] =
        "%s\n"
        "\n"
        "%s\n"
        "\n"
        "#ifdef __cplusplus\n"
        "extern \"C\" {\n"
        "#endif\n"
        "\n"
        "%s\n"
        "\n"
        "#ifdef __cplusplus\n"
        "}\n"
        "#endif\n"
        "\n"
        "%s\n"
        "\n"
        "%s\n"
        "\n";
    char *file_content
        = CFCUtil_sprintf(pattern, header, include_guard_start, content,
                          include_guard_close, footer);

    // Unlink then write file.
    remove(h_path);
    CFCUtil_write_file(h_path, file_content, strlen(file_content));

    FREEMEM(content);
    FREEMEM(file_content);
    FREEMEM(h_path);
}