-OBJS := tools/ccanlint/has_info.o \
- tools/ccanlint/has_main_header.o \
- tools/ccanlint/has_tests.o \
- tools/ccanlint/trailing_whitespace.o \
- tools/ccanlint/idempotent.o \
- tools/ccanlint/has_info_documentation.o \
+TEST_CFILES := $(wildcard tools/ccanlint/compulsory_tests/*.c) \
+ $(wildcard tools/ccanlint/tests/*.c)
+TEST_OBJS := $(TEST_CFILES:.c=.o)
-FUTURE:=tools/ccanlint/if_have_not_ifdef.o \
- tools/ccanlint/needs_depends.o \
- tools/ccanlint/has_info_documentation.o \
- tools/ccanlint/has_header_documentation.o \
- tools/ccanlint/has_tests.o \
- tools/ccanlint/builds_ok.o \
- tools/ccanlint/builds_ok_all_have_variants.o \
- tools/ccanlint/run_tests.o \
- tools/ccanlint/test_coverage.o \
+CORE_OBJS := tools/ccanlint/ccanlint.o \
+ tools/ccanlint/file_analysis.o \
+ tools/doc_extract-core.o \
+ ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o \
+ ccan/talloc/talloc.o ccan/noerr/noerr.o
+
+OBJS := $(CORE_OBJS) $(TEST_OBJS)
tools/ccanlint/generated-init-tests: $(OBJS)
cat $(OBJS:.o=.c) | sed -n 's/^struct ccanlint \([A-Za-z0-9_]*\) = {/{ extern struct ccanlint \1; list_add(\&tests, \&\1.list); }/p' >$@
tools/ccanlint/ccanlint.o: tools/ccanlint/generated-init-tests
-tools/ccanlint/ccanlint: \
- $(OBJS) \
- tools/ccanlint/ccanlint.o \
- tools/ccanlint/file_analysis.o \
- tools/doc_extract-core.o \
- ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o \
- ccan/talloc/talloc.o ccan/noerr/noerr.o
+tools/ccanlint/ccanlint: $(OBJS)
ccanlint-clean:
$(RM) tools/ccanlint/generated-init-tests
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ccan/noerr/noerr.h>
+
+static void *check_has_info(struct manifest *m)
+{
+ if (m->info_file)
+ return NULL;
+ return m;
+}
+
+static const char *describe_has_info(struct manifest *m, void *check_result)
+{
+ return "You have no _info.c file.\n\n"
+ "The file _info.c contains the metadata for a ccan package: things\n"
+ "like the dependencies, the documentation for the package as a whole\n"
+ "and license information.\n";
+}
+
+static const char template[] =
+ "#include <string.h>\n"
+ "#include \"config.h\"\n"
+ "\n"
+ "/**\n"
+ " * %s - YOUR-ONE-LINE-DESCRIPTION-HERE\n"
+ " *\n"
+ " * This code ... YOUR-BRIEF-SUMMARY-HERE\n"
+ " *\n"
+ " * Example:\n"
+ " * FULLY-COMPILABLE-INDENTED-TRIVIAL-BUT-USEFUL-EXAMPLE-HERE\n"
+ " */\n"
+ "int main(int argc, char *argv[])\n"
+ "{\n"
+ " /* Expect exactly one argument */\n"
+ " if (argc != 2)\n"
+ " return 1;\n"
+ "\n"
+ " if (strcmp(argv[1], \"depends\") == 0) {\n"
+ " PRINTF-CCAN-PACKAGES-YOU-NEED-ONE-PER-LINE-IF-ANY\n"
+ " return 0;\n"
+ " }\n"
+ "\n"
+ " return 1;\n"
+ "}\n";
+
+static void create_info_template(struct manifest *m, void *check_result)
+{
+ FILE *info;
+
+ if (!ask("Should I create a template _info.c file for you?"))
+ return;
+
+ info = fopen("_info.c", "w");
+ if (!info)
+ err(1, "Trying to create a template _info.c");
+
+ if (fprintf(info, template, m->basename) < 0) {
+ unlink_noerr("_info.c");
+ err(1, "Writing template into _info.c");
+ }
+ fclose(info);
+}
+
+struct ccanlint has_info = {
+ .name = "Has _info.c file",
+ .check = check_has_info,
+ .describe = describe_has_info,
+ .handle = create_info_template,
+};
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <ccan/str/str.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/noerr/noerr.h>
+
+static void *check_has_main_header(struct manifest *m)
+{
+ struct ccan_file *f;
+
+ list_for_each(&m->h_files, f, list) {
+ if (strstarts(f->name, m->basename)
+ && strlen(f->name) == strlen(m->basename) + 2)
+ return NULL;
+ }
+ return m;
+}
+
+static const char *describe_has_main_header(struct manifest *m,
+ void *check_result)
+{
+ return talloc_asprintf(m,
+ "You have no %s/%s.h header file.\n\n"
+ "CCAN modules have a name, the same as the directory name. They're\n"
+ "expected to have an interface in the header of the same name.\n",
+ m->basename, m->basename);
+}
+
+struct ccanlint has_main_header = {
+ .name = "No main header file",
+ .check = check_has_main_header,
+ .describe = describe_has_main_header,
+};
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <ccan/talloc/talloc.h>
+
+static char test_is_not_dir[] = "test is not a directory";
+
+static void *check_has_tests(struct manifest *m)
+{
+ struct stat st;
+
+ if (lstat("test", &st) != 0) {
+ if (errno != ENOENT)
+ err(1, "statting test/");
+ return "You have no test directory";
+ }
+
+ if (!S_ISDIR(st.st_mode))
+ return test_is_not_dir;
+
+ if (list_empty(&m->api_tests)
+ && list_empty(&m->run_tests)
+ && list_empty(&m->compile_ok_tests)) {
+ if (list_empty(&m->compile_fail_tests))
+ return "You have no tests in the test directory";
+ else
+ return "You have no positive tests in the test directory";
+ }
+ return NULL;
+}
+
+static const char *describe_has_tests(struct manifest *m, void *check_result)
+{
+ return talloc_asprintf(m, "%s\n\n"
+ "CCAN modules have a directory called test/ which contains tests.\n"
+ "There are four kinds of tests: api, run, compile_ok and compile_fail:\n"
+ "you can tell which type of test a C file is by its name, eg 'run.c'\n"
+ "and 'run-simple.c' are both run tests.\n\n"
+
+ "The simplest kind of test is a run test, which must compile with no\n"
+ "warnings, and then run: it is expected to use libtap to report its\n"
+ "results in a simple and portable format. It should #include the C\n"
+ "files from the module directly (so it can probe the internals): the\n"
+ "module will not be linked in.\n\n"
+
+ "api tests are just like a run test, except it is a guarantee of API\n"
+ "stability: this test should pass on all future versions of the\n"
+ "module. They *are* linked to the module, since they should only\n"
+ "test the API, not the internal state.\n\n"
+
+ "compile_ok tests are a subset of run tests: they must compile and\n"
+ "link, but aren't run.\n\n"
+
+ "compile_fail tests are tests which should fail to compile (or emit\n"
+ "warnings) or link when FAIL is defined, but should compile and link\n"
+ "when it's not defined: this helps ensure unrelated errors don't make\n"
+ "compilation fail.\n\n"
+
+ "Note that the tests are not linked against the files in the\n"
+ "above: you should directly #include those C files you want. This\n"
+ "allows access to static functions and use special effects inside\n"
+ "test files\n", (char *)check_result);
+}
+
+static void handle_no_tests(struct manifest *m, void *check_result)
+{
+ FILE *run;
+ struct ccan_file *i;
+
+ if (check_result == test_is_not_dir)
+ return;
+
+ if (!ask("Should I create a template test/run.c file for you?"))
+ return;
+
+ if (mkdir("test", 0700) != 0) {
+ if (errno != EEXIST)
+ err(1, "Creating test/ directory");
+ }
+
+ run = fopen("test/run.c", "w");
+ if (!run)
+ err(1, "Trying to create a test/run.c");
+
+ fputs("/* Include the main header first, to test it works */\n", run);
+ fprintf(run, "#include \"%s/%s.h\"\n", m->basename, m->basename);
+ fputs("/* Include the C files directly. */\n", run);
+ list_for_each(&m->c_files, i, list)
+ fprintf(run, "#include \"%s/%s\"\n", m->basename, i->name);
+ fputs("#include \"tap/tap.h\"\n", run);
+ fputs("\n", run);
+
+ fputs("int main(void)\n", run);
+ fputs("{\n", run);
+ fputs("\t/* This is how many tests you plan to run */\n", run);
+ fputs("\tplan_tests(3);\n", run);
+ fputs("\n", run);
+ fputs("\t/* Simple thing we expect to succeed */\n", run);
+ fputs("\tok1(some_test())\n", run);
+ fputs("\t/* Same, with an explicit description of the test. */\n", run);
+ fputs("\tok(some_test(), \"%s with no args should return 1\", \"some_test\")\n", run);
+ fputs("\t/* How to print out messages for debugging. */\n", run);
+ fputs("\tdiag(\"Address of some_test is %p\", &some_test)\n", run);
+ fputs("\t/* Conditional tests must be explicitly skipped. */\n", run);
+ fputs("#if HAVE_SOME_FEATURE\n", run);
+ fputs("\tok1(test_some_feature())\n", run);
+ fputs("#else\n", run);
+ fputs("\tskip(1, \"Don\'t have SOME_FEATURE\")\n", run);
+ fputs("#endif\n", run);
+ fputs("\n", run);
+ fputs("\t/* This exits depending on whether all tests passed */\n", run);
+ fputs("\treturn exit_status();\n", run);
+ fputs("}\n", run);
+
+ fclose(run);
+}
+
+struct ccanlint has_tests = {
+ .name = "No tests",
+ .check = check_has_tests,
+ .describe = describe_has_tests,
+ .handle = handle_no_tests,
+};
+++ /dev/null
-#include "ccanlint.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ccan/noerr/noerr.h>
-
-static void *check_has_info(struct manifest *m)
-{
- if (m->info_file)
- return NULL;
- return m;
-}
-
-static const char *describe_has_info(struct manifest *m, void *check_result)
-{
- return "You have no _info.c file.\n\n"
- "The file _info.c contains the metadata for a ccan package: things\n"
- "like the dependencies, the documentation for the package as a whole\n"
- "and license information.\n";
-}
-
-static const char template[] =
- "#include <string.h>\n"
- "#include \"config.h\"\n"
- "\n"
- "/**\n"
- " * %s - YOUR-ONE-LINE-DESCRIPTION-HERE\n"
- " *\n"
- " * This code ... YOUR-BRIEF-SUMMARY-HERE\n"
- " *\n"
- " * Example:\n"
- " * FULLY-COMPILABLE-INDENTED-TRIVIAL-BUT-USEFUL-EXAMPLE-HERE\n"
- " */\n"
- "int main(int argc, char *argv[])\n"
- "{\n"
- " /* Expect exactly one argument */\n"
- " if (argc != 2)\n"
- " return 1;\n"
- "\n"
- " if (strcmp(argv[1], \"depends\") == 0) {\n"
- " PRINTF-CCAN-PACKAGES-YOU-NEED-ONE-PER-LINE-IF-ANY\n"
- " return 0;\n"
- " }\n"
- "\n"
- " return 1;\n"
- "}\n";
-
-static void create_info_template(struct manifest *m, void *check_result)
-{
- FILE *info;
-
- if (!ask("Should I create a template _info.c file for you?"))
- return;
-
- info = fopen("_info.c", "w");
- if (!info)
- err(1, "Trying to create a template _info.c");
-
- if (fprintf(info, template, m->basename) < 0) {
- unlink_noerr("_info.c");
- err(1, "Writing template into _info.c");
- }
- fclose(info);
-}
-
-struct ccanlint has_info = {
- .name = "Has _info.c file",
- .check = check_has_info,
- .describe = describe_has_info,
- .handle = create_info_template,
-};
+++ /dev/null
-#include "ccanlint.h"
-#include "../doc_extract.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <ccan/str/str.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/noerr/noerr.h>
-#include <ccan/grab_file/grab_file.h>
-
-struct info_docs
-{
- bool summary;
- bool description;
- bool example;
-};
-
-static void *check_has_info_documentation(struct manifest *m)
-{
- struct list_head *infodocs = get_ccan_file_docs(m->info_file);
- struct doc_section *d;
- struct info_docs id = { false, false, false };
-
- list_for_each(infodocs, d, list) {
- if (!streq(d->function, m->basename))
- continue;
- if (streq(d->type, "summary"))
- id.summary = true;
- if (streq(d->type, "description"))
- id.description = true;
- if (streq(d->type, "example"))
- id.example = true;
- }
-
- if (id.summary && id.description && id.example)
- return NULL;
- return talloc_memdup(m, &id, sizeof(id));
-}
-
-/* This is defined below. */
-extern struct ccanlint has_info_documentation;
-
-static void create_info_template_doc(struct manifest *m, void *check_result)
-{
- int fd = open("_info.c.new", O_WRONLY|O_CREAT|O_EXCL, 0666);
- FILE *new;
- char *oldcontents;
-
- if (fd < 0 || !(new = fdopen(fd, "w")))
- err(1, "Creating _info.c.new to insert documentation");
-
- if (fprintf(new,
- "/**\n"
- " * %s - [[ONE LINE DESCRIPTION HERE]]\n"
- " *\n"
- " * Paragraphs why %s exists and where to use it.\n"
- " *\n"
- " * Followed by an Example: section with a standalone\n"
- " * (trivial and usually useless) program\n"
- " */\n", m->basename, m->basename) < 0) {
- unlink_noerr("_info.c.new");
- err(1, "Writing to _info.c.new to insert documentation");
- }
-
- oldcontents = grab_file(m, "_info.c", NULL);
- if (!oldcontents) {
- unlink_noerr("_info.c.new");
- err(1, "Reading _info.c");
- }
- if (fprintf(new, "%s", oldcontents) < 0) {
- unlink_noerr("_info.c.new");
- err(1, "Appending _info.c to _info.c.new");
- }
- if (fclose(new) != 0) {
- unlink_noerr("_info.c.new");
- err(1, "Closing _info.c.new");
- }
- if (rename("_info.c.new", "_info.c") != 0) {
- unlink_noerr("_info.c.new");
- err(1, "Renaming _info.c.new to _info.c");
- }
-}
-
-static const char *describe_has_info_documentation(struct manifest *m,
- void *check_result)
-{
- struct info_docs *id = check_result;
- char *reason = talloc_strdup(m, "");
-
- if (!id->summary) {
- has_info_documentation.handle = create_info_template_doc;
- reason = talloc_asprintf_append(reason,
- "Your _info.c has no module documentation.\n\n"
- "CCAN modules use /**-style comments for documentation: the\n"
- "overall documentation belongs in the _info.c metafile.\n");
- }
- if (!id->description)
- reason = talloc_asprintf_append(reason,
- "Your _info.c has no module description.\n\n"
- "The lines after the first summary line in the _info.c file\n"
- "documentation should describe the purpose and use of the\n"
- "overall package\n");
- if (!id->example)
- reason = talloc_asprintf_append(reason,
- "Your _info.c has no module example.\n\n"
- "There should be an Example: section of the _info.c documentation\n"
- "which provides a concise toy program which uses your module\n");
- return reason;
-}
-
-static unsigned int has_info_documentation_score(struct manifest *m,
- void *check_result)
-{
- struct info_docs *id = check_result;
- return id->summary + id->description + id->example;
-}
-
-struct ccanlint has_info_documentation = {
- .name = "Documentation in _info.c",
- .total_score = 3,
- .score = has_info_documentation_score,
- .check = check_has_info_documentation,
- .describe = describe_has_info_documentation,
-};
+++ /dev/null
-#include "ccanlint.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <ccan/str/str.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/noerr/noerr.h>
-
-static void *check_has_main_header(struct manifest *m)
-{
- struct ccan_file *f;
-
- list_for_each(&m->h_files, f, list) {
- if (strstarts(f->name, m->basename)
- && strlen(f->name) == strlen(m->basename) + 2)
- return NULL;
- }
- return m;
-}
-
-static const char *describe_has_main_header(struct manifest *m,
- void *check_result)
-{
- return talloc_asprintf(m,
- "You have no %s/%s.h header file.\n\n"
- "CCAN modules have a name, the same as the directory name. They're\n"
- "expected to have an interface in the header of the same name.\n",
- m->basename, m->basename);
-}
-
-struct ccanlint has_main_header = {
- .name = "No main header file",
- .check = check_has_main_header,
- .describe = describe_has_main_header,
-};
+++ /dev/null
-#include "ccanlint.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <ccan/talloc/talloc.h>
-
-static char test_is_not_dir[] = "test is not a directory";
-
-static void *check_has_tests(struct manifest *m)
-{
- struct stat st;
-
- if (lstat("test", &st) != 0) {
- if (errno != ENOENT)
- err(1, "statting test/");
- return "You have no test directory";
- }
-
- if (!S_ISDIR(st.st_mode))
- return test_is_not_dir;
-
- if (list_empty(&m->api_tests)
- && list_empty(&m->run_tests)
- && list_empty(&m->compile_ok_tests)) {
- if (list_empty(&m->compile_fail_tests))
- return "You have no tests in the test directory";
- else
- return "You have no positive tests in the test directory";
- }
- return NULL;
-}
-
-static const char *describe_has_tests(struct manifest *m, void *check_result)
-{
- return talloc_asprintf(m, "%s\n\n"
- "CCAN modules have a directory called test/ which contains tests.\n"
- "There are four kinds of tests: api, run, compile_ok and compile_fail:\n"
- "you can tell which type of test a C file is by its name, eg 'run.c'\n"
- "and 'run-simple.c' are both run tests.\n\n"
-
- "The simplest kind of test is a run test, which must compile with no\n"
- "warnings, and then run: it is expected to use libtap to report its\n"
- "results in a simple and portable format. It should #include the C\n"
- "files from the module directly (so it can probe the internals): the\n"
- "module will not be linked in.\n\n"
-
- "api tests are just like a run test, except it is a guarantee of API\n"
- "stability: this test should pass on all future versions of the\n"
- "module. They *are* linked to the module, since they should only\n"
- "test the API, not the internal state.\n\n"
-
- "compile_ok tests are a subset of run tests: they must compile and\n"
- "link, but aren't run.\n\n"
-
- "compile_fail tests are tests which should fail to compile (or emit\n"
- "warnings) or link when FAIL is defined, but should compile and link\n"
- "when it's not defined: this helps ensure unrelated errors don't make\n"
- "compilation fail.\n\n"
-
- "Note that the tests are not linked against the files in the\n"
- "above: you should directly #include those C files you want. This\n"
- "allows access to static functions and use special effects inside\n"
- "test files\n", (char *)check_result);
-}
-
-static void handle_no_tests(struct manifest *m, void *check_result)
-{
- FILE *run;
- struct ccan_file *i;
-
- if (check_result == test_is_not_dir)
- return;
-
- if (!ask("Should I create a template test/run.c file for you?"))
- return;
-
- if (mkdir("test", 0700) != 0) {
- if (errno != EEXIST)
- err(1, "Creating test/ directory");
- }
-
- run = fopen("test/run.c", "w");
- if (!run)
- err(1, "Trying to create a test/run.c");
-
- fputs("/* Include the main header first, to test it works */\n", run);
- fprintf(run, "#include \"%s/%s.h\"\n", m->basename, m->basename);
- fputs("/* Include the C files directly. */\n", run);
- list_for_each(&m->c_files, i, list)
- fprintf(run, "#include \"%s/%s\"\n", m->basename, i->name);
- fputs("#include \"tap/tap.h\"\n", run);
- fputs("\n", run);
-
- fputs("int main(void)\n", run);
- fputs("{\n", run);
- fputs("\t/* This is how many tests you plan to run */\n", run);
- fputs("\tplan_tests(3);\n", run);
- fputs("\n", run);
- fputs("\t/* Simple thing we expect to succeed */\n", run);
- fputs("\tok1(some_test())\n", run);
- fputs("\t/* Same, with an explicit description of the test. */\n", run);
- fputs("\tok(some_test(), \"%s with no args should return 1\", \"some_test\")\n", run);
- fputs("\t/* How to print out messages for debugging. */\n", run);
- fputs("\tdiag(\"Address of some_test is %p\", &some_test)\n", run);
- fputs("\t/* Conditional tests must be explicitly skipped. */\n", run);
- fputs("#if HAVE_SOME_FEATURE\n", run);
- fputs("\tok1(test_some_feature())\n", run);
- fputs("#else\n", run);
- fputs("\tskip(1, \"Don\'t have SOME_FEATURE\")\n", run);
- fputs("#endif\n", run);
- fputs("\n", run);
- fputs("\t/* This exits depending on whether all tests passed */\n", run);
- fputs("\treturn exit_status();\n", run);
- fputs("}\n", run);
-
- fclose(run);
-}
-
-struct ccanlint has_tests = {
- .name = "No tests",
- .check = check_has_tests,
- .describe = describe_has_tests,
- .handle = handle_no_tests,
-};
+++ /dev/null
-#include "ccanlint.h"
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-#include "../tools.h"
-
-static const char explain[]
-= "Headers usually start with the C preprocessor lines to prevent multiple\n"
- "inclusions. These look like the following:\n"
- "#ifndef MY_HEADER_H\n"
- "#define MY_HEADER_H\n"
- "...\n"
- "#endif /* MY_HEADER_H */\n";
-
-static char *report_idem(struct ccan_file *f, char *sofar)
-{
- struct line_info *line_info;
- unsigned int i, first_preproc_line;
- const char *line, *sym;
-
- line_info = get_ccan_line_info(f);
- if (f->num_lines < 3)
- /* FIXME: We assume small headers probably uninteresting. */
- return sofar;
-
- for (i = 0; i < f->num_lines; i++) {
- if (line_info[i].type == DOC_LINE
- || line_info[i].type == COMMENT_LINE)
- continue;
- if (line_info[i].type == CODE_LINE)
- return talloc_asprintf_append(sofar,
- "%s:%u:expect first non-comment line to be #ifndef.\n", f->name, i+1);
- else if (line_info[i].type == PREPROC_LINE)
- break;
- }
-
- /* No code at all? Don't complain. */
- if (i == f->num_lines)
- return sofar;
-
- first_preproc_line = i;
- for (i = first_preproc_line+1; i < f->num_lines; i++) {
- if (line_info[i].type == DOC_LINE
- || line_info[i].type == COMMENT_LINE)
- continue;
- if (line_info[i].type == CODE_LINE)
- return talloc_asprintf_append(sofar,
- "%s:%u:expect second line to be #define.\n", f->name, i+1);
- else if (line_info[i].type == PREPROC_LINE)
- break;
- }
-
- /* No code at all? Weird. */
- if (i == f->num_lines)
- return sofar;
-
- /* We expect a condition on this line. */
- if (!line_info[i].cond) {
- return talloc_asprintf_append(sofar,
- "%s:%u:expected #ifndef.\n",
- f->name, first_preproc_line+1);
- }
-
- line = f->lines[i];
-
- /* We expect the condition to be ! IFDEF <symbol>. */
- if (line_info[i].cond->type != PP_COND_IFDEF
- || !line_info[i].cond->inverse) {
- return talloc_asprintf_append(sofar,
- "%s:%u:expected #ifndef.\n",
- f->name, first_preproc_line+1);
- }
-
- /* And this to be #define <symbol> */
- if (!get_token(&line, "#"))
- abort();
- if (!get_token(&line, "define")) {
- return talloc_asprintf_append(sofar,
- "%s:%u:expected '#define %s'.\n",
- f->name, i+1, line_info[i].cond->symbol);
- }
- sym = get_symbol_token(f, &line);
- if (!sym || !streq(sym, line_info[i].cond->symbol)) {
- return talloc_asprintf_append(sofar,
- "%s:%u:expected '#define %s'.\n",
- f->name, i+1, line_info[i].cond->symbol);
- }
-
- /* Rest of code should all be covered by that conditional. */
- for (i++; i < f->num_lines; i++) {
- unsigned int val = 0;
- if (line_info[i].type == DOC_LINE
- || line_info[i].type == COMMENT_LINE)
- continue;
- if (get_ccan_line_pp(line_info[i].cond, sym, &val, NULL)
- != NOT_COMPILED)
- return talloc_asprintf_append(sofar,
- "%s:%u:code outside idempotent region.\n",
- f->name, i+1);
- }
-
- return sofar;
-}
-
-static void *check_idempotent(struct manifest *m)
-{
- struct ccan_file *f;
- char *report = NULL;
-
- list_for_each(&m->h_files, f, list)
- report = report_idem(f, report);
-
- return report;
-}
-
-static const char *describe_idempotent(struct manifest *m, void *check_result)
-{
- return talloc_asprintf(check_result,
- "Some headers not idempotent:\n"
- "%s\n%s", (char *)check_result,
- explain);
-}
-
-struct ccanlint idempotent = {
- .name = "Headers are #ifndef/#define idempotent wrapped",
- .total_score = 1,
- .check = check_idempotent,
- .describe = describe_idempotent,
-};
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/doc_extract.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <ccan/str/str.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/noerr/noerr.h>
+#include <ccan/grab_file/grab_file.h>
+
+struct info_docs
+{
+ bool summary;
+ bool description;
+ bool example;
+};
+
+static void *check_has_info_documentation(struct manifest *m)
+{
+ struct list_head *infodocs = get_ccan_file_docs(m->info_file);
+ struct doc_section *d;
+ struct info_docs id = { false, false, false };
+
+ list_for_each(infodocs, d, list) {
+ if (!streq(d->function, m->basename))
+ continue;
+ if (streq(d->type, "summary"))
+ id.summary = true;
+ if (streq(d->type, "description"))
+ id.description = true;
+ if (streq(d->type, "example"))
+ id.example = true;
+ }
+
+ if (id.summary && id.description && id.example)
+ return NULL;
+ return talloc_memdup(m, &id, sizeof(id));
+}
+
+/* This is defined below. */
+extern struct ccanlint has_info_documentation;
+
+static void create_info_template_doc(struct manifest *m, void *check_result)
+{
+ int fd = open("_info.c.new", O_WRONLY|O_CREAT|O_EXCL, 0666);
+ FILE *new;
+ char *oldcontents;
+
+ if (fd < 0 || !(new = fdopen(fd, "w")))
+ err(1, "Creating _info.c.new to insert documentation");
+
+ if (fprintf(new,
+ "/**\n"
+ " * %s - [[ONE LINE DESCRIPTION HERE]]\n"
+ " *\n"
+ " * Paragraphs why %s exists and where to use it.\n"
+ " *\n"
+ " * Followed by an Example: section with a standalone\n"
+ " * (trivial and usually useless) program\n"
+ " */\n", m->basename, m->basename) < 0) {
+ unlink_noerr("_info.c.new");
+ err(1, "Writing to _info.c.new to insert documentation");
+ }
+
+ oldcontents = grab_file(m, "_info.c", NULL);
+ if (!oldcontents) {
+ unlink_noerr("_info.c.new");
+ err(1, "Reading _info.c");
+ }
+ if (fprintf(new, "%s", oldcontents) < 0) {
+ unlink_noerr("_info.c.new");
+ err(1, "Appending _info.c to _info.c.new");
+ }
+ if (fclose(new) != 0) {
+ unlink_noerr("_info.c.new");
+ err(1, "Closing _info.c.new");
+ }
+ if (rename("_info.c.new", "_info.c") != 0) {
+ unlink_noerr("_info.c.new");
+ err(1, "Renaming _info.c.new to _info.c");
+ }
+}
+
+static const char *describe_has_info_documentation(struct manifest *m,
+ void *check_result)
+{
+ struct info_docs *id = check_result;
+ char *reason = talloc_strdup(m, "");
+
+ if (!id->summary) {
+ has_info_documentation.handle = create_info_template_doc;
+ reason = talloc_asprintf_append(reason,
+ "Your _info.c has no module documentation.\n\n"
+ "CCAN modules use /**-style comments for documentation: the\n"
+ "overall documentation belongs in the _info.c metafile.\n");
+ }
+ if (!id->description)
+ reason = talloc_asprintf_append(reason,
+ "Your _info.c has no module description.\n\n"
+ "The lines after the first summary line in the _info.c file\n"
+ "documentation should describe the purpose and use of the\n"
+ "overall package\n");
+ if (!id->example)
+ reason = talloc_asprintf_append(reason,
+ "Your _info.c has no module example.\n\n"
+ "There should be an Example: section of the _info.c documentation\n"
+ "which provides a concise toy program which uses your module\n");
+ return reason;
+}
+
+static unsigned int has_info_documentation_score(struct manifest *m,
+ void *check_result)
+{
+ struct info_docs *id = check_result;
+ return id->summary + id->description + id->example;
+}
+
+struct ccanlint has_info_documentation = {
+ .name = "Documentation in _info.c",
+ .total_score = 3,
+ .score = has_info_documentation_score,
+ .check = check_has_info_documentation,
+ .describe = describe_has_info_documentation,
+};
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static const char explain[]
+= "Headers usually start with the C preprocessor lines to prevent multiple\n"
+ "inclusions. These look like the following:\n"
+ "#ifndef MY_HEADER_H\n"
+ "#define MY_HEADER_H\n"
+ "...\n"
+ "#endif /* MY_HEADER_H */\n";
+
+static char *report_idem(struct ccan_file *f, char *sofar)
+{
+ struct line_info *line_info;
+ unsigned int i, first_preproc_line;
+ const char *line, *sym;
+
+ line_info = get_ccan_line_info(f);
+ if (f->num_lines < 3)
+ /* FIXME: We assume small headers probably uninteresting. */
+ return sofar;
+
+ for (i = 0; i < f->num_lines; i++) {
+ if (line_info[i].type == DOC_LINE
+ || line_info[i].type == COMMENT_LINE)
+ continue;
+ if (line_info[i].type == CODE_LINE)
+ return talloc_asprintf_append(sofar,
+ "%s:%u:expect first non-comment line to be #ifndef.\n", f->name, i+1);
+ else if (line_info[i].type == PREPROC_LINE)
+ break;
+ }
+
+ /* No code at all? Don't complain. */
+ if (i == f->num_lines)
+ return sofar;
+
+ first_preproc_line = i;
+ for (i = first_preproc_line+1; i < f->num_lines; i++) {
+ if (line_info[i].type == DOC_LINE
+ || line_info[i].type == COMMENT_LINE)
+ continue;
+ if (line_info[i].type == CODE_LINE)
+ return talloc_asprintf_append(sofar,
+ "%s:%u:expect second line to be #define.\n", f->name, i+1);
+ else if (line_info[i].type == PREPROC_LINE)
+ break;
+ }
+
+ /* No code at all? Weird. */
+ if (i == f->num_lines)
+ return sofar;
+
+ /* We expect a condition on this line. */
+ if (!line_info[i].cond) {
+ return talloc_asprintf_append(sofar,
+ "%s:%u:expected #ifndef.\n",
+ f->name, first_preproc_line+1);
+ }
+
+ line = f->lines[i];
+
+ /* We expect the condition to be ! IFDEF <symbol>. */
+ if (line_info[i].cond->type != PP_COND_IFDEF
+ || !line_info[i].cond->inverse) {
+ return talloc_asprintf_append(sofar,
+ "%s:%u:expected #ifndef.\n",
+ f->name, first_preproc_line+1);
+ }
+
+ /* And this to be #define <symbol> */
+ if (!get_token(&line, "#"))
+ abort();
+ if (!get_token(&line, "define")) {
+ return talloc_asprintf_append(sofar,
+ "%s:%u:expected '#define %s'.\n",
+ f->name, i+1, line_info[i].cond->symbol);
+ }
+ sym = get_symbol_token(f, &line);
+ if (!sym || !streq(sym, line_info[i].cond->symbol)) {
+ return talloc_asprintf_append(sofar,
+ "%s:%u:expected '#define %s'.\n",
+ f->name, i+1, line_info[i].cond->symbol);
+ }
+
+ /* Rest of code should all be covered by that conditional. */
+ for (i++; i < f->num_lines; i++) {
+ unsigned int val = 0;
+ if (line_info[i].type == DOC_LINE
+ || line_info[i].type == COMMENT_LINE)
+ continue;
+ if (get_ccan_line_pp(line_info[i].cond, sym, &val, NULL)
+ != NOT_COMPILED)
+ return talloc_asprintf_append(sofar,
+ "%s:%u:code outside idempotent region.\n",
+ f->name, i+1);
+ }
+
+ return sofar;
+}
+
+static void *check_idempotent(struct manifest *m)
+{
+ struct ccan_file *f;
+ char *report = NULL;
+
+ list_for_each(&m->h_files, f, list)
+ report = report_idem(f, report);
+
+ return report;
+}
+
+static const char *describe_idempotent(struct manifest *m, void *check_result)
+{
+ return talloc_asprintf(check_result,
+ "Some headers not idempotent:\n"
+ "%s\n%s", (char *)check_result,
+ explain);
+}
+
+struct ccanlint idempotent = {
+ .name = "Headers are #ifndef/#define idempotent wrapped",
+ .total_score = 1,
+ .check = check_idempotent,
+ .describe = describe_idempotent,
+};
--- /dev/null
+/* Trailing whitespace test. Almost embarrassing, but trivial. */
+#include <tools/ccanlint/ccanlint.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+
+static char *report_on_trailing_whitespace(const char *line)
+{
+ const char *e = strchr(line, 0);
+ while (e>line && (e[-1]==' ' || e[-1]=='\t'))
+ e--;
+ if (*e == 0)
+ return NULL; //there were no trailing spaces
+ if (e == line)
+ return NULL; //the line only consists of spaces
+
+ if (strlen(line) > 20)
+ return talloc_asprintf(line, "...'%s'",
+ line + strlen(line) - 20);
+ return talloc_asprintf(line, "'%s'", line);
+}
+
+static void *check_trailing_whitespace(struct manifest *m)
+{
+ char *report;
+
+ report = report_on_lines(&m->c_files, report_on_trailing_whitespace,
+ NULL);
+ report = report_on_lines(&m->h_files, report_on_trailing_whitespace,
+ report);
+
+ return report;
+}
+
+static const char *describe_trailing_whitespace(struct manifest *m,
+ void *check_result)
+{
+ return talloc_asprintf(check_result,
+ "Some source files have trailing whitespace:\n"
+ "%s", (char *)check_result);
+}
+
+struct ccanlint trailing_whitespace = {
+ .name = "No lines with unnecessary trailing whitespace",
+ .total_score = 1,
+ .check = check_trailing_whitespace,
+ .describe = describe_trailing_whitespace,
+};
+++ /dev/null
-/* Trailing whitespace test. Almost embarrassing, but trivial. */
-#include "ccanlint.h"
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-
-static char *report_on_trailing_whitespace(const char *line)
-{
- const char *e = strchr(line, 0);
- while (e>line && (e[-1]==' ' || e[-1]=='\t'))
- e--;
- if (*e == 0)
- return NULL; //there were no trailing spaces
- if (e == line)
- return NULL; //the line only consists of spaces
-
- if (strlen(line) > 20)
- return talloc_asprintf(line, "...'%s'",
- line + strlen(line) - 20);
- return talloc_asprintf(line, "'%s'", line);
-}
-
-static void *check_trailing_whitespace(struct manifest *m)
-{
- char *report;
-
- report = report_on_lines(&m->c_files, report_on_trailing_whitespace,
- NULL);
- report = report_on_lines(&m->h_files, report_on_trailing_whitespace,
- report);
-
- return report;
-}
-
-static const char *describe_trailing_whitespace(struct manifest *m,
- void *check_result)
-{
- return talloc_asprintf(check_result,
- "Some source files have trailing whitespace:\n"
- "%s", (char *)check_result);
-}
-
-struct ccanlint trailing_whitespace = {
- .name = "No lines with unnecessary trailing whitespace",
- .total_score = 1,
- .check = check_trailing_whitespace,
- .describe = describe_trailing_whitespace,
-};