+++ /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>
-#include "build.h"
-
-static const char *can_build(struct manifest *m)
-{
- if (safe_mode)
- return "Safe mode enabled";
- return NULL;
-}
-
-static char *obj_list(const struct manifest *m)
-{
- char *list = talloc_strdup(m, "");
- struct ccan_file *i;
-
- /* Objects from all the C files. */
- list_for_each(&m->c_files, i, list)
- list = talloc_asprintf_append(list, "%s ", i->compiled);
-
- return list;
-}
-
-char *build_module(struct manifest *m, bool keep, char **errstr)
-{
- char *name = link_objects(m, m->basename, false, obj_list(m), errstr);
- if (name) {
- if (keep) {
- char *realname = talloc_asprintf(m, "%s.o", m->dir);
- /* We leave this object file around, all built. */
- if (!move_file(name, realname))
- err(1, "Renaming %s to %s", name, realname);
- name = realname;
- }
- }
- return name;
-}
-
-static void do_build(struct manifest *m,
- bool keep,
- unsigned int *timeleft,
- struct score *score)
-{
- char *errstr;
-
- if (list_empty(&m->c_files)) {
- /* No files? No score, but we "pass". */
- score->total = 0;
- score->pass = true;
- return;
- }
-
- m->compiled = build_module(m, keep, &errstr);
- if (!m->compiled) {
- score_file_error(score, NULL, 0, errstr);
- return;
- }
-
- score->pass = true;
- score->score = score->total;
-}
-
-struct ccanlint module_builds = {
- .key = "module_builds",
- .name = "Module can be built from object files",
- .check = do_build,
- .can_run = can_build,
- .needs = "objects_build"
-};
-
-REGISTER_TEST(module_builds);
+++ /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 *can_build(struct manifest *m)
-{
- if (safe_mode)
- return "Safe mode enabled";
- return NULL;
-}
-
-static void check_objs_build(struct manifest *m,
- bool keep,
- unsigned int *timeleft, struct score *score)
-{
- struct ccan_file *i;
- bool errors = false, warnings = false;
-
- if (list_empty(&m->c_files))
- score->total = 0;
- else
- score->total = 2;
-
- list_for_each(&m->c_files, i, list) {
- char *output;
- char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
-
- i->compiled = maybe_temp_file(m, "", keep, fullfile);
- if (!compile_object(score, fullfile, ccan_dir, "", i->compiled,
- &output)) {
- talloc_free(i->compiled);
- score->error = "Compiling object files";
- score_file_error(score, i, 0, output);
- errors = true;
- } else if (!streq(output, "")) {
- score->error = "Compiling object files gave warnings";
- score_file_error(score, i, 0, output);
- warnings = true;
- }
- }
-
- if (!errors) {
- score->pass = true;
- score->score = score->total - warnings;
- }
-}
-
-struct ccanlint objects_build = {
- .key = "objects_build",
- .name = "Module object files can be built",
- .check = check_objs_build,
- .can_run = can_build,
- .needs = "depends_exist"
-};
-
-REGISTER_TEST(objects_build);
+++ /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 *can_build(struct manifest *m)
-{
- if (safe_mode)
- return "Safe mode enabled";
- return NULL;
-}
-
-static char *obj_list(const struct manifest *m)
-{
- char *list = talloc_strdup(m, "");
- struct manifest *i;
-
- /* Other CCAN deps. */
- list_for_each(&m->deps, i, list) {
- if (i->compiled)
- list = talloc_asprintf_append(list, "%s ",
- i->compiled);
- }
- return list;
-}
-
-static char *lib_list(const struct manifest *m)
-{
- unsigned int i, num;
- char **libs = get_libs(m, ".", &num, &m->info_file->compiled);
- char *ret = talloc_strdup(m, "");
-
- for (i = 0; i < num; i++)
- ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
- return ret;
-}
-
-static void check_use_build(struct manifest *m,
- bool keep,
- unsigned int *timeleft, struct score *score)
-{
- char *contents;
- char *tmpfile, *cmdout;
- char *basename = talloc_asprintf(m, "%s/example.c", m->dir);
- int fd;
-
- tmpfile = maybe_temp_file(m, ".c", keep, basename);
-
- fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
- if (fd < 0)
- err(1, "Creating temporary file %s", tmpfile);
-
- contents = talloc_asprintf(tmpfile,
- "#include <ccan/%s/%s.h>\n"
- "int main(void)\n"
- "{\n"
- " return 0;\n"
- "}\n",
- m->basename, m->basename);
- if (write(fd, contents, strlen(contents)) != strlen(contents))
- err(1, "Failure writing to temporary file %s", tmpfile);
- close(fd);
-
- if (compile_and_link(score, tmpfile, ccan_dir, obj_list(m), "",
- lib_list(m),
- maybe_temp_file(m, "", keep, tmpfile),
- &cmdout)) {
- score->pass = true;
- score->score = score->total;
- } else {
- score->error = cmdout;
- }
-}
-
-struct ccanlint module_links = {
- .key = "module_links",
- .name = "Module can be linked against trivial program",
- .check = check_use_build,
- .can_run = can_build,
- .needs = "module_builds depends_build"
-};
-
-REGISTER_TEST(module_links);
+++ /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>
-#include "build.h"
-
-static const char *can_build(struct manifest *m)
-{
- if (safe_mode)
- return "Safe mode enabled";
- return NULL;
-}
-
-static bool expect_obj_file(struct manifest *m)
-{
- /* If it has C files, we expect an object file built from them. */
- return !list_empty(&m->c_files);
-}
-
-static char *build_subdir_objs(struct manifest *m)
-{
- struct ccan_file *i;
-
- list_for_each(&m->c_files, i, list) {
- char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
- char *output;
-
- i->compiled = maybe_temp_file(m, "", false, fullfile);
- if (!compile_object(m, fullfile, ccan_dir, "", i->compiled,
- &output)) {
- talloc_free(i->compiled);
- i->compiled = NULL;
- return talloc_asprintf(m,
- "Dependency %s"
- " did not build:\n%s",
- m->basename, output);
- }
- }
- return NULL;
-}
-
-char *build_submodule(struct manifest *m)
-{
- char *errstr;
- struct stat st;
-
- if (m->compiled)
- return NULL;
-
- if (!expect_obj_file(m))
- return NULL;
-
- m->compiled = talloc_asprintf(m, "%s.o", m->dir);
- if (stat(m->compiled, &st) == 0)
- return NULL;
-
- if (verbose >= 2)
- printf(" Building dependency %s\n", m->dir);
-
- errstr = build_subdir_objs(m);
- if (errstr)
- return errstr;
-
- m->compiled = build_module(m, false, &errstr);
- if (!m->compiled)
- return errstr;
- return NULL;
-}
-
-static void check_depends_built(struct manifest *m,
- bool keep,
- unsigned int *timeleft, struct score *score)
-{
- struct manifest *i;
-
- list_for_each(&m->deps, i, list) {
- char *errstr = build_submodule(i);
-
- if (errstr) {
- score->error = talloc_asprintf(score,
- "Dependency %s"
- " did not build:\n%s",
- i->basename, errstr);
- return;
- }
- }
-
- score->pass = true;
- score->score = score->total;
-}
-
-struct ccanlint depends_build = {
- .key = "depends_build",
- .name = "Module's CCAN dependencies can be found or built",
- .check = check_depends_built,
- .can_run = can_build,
- .needs = "depends_exist"
-};
-
-REGISTER_TEST(depends_build);
+++ /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 bool add_dep(struct manifest *m, const char *dep, struct score *score)
-{
- struct stat st;
- struct manifest *subm;
- char *dir = talloc_asprintf(m, "%s/%s", ccan_dir, dep);
-
- /* FIXME: get_manifest has a tendency to exit. */
- if (stat(dir, &st) != 0) {
- score->error
- = talloc_asprintf(m,
- "Could not stat dependency %s: %s",
- dir, strerror(errno));
- return false;
- }
- subm = get_manifest(m, dir);
- list_add_tail(&m->deps, &subm->list);
- return true;
-}
-
-static void check_depends_exist(struct manifest *m,
- bool keep,
- unsigned int *timeleft, struct score *score)
-{
- unsigned int i;
- char **deps;
- char *updir = talloc_strdup(m, m->dir);
-
- *strrchr(updir, '/') = '\0';
-
- if (safe_mode)
- deps = get_safe_ccan_deps(m, m->dir, true,
- &m->info_file->compiled);
- else
- deps = get_deps(m, m->dir, true, &m->info_file->compiled);
-
- for (i = 0; deps[i]; i++) {
- if (!strstarts(deps[i], "ccan/"))
- continue;
-
- if (!add_dep(m, deps[i], score))
- return;
- }
-
- /* We may need libtap for testing, unless we're "tap" */
- if (!streq(m->basename, "tap")
- && (!list_empty(&m->run_tests) || !list_empty(&m->api_tests))) {
- if (!add_dep(m, "ccan/tap", score))
- return;
- }
-
- score->pass = true;
- score->score = score->total;
-}
-
-struct ccanlint depends_exist = {
- .key = "depends_exist",
- .name = "Module's CCAN dependencies can be found",
- .check = check_depends_exist,
- .needs = "info_exists"
-};
-
-REGISTER_TEST(depends_exist);
+++ /dev/null
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/grab_file/grab_file.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 *can_build(struct manifest *m)
-{
- if (safe_mode)
- return "Safe mode enabled";
- return NULL;
-}
-
-static struct ccan_file *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 f;
- }
- /* Should not happen: we depend on has_main_header */
- abort();
-}
-
-static void check_includes_build(struct manifest *m,
- bool keep,
- unsigned int *timeleft, struct score *score)
-{
- char *contents;
- char *tmpsrc, *tmpobj, *cmdout;
- int fd;
- struct ccan_file *mainh = main_header(m);
-
- tmpsrc = maybe_temp_file(m, "-included.c", keep, mainh->fullname);
- tmpobj = maybe_temp_file(m, ".o", keep, tmpsrc);
-
- fd = open(tmpsrc, O_WRONLY | O_CREAT | O_EXCL, 0600);
- if (fd < 0)
- err(1, "Creating temporary file %s", tmpsrc);
-
- contents = talloc_asprintf(tmpsrc, "#include <ccan/%s/%s.h>\n",
- m->basename, m->basename);
- if (write(fd, contents, strlen(contents)) != strlen(contents))
- err(1, "writing to temporary file %s", tmpsrc);
- close(fd);
-
- if (compile_object(score, tmpsrc, ccan_dir, "", tmpobj, &cmdout)) {
- score->pass = true;
- score->score = score->total;
- } else {
- score->error = talloc_asprintf(score,
- "#include of the main header file:\n%s",
- cmdout);
- }
-}
-
-struct ccanlint main_header_compiles = {
- .key = "main_header_compiles",
- .name = "Modules main header compiles",
- .check = check_includes_build,
- .can_run = can_build,
- .needs = "depends_exist main_header_exists"
-};
-
-REGISTER_TEST(main_header_compiles);
--- /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>
+#include "build.h"
+
+static const char *can_build(struct manifest *m)
+{
+ if (safe_mode)
+ return "Safe mode enabled";
+ return NULL;
+}
+
+static bool expect_obj_file(struct manifest *m)
+{
+ /* If it has C files, we expect an object file built from them. */
+ return !list_empty(&m->c_files);
+}
+
+static char *build_subdir_objs(struct manifest *m)
+{
+ struct ccan_file *i;
+
+ list_for_each(&m->c_files, i, list) {
+ char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
+ char *output;
+
+ i->compiled = maybe_temp_file(m, "", false, fullfile);
+ if (!compile_object(m, fullfile, ccan_dir, "", i->compiled,
+ &output)) {
+ talloc_free(i->compiled);
+ i->compiled = NULL;
+ return talloc_asprintf(m,
+ "Dependency %s"
+ " did not build:\n%s",
+ m->basename, output);
+ }
+ }
+ return NULL;
+}
+
+char *build_submodule(struct manifest *m)
+{
+ char *errstr;
+ struct stat st;
+
+ if (m->compiled)
+ return NULL;
+
+ if (!expect_obj_file(m))
+ return NULL;
+
+ m->compiled = talloc_asprintf(m, "%s.o", m->dir);
+ if (stat(m->compiled, &st) == 0)
+ return NULL;
+
+ if (verbose >= 2)
+ printf(" Building dependency %s\n", m->dir);
+
+ errstr = build_subdir_objs(m);
+ if (errstr)
+ return errstr;
+
+ m->compiled = build_module(m, false, &errstr);
+ if (!m->compiled)
+ return errstr;
+ return NULL;
+}
+
+static void check_depends_built(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft, struct score *score)
+{
+ struct manifest *i;
+
+ list_for_each(&m->deps, i, list) {
+ char *errstr = build_submodule(i);
+
+ if (errstr) {
+ score->error = talloc_asprintf(score,
+ "Dependency %s"
+ " did not build:\n%s",
+ i->basename, errstr);
+ return;
+ }
+ }
+
+ score->pass = true;
+ score->score = score->total;
+}
+
+struct ccanlint depends_build = {
+ .key = "depends_build",
+ .name = "Module's CCAN dependencies can be found or built",
+ .check = check_depends_built,
+ .can_run = can_build,
+ .needs = "depends_exist"
+};
+
+REGISTER_TEST(depends_build);
--- /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 bool add_dep(struct manifest *m, const char *dep, struct score *score)
+{
+ struct stat st;
+ struct manifest *subm;
+ char *dir = talloc_asprintf(m, "%s/%s", ccan_dir, dep);
+
+ /* FIXME: get_manifest has a tendency to exit. */
+ if (stat(dir, &st) != 0) {
+ score->error
+ = talloc_asprintf(m,
+ "Could not stat dependency %s: %s",
+ dir, strerror(errno));
+ return false;
+ }
+ subm = get_manifest(m, dir);
+ list_add_tail(&m->deps, &subm->list);
+ return true;
+}
+
+static void check_depends_exist(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft, struct score *score)
+{
+ unsigned int i;
+ char **deps;
+ char *updir = talloc_strdup(m, m->dir);
+
+ *strrchr(updir, '/') = '\0';
+
+ if (safe_mode)
+ deps = get_safe_ccan_deps(m, m->dir, true,
+ &m->info_file->compiled);
+ else
+ deps = get_deps(m, m->dir, true, &m->info_file->compiled);
+
+ for (i = 0; deps[i]; i++) {
+ if (!strstarts(deps[i], "ccan/"))
+ continue;
+
+ if (!add_dep(m, deps[i], score))
+ return;
+ }
+
+ /* We may need libtap for testing, unless we're "tap" */
+ if (!streq(m->basename, "tap")
+ && (!list_empty(&m->run_tests) || !list_empty(&m->api_tests))) {
+ if (!add_dep(m, "ccan/tap", score))
+ return;
+ }
+
+ score->pass = true;
+ score->score = score->total;
+}
+
+struct ccanlint depends_exist = {
+ .key = "depends_exist",
+ .name = "Module's CCAN dependencies can be found",
+ .check = check_depends_exist,
+ .needs = "info_exists"
+};
+
+REGISTER_TEST(depends_exist);
+++ /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>
-#include <ccan/talloc/talloc.h>
-
-static void check_has_info(struct manifest *m,
- bool keep,
- unsigned int *timeleft,
- struct score *score)
-{
- if (m->info_file) {
- score->pass = true;
- score->score = score->total;
- } else {
- score->error = "You have no _info file.\n\n"
- "The file _info 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, struct score *score)
-{
- FILE *info;
- const char *filename;
-
- if (!ask("Should I create a template _info file for you?"))
- return;
-
- filename = talloc_asprintf(m, "%s/%s", m->dir, "_info");
- info = fopen(filename, "w");
- if (!info)
- err(1, "Trying to create a template _info in %s", filename);
-
- if (fprintf(info, template, m->basename) < 0) {
- unlink_noerr(filename);
- err(1, "Writing template into %s", filename);
- }
- fclose(info);
-}
-
-struct ccanlint info_exists = {
- .key = "info_exists",
- .name = "Module has _info file",
- .check = check_has_info,
- .handle = create_info_template,
- .needs = ""
-};
-
-REGISTER_TEST(info_exists);
+++ /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,
- bool keep,
- unsigned int *timeleft, struct score *score)
-{
- 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) {
- score->pass = true;
- score->score = score->total;
- return;
- }
- }
- score->error = talloc_asprintf(score,
- "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 main_header_exists = {
- .key = "main_header_exists",
- .name = "Module has main header file",
- .check = check_has_main_header,
- .needs = ""
-};
-
-REGISTER_TEST(main_header_exists);
--- /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>
+#include <ccan/talloc/talloc.h>
+
+static void check_has_info(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
+{
+ if (m->info_file) {
+ score->pass = true;
+ score->score = score->total;
+ } else {
+ score->error = "You have no _info file.\n\n"
+ "The file _info 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, struct score *score)
+{
+ FILE *info;
+ const char *filename;
+
+ if (!ask("Should I create a template _info file for you?"))
+ return;
+
+ filename = talloc_asprintf(m, "%s/%s", m->dir, "_info");
+ info = fopen(filename, "w");
+ if (!info)
+ err(1, "Trying to create a template _info in %s", filename);
+
+ if (fprintf(info, template, m->basename) < 0) {
+ unlink_noerr(filename);
+ err(1, "Writing template into %s", filename);
+ }
+ fclose(info);
+}
+
+struct ccanlint info_exists = {
+ .key = "info_exists",
+ .name = "Module has _info file",
+ .check = check_has_info,
+ .handle = create_info_template,
+ .needs = ""
+};
+
+REGISTER_TEST(info_exists);
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/grab_file/grab_file.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 *can_build(struct manifest *m)
+{
+ if (safe_mode)
+ return "Safe mode enabled";
+ return NULL;
+}
+
+static struct ccan_file *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 f;
+ }
+ /* Should not happen: we depend on has_main_header */
+ abort();
+}
+
+static void check_includes_build(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft, struct score *score)
+{
+ char *contents;
+ char *tmpsrc, *tmpobj, *cmdout;
+ int fd;
+ struct ccan_file *mainh = main_header(m);
+
+ tmpsrc = maybe_temp_file(m, "-included.c", keep, mainh->fullname);
+ tmpobj = maybe_temp_file(m, ".o", keep, tmpsrc);
+
+ fd = open(tmpsrc, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ if (fd < 0)
+ err(1, "Creating temporary file %s", tmpsrc);
+
+ contents = talloc_asprintf(tmpsrc, "#include <ccan/%s/%s.h>\n",
+ m->basename, m->basename);
+ if (write(fd, contents, strlen(contents)) != strlen(contents))
+ err(1, "writing to temporary file %s", tmpsrc);
+ close(fd);
+
+ if (compile_object(score, tmpsrc, ccan_dir, "", tmpobj, &cmdout)) {
+ score->pass = true;
+ score->score = score->total;
+ } else {
+ score->error = talloc_asprintf(score,
+ "#include of the main header file:\n%s",
+ cmdout);
+ }
+}
+
+struct ccanlint main_header_compiles = {
+ .key = "main_header_compiles",
+ .name = "Modules main header compiles",
+ .check = check_includes_build,
+ .can_run = can_build,
+ .needs = "depends_exist main_header_exists"
+};
+
+REGISTER_TEST(main_header_compiles);
--- /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,
+ bool keep,
+ unsigned int *timeleft, struct score *score)
+{
+ 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) {
+ score->pass = true;
+ score->score = score->total;
+ return;
+ }
+ }
+ score->error = talloc_asprintf(score,
+ "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 main_header_exists = {
+ .key = "main_header_exists",
+ .name = "Module has main header file",
+ .check = check_has_main_header,
+ .needs = ""
+};
+
+REGISTER_TEST(main_header_exists);
--- /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>
+#include "build.h"
+
+static const char *can_build(struct manifest *m)
+{
+ if (safe_mode)
+ return "Safe mode enabled";
+ return NULL;
+}
+
+static char *obj_list(const struct manifest *m)
+{
+ char *list = talloc_strdup(m, "");
+ struct ccan_file *i;
+
+ /* Objects from all the C files. */
+ list_for_each(&m->c_files, i, list)
+ list = talloc_asprintf_append(list, "%s ", i->compiled);
+
+ return list;
+}
+
+char *build_module(struct manifest *m, bool keep, char **errstr)
+{
+ char *name = link_objects(m, m->basename, false, obj_list(m), errstr);
+ if (name) {
+ if (keep) {
+ char *realname = talloc_asprintf(m, "%s.o", m->dir);
+ /* We leave this object file around, all built. */
+ if (!move_file(name, realname))
+ err(1, "Renaming %s to %s", name, realname);
+ name = realname;
+ }
+ }
+ return name;
+}
+
+static void do_build(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
+{
+ char *errstr;
+
+ if (list_empty(&m->c_files)) {
+ /* No files? No score, but we "pass". */
+ score->total = 0;
+ score->pass = true;
+ return;
+ }
+
+ m->compiled = build_module(m, keep, &errstr);
+ if (!m->compiled) {
+ score_file_error(score, NULL, 0, errstr);
+ return;
+ }
+
+ score->pass = true;
+ score->score = score->total;
+}
+
+struct ccanlint module_builds = {
+ .key = "module_builds",
+ .name = "Module can be built from object files",
+ .check = do_build,
+ .can_run = can_build,
+ .needs = "objects_build"
+};
+
+REGISTER_TEST(module_builds);
--- /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 *can_build(struct manifest *m)
+{
+ if (safe_mode)
+ return "Safe mode enabled";
+ return NULL;
+}
+
+static char *obj_list(const struct manifest *m)
+{
+ char *list = talloc_strdup(m, "");
+ struct manifest *i;
+
+ /* Other CCAN deps. */
+ list_for_each(&m->deps, i, list) {
+ if (i->compiled)
+ list = talloc_asprintf_append(list, "%s ",
+ i->compiled);
+ }
+ return list;
+}
+
+static char *lib_list(const struct manifest *m)
+{
+ unsigned int i, num;
+ char **libs = get_libs(m, ".", &num, &m->info_file->compiled);
+ char *ret = talloc_strdup(m, "");
+
+ for (i = 0; i < num; i++)
+ ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
+ return ret;
+}
+
+static void check_use_build(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft, struct score *score)
+{
+ char *contents;
+ char *tmpfile, *cmdout;
+ char *basename = talloc_asprintf(m, "%s/example.c", m->dir);
+ int fd;
+
+ tmpfile = maybe_temp_file(m, ".c", keep, basename);
+
+ fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ if (fd < 0)
+ err(1, "Creating temporary file %s", tmpfile);
+
+ contents = talloc_asprintf(tmpfile,
+ "#include <ccan/%s/%s.h>\n"
+ "int main(void)\n"
+ "{\n"
+ " return 0;\n"
+ "}\n",
+ m->basename, m->basename);
+ if (write(fd, contents, strlen(contents)) != strlen(contents))
+ err(1, "Failure writing to temporary file %s", tmpfile);
+ close(fd);
+
+ if (compile_and_link(score, tmpfile, ccan_dir, obj_list(m), "",
+ lib_list(m),
+ maybe_temp_file(m, "", keep, tmpfile),
+ &cmdout)) {
+ score->pass = true;
+ score->score = score->total;
+ } else {
+ score->error = cmdout;
+ }
+}
+
+struct ccanlint module_links = {
+ .key = "module_links",
+ .name = "Module can be linked against trivial program",
+ .check = check_use_build,
+ .can_run = can_build,
+ .needs = "module_builds depends_build"
+};
+
+REGISTER_TEST(module_links);
--- /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 *can_build(struct manifest *m)
+{
+ if (safe_mode)
+ return "Safe mode enabled";
+ return NULL;
+}
+
+static void check_objs_build(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft, struct score *score)
+{
+ struct ccan_file *i;
+ bool errors = false, warnings = false;
+
+ if (list_empty(&m->c_files))
+ score->total = 0;
+ else
+ score->total = 2;
+
+ list_for_each(&m->c_files, i, list) {
+ char *output;
+ char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
+
+ i->compiled = maybe_temp_file(m, "", keep, fullfile);
+ if (!compile_object(score, fullfile, ccan_dir, "", i->compiled,
+ &output)) {
+ talloc_free(i->compiled);
+ score->error = "Compiling object files";
+ score_file_error(score, i, 0, output);
+ errors = true;
+ } else if (!streq(output, "")) {
+ score->error = "Compiling object files gave warnings";
+ score_file_error(score, i, 0, output);
+ warnings = true;
+ }
+ }
+
+ if (!errors) {
+ score->pass = true;
+ score->score = score->total - warnings;
+ }
+}
+
+struct ccanlint objects_build = {
+ .key = "objects_build",
+ .name = "Module object files can be built",
+ .check = check_objs_build,
+ .can_run = can_build,
+ .needs = "depends_exist"
+};
+
+REGISTER_TEST(objects_build);
+++ /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>
-
-/* Note: we already test safe_mode in run_tests.c */
-static const char *can_run_coverage(struct manifest *m)
-{
- unsigned int timeleft = default_timeout_ms;
- char *output;
-
- if (!run_command(m, &timeleft, &output, "gcov -h"))
- return talloc_asprintf(m, "No gcov support: %s", output);
- return NULL;
-}
-
-static bool build_module_objs_with_coverage(struct manifest *m, bool keep,
- struct score *score,
- char **modobjs)
-{
- struct ccan_file *i;
-
- *modobjs = talloc_strdup(m, "");
- list_for_each(&m->c_files, i, list) {
- char *err;
- char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
-
- i->cov_compiled = maybe_temp_file(m, "", keep, fullfile);
- if (!compile_object(m, fullfile, ccan_dir, "",
- i->cov_compiled, &err)) {
- score_file_error(score, i, 0, err);
- talloc_free(i->cov_compiled);
- i->cov_compiled = NULL;
- return false;
- }
- *modobjs = talloc_asprintf_append(*modobjs,
- " %s", i->cov_compiled);
- }
- return true;
-}
-
-/* FIXME: Merge this into one place. */
-static char *obj_list(const struct manifest *m, const char *modobjs)
-{
- char *list = talloc_strdup(m, "");
- struct ccan_file *i;
- struct manifest *subm;
-
- /* Objects from any other C files. */
- list_for_each(&m->other_test_c_files, i, list)
- list = talloc_asprintf_append(list, " %s", i->compiled);
-
- if (modobjs)
- list = talloc_append_string(list, modobjs);
-
- /* Other ccan modules (don't need coverage versions of those). */
- list_for_each(&m->deps, subm, list) {
- if (subm->compiled)
- list = talloc_asprintf_append(list, " %s",
- subm->compiled);
- }
-
- return list;
-}
-
-static char *lib_list(const struct manifest *m)
-{
- unsigned int i, num;
- char **libs = get_libs(m, m->dir, &num, &m->info_file->compiled);
- char *ret = talloc_strdup(m, "");
-
- for (i = 0; i < num; i++)
- ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
- return ret;
-}
-
-static char *cov_compile(const void *ctx,
- struct manifest *m,
- struct ccan_file *file,
- const char *modobjs,
- bool keep)
-{
- char *output;
-
- file->cov_compiled = maybe_temp_file(ctx, "", keep, file->fullname);
- if (!compile_and_link(ctx, file->fullname, ccan_dir,
- obj_list(m, modobjs),
- COVERAGE_CFLAGS,
- lib_list(m), file->cov_compiled, &output)) {
- talloc_free(file->cov_compiled);
- file->cov_compiled = NULL;
- return output;
- }
- talloc_free(output);
- return NULL;
-}
-
-/* FIXME: Coverage from testable examples as well. */
-static void do_compile_coverage_tests(struct manifest *m,
- bool keep,
- unsigned int *timeleft,
- struct score *score)
-{
- char *cmdout, *modobjs = NULL;
- struct ccan_file *i;
-
- if (!list_empty(&m->api_tests)
- && !build_module_objs_with_coverage(m, keep, score, &modobjs)) {
- score->error = "Failed to compile module objects with coverage";
- return;
- }
-
- list_for_each(&m->run_tests, i, list) {
- cmdout = cov_compile(m, m, i, NULL, keep);
- if (cmdout) {
- score->error = "Failed to compile test with coverage";
- score_file_error(score, i, 0, cmdout);
- }
- }
-
- list_for_each(&m->api_tests, i, list) {
- cmdout = cov_compile(m, m, i, modobjs, keep);
- if (cmdout) {
- score->error = "Failed to compile test with coverage";
- score_file_error(score, i, 0, cmdout);
- }
- }
- if (!score->error) {
- score->pass = true;
- score->score = score->total;
- }
-}
-
-struct ccanlint tests_compile_coverage = {
- .key = "tests_compile_coverage",
- .name = "Module tests compile with " COVERAGE_CFLAGS,
- .check = do_compile_coverage_tests,
- .can_run = can_run_coverage,
- .needs = "tests_compile"
-};
-
-REGISTER_TEST(tests_compile_coverage);
+++ /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 *can_run(struct manifest *m)
-{
- if (safe_mode)
- return "Safe mode enabled";
- return NULL;
-}
-
-static bool compile(struct manifest *m,
- bool keep,
- struct ccan_file *cfile,
- char **output)
-{
- cfile->compiled = maybe_temp_file(m, ".o", keep, cfile->fullname);
- return compile_object(m, cfile->fullname, ccan_dir, "",
- cfile->compiled, output);
-}
-
-static void do_compile_test_helpers(struct manifest *m,
- bool keep,
- unsigned int *timeleft,
- struct score *score)
-{
- struct ccan_file *i;
- bool errors = false, warnings = false;
-
- if (list_empty(&m->other_test_c_files))
- score->total = 0;
- else
- score->total = 2;
-
- list_for_each(&m->other_test_c_files, i, list) {
- char *cmdout;
-
- if (!compile(m, keep, i, &cmdout)) {
- errors = true;
- score->error = "Failed to compile helper C files";
- score_file_error(score, i, 0, cmdout);
- } else if (!streq(cmdout, "")) {
- warnings = true;
- score->error = "Helper C files gave warnings";
- score_file_error(score, i, 0, cmdout);
- }
- }
-
- if (!errors) {
- score->pass = true;
- score->score = score->total - warnings;
- }
-}
-
-struct ccanlint tests_helpers_compile = {
- .key = "tests_helpers_compile",
- .name = "Module test helper objects compile",
- .check = do_compile_test_helpers,
- .can_run = can_run,
- .needs = "depends_build tests_exist"
-};
-
-REGISTER_TEST(tests_helpers_compile);
+++ /dev/null
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <ccan/foreach/foreach.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 *can_build(struct manifest *m)
-{
- if (safe_mode)
- return "Safe mode enabled";
- return NULL;
-}
-
-/* FIXME: Merge this into one place. */
-static char *obj_list(const struct manifest *m, bool link_with_module)
-{
- char *list = talloc_strdup(m, "");
- struct ccan_file *i;
- struct manifest *subm;
-
- /* Objects from any other C files. */
- list_for_each(&m->other_test_c_files, i, list)
- list = talloc_asprintf_append(list, " %s", i->compiled);
-
- /* Our own object files. */
- if (link_with_module)
- list_for_each(&m->c_files, i, list)
- list = talloc_asprintf_append(list, " %s", i->compiled);
-
- /* Other ccan modules. */
- list_for_each(&m->deps, subm, list) {
- if (subm->compiled)
- list = talloc_asprintf_append(list, " %s",
- subm->compiled);
- }
-
- return list;
-}
-
-static char *lib_list(const struct manifest *m)
-{
- unsigned int i, num;
- char **libs = get_libs(m, m->dir, &num, &m->info_file->compiled);
- char *ret = talloc_strdup(m, "");
-
- for (i = 0; i < num; i++)
- ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
- return ret;
-}
-
-static bool compile(const void *ctx,
- struct manifest *m,
- struct ccan_file *file,
- bool fail,
- bool link_with_module,
- bool keep, char **output)
-{
- file->compiled = maybe_temp_file(ctx, "", keep, file->fullname);
- if (!compile_and_link(ctx, file->fullname, ccan_dir,
- obj_list(m, link_with_module),
- fail ? "-DFAIL" : "",
- lib_list(m), file->compiled, output)) {
- talloc_free(file->compiled);
- return false;
- }
- return true;
-}
-
-static void do_compile_tests(struct manifest *m,
- bool keep,
- unsigned int *timeleft, struct score *score)
-{
- char *cmdout;
- struct ccan_file *i;
- struct list_head *list;
- bool errors = false, warnings = false;
-
- foreach_ptr(list, &m->compile_ok_tests, &m->run_tests, &m->api_tests) {
- list_for_each(list, i, list) {
- if (!compile(score, m, i, false, list == &m->api_tests,
- keep, &cmdout)) {
- score->error = "Failed to compile tests";
- score_file_error(score, i, 0, cmdout);
- errors = true;
- } else if (!streq(cmdout, "")) {
- score->error = "Test compiled with warnings";
- score_file_error(score, i, 0, cmdout);
- warnings = true;
- }
- }
- }
-
- /* The compile fail tests are a bit weird, handle them separately */
- if (errors)
- return;
-
- /* For historical reasons, "fail" often means "gives warnings" */
- list_for_each(&m->compile_fail_tests, i, list) {
- if (!compile(score, m, i, false, false, false, &cmdout)) {
- score->error = "Failed to compile without -DFAIL";
- score_file_error(score, i, 0, cmdout);
- return;
- }
- if (!streq(cmdout, "")) {
- score->error = "Compile with warnigns without -DFAIL";
- score_file_error(score, i, 0, cmdout);
- return;
- }
- if (compile(score, m, i, true, false, false, &cmdout)
- && streq(cmdout, "")) {
- score->error = "Compiled successfully with -DFAIL?";
- score_file_error(score, i, 0, NULL);
- return;
- }
- }
-
- score->pass = true;
- score->total = 2;
- score->score = 1 + !warnings;
-}
-
-struct ccanlint tests_compile = {
- .key = "tests_compile",
- .name = "Module tests compile",
- .check = do_compile_tests,
- .can_run = can_build,
- .needs = "tests_helpers_compile objects_build"
-};
-
-REGISTER_TEST(tests_compile);
--- /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>
+
+/* Creates and adds an example file. */
+static char *add_example(struct manifest *m, struct ccan_file *source,
+ bool keep,
+ struct doc_section *example)
+{
+ char *name;
+ unsigned int i;
+ int fd;
+ struct ccan_file *f;
+
+ name = talloc_asprintf(m, "%s/example-%s-%s.c",
+ talloc_dirname(m,
+ source->fullname),
+ source->name,
+ example->function);
+ /* example->function == 'struct foo' */
+ while (strchr(name, ' '))
+ *strchr(name, ' ') = '_';
+
+ name = maybe_temp_file(m, ".c", keep, name);
+ f = new_ccan_file(m, talloc_dirname(m, name), talloc_basename(m, name));
+ talloc_steal(f, name);
+ list_add_tail(&m->examples, &f->list);
+
+ fd = open(f->fullname, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ if (fd < 0)
+ return talloc_asprintf(m, "Creating temporary file %s: %s",
+ f->fullname, strerror(errno));
+
+ for (i = 0; i < example->num_lines; i++) {
+ if (write(fd, example->lines[i], strlen(example->lines[i]))
+ != strlen(example->lines[i])
+ || write(fd, "\n", 1) != 1) {
+ close(fd);
+ return "Failure writing to temporary file";
+ }
+ }
+ close(fd);
+ return NULL;
+}
+
+/* FIXME: We should have one example per function in header. */
+static void extract_examples(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
+{
+ struct ccan_file *f, *mainh = NULL; /* gcc complains uninitialized */
+ struct doc_section *d;
+ bool have_info_example = false, have_header_example = false;
+
+ score->total = 2;
+ list_for_each(get_ccan_file_docs(m->info_file), d, list) {
+ if (streq(d->type, "example")) {
+ score->error = add_example(m, m->info_file, keep, d);
+ if (score->error)
+ return;
+ have_info_example = true;
+ }
+ }
+
+ /* Check main header. */
+ list_for_each(&m->h_files, f, list) {
+ if (!strstarts(f->name, m->basename)
+ || strlen(f->name) != strlen(m->basename) + 2)
+ continue;
+
+ mainh = f;
+ list_for_each(get_ccan_file_docs(f), d, list) {
+ if (streq(d->type, "example")) {
+ score->error = add_example(m, f, keep, d);
+ if (score->error)
+ return;
+ have_header_example = true;
+ }
+ }
+ }
+
+ if (have_info_example && have_header_example) {
+ score->score = score->total;
+ score->pass = true;
+ return;
+ }
+
+ score->error = "Expect examples in header and _info";
+ if (!have_info_example)
+ score_file_error(score, m->info_file, 0, "No Example: section");
+ if (!have_header_example)
+ score_file_error(score, mainh, 0, "No Example: section");
+
+ score->score = have_info_example + have_header_example;
+ /* We pass if we find any example. */
+ score->pass = score->score != 0;
+}
+
+struct ccanlint examples_exist = {
+ .key = "examples_exist",
+ .name = "_info and main header file have Example: sections",
+ .check = extract_examples,
+ .needs = "info_exists"
+};
+
+REGISTER_TEST(examples_exist);
+++ /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>
-
-/* Creates and adds an example file. */
-static char *add_example(struct manifest *m, struct ccan_file *source,
- bool keep,
- struct doc_section *example)
-{
- char *name;
- unsigned int i;
- int fd;
- struct ccan_file *f;
-
- name = talloc_asprintf(m, "%s/example-%s-%s.c",
- talloc_dirname(m,
- source->fullname),
- source->name,
- example->function);
- /* example->function == 'struct foo' */
- while (strchr(name, ' '))
- *strchr(name, ' ') = '_';
-
- name = maybe_temp_file(m, ".c", keep, name);
- f = new_ccan_file(m, talloc_dirname(m, name), talloc_basename(m, name));
- talloc_steal(f, name);
- list_add_tail(&m->examples, &f->list);
-
- fd = open(f->fullname, O_WRONLY | O_CREAT | O_EXCL, 0600);
- if (fd < 0)
- return talloc_asprintf(m, "Creating temporary file %s: %s",
- f->fullname, strerror(errno));
-
- for (i = 0; i < example->num_lines; i++) {
- if (write(fd, example->lines[i], strlen(example->lines[i]))
- != strlen(example->lines[i])
- || write(fd, "\n", 1) != 1) {
- close(fd);
- return "Failure writing to temporary file";
- }
- }
- close(fd);
- return NULL;
-}
-
-/* FIXME: We should have one example per function in header. */
-static void extract_examples(struct manifest *m,
- bool keep,
- unsigned int *timeleft,
- struct score *score)
-{
- struct ccan_file *f, *mainh = NULL; /* gcc complains uninitialized */
- struct doc_section *d;
- bool have_info_example = false, have_header_example = false;
-
- score->total = 2;
- list_for_each(get_ccan_file_docs(m->info_file), d, list) {
- if (streq(d->type, "example")) {
- score->error = add_example(m, m->info_file, keep, d);
- if (score->error)
- return;
- have_info_example = true;
- }
- }
-
- /* Check main header. */
- list_for_each(&m->h_files, f, list) {
- if (!strstarts(f->name, m->basename)
- || strlen(f->name) != strlen(m->basename) + 2)
- continue;
-
- mainh = f;
- list_for_each(get_ccan_file_docs(f), d, list) {
- if (streq(d->type, "example")) {
- score->error = add_example(m, f, keep, d);
- if (score->error)
- return;
- have_header_example = true;
- }
- }
- }
-
- if (have_info_example && have_header_example) {
- score->score = score->total;
- score->pass = true;
- return;
- }
-
- score->error = "Expect examples in header and _info";
- if (!have_info_example)
- score_file_error(score, m->info_file, 0, "No Example: section");
- if (!have_header_example)
- score_file_error(score, mainh, 0, "No Example: section");
-
- score->score = have_info_example + have_header_example;
- /* We pass if we find any example. */
- score->pass = score->score != 0;
-}
-
-struct ccanlint examples_exist = {
- .key = "examples_exist",
- .name = "_info and main header file have Example: sections",
- .check = extract_examples,
- .needs = "info_exists"
-};
-
-REGISTER_TEST(examples_exist);
+++ /dev/null
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/doc_extract.h>
-#include <tools/tools.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>
-
-extern struct ccanlint info_documentation_exists;
-
-static void create_info_template_doc(struct manifest *m, struct score *score)
-{
- int fd = open("_info.new", O_WRONLY|O_CREAT|O_EXCL, 0666);
- FILE *new;
- char *oldcontents;
-
- if (fd < 0 || !(new = fdopen(fd, "w")))
- err(1, "Creating _info.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.new");
- err(1, "Writing to _info.new to insert documentation");
- }
-
- oldcontents = grab_file(m, "_info", NULL);
- if (!oldcontents) {
- unlink_noerr("_info.new");
- err(1, "Reading _info");
- }
- if (fprintf(new, "%s", oldcontents) < 0) {
- unlink_noerr("_info.new");
- err(1, "Appending _info to _info.new");
- }
- if (fclose(new) != 0) {
- unlink_noerr("_info.new");
- err(1, "Closing _info.new");
- }
- if (!move_file("_info.new", "_info")) {
- unlink_noerr("_info.new");
- err(1, "Renaming _info.new to _info");
- }
-}
-
-static void check_info_documentation_exists(struct manifest *m,
- bool keep,
- unsigned int *timeleft,
- struct score *score)
-{
- struct list_head *infodocs = get_ccan_file_docs(m->info_file);
- struct doc_section *d;
- bool summary = false, description = false;
-
- list_for_each(infodocs, d, list) {
- if (!streq(d->function, m->basename))
- continue;
- if (streq(d->type, "summary"))
- summary = true;
- if (streq(d->type, "description"))
- description = true;
- }
-
- if (summary && description) {
- score->score = score->total;
- score->pass = true;
- } else if (!summary) {
- score->error = "_info file has no module documentation.\n\n"
- "CCAN modules use /**-style comments for documentation: the\n"
- "overall documentation belongs in the _info metafile.\n";
- info_documentation_exists.handle = create_info_template_doc;
- } else if (!description) {
- score->error = "_info file has no module description.\n\n"
- "The lines after the first summary line in the _info file\n"
- "documentation should describe the purpose and use of the\n"
- "overall package\n";
- }
-}
-
-struct ccanlint info_documentation_exists = {
- .key = "info_documentation_exists",
- .name = "Module has documentation in _info",
- .check = check_info_documentation_exists,
- .needs = "info_exists"
-};
-
-REGISTER_TEST(info_documentation_exists);
+++ /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>
-
-extern struct ccanlint tests_exist;
-
-static void handle_no_tests(struct manifest *m, struct score *score)
-{
- FILE *run;
- struct ccan_file *i;
- char *test_dir = talloc_asprintf(m, "%s/test", m->dir);
-
- printf(
- "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 ccan/tap 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. The test will be run in a temporary\n"
- "directory, with the test directory symlinked under test/.\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 only API tests are linked against the files in the module!\n"
- );
-
- if (!ask("Should I create a template test/run.c file for you?"))
- return;
-
- if (mkdir(test_dir, 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");
-
- fprintf(run, "#include <ccan/%s/%s.h>\n", m->basename, m->basename);
- if (!list_empty(&m->c_files)) {
- fputs("/* Include the C files directly. */\n", run);
- list_for_each(&m->c_files, i, list)
- fprintf(run, "#include <ccan/%s/%s>\n",
- m->basename, i->name);
- }
- fprintf(run, "%s",
- "#include <ccan/tap/tap.h>\n\n"
- "int main(void)\n"
- "{\n"
- " /* This is how many tests you plan to run */\n"
- " plan_tests(3);\n"
- "\n"
- " /* Simple thing we expect to succeed */\n"
- " ok1(some_test())\n"
- " /* Same, with an explicit description of the test. */\n"
- " ok(some_test(), \"%s with no args should return 1\", \"some_test\")\n"
- " /* How to print out messages for debugging. */\n"
- " diag(\"Address of some_test is %p\", &some_test)\n"
- " /* Conditional tests must be explicitly skipped. */\n"
- "#if HAVE_SOME_FEATURE\n"
- " ok1(test_some_feature())\n"
- "#else\n"
- " skip(1, \"Don\'t have SOME_FEATURE\")\n"
- "#endif\n"
- "\n"
- " /* This exits depending on whether all tests passed */\n"
- " return exit_status();\n"
- "}\n");
- fclose(run);
-}
-
-static void check_tests_exist(struct manifest *m,
- bool keep,
- unsigned int *timeleft, struct score *score)
-{
- struct stat st;
- char *test_dir = talloc_asprintf(m, "%s/test", m->dir);
-
- if (lstat(test_dir, &st) != 0) {
- score->error = "No test directory";
- if (errno != ENOENT)
- err(1, "statting %s", test_dir);
- tests_exist.handle = handle_no_tests;
- return;
- }
-
- if (!S_ISDIR(st.st_mode)) {
- score->error = "test is not a directory";
- return;
- }
-
- if (list_empty(&m->api_tests)
- && list_empty(&m->run_tests)
- && list_empty(&m->compile_ok_tests)) {
- if (list_empty(&m->compile_fail_tests)) {
- score->error = "No tests in test directory";
- tests_exist.handle = handle_no_tests;
- } else
- score->error = "No positive tests in test directory";
- return;
- }
- score->pass = true;
- score->score = score->total;
-}
-
-struct ccanlint tests_exist = {
- .key = "tests_exist",
- .name = "Module has test directory with tests in it",
- .check = check_tests_exist,
- .needs = ""
-};
-
-REGISTER_TEST(tests_exist);
--- /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 CCAN_<MODNAME>_H\n"
+ "#define CCAN_<MODNAME>_H\n"
+ "...\n"
+ "#endif /* CCAN_<MODNAME>_H */\n";
+
+static void fix_name(char *name)
+{
+ unsigned int i;
+
+ for (i = 0; name[i]; i++) {
+ if (isalnum(name[i]))
+ name[i] = toupper(name[i]);
+ else
+ name[i] = '_';
+ }
+}
+
+static void handle_idem(struct manifest *m, struct score *score)
+{
+ struct file_error *e;
+
+ list_for_each(&score->per_file_errors, e, list) {
+ char *name, *q, *tmpname;
+ FILE *out;
+ unsigned int i;
+
+ /* Main header gets CCAN_FOO_H, others CCAN_FOO_XXX_H */
+ if (strstarts(e->file->name, m->basename)
+ || strlen(e->file->name) == strlen(m->basename) + 2)
+ name = talloc_asprintf(score, "CCAN_%s_H", m->basename);
+ else
+ name = talloc_asprintf(score, "CCAN_%s_%s",
+ m->basename, e->file->name);
+ fix_name(name);
+
+ q = talloc_asprintf(score,
+ "Should I wrap %s in #ifndef/#define %s for you?",
+ e->file->name, name);
+ if (!ask(q))
+ continue;
+
+ tmpname = maybe_temp_file(score, ".h", false, e->file->name);
+ out = fopen(tmpname, "w");
+ if (!out)
+ err(1, "Opening %s", tmpname);
+ if (fprintf(out, "#ifndef %s\n#define %s\n", name, name) < 0)
+ err(1, "Writing %s", tmpname);
+
+ for (i = 0; i < e->file->num_lines; i++)
+ if (fprintf(out, "%s\n", e->file->lines[i]) < 0)
+ err(1, "Writing %s", tmpname);
+
+ if (fprintf(out, "#endif /* %s */\n", name) < 0)
+ err(1, "Writing %s", tmpname);
+
+ if (fclose(out) != 0)
+ err(1, "Closing %s", tmpname);
+
+ if (!move_file(tmpname, e->file->fullname))
+ err(1, "Moving %s to %s", tmpname, e->file->fullname);
+ }
+}
+
+static bool check_idem(struct ccan_file *f, struct score *score)
+{
+ 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 true;
+
+ 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) {
+ score_file_error(score, f, i+1,
+ "Expect first non-comment line to be"
+ " #ifndef.");
+ return false;
+ } else if (line_info[i].type == PREPROC_LINE)
+ break;
+ }
+
+ /* No code at all? Don't complain. */
+ if (i == f->num_lines)
+ return true;
+
+ 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) {
+ score_file_error(score, f, i+1,
+ "Expect second non-comment line to be"
+ " #define.");
+ return false;
+ } else if (line_info[i].type == PREPROC_LINE)
+ break;
+ }
+
+ /* No code at all? Weird. */
+ if (i == f->num_lines)
+ return true;
+
+ /* We expect a condition on this line. */
+ if (!line_info[i].cond) {
+ score_file_error(score, f, i+1, "Expected #ifndef");
+ return false;
+ }
+
+ 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) {
+ score_file_error(score, f, i+1, "Expected #ifndef");
+ return false;
+ }
+
+ /* And this to be #define <symbol> */
+ if (!get_token(&line, "#"))
+ abort();
+ if (!get_token(&line, "define")) {
+ char *str = talloc_asprintf(score,
+ "expected '#define %s'",
+ line_info[i].cond->symbol);
+ score_file_error(score, f, i+1, str);
+ return false;
+ }
+ sym = get_symbol_token(f, &line);
+ if (!sym || !streq(sym, line_info[i].cond->symbol)) {
+ char *str = talloc_asprintf(score,
+ "expected '#define %s'",
+ line_info[i].cond->symbol);
+ score_file_error(score, f, i+1, str);
+ return false;
+ }
+
+ /* 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) {
+ score_file_error(score, f, i+1, "code outside"
+ " idempotent region");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void check_idempotent(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft, struct score *score)
+{
+ struct ccan_file *f;
+
+ list_for_each(&m->h_files, f, list) {
+ if (!check_idem(f, score))
+ score->error = "Headers are not idempotent";
+ }
+ if (!score->error) {
+ score->pass = true;
+ score->score = score->total;
+ }
+}
+
+struct ccanlint headers_idempotent = {
+ .key = "headers_idempotent",
+ .name = "Module headers are #ifndef/#define wrapped",
+ .check = check_idempotent,
+ .handle = handle_idem,
+ .needs = ""
+};
+
+REGISTER_TEST(headers_idempotent);
+++ /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 CCAN_<MODNAME>_H\n"
- "#define CCAN_<MODNAME>_H\n"
- "...\n"
- "#endif /* CCAN_<MODNAME>_H */\n";
-
-static void fix_name(char *name)
-{
- unsigned int i;
-
- for (i = 0; name[i]; i++) {
- if (isalnum(name[i]))
- name[i] = toupper(name[i]);
- else
- name[i] = '_';
- }
-}
-
-static void handle_idem(struct manifest *m, struct score *score)
-{
- struct file_error *e;
-
- list_for_each(&score->per_file_errors, e, list) {
- char *name, *q, *tmpname;
- FILE *out;
- unsigned int i;
-
- /* Main header gets CCAN_FOO_H, others CCAN_FOO_XXX_H */
- if (strstarts(e->file->name, m->basename)
- || strlen(e->file->name) == strlen(m->basename) + 2)
- name = talloc_asprintf(score, "CCAN_%s_H", m->basename);
- else
- name = talloc_asprintf(score, "CCAN_%s_%s",
- m->basename, e->file->name);
- fix_name(name);
-
- q = talloc_asprintf(score,
- "Should I wrap %s in #ifndef/#define %s for you?",
- e->file->name, name);
- if (!ask(q))
- continue;
-
- tmpname = maybe_temp_file(score, ".h", false, e->file->name);
- out = fopen(tmpname, "w");
- if (!out)
- err(1, "Opening %s", tmpname);
- if (fprintf(out, "#ifndef %s\n#define %s\n", name, name) < 0)
- err(1, "Writing %s", tmpname);
-
- for (i = 0; i < e->file->num_lines; i++)
- if (fprintf(out, "%s\n", e->file->lines[i]) < 0)
- err(1, "Writing %s", tmpname);
-
- if (fprintf(out, "#endif /* %s */\n", name) < 0)
- err(1, "Writing %s", tmpname);
-
- if (fclose(out) != 0)
- err(1, "Closing %s", tmpname);
-
- if (!move_file(tmpname, e->file->fullname))
- err(1, "Moving %s to %s", tmpname, e->file->fullname);
- }
-}
-
-static bool check_idem(struct ccan_file *f, struct score *score)
-{
- 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 true;
-
- 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) {
- score_file_error(score, f, i+1,
- "Expect first non-comment line to be"
- " #ifndef.");
- return false;
- } else if (line_info[i].type == PREPROC_LINE)
- break;
- }
-
- /* No code at all? Don't complain. */
- if (i == f->num_lines)
- return true;
-
- 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) {
- score_file_error(score, f, i+1,
- "Expect second non-comment line to be"
- " #define.");
- return false;
- } else if (line_info[i].type == PREPROC_LINE)
- break;
- }
-
- /* No code at all? Weird. */
- if (i == f->num_lines)
- return true;
-
- /* We expect a condition on this line. */
- if (!line_info[i].cond) {
- score_file_error(score, f, i+1, "Expected #ifndef");
- return false;
- }
-
- 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) {
- score_file_error(score, f, i+1, "Expected #ifndef");
- return false;
- }
-
- /* And this to be #define <symbol> */
- if (!get_token(&line, "#"))
- abort();
- if (!get_token(&line, "define")) {
- char *str = talloc_asprintf(score,
- "expected '#define %s'",
- line_info[i].cond->symbol);
- score_file_error(score, f, i+1, str);
- return false;
- }
- sym = get_symbol_token(f, &line);
- if (!sym || !streq(sym, line_info[i].cond->symbol)) {
- char *str = talloc_asprintf(score,
- "expected '#define %s'",
- line_info[i].cond->symbol);
- score_file_error(score, f, i+1, str);
- return false;
- }
-
- /* 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) {
- score_file_error(score, f, i+1, "code outside"
- " idempotent region");
- return false;
- }
- }
-
- return true;
-}
-
-static void check_idempotent(struct manifest *m,
- bool keep,
- unsigned int *timeleft, struct score *score)
-{
- struct ccan_file *f;
-
- list_for_each(&m->h_files, f, list) {
- if (!check_idem(f, score))
- score->error = "Headers are not idempotent";
- }
- if (!score->error) {
- score->pass = true;
- score->score = score->total;
- }
-}
-
-struct ccanlint headers_idempotent = {
- .key = "headers_idempotent",
- .name = "Module headers are #ifndef/#define wrapped",
- .check = check_idempotent,
- .handle = handle_idem,
- .needs = ""
-};
-
-REGISTER_TEST(headers_idempotent);
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/doc_extract.h>
+#include <tools/tools.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>
+
+extern struct ccanlint info_documentation_exists;
+
+static void create_info_template_doc(struct manifest *m, struct score *score)
+{
+ int fd = open("_info.new", O_WRONLY|O_CREAT|O_EXCL, 0666);
+ FILE *new;
+ char *oldcontents;
+
+ if (fd < 0 || !(new = fdopen(fd, "w")))
+ err(1, "Creating _info.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.new");
+ err(1, "Writing to _info.new to insert documentation");
+ }
+
+ oldcontents = grab_file(m, "_info", NULL);
+ if (!oldcontents) {
+ unlink_noerr("_info.new");
+ err(1, "Reading _info");
+ }
+ if (fprintf(new, "%s", oldcontents) < 0) {
+ unlink_noerr("_info.new");
+ err(1, "Appending _info to _info.new");
+ }
+ if (fclose(new) != 0) {
+ unlink_noerr("_info.new");
+ err(1, "Closing _info.new");
+ }
+ if (!move_file("_info.new", "_info")) {
+ unlink_noerr("_info.new");
+ err(1, "Renaming _info.new to _info");
+ }
+}
+
+static void check_info_documentation_exists(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
+{
+ struct list_head *infodocs = get_ccan_file_docs(m->info_file);
+ struct doc_section *d;
+ bool summary = false, description = false;
+
+ list_for_each(infodocs, d, list) {
+ if (!streq(d->function, m->basename))
+ continue;
+ if (streq(d->type, "summary"))
+ summary = true;
+ if (streq(d->type, "description"))
+ description = true;
+ }
+
+ if (summary && description) {
+ score->score = score->total;
+ score->pass = true;
+ } else if (!summary) {
+ score->error = "_info file has no module documentation.\n\n"
+ "CCAN modules use /**-style comments for documentation: the\n"
+ "overall documentation belongs in the _info metafile.\n";
+ info_documentation_exists.handle = create_info_template_doc;
+ } else if (!description) {
+ score->error = "_info file has no module description.\n\n"
+ "The lines after the first summary line in the _info file\n"
+ "documentation should describe the purpose and use of the\n"
+ "overall package\n";
+ }
+}
+
+struct ccanlint info_documentation_exists = {
+ .key = "info_documentation_exists",
+ .name = "Module has documentation in _info",
+ .check = check_info_documentation_exists,
+ .needs = "info_exists"
+};
+
+REGISTER_TEST(info_documentation_exists);
+++ /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>
-#include <ccan/str/str.h>
-
-struct ccanlint has_license;
-
-static struct doc_section *find_license(const struct manifest *m)
-{
- struct doc_section *d;
-
- list_for_each(m->info_file->doc_sections, d, list) {
- if (!streq(d->function, m->basename))
- continue;
- if (streq(d->type, "license"))
- return d;
- }
- return NULL;
-}
-
-static const char *expected_link(const struct manifest *m,
- struct doc_section *d)
-{
- if (streq(d->lines[0], "GPL")
- || streq(d->lines[0], "GPLv3")
- || streq(d->lines[0], "GPLv3 or later")
- || streq(d->lines[0], "GPLv3 (or later)")
- || streq(d->lines[0], "GPL (3 or any later version)"))
- return "../../licenses/GPL-3";
- if (streq(d->lines[0], "GPLv2")
- || streq(d->lines[0], "GPLv2 or later")
- || streq(d->lines[0], "GPLv2 (or later)")
- || streq(d->lines[0], "GPL (2 or any later version)"))
- return "../../licenses/GPL-3";
- if (streq(d->lines[0], "LGPL")
- || streq(d->lines[0], "LGPLv3")
- || streq(d->lines[0], "LGPLv3 or later")
- || streq(d->lines[0], "LGPLv3 (or later)")
- || streq(d->lines[0], "LGPL (3 or any later version)"))
- return "../../licenses/LGPL-3";
- if (streq(d->lines[0], "LGPLv2")
- || streq(d->lines[0], "LGPLv2 or later")
- || streq(d->lines[0], "LGPLv2 (or later)")
- || streq(d->lines[0], "LGPL (2 or any later version)"))
- return "../../licenses/LGPL-2.1";
- if (streq(d->lines[0], "BSD")
- || streq(d->lines[0], "BSD-MIT")
- || streq(d->lines[0], "MIT"))
- return "../../licenses/BSD-MIT";
- return NULL;
-}
-
-static void handle_license_link(struct manifest *m, struct score *score)
-{
- const char *link = talloc_asprintf(m, "%s/LICENSE", m->dir);
- struct doc_section *d = find_license(m);
- const char *ldest = expected_link(m, d);
- char *q;
-
- printf(
- "Most modules want a copy of their license, so usually we create a\n"
- "LICENSE symlink into ../../licenses to avoid too many copies.\n");
-
- /* FIXME: make ask printf-like */
- q = talloc_asprintf(m, "Set up link to %s (license is %s)?",
- ldest, d->lines[0]);
- if (ask(q)) {
- if (symlink(ldest, link) != 0)
- err(1, "Creating symlink %s -> %s", link, ldest);
- }
-}
-
-static void check_has_license(struct manifest *m,
- bool keep,
- unsigned int *timeleft, struct score *score)
-{
- char buf[PATH_MAX];
- ssize_t len;
- char *license = talloc_asprintf(m, "%s/LICENSE", m->dir);
- const char *expected;
- struct doc_section *d;
-
- d = find_license(m);
- if (!d) {
- score->error = "No License: tag in _info";
- return;
- }
- expected = expected_link(m, d);
-
- len = readlink(license, buf, sizeof(buf));
- if (len < 0) {
- /* Could be a real file... OK if not a standard license. */
- if (errno == EINVAL) {
- if (!expected) {
- score->pass = true;
- return;
- }
- score->error
- = talloc_asprintf(score,
- "License in _info is '%s',"
- " expect LICENSE symlink '%s'",
- d->lines[0], expected);
- return;
- }
- if (errno == ENOENT) {
- score->error = "LICENSE does not exist";
- if (expected)
- has_license.handle = handle_license_link;
- return;
- }
- err(1, "readlink on %s", license);
- }
- if (len >= sizeof(buf))
- errx(1, "Reading symlink %s gave huge result", license);
-
- buf[len] = '\0';
-
- if (!strstarts(buf, "../../licenses/")) {
- score->error = talloc_asprintf(score,
- "Expected symlink to"
- " ../../licenses/..."
- " not %s", buf);
- return;
- }
-
- if (!expected) {
- score->error = talloc_asprintf(score,
- "License in _info is unknown '%s',"
- " but LICENSE symlink is '%s'",
- d->lines[0], buf);
- return;
- }
-
- if (!streq(buf, expected)) {
- score->error = talloc_asprintf(score,
- "Expected symlink to %s not %s",
- expected, buf);
- return;
- }
- score->pass = true;
- score->score = score->total;
-}
-
-struct ccanlint license_exists = {
- .key = "license_exists",
- .name = "Module has License: entry in _info, and LICENSE symlink/file",
- .check = check_has_license,
- .needs = "info_exists"
-};
-
-REGISTER_TEST(license_exists);
--- /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>
+#include <ccan/str/str.h>
+
+struct ccanlint has_license;
+
+static struct doc_section *find_license(const struct manifest *m)
+{
+ struct doc_section *d;
+
+ list_for_each(m->info_file->doc_sections, d, list) {
+ if (!streq(d->function, m->basename))
+ continue;
+ if (streq(d->type, "license"))
+ return d;
+ }
+ return NULL;
+}
+
+static const char *expected_link(const struct manifest *m,
+ struct doc_section *d)
+{
+ if (streq(d->lines[0], "GPL")
+ || streq(d->lines[0], "GPLv3")
+ || streq(d->lines[0], "GPLv3 or later")
+ || streq(d->lines[0], "GPLv3 (or later)")
+ || streq(d->lines[0], "GPL (3 or any later version)"))
+ return "../../licenses/GPL-3";
+ if (streq(d->lines[0], "GPLv2")
+ || streq(d->lines[0], "GPLv2 or later")
+ || streq(d->lines[0], "GPLv2 (or later)")
+ || streq(d->lines[0], "GPL (2 or any later version)"))
+ return "../../licenses/GPL-3";
+ if (streq(d->lines[0], "LGPL")
+ || streq(d->lines[0], "LGPLv3")
+ || streq(d->lines[0], "LGPLv3 or later")
+ || streq(d->lines[0], "LGPLv3 (or later)")
+ || streq(d->lines[0], "LGPL (3 or any later version)"))
+ return "../../licenses/LGPL-3";
+ if (streq(d->lines[0], "LGPLv2")
+ || streq(d->lines[0], "LGPLv2 or later")
+ || streq(d->lines[0], "LGPLv2 (or later)")
+ || streq(d->lines[0], "LGPL (2 or any later version)"))
+ return "../../licenses/LGPL-2.1";
+ if (streq(d->lines[0], "BSD")
+ || streq(d->lines[0], "BSD-MIT")
+ || streq(d->lines[0], "MIT"))
+ return "../../licenses/BSD-MIT";
+ return NULL;
+}
+
+static void handle_license_link(struct manifest *m, struct score *score)
+{
+ const char *link = talloc_asprintf(m, "%s/LICENSE", m->dir);
+ struct doc_section *d = find_license(m);
+ const char *ldest = expected_link(m, d);
+ char *q;
+
+ printf(
+ "Most modules want a copy of their license, so usually we create a\n"
+ "LICENSE symlink into ../../licenses to avoid too many copies.\n");
+
+ /* FIXME: make ask printf-like */
+ q = talloc_asprintf(m, "Set up link to %s (license is %s)?",
+ ldest, d->lines[0]);
+ if (ask(q)) {
+ if (symlink(ldest, link) != 0)
+ err(1, "Creating symlink %s -> %s", link, ldest);
+ }
+}
+
+static void check_has_license(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft, struct score *score)
+{
+ char buf[PATH_MAX];
+ ssize_t len;
+ char *license = talloc_asprintf(m, "%s/LICENSE", m->dir);
+ const char *expected;
+ struct doc_section *d;
+
+ d = find_license(m);
+ if (!d) {
+ score->error = "No License: tag in _info";
+ return;
+ }
+ expected = expected_link(m, d);
+
+ len = readlink(license, buf, sizeof(buf));
+ if (len < 0) {
+ /* Could be a real file... OK if not a standard license. */
+ if (errno == EINVAL) {
+ if (!expected) {
+ score->pass = true;
+ return;
+ }
+ score->error
+ = talloc_asprintf(score,
+ "License in _info is '%s',"
+ " expect LICENSE symlink '%s'",
+ d->lines[0], expected);
+ return;
+ }
+ if (errno == ENOENT) {
+ score->error = "LICENSE does not exist";
+ if (expected)
+ has_license.handle = handle_license_link;
+ return;
+ }
+ err(1, "readlink on %s", license);
+ }
+ if (len >= sizeof(buf))
+ errx(1, "Reading symlink %s gave huge result", license);
+
+ buf[len] = '\0';
+
+ if (!strstarts(buf, "../../licenses/")) {
+ score->error = talloc_asprintf(score,
+ "Expected symlink to"
+ " ../../licenses/..."
+ " not %s", buf);
+ return;
+ }
+
+ if (!expected) {
+ score->error = talloc_asprintf(score,
+ "License in _info is unknown '%s',"
+ " but LICENSE symlink is '%s'",
+ d->lines[0], buf);
+ return;
+ }
+
+ if (!streq(buf, expected)) {
+ score->error = talloc_asprintf(score,
+ "Expected symlink to %s not %s",
+ expected, buf);
+ return;
+ }
+ score->pass = true;
+ score->score = score->total;
+}
+
+struct ccanlint license_exists = {
+ .key = "license_exists",
+ .name = "Module has License: entry in _info, and LICENSE symlink/file",
+ .check = check_has_license,
+ .needs = "info_exists"
+};
+
+REGISTER_TEST(license_exists);
--- /dev/null
+/* Trailing whitespace test. Almost embarrassing, but trivial. */
+#include <tools/ccanlint/ccanlint.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/foreach/foreach.h>
+#include <ccan/str/str.h>
+
+/* FIXME: only print full analysis if verbose >= 2. */
+static char *get_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,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
+{
+ struct list_head *list;
+ struct ccan_file *f;
+ unsigned int i;
+
+ foreach_ptr(list, &m->c_files, &m->h_files) {
+ list_for_each(list, f, list) {
+ char **lines = get_ccan_file_lines(f);
+ for (i = 0; i < f->num_lines; i++) {
+ char *err = get_trailing_whitespace(lines[i]);
+ if (err) {
+ score->error = "Trailing whitespace"
+ " found";
+ score_file_error(score, f, i+1, err);
+ }
+ }
+ }
+ }
+ if (!score->error) {
+ score->pass = true;
+ score->score = score->total;
+ }
+}
+
+struct ccanlint no_trailing_whitespace = {
+ .key = "no_trailing_whitespace",
+ .name = "Module's source code has no trailing whitespace",
+ .check = check_trailing_whitespace,
+ .needs = ""
+};
+
+
+REGISTER_TEST(no_trailing_whitespace);
+++ /dev/null
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str_talloc/str_talloc.h>
-#include <ccan/grab_file/grab_file.h>
-#include <ccan/str/str.h>
-#include <ccan/foreach/foreach.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 bool find_source_file(const struct manifest *m, const char *filename)
-{
- const struct ccan_file *i;
-
- list_for_each(&m->c_files, i, list) {
- if (streq(i->fullname, filename))
- return true;
- }
- list_for_each(&m->h_files, i, list) {
- if (streq(i->fullname, filename))
- return true;
- }
- return false;
-}
-
-/* 1 point for 50%, 2 points for 75%, 3 points for 87.5%... Bonus for 100%. */
-static unsigned int score_coverage(float covered, unsigned total)
-{
- float thresh, uncovered = 1.0 - covered;
- unsigned int i;
-
- if (covered == 1.0)
- return total;
-
- total--;
- for (i = 0, thresh = 0.5; i < total; i++, thresh /= 2) {
- if (uncovered > thresh)
- break;
- }
- return i;
-}
-
-
-/* FIXME: Don't know how stable this is. Read cov files directly? */
-static void analyze_coverage(struct manifest *m, bool full_gcov,
- const char *output, struct score *score)
-{
- char **lines = strsplit(score, output, "\n", NULL);
- float covered_lines = 0.0;
- unsigned int i, total_lines = 0;
- bool lines_matter = false;
-
- /*
- Output looks like:
- File '../../../ccan/tdb2/private.h'
- Lines executed:0.00% of 8
- /home/ccan/ccan/tdb2/test/run-simple-delete.c:creating 'run-simple-delete.c.gcov'
-
- File '../../../ccan/tdb2/tdb.c'
- Lines executed:0.00% of 450
- */
-
- for (i = 0; lines[i]; i++) {
- if (strstarts(lines[i], "File '")) {
- char *file = lines[i] + strlen("File '");
- file[strcspn(file, "'")] = '\0';
- if (find_source_file(m, file))
- lines_matter = true;
- else
- lines_matter = false;
- } else if (lines_matter
- && strstarts(lines[i], "Lines executed:")) {
- float ex;
- unsigned of;
- if (sscanf(lines[i], "Lines executed:%f%% of %u",
- &ex, &of) != 2)
- errx(1, "Could not parse line '%s'", lines[i]);
- total_lines += of;
- covered_lines += ex / 100.0 * of;
- } else if (full_gcov && strstr(lines[i], ":creating '")) {
- char *file, *filename, *apostrophe;
- apostrophe = strchr(lines[i], '\'');
- filename = apostrophe + 1;
- apostrophe = strchr(filename, '\'');
- *apostrophe = '\0';
- if (lines_matter) {
- file = grab_file(score, filename, NULL);
- if (!file) {
- score->error = talloc_asprintf(score,
- "Reading %s",
- filename);
- return;
- }
- printf("%s", file);
- }
- if (tools_verbose)
- printf("Unlinking %s", filename);
- unlink(filename);
- }
- }
-
- score->pass = true;
-
- /* Nothing covered? We can't tell if there's a source file which
- * was never executed, or there really is no code to execute, so
- * assume the latter: this test deserves no score. */
- if (total_lines == 0)
- score->total = score->score = 0;
- else {
- score->total = 6;
- score->score = score_coverage(covered_lines / total_lines,
- score->total);
- }
-}
-
-static void do_run_coverage_tests(struct manifest *m,
- bool keep,
- unsigned int *timeleft, struct score *score)
-{
- struct ccan_file *i;
- char *cmdout;
- char *covcmd;
- bool full_gcov = (verbose > 1);
- struct list_head *list;
-
- /* This tells gcov where we put those .gcno files. */
- covcmd = talloc_asprintf(m, "gcov %s -o %s",
- full_gcov ? "" : "-n",
- talloc_dirname(score, m->info_file->compiled));
-
- /* Run them all. */
- foreach_ptr(list, &m->run_tests, &m->api_tests) {
- list_for_each(list, i, list) {
- if (run_command(score, timeleft, &cmdout,
- "%s", i->cov_compiled)) {
- covcmd = talloc_asprintf_append(covcmd, " %s",
- i->fullname);
- } else {
- score->error = "Running test with coverage";
- score_file_error(score, i, 0, cmdout);
- return;
- }
- }
- }
-
- /* Now run gcov: we want output even if it succeeds. */
- if (!run_command(score, timeleft, &cmdout, "%s", covcmd)) {
- score->error = talloc_asprintf(score, "Running gcov: %s",
- cmdout);
- return;
- }
-
- analyze_coverage(m, full_gcov, cmdout, score);
-}
-
-struct ccanlint tests_coverage = {
- .key = "tests_coverage",
- .name = "Module's tests cover all the code",
- .check = do_run_coverage_tests,
- .needs = "tests_compile_coverage tests_pass"
-};
-
-REGISTER_TEST(tests_coverage);
+++ /dev/null
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <ccan/foreach/foreach.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 *can_run(struct manifest *m)
-{
- if (safe_mode)
- return "Safe mode enabled";
- return NULL;
-}
-
-static void do_run_tests(struct manifest *m,
- bool keep,
- unsigned int *timeleft,
- struct score *score)
-{
- struct list_head *list;
- struct ccan_file *i;
- char *cmdout;
-
- score->total = 0;
- foreach_ptr(list, &m->run_tests, &m->api_tests) {
- list_for_each(list, i, list) {
- score->total++;
- if (run_command(m, timeleft, &cmdout, "%s",
- i->compiled))
- score->score++;
- else
- score_file_error(score, i, 0, cmdout);
- }
- }
-
- if (score->score == score->total)
- score->pass = true;
-}
-
-/* Gcc's warn_unused_result is fascist bullshit. */
-#define doesnt_matter()
-
-static void run_under_debugger(struct manifest *m, struct score *score)
-{
- char *command;
- struct file_error *first;
-
- if (!ask("Should I run the first failing test under the debugger?"))
- return;
-
- first = list_top(&score->per_file_errors, struct file_error, list);
- command = talloc_asprintf(m, "gdb -ex 'break tap.c:136' -ex 'run' %s",
- first->file->compiled);
- if (system(command))
- doesnt_matter();
-}
-
-struct ccanlint tests_pass = {
- .key = "tests_pass",
- .name = "Module's run and api tests pass",
- .check = do_run_tests,
- .handle = run_under_debugger,
- .can_run = can_run,
- .needs = "tests_compile"
-};
-
-REGISTER_TEST(tests_pass);
+++ /dev/null
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <ccan/foreach/foreach.h>
-#include <ccan/grab_file/grab_file.h>
-#include <ccan/str_talloc/str_talloc.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>
-
-struct ccanlint run_tests_vg;
-
-/* Note: we already test safe_mode in run_tests.c */
-static const char *can_run_vg(struct manifest *m)
-{
- unsigned int timeleft = default_timeout_ms;
- char *output;
-
- if (!run_command(m, &timeleft, &output,
- "valgrind -q --error-exitcode=0 true"))
- return talloc_asprintf(m, "No valgrind support: %s", output);
- return NULL;
-}
-
-/* Example output:
-==2749== Conditional jump or move depends on uninitialised value(s)
-==2749== at 0x4026C60: strnlen (mc_replace_strmem.c:263)
-==2749== by 0x40850E3: vfprintf (vfprintf.c:1614)
-==2749== by 0x408EACF: printf (printf.c:35)
-==2749== by 0x8048465: main (in /tmp/foo)
-==2749==
-==2749== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1
-==2749== at 0x4025BD3: malloc (vg_replace_malloc.c:236)
-==2749== by 0x8048444: main (in /tmp/foo)
-==2749==
-*/
-
-static bool blank_line(const char *line)
-{
- return line[strspn(line, "=0123456789 ")] == '\0';
-}
-
-static char *get_leaks(const char *output, char **errs)
-{
- char *leaks = talloc_strdup(output, "");
- unsigned int i, num;
- char **lines = strsplit(output, output, "\n", &num);
-
- *errs = talloc_strdup(output, "");
- for (i = 0; i < num; i++) {
- if (strstr(lines[i], " lost ")) {
- /* A leak... */
- if (strstr(lines[i], " definitely lost ")) {
- /* Definite leak, report. */
- while (lines[i] && !blank_line(lines[i])) {
- leaks = talloc_append_string(leaks,
- lines[i]);
- leaks = talloc_append_string(leaks,
- "\n");
- i++;
- }
- } else
- /* Not definite, ignore. */
- while (lines[i] && !blank_line(lines[i]))
- i++;
- } else {
- /* A real error. */
- while (lines[i] && !blank_line(lines[i])) {
- *errs = talloc_append_string(*errs, lines[i]);
- *errs = talloc_append_string(*errs, "\n");
- i++;
- }
- }
- }
- if (!leaks[0]) {
- talloc_free(leaks);
- leaks = NULL;
- }
- if (!(*errs)[0]) {
- talloc_free(*errs);
- *errs = NULL;
- }
- return leaks;
-}
-
-/* FIXME: Run examples, too! */
-static void do_run_tests_vg(struct manifest *m,
- bool keep,
- unsigned int *timeleft,
- struct score *score)
-{
- struct ccan_file *i;
- struct list_head *list;
- char *cmdout;
-
- /* This is slow, so we run once but grab leak info. */
- score->total = 0;
- foreach_ptr(list, &m->run_tests, &m->api_tests) {
- list_for_each(list, i, list) {
- char *output, *err;
- score->total++;
- /* FIXME: Valgrind's output sucks. XML is unreadable by
- * humans, and you can't have both. */
- run_command(score, timeleft, &cmdout,
- "valgrind -q --error-exitcode=101"
- " --leak-check=full"
- " --log-fd=3 %s %s"
- " 3> valgrind.log",
- run_tests_vg.options ?
- run_tests_vg.options : "",
- i->compiled);
- output = grab_file(i, "valgrind.log", NULL);
- if (!output || output[0] == '\0') {
- err = NULL;
- } else {
- i->leak_info = get_leaks(output, &err);
- }
- if (err)
- score_file_error(score, i, 0, err);
- else
- score->score++;
- }
- }
-
- if (score->score == score->total)
- score->pass = true;
-}
-
-static void do_leakcheck_vg(struct manifest *m,
- bool keep,
- unsigned int *timeleft,
- struct score *score)
-{
- struct ccan_file *i;
- struct list_head *list;
- bool leaks = false;
-
- foreach_ptr(list, &m->run_tests, &m->api_tests) {
- list_for_each(list, i, list) {
- if (i->leak_info) {
- score_file_error(score, i, 0, i->leak_info);
- leaks = true;
- }
- }
- }
-
- if (!leaks) {
- score->score = 1;
- score->pass = true;
- }
-}
-
-/* Gcc's warn_unused_result is fascist bullshit. */
-#define doesnt_matter()
-
-static void run_under_debugger_vg(struct manifest *m, struct score *score)
-{
- struct file_error *first;
- char *command;
-
- if (!ask("Should I run the first failing test under the debugger?"))
- return;
-
- first = list_top(&score->per_file_errors, struct file_error, list);
- command = talloc_asprintf(m, "valgrind --db-attach=yes%s %s",
- run_tests_vg.options ?
- run_tests_vg.options : "",
- first->file->compiled);
- if (system(command))
- doesnt_matter();
-}
-
-struct ccanlint tests_pass_valgrind = {
- .key = "tests_pass_valgrind",
- .name = "Module's run and api tests succeed under valgrind",
- .can_run = can_run_vg,
- .check = do_run_tests_vg,
- .handle = run_under_debugger_vg,
- .takes_options = true,
- .needs = "tests_pass"
-};
-
-REGISTER_TEST(tests_pass_valgrind);
-
-struct ccanlint tests_pass_valgrind_noleaks = {
- .key = "tests_pass_valgrind_noleaks",
- .name = "Module's run and api tests leak memory",
- .check = do_leakcheck_vg,
- .needs = "tests_pass_valgrind"
-};
-
-REGISTER_TEST(tests_pass_valgrind_noleaks);
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <ccan/foreach/foreach.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 *can_build(struct manifest *m)
+{
+ if (safe_mode)
+ return "Safe mode enabled";
+ return NULL;
+}
+
+/* FIXME: Merge this into one place. */
+static char *obj_list(const struct manifest *m, bool link_with_module)
+{
+ char *list = talloc_strdup(m, "");
+ struct ccan_file *i;
+ struct manifest *subm;
+
+ /* Objects from any other C files. */
+ list_for_each(&m->other_test_c_files, i, list)
+ list = talloc_asprintf_append(list, " %s", i->compiled);
+
+ /* Our own object files. */
+ if (link_with_module)
+ list_for_each(&m->c_files, i, list)
+ list = talloc_asprintf_append(list, " %s", i->compiled);
+
+ /* Other ccan modules. */
+ list_for_each(&m->deps, subm, list) {
+ if (subm->compiled)
+ list = talloc_asprintf_append(list, " %s",
+ subm->compiled);
+ }
+
+ return list;
+}
+
+static char *lib_list(const struct manifest *m)
+{
+ unsigned int i, num;
+ char **libs = get_libs(m, m->dir, &num, &m->info_file->compiled);
+ char *ret = talloc_strdup(m, "");
+
+ for (i = 0; i < num; i++)
+ ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
+ return ret;
+}
+
+static bool compile(const void *ctx,
+ struct manifest *m,
+ struct ccan_file *file,
+ bool fail,
+ bool link_with_module,
+ bool keep, char **output)
+{
+ file->compiled = maybe_temp_file(ctx, "", keep, file->fullname);
+ if (!compile_and_link(ctx, file->fullname, ccan_dir,
+ obj_list(m, link_with_module),
+ fail ? "-DFAIL" : "",
+ lib_list(m), file->compiled, output)) {
+ talloc_free(file->compiled);
+ return false;
+ }
+ return true;
+}
+
+static void do_compile_tests(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft, struct score *score)
+{
+ char *cmdout;
+ struct ccan_file *i;
+ struct list_head *list;
+ bool errors = false, warnings = false;
+
+ foreach_ptr(list, &m->compile_ok_tests, &m->run_tests, &m->api_tests) {
+ list_for_each(list, i, list) {
+ if (!compile(score, m, i, false, list == &m->api_tests,
+ keep, &cmdout)) {
+ score->error = "Failed to compile tests";
+ score_file_error(score, i, 0, cmdout);
+ errors = true;
+ } else if (!streq(cmdout, "")) {
+ score->error = "Test compiled with warnings";
+ score_file_error(score, i, 0, cmdout);
+ warnings = true;
+ }
+ }
+ }
+
+ /* The compile fail tests are a bit weird, handle them separately */
+ if (errors)
+ return;
+
+ /* For historical reasons, "fail" often means "gives warnings" */
+ list_for_each(&m->compile_fail_tests, i, list) {
+ if (!compile(score, m, i, false, false, false, &cmdout)) {
+ score->error = "Failed to compile without -DFAIL";
+ score_file_error(score, i, 0, cmdout);
+ return;
+ }
+ if (!streq(cmdout, "")) {
+ score->error = "Compile with warnigns without -DFAIL";
+ score_file_error(score, i, 0, cmdout);
+ return;
+ }
+ if (compile(score, m, i, true, false, false, &cmdout)
+ && streq(cmdout, "")) {
+ score->error = "Compiled successfully with -DFAIL?";
+ score_file_error(score, i, 0, NULL);
+ return;
+ }
+ }
+
+ score->pass = true;
+ score->total = 2;
+ score->score = 1 + !warnings;
+}
+
+struct ccanlint tests_compile = {
+ .key = "tests_compile",
+ .name = "Module tests compile",
+ .check = do_compile_tests,
+ .can_run = can_build,
+ .needs = "tests_helpers_compile objects_build"
+};
+
+REGISTER_TEST(tests_compile);
--- /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>
+
+/* Note: we already test safe_mode in run_tests.c */
+static const char *can_run_coverage(struct manifest *m)
+{
+ unsigned int timeleft = default_timeout_ms;
+ char *output;
+
+ if (!run_command(m, &timeleft, &output, "gcov -h"))
+ return talloc_asprintf(m, "No gcov support: %s", output);
+ return NULL;
+}
+
+static bool build_module_objs_with_coverage(struct manifest *m, bool keep,
+ struct score *score,
+ char **modobjs)
+{
+ struct ccan_file *i;
+
+ *modobjs = talloc_strdup(m, "");
+ list_for_each(&m->c_files, i, list) {
+ char *err;
+ char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
+
+ i->cov_compiled = maybe_temp_file(m, "", keep, fullfile);
+ if (!compile_object(m, fullfile, ccan_dir, "",
+ i->cov_compiled, &err)) {
+ score_file_error(score, i, 0, err);
+ talloc_free(i->cov_compiled);
+ i->cov_compiled = NULL;
+ return false;
+ }
+ *modobjs = talloc_asprintf_append(*modobjs,
+ " %s", i->cov_compiled);
+ }
+ return true;
+}
+
+/* FIXME: Merge this into one place. */
+static char *obj_list(const struct manifest *m, const char *modobjs)
+{
+ char *list = talloc_strdup(m, "");
+ struct ccan_file *i;
+ struct manifest *subm;
+
+ /* Objects from any other C files. */
+ list_for_each(&m->other_test_c_files, i, list)
+ list = talloc_asprintf_append(list, " %s", i->compiled);
+
+ if (modobjs)
+ list = talloc_append_string(list, modobjs);
+
+ /* Other ccan modules (don't need coverage versions of those). */
+ list_for_each(&m->deps, subm, list) {
+ if (subm->compiled)
+ list = talloc_asprintf_append(list, " %s",
+ subm->compiled);
+ }
+
+ return list;
+}
+
+static char *lib_list(const struct manifest *m)
+{
+ unsigned int i, num;
+ char **libs = get_libs(m, m->dir, &num, &m->info_file->compiled);
+ char *ret = talloc_strdup(m, "");
+
+ for (i = 0; i < num; i++)
+ ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
+ return ret;
+}
+
+static char *cov_compile(const void *ctx,
+ struct manifest *m,
+ struct ccan_file *file,
+ const char *modobjs,
+ bool keep)
+{
+ char *output;
+
+ file->cov_compiled = maybe_temp_file(ctx, "", keep, file->fullname);
+ if (!compile_and_link(ctx, file->fullname, ccan_dir,
+ obj_list(m, modobjs),
+ COVERAGE_CFLAGS,
+ lib_list(m), file->cov_compiled, &output)) {
+ talloc_free(file->cov_compiled);
+ file->cov_compiled = NULL;
+ return output;
+ }
+ talloc_free(output);
+ return NULL;
+}
+
+/* FIXME: Coverage from testable examples as well. */
+static void do_compile_coverage_tests(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
+{
+ char *cmdout, *modobjs = NULL;
+ struct ccan_file *i;
+
+ if (!list_empty(&m->api_tests)
+ && !build_module_objs_with_coverage(m, keep, score, &modobjs)) {
+ score->error = "Failed to compile module objects with coverage";
+ return;
+ }
+
+ list_for_each(&m->run_tests, i, list) {
+ cmdout = cov_compile(m, m, i, NULL, keep);
+ if (cmdout) {
+ score->error = "Failed to compile test with coverage";
+ score_file_error(score, i, 0, cmdout);
+ }
+ }
+
+ list_for_each(&m->api_tests, i, list) {
+ cmdout = cov_compile(m, m, i, modobjs, keep);
+ if (cmdout) {
+ score->error = "Failed to compile test with coverage";
+ score_file_error(score, i, 0, cmdout);
+ }
+ }
+ if (!score->error) {
+ score->pass = true;
+ score->score = score->total;
+ }
+}
+
+struct ccanlint tests_compile_coverage = {
+ .key = "tests_compile_coverage",
+ .name = "Module tests compile with " COVERAGE_CFLAGS,
+ .check = do_compile_coverage_tests,
+ .can_run = can_run_coverage,
+ .needs = "tests_compile"
+};
+
+REGISTER_TEST(tests_compile_coverage);
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str_talloc/str_talloc.h>
+#include <ccan/grab_file/grab_file.h>
+#include <ccan/str/str.h>
+#include <ccan/foreach/foreach.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 bool find_source_file(const struct manifest *m, const char *filename)
+{
+ const struct ccan_file *i;
+
+ list_for_each(&m->c_files, i, list) {
+ if (streq(i->fullname, filename))
+ return true;
+ }
+ list_for_each(&m->h_files, i, list) {
+ if (streq(i->fullname, filename))
+ return true;
+ }
+ return false;
+}
+
+/* 1 point for 50%, 2 points for 75%, 3 points for 87.5%... Bonus for 100%. */
+static unsigned int score_coverage(float covered, unsigned total)
+{
+ float thresh, uncovered = 1.0 - covered;
+ unsigned int i;
+
+ if (covered == 1.0)
+ return total;
+
+ total--;
+ for (i = 0, thresh = 0.5; i < total; i++, thresh /= 2) {
+ if (uncovered > thresh)
+ break;
+ }
+ return i;
+}
+
+
+/* FIXME: Don't know how stable this is. Read cov files directly? */
+static void analyze_coverage(struct manifest *m, bool full_gcov,
+ const char *output, struct score *score)
+{
+ char **lines = strsplit(score, output, "\n", NULL);
+ float covered_lines = 0.0;
+ unsigned int i, total_lines = 0;
+ bool lines_matter = false;
+
+ /*
+ Output looks like:
+ File '../../../ccan/tdb2/private.h'
+ Lines executed:0.00% of 8
+ /home/ccan/ccan/tdb2/test/run-simple-delete.c:creating 'run-simple-delete.c.gcov'
+
+ File '../../../ccan/tdb2/tdb.c'
+ Lines executed:0.00% of 450
+ */
+
+ for (i = 0; lines[i]; i++) {
+ if (strstarts(lines[i], "File '")) {
+ char *file = lines[i] + strlen("File '");
+ file[strcspn(file, "'")] = '\0';
+ if (find_source_file(m, file))
+ lines_matter = true;
+ else
+ lines_matter = false;
+ } else if (lines_matter
+ && strstarts(lines[i], "Lines executed:")) {
+ float ex;
+ unsigned of;
+ if (sscanf(lines[i], "Lines executed:%f%% of %u",
+ &ex, &of) != 2)
+ errx(1, "Could not parse line '%s'", lines[i]);
+ total_lines += of;
+ covered_lines += ex / 100.0 * of;
+ } else if (full_gcov && strstr(lines[i], ":creating '")) {
+ char *file, *filename, *apostrophe;
+ apostrophe = strchr(lines[i], '\'');
+ filename = apostrophe + 1;
+ apostrophe = strchr(filename, '\'');
+ *apostrophe = '\0';
+ if (lines_matter) {
+ file = grab_file(score, filename, NULL);
+ if (!file) {
+ score->error = talloc_asprintf(score,
+ "Reading %s",
+ filename);
+ return;
+ }
+ printf("%s", file);
+ }
+ if (tools_verbose)
+ printf("Unlinking %s", filename);
+ unlink(filename);
+ }
+ }
+
+ score->pass = true;
+
+ /* Nothing covered? We can't tell if there's a source file which
+ * was never executed, or there really is no code to execute, so
+ * assume the latter: this test deserves no score. */
+ if (total_lines == 0)
+ score->total = score->score = 0;
+ else {
+ score->total = 6;
+ score->score = score_coverage(covered_lines / total_lines,
+ score->total);
+ }
+}
+
+static void do_run_coverage_tests(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft, struct score *score)
+{
+ struct ccan_file *i;
+ char *cmdout;
+ char *covcmd;
+ bool full_gcov = (verbose > 1);
+ struct list_head *list;
+
+ /* This tells gcov where we put those .gcno files. */
+ covcmd = talloc_asprintf(m, "gcov %s -o %s",
+ full_gcov ? "" : "-n",
+ talloc_dirname(score, m->info_file->compiled));
+
+ /* Run them all. */
+ foreach_ptr(list, &m->run_tests, &m->api_tests) {
+ list_for_each(list, i, list) {
+ if (run_command(score, timeleft, &cmdout,
+ "%s", i->cov_compiled)) {
+ covcmd = talloc_asprintf_append(covcmd, " %s",
+ i->fullname);
+ } else {
+ score->error = "Running test with coverage";
+ score_file_error(score, i, 0, cmdout);
+ return;
+ }
+ }
+ }
+
+ /* Now run gcov: we want output even if it succeeds. */
+ if (!run_command(score, timeleft, &cmdout, "%s", covcmd)) {
+ score->error = talloc_asprintf(score, "Running gcov: %s",
+ cmdout);
+ return;
+ }
+
+ analyze_coverage(m, full_gcov, cmdout, score);
+}
+
+struct ccanlint tests_coverage = {
+ .key = "tests_coverage",
+ .name = "Module's tests cover all the code",
+ .check = do_run_coverage_tests,
+ .needs = "tests_compile_coverage tests_pass"
+};
+
+REGISTER_TEST(tests_coverage);
--- /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>
+
+extern struct ccanlint tests_exist;
+
+static void handle_no_tests(struct manifest *m, struct score *score)
+{
+ FILE *run;
+ struct ccan_file *i;
+ char *test_dir = talloc_asprintf(m, "%s/test", m->dir);
+
+ printf(
+ "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 ccan/tap 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. The test will be run in a temporary\n"
+ "directory, with the test directory symlinked under test/.\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 only API tests are linked against the files in the module!\n"
+ );
+
+ if (!ask("Should I create a template test/run.c file for you?"))
+ return;
+
+ if (mkdir(test_dir, 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");
+
+ fprintf(run, "#include <ccan/%s/%s.h>\n", m->basename, m->basename);
+ if (!list_empty(&m->c_files)) {
+ fputs("/* Include the C files directly. */\n", run);
+ list_for_each(&m->c_files, i, list)
+ fprintf(run, "#include <ccan/%s/%s>\n",
+ m->basename, i->name);
+ }
+ fprintf(run, "%s",
+ "#include <ccan/tap/tap.h>\n\n"
+ "int main(void)\n"
+ "{\n"
+ " /* This is how many tests you plan to run */\n"
+ " plan_tests(3);\n"
+ "\n"
+ " /* Simple thing we expect to succeed */\n"
+ " ok1(some_test())\n"
+ " /* Same, with an explicit description of the test. */\n"
+ " ok(some_test(), \"%s with no args should return 1\", \"some_test\")\n"
+ " /* How to print out messages for debugging. */\n"
+ " diag(\"Address of some_test is %p\", &some_test)\n"
+ " /* Conditional tests must be explicitly skipped. */\n"
+ "#if HAVE_SOME_FEATURE\n"
+ " ok1(test_some_feature())\n"
+ "#else\n"
+ " skip(1, \"Don\'t have SOME_FEATURE\")\n"
+ "#endif\n"
+ "\n"
+ " /* This exits depending on whether all tests passed */\n"
+ " return exit_status();\n"
+ "}\n");
+ fclose(run);
+}
+
+static void check_tests_exist(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft, struct score *score)
+{
+ struct stat st;
+ char *test_dir = talloc_asprintf(m, "%s/test", m->dir);
+
+ if (lstat(test_dir, &st) != 0) {
+ score->error = "No test directory";
+ if (errno != ENOENT)
+ err(1, "statting %s", test_dir);
+ tests_exist.handle = handle_no_tests;
+ return;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ score->error = "test is not a directory";
+ return;
+ }
+
+ if (list_empty(&m->api_tests)
+ && list_empty(&m->run_tests)
+ && list_empty(&m->compile_ok_tests)) {
+ if (list_empty(&m->compile_fail_tests)) {
+ score->error = "No tests in test directory";
+ tests_exist.handle = handle_no_tests;
+ } else
+ score->error = "No positive tests in test directory";
+ return;
+ }
+ score->pass = true;
+ score->score = score->total;
+}
+
+struct ccanlint tests_exist = {
+ .key = "tests_exist",
+ .name = "Module has test directory with tests in it",
+ .check = check_tests_exist,
+ .needs = ""
+};
+
+REGISTER_TEST(tests_exist);
--- /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 *can_run(struct manifest *m)
+{
+ if (safe_mode)
+ return "Safe mode enabled";
+ return NULL;
+}
+
+static bool compile(struct manifest *m,
+ bool keep,
+ struct ccan_file *cfile,
+ char **output)
+{
+ cfile->compiled = maybe_temp_file(m, ".o", keep, cfile->fullname);
+ return compile_object(m, cfile->fullname, ccan_dir, "",
+ cfile->compiled, output);
+}
+
+static void do_compile_test_helpers(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
+{
+ struct ccan_file *i;
+ bool errors = false, warnings = false;
+
+ if (list_empty(&m->other_test_c_files))
+ score->total = 0;
+ else
+ score->total = 2;
+
+ list_for_each(&m->other_test_c_files, i, list) {
+ char *cmdout;
+
+ if (!compile(m, keep, i, &cmdout)) {
+ errors = true;
+ score->error = "Failed to compile helper C files";
+ score_file_error(score, i, 0, cmdout);
+ } else if (!streq(cmdout, "")) {
+ warnings = true;
+ score->error = "Helper C files gave warnings";
+ score_file_error(score, i, 0, cmdout);
+ }
+ }
+
+ if (!errors) {
+ score->pass = true;
+ score->score = score->total - warnings;
+ }
+}
+
+struct ccanlint tests_helpers_compile = {
+ .key = "tests_helpers_compile",
+ .name = "Module test helper objects compile",
+ .check = do_compile_test_helpers,
+ .can_run = can_run,
+ .needs = "depends_build tests_exist"
+};
+
+REGISTER_TEST(tests_helpers_compile);
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <ccan/foreach/foreach.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 *can_run(struct manifest *m)
+{
+ if (safe_mode)
+ return "Safe mode enabled";
+ return NULL;
+}
+
+static void do_run_tests(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
+{
+ struct list_head *list;
+ struct ccan_file *i;
+ char *cmdout;
+
+ score->total = 0;
+ foreach_ptr(list, &m->run_tests, &m->api_tests) {
+ list_for_each(list, i, list) {
+ score->total++;
+ if (run_command(m, timeleft, &cmdout, "%s",
+ i->compiled))
+ score->score++;
+ else
+ score_file_error(score, i, 0, cmdout);
+ }
+ }
+
+ if (score->score == score->total)
+ score->pass = true;
+}
+
+/* Gcc's warn_unused_result is fascist bullshit. */
+#define doesnt_matter()
+
+static void run_under_debugger(struct manifest *m, struct score *score)
+{
+ char *command;
+ struct file_error *first;
+
+ if (!ask("Should I run the first failing test under the debugger?"))
+ return;
+
+ first = list_top(&score->per_file_errors, struct file_error, list);
+ command = talloc_asprintf(m, "gdb -ex 'break tap.c:136' -ex 'run' %s",
+ first->file->compiled);
+ if (system(command))
+ doesnt_matter();
+}
+
+struct ccanlint tests_pass = {
+ .key = "tests_pass",
+ .name = "Module's run and api tests pass",
+ .check = do_run_tests,
+ .handle = run_under_debugger,
+ .can_run = can_run,
+ .needs = "tests_compile"
+};
+
+REGISTER_TEST(tests_pass);
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <ccan/foreach/foreach.h>
+#include <ccan/grab_file/grab_file.h>
+#include <ccan/str_talloc/str_talloc.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>
+
+struct ccanlint run_tests_vg;
+
+/* Note: we already test safe_mode in run_tests.c */
+static const char *can_run_vg(struct manifest *m)
+{
+ unsigned int timeleft = default_timeout_ms;
+ char *output;
+
+ if (!run_command(m, &timeleft, &output,
+ "valgrind -q --error-exitcode=0 true"))
+ return talloc_asprintf(m, "No valgrind support: %s", output);
+ return NULL;
+}
+
+/* Example output:
+==2749== Conditional jump or move depends on uninitialised value(s)
+==2749== at 0x4026C60: strnlen (mc_replace_strmem.c:263)
+==2749== by 0x40850E3: vfprintf (vfprintf.c:1614)
+==2749== by 0x408EACF: printf (printf.c:35)
+==2749== by 0x8048465: main (in /tmp/foo)
+==2749==
+==2749== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1
+==2749== at 0x4025BD3: malloc (vg_replace_malloc.c:236)
+==2749== by 0x8048444: main (in /tmp/foo)
+==2749==
+*/
+
+static bool blank_line(const char *line)
+{
+ return line[strspn(line, "=0123456789 ")] == '\0';
+}
+
+static char *get_leaks(const char *output, char **errs)
+{
+ char *leaks = talloc_strdup(output, "");
+ unsigned int i, num;
+ char **lines = strsplit(output, output, "\n", &num);
+
+ *errs = talloc_strdup(output, "");
+ for (i = 0; i < num; i++) {
+ if (strstr(lines[i], " lost ")) {
+ /* A leak... */
+ if (strstr(lines[i], " definitely lost ")) {
+ /* Definite leak, report. */
+ while (lines[i] && !blank_line(lines[i])) {
+ leaks = talloc_append_string(leaks,
+ lines[i]);
+ leaks = talloc_append_string(leaks,
+ "\n");
+ i++;
+ }
+ } else
+ /* Not definite, ignore. */
+ while (lines[i] && !blank_line(lines[i]))
+ i++;
+ } else {
+ /* A real error. */
+ while (lines[i] && !blank_line(lines[i])) {
+ *errs = talloc_append_string(*errs, lines[i]);
+ *errs = talloc_append_string(*errs, "\n");
+ i++;
+ }
+ }
+ }
+ if (!leaks[0]) {
+ talloc_free(leaks);
+ leaks = NULL;
+ }
+ if (!(*errs)[0]) {
+ talloc_free(*errs);
+ *errs = NULL;
+ }
+ return leaks;
+}
+
+/* FIXME: Run examples, too! */
+static void do_run_tests_vg(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
+{
+ struct ccan_file *i;
+ struct list_head *list;
+ char *cmdout;
+
+ /* This is slow, so we run once but grab leak info. */
+ score->total = 0;
+ foreach_ptr(list, &m->run_tests, &m->api_tests) {
+ list_for_each(list, i, list) {
+ char *output, *err;
+ score->total++;
+ /* FIXME: Valgrind's output sucks. XML is unreadable by
+ * humans, and you can't have both. */
+ run_command(score, timeleft, &cmdout,
+ "valgrind -q --error-exitcode=101"
+ " --leak-check=full"
+ " --log-fd=3 %s %s"
+ " 3> valgrind.log",
+ run_tests_vg.options ?
+ run_tests_vg.options : "",
+ i->compiled);
+ output = grab_file(i, "valgrind.log", NULL);
+ if (!output || output[0] == '\0') {
+ err = NULL;
+ } else {
+ i->leak_info = get_leaks(output, &err);
+ }
+ if (err)
+ score_file_error(score, i, 0, err);
+ else
+ score->score++;
+ }
+ }
+
+ if (score->score == score->total)
+ score->pass = true;
+}
+
+static void do_leakcheck_vg(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
+{
+ struct ccan_file *i;
+ struct list_head *list;
+ bool leaks = false;
+
+ foreach_ptr(list, &m->run_tests, &m->api_tests) {
+ list_for_each(list, i, list) {
+ if (i->leak_info) {
+ score_file_error(score, i, 0, i->leak_info);
+ leaks = true;
+ }
+ }
+ }
+
+ if (!leaks) {
+ score->score = 1;
+ score->pass = true;
+ }
+}
+
+/* Gcc's warn_unused_result is fascist bullshit. */
+#define doesnt_matter()
+
+static void run_under_debugger_vg(struct manifest *m, struct score *score)
+{
+ struct file_error *first;
+ char *command;
+
+ if (!ask("Should I run the first failing test under the debugger?"))
+ return;
+
+ first = list_top(&score->per_file_errors, struct file_error, list);
+ command = talloc_asprintf(m, "valgrind --db-attach=yes%s %s",
+ run_tests_vg.options ?
+ run_tests_vg.options : "",
+ first->file->compiled);
+ if (system(command))
+ doesnt_matter();
+}
+
+struct ccanlint tests_pass_valgrind = {
+ .key = "tests_pass_valgrind",
+ .name = "Module's run and api tests succeed under valgrind",
+ .can_run = can_run_vg,
+ .check = do_run_tests_vg,
+ .handle = run_under_debugger_vg,
+ .takes_options = true,
+ .needs = "tests_pass"
+};
+
+REGISTER_TEST(tests_pass_valgrind);
+
+struct ccanlint tests_pass_valgrind_noleaks = {
+ .key = "tests_pass_valgrind_noleaks",
+ .name = "Module's run and api tests leak memory",
+ .check = do_leakcheck_vg,
+ .needs = "tests_pass_valgrind"
+};
+
+REGISTER_TEST(tests_pass_valgrind_noleaks);
+++ /dev/null
-/* Trailing whitespace test. Almost embarrassing, but trivial. */
-#include <tools/ccanlint/ccanlint.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/foreach/foreach.h>
-#include <ccan/str/str.h>
-
-/* FIXME: only print full analysis if verbose >= 2. */
-static char *get_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,
- bool keep,
- unsigned int *timeleft,
- struct score *score)
-{
- struct list_head *list;
- struct ccan_file *f;
- unsigned int i;
-
- foreach_ptr(list, &m->c_files, &m->h_files) {
- list_for_each(list, f, list) {
- char **lines = get_ccan_file_lines(f);
- for (i = 0; i < f->num_lines; i++) {
- char *err = get_trailing_whitespace(lines[i]);
- if (err) {
- score->error = "Trailing whitespace"
- " found";
- score_file_error(score, f, i+1, err);
- }
- }
- }
- }
- if (!score->error) {
- score->pass = true;
- score->score = score->total;
- }
-}
-
-struct ccanlint no_trailing_whitespace = {
- .key = "no_trailing_whitespace",
- .name = "Module's source code has no trailing whitespace",
- .check = check_trailing_whitespace,
- .needs = ""
-};
-
-
-REGISTER_TEST(no_trailing_whitespace);