Updated Branches: refs/heads/c-bindings-wip1 b9b16b0a2 -> fd1e1c40b
Generate man pages from DocuComments Initial implementation in the CFCBind classes. Might be moved to separate files. Project: http://git-wip-us.apache.org/repos/asf/lucy/repo Commit: http://git-wip-us.apache.org/repos/asf/lucy/commit/fd1e1c40 Tree: http://git-wip-us.apache.org/repos/asf/lucy/tree/fd1e1c40 Diff: http://git-wip-us.apache.org/repos/asf/lucy/diff/fd1e1c40 Branch: refs/heads/c-bindings-wip1 Commit: fd1e1c40b72bdbe73f3d8632739957cfc14e3087 Parents: c6ef5a7 Author: Nick Wellnhofer <[email protected]> Authored: Sun Dec 23 16:39:57 2012 +0100 Committer: Nick Wellnhofer <[email protected]> Committed: Sun Dec 23 22:30:57 2012 +0100 ---------------------------------------------------------------------- clownfish/compiler/c/cfc.c | 2 + clownfish/compiler/src/CFCBindClass.c | 291 ++++++++++++++++++++++++++++ clownfish/compiler/src/CFCBindClass.h | 5 + clownfish/compiler/src/CFCBindCore.c | 57 ++++++ clownfish/compiler/src/CFCBindCore.h | 5 + 5 files changed, 360 insertions(+), 0 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucy/blob/fd1e1c40/clownfish/compiler/c/cfc.c ---------------------------------------------------------------------- diff --git a/clownfish/compiler/c/cfc.c b/clownfish/compiler/c/cfc.c index cf27569..dbd551b 100644 --- a/clownfish/compiler/c/cfc.c +++ b/clownfish/compiler/c/cfc.c @@ -186,6 +186,8 @@ main(int argc, char **argv) { CFCBindCore_write_all_modified(core_binding, 0); + CFCBindCore_write_man_pages(core_binding); + CFCBase_decref((CFCBase*)core_binding); CFCBase_decref((CFCBase*)hierarchy); http://git-wip-us.apache.org/repos/asf/lucy/blob/fd1e1c40/clownfish/compiler/src/CFCBindClass.c ---------------------------------------------------------------------- diff --git a/clownfish/compiler/src/CFCBindClass.c b/clownfish/compiler/src/CFCBindClass.c index 2fd6105..3ad6178 100644 --- a/clownfish/compiler/src/CFCBindClass.c +++ b/clownfish/compiler/src/CFCBindClass.c @@ -23,6 +23,7 @@ #include "CFCBindMethod.h" #include "CFCBase.h" #include "CFCClass.h" +#include "CFCDocuComment.h" #include "CFCFunction.h" #include "CFCMethod.h" #include "CFCParamList.h" @@ -77,6 +78,25 @@ S_method_defs(CFCBindClass *self); static char* S_short_names(CFCBindClass *self); +static char* +S_man_create_synopsis(CFCBindClass *self); + +static char* +S_man_create_functions(CFCBindClass *self); + +static char* +S_man_create_methods(CFCBindClass *self); + +static char* +S_man_create_func(CFCClass *client, CFCFunction *func, const char *short_sym, + const char *full_sym); + +static char* +S_man_create_inheritance(CFCBindClass *self); + +static char* +S_man_escape_content(const char *content); + const static CFCMeta CFCBINDCLASS_META = { "Clownfish::CFC::Binding::Core::Class", sizeof(CFCBindClass), @@ -645,3 +665,274 @@ S_short_names(CFCBindClass *self) { return short_names; } +char* +CFCBindClass_create_man_page(CFCBindClass *self) { + CFCClass *client = self->client; + const char *class_name = CFCClass_get_class_name(client); + + CFCDocuComment *docucom = CFCClass_get_docucomment(client); + if (!docucom) { return NULL; } + + // Get the class's brief description. + const char *raw_brief = CFCDocuComment_get_brief(docucom); + char *brief = S_man_escape_content(raw_brief); + + // Get the class's long description. + const char *raw_description = CFCDocuComment_get_long(docucom); + char *description = S_man_escape_content(raw_description); + + // Create SYNOPSIS. + char *synopsis = S_man_create_synopsis(self); + + // Create CONSTRUCTORS. + char *functions_man = S_man_create_functions(self); + + // Create METHODS, possibly including an ABSTRACT METHODS section. + char *methods_man = S_man_create_methods(self); + + // Build an INHERITANCE section describing class ancestry. + char *inheritance = S_man_create_inheritance(self); + + // Put it all together. + const char pattern[] = + ".\\\" Licensed to the Apache Software Foundation (ASF) under one or more\n" + ".\\\" contributor license agreements. See the NOTICE file distributed with\n" + ".\\\" this work for additional information regarding copyright ownership.\n" + ".\\\" The ASF licenses this file to You under the Apache License, Version 2.0\n" + ".\\\" (the \"License\"); you may not use this file except in compliance with\n" + ".\\\" the License. You may obtain a copy of the License at\n" + ".\\\"\n" + ".\\\" http://www.apache.org/licenses/LICENSE-2.0\n" + ".\\\"\n" + ".\\\" Unless required by applicable law or agreed to in writing, software\n" + ".\\\" distributed under the License is distributed on an \"AS IS\" BASIS,\n" + ".\\\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + ".\\\" See the License for the specific language governing permissions and\n" + ".\\\" limitations under the License.\n" + ".TH %s 3\n" + ".SH NAME\n" + "%s - %s\n" + "%s" + ".SH DESCRIPTION\n" + "%s\n" + "%s" + "%s" + "%s"; + + size_t size = sizeof(pattern) + + strlen(class_name) + + strlen(class_name) + + strlen(brief) + + strlen(synopsis) + + strlen(description) + + strlen(functions_man) + + strlen(methods_man) + + strlen(inheritance) + + 20; + + char *pod = (char*)MALLOCATE(size); + sprintf(pod, pattern, class_name, class_name, brief, synopsis, description, + functions_man, methods_man, inheritance); + + FREEMEM(brief); + FREEMEM(synopsis); + FREEMEM(description); + FREEMEM(functions_man); + FREEMEM(methods_man); + FREEMEM(inheritance); + + return pod; +} + +static char* +S_man_create_synopsis(CFCBindClass *self) { + return CFCUtil_strdup(""); +} + +static char* +S_man_create_functions(CFCBindClass *self) { + CFCClass *client = self->client; + CFCFunction **functions = CFCClass_functions(client); + const char *class_name = CFCClass_get_class_name(client); + char *result = CFCUtil_strdup(".SH FUNCTIONS\n"); + + for (int func_num = 0; functions[func_num] != NULL; func_num++) { + CFCFunction *func = functions[func_num]; + if (!CFCFunction_public(func)) { continue; } + + const char *micro_sym = CFCFunction_micro_sym(func); + const char *full_func_sym = CFCFunction_full_func_sym(func); + + char *redman = S_man_create_func(client, func, micro_sym, + full_func_sym); + result = CFCUtil_cat(result, redman, NULL); + FREEMEM(redman); + } + + return result; +} + +static char* +S_man_create_methods(CFCBindClass *self) { + CFCClass *client = self->client; + CFCMethod **fresh_methods = CFCClass_fresh_methods(client); + const char *class_name = CFCClass_get_class_name(client); + char *result = CFCUtil_strdup(".SH METHODS\n"); + + for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { + CFCMethod *method = fresh_methods[meth_num]; + if (!CFCMethod_public(method)) { continue; } + + const char *macro_sym = CFCMethod_get_macro_sym(method); + + size_t sym_len = CFCMethod_full_method_sym(method, NULL, NULL, 0); + char *full_method_sym = (char*)MALLOCATE(sym_len); + CFCMethod_full_method_sym(method, NULL, full_method_sym, sym_len); + + char *method_man = S_man_create_func(client, (CFCFunction*)method, + macro_sym, full_method_sym); + result = CFCUtil_cat(result, method_man, NULL); + + FREEMEM(method_man); + FREEMEM(full_method_sym); + } + + FREEMEM(fresh_methods); + + return result; +} + +static char* +S_man_create_func(CFCClass *client, CFCFunction *func, const char *short_sym, + const char *full_sym) { + CFCType *return_type + = CFCFunction_get_return_type(func); + const char *return_type_c = CFCType_to_c(return_type); + + CFCParamList *param_list + = CFCFunction_get_param_list(func); + const char *param_list_c = CFCParamList_to_c(param_list); + + char *result + = CFCUtil_cat(CFCUtil_strdup(".TP\n.B "), short_sym, "\n.na\n", + return_type_c, "\n.br\n.BR \"", full_sym, "\" \"(", + param_list_c, ");\"\n.ad\n", NULL); + + // Get documentation, which may be inherited. + CFCDocuComment *docucomment = CFCFunction_get_docucomment(func); + if (!docucomment) { + const char *micro_sym = CFCFunction_micro_sym(func); + CFCClass *parent = client; + while (NULL != (parent = CFCClass_get_parent(parent))) { + CFCFunction *parent_func + = (CFCFunction*)CFCClass_method(parent, micro_sym); + if (!parent_func) { break; } + docucomment = CFCFunction_get_docucomment(parent_func); + if (docucomment) { break; } + } + } + + if (docucomment) { + // Description + const char *raw_desc = CFCDocuComment_get_description(docucomment); + char *desc = S_man_escape_content(raw_desc); + result = CFCUtil_cat(result, ".IP\n", desc, "\n", NULL); + FREEMEM(desc); + + // Params + const char **param_names + = CFCDocuComment_get_param_names(docucomment); + const char **param_docs + = CFCDocuComment_get_param_docs(docucomment); + if (param_names[0]) { + result = CFCUtil_cat(result, ".RS\n", NULL); + for (size_t i = 0; param_names[i] != NULL; i++) { + char *doc = S_man_escape_content(param_docs[i]); + result = CFCUtil_cat(result, ".TP\n.I ", param_names[i], + "\n", doc, "\n", NULL); + FREEMEM(doc); + } + result = CFCUtil_cat(result, ".RE\n", NULL); + } + + // Return value + const char *retval_doc = CFCDocuComment_get_retval(docucomment); + if (retval_doc && strlen(retval_doc)) { + char *doc = S_man_escape_content(retval_doc); + result = CFCUtil_cat(result, ".IP\n.B Returns:\n", doc, "\n", + NULL); + FREEMEM(doc); + } + } + + return result; +} + +static char* +S_man_create_inheritance(CFCBindClass *self) { + CFCClass *client = self->client; + CFCClass *ancestor = CFCClass_get_parent(client); + char *result = CFCUtil_strdup(""); + + if (!ancestor) { return result; } + + const char *class_name = CFCClass_get_class_name(client); + result = CFCUtil_cat(result, ".SH INHERITANCE\n", class_name, NULL); + while (ancestor) { + const char *ancestor_name = CFCClass_get_class_name(ancestor); + result = CFCUtil_cat(result, " is a ", ancestor_name, NULL); + ancestor = CFCClass_get_parent(ancestor); + } + result = CFCUtil_cat(result, ".\n", NULL); + + return result; +} + +static char* +S_man_escape_content(const char *content) { + size_t result_len = 0; + size_t result_cap = strlen(content) + 20; + char *result = (char*)MALLOCATE(result_cap + 1); + int fill = 1; + + for (size_t i = 0; content[i]; i++) { + char c[8]; + int num_chars = 1; + + c[0] = content[i]; + + switch (c[0]) { + case '\\': + c[1] = 'e'; + num_chars = 2; + break; + case '-': + c[0] = '\\'; + c[1] = '-'; + num_chars = 2; + break; + case '\n': + if (content[i+1] == '.') { + memcpy(c + num_chars, "\\&", 2); + num_chars += 2; + } + break; + default: + break; + } + + if (result_len + num_chars > result_cap) { + result_cap += 50; + result = (char*)REALLOCATE(result, result_cap + 1); + } + + memcpy(result + result_len, c, num_chars); + result_len += num_chars; + } + + result[result_len] = '\0'; + + return result; +} + + http://git-wip-us.apache.org/repos/asf/lucy/blob/fd1e1c40/clownfish/compiler/src/CFCBindClass.h ---------------------------------------------------------------------- diff --git a/clownfish/compiler/src/CFCBindClass.h b/clownfish/compiler/src/CFCBindClass.h index c84954e..e14c94e 100644 --- a/clownfish/compiler/src/CFCBindClass.h +++ b/clownfish/compiler/src/CFCBindClass.h @@ -59,6 +59,11 @@ CFCBindClass_to_c_data(CFCBindClass *self); char* CFCBindClass_spec_def(CFCBindClass *self); +/** Return the man page for the class. + */ +char* +CFCBindClass_create_man_page(CFCBindClass *self); + #ifdef __cplusplus } #endif http://git-wip-us.apache.org/repos/asf/lucy/blob/fd1e1c40/clownfish/compiler/src/CFCBindCore.c ---------------------------------------------------------------------- diff --git a/clownfish/compiler/src/CFCBindCore.c b/clownfish/compiler/src/CFCBindCore.c index 998dada..84c6165 100644 --- a/clownfish/compiler/src/CFCBindCore.c +++ b/clownfish/compiler/src/CFCBindCore.c @@ -436,3 +436,60 @@ S_write_parcel_c(CFCBindCore *self) { FREEMEM(vt_specs); } +void +CFCBindCore_write_man_pages(CFCBindCore *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; } + + CFCBindClass *class_binding = CFCBindClass_new(klass); + char *man_page = CFCBindClass_create_man_page(class_binding); + man_pages[j++] = man_page; + CFCBase_decref((CFCBase*)class_binding); + } + + const char *dest = CFCHierarchy_get_dest(hierarchy); + char *man3_path + = CFCUtil_cat(CFCUtil_strdup(dest), CFCUTIL_PATH_SEP, "man", + CFCUTIL_PATH_SEP, "man3", NULL); + 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 *man_page = man_pages[j++]; + if (!man_page) { continue; } + + const char *class_name = CFCClass_get_class_name(klass); + char *filename + = CFCUtil_cat(CFCUtil_strdup(man3_path), CFCUTIL_PATH_SEP, + class_name, ".3", NULL); + CFCUtil_write_if_changed(filename, man_page, strlen(man_page)); + FREEMEM(filename); + FREEMEM(man_page); + } + + FREEMEM(man3_path); +} + + http://git-wip-us.apache.org/repos/asf/lucy/blob/fd1e1c40/clownfish/compiler/src/CFCBindCore.h ---------------------------------------------------------------------- diff --git a/clownfish/compiler/src/CFCBindCore.h b/clownfish/compiler/src/CFCBindCore.h index 2b667f7..963c74e 100644 --- a/clownfish/compiler/src/CFCBindCore.h +++ b/clownfish/compiler/src/CFCBindCore.h @@ -57,6 +57,11 @@ CFCBindCore_destroy(CFCBindCore *self); int CFCBindCore_write_all_modified(CFCBindCore *self, int modified); +/** Write all man pages. + */ +void +CFCBindCore_write_man_pages(CFCBindCore *self); + #ifdef __cplusplus } #endif
