Build tests for ccan.
authorRusty Russell <rusty@rustcorp.com.au>
Sat, 12 Sep 2009 01:27:44 +0000 (10:57 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Sat, 12 Sep 2009 01:27:44 +0000 (10:57 +0930)
More sophisticated skipping: skip dependencies when one fails as well.
Allow tests to change their total_score; only access it after running (other than to check it's non-zero).

15 files changed:
tools/Makefile
tools/ccanlint/Makefile
tools/ccanlint/ccanlint.c
tools/ccanlint/ccanlint.h
tools/ccanlint/file_analysis.c
tools/ccanlint/tests/build.c [new file with mode: 0644]
tools/ccanlint/tests/build_objs.c [new file with mode: 0644]
tools/ccanlint/tests/check_build.c [new file with mode: 0644]
tools/ccanlint/tests/check_depends.c [deleted file]
tools/ccanlint/tests/check_depends_built.c [new file with mode: 0644]
tools/ccanlint/tests/check_depends_exist.c [new file with mode: 0644]
tools/ccanlint/tests/check_includes_build.c [new file with mode: 0644]
tools/depends.c
tools/tools.c [new file with mode: 0644]
tools/tools.h

index 173355ab396ee1992fcfaef03e99e2db848af72b..f3575b3dda6dde08948beb44960521a844e2c00d 100644 (file)
@@ -3,13 +3,13 @@ ALL_TOOLS = tools/ccan_depends tools/run_tests tools/doc_extract tools/namespaci
 .PHONY: tools
 tools: $(ALL_TOOLS)
 
-tools/ccan_depends: tools/ccan_depends.o tools/depends.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/talloc/talloc.o ccan/noerr/noerr.o
+tools/ccan_depends: tools/ccan_depends.o tools/depends.o tools/tools.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/talloc/talloc.o ccan/noerr/noerr.o
 
 tools/run_tests: tools/run_tests.o tools/depends.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/tap/tap.o ccan/noerr/noerr.o ccan/talloc/talloc.o
 
 tools/doc_extract: tools/doc_extract.o tools/doc_extract-core.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/noerr/noerr.o ccan/talloc/talloc.o
 
-tools/namespacize: tools/namespacize.o tools/depends.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/noerr/noerr.o ccan/talloc/talloc.o
+tools/namespacize: tools/namespacize.o tools/depends.o tools/tools.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/noerr/noerr.o ccan/talloc/talloc.o
 
 tools/run_tests.o tools/namespacize.o tools/depends.o: tools/tools.h
 
index e40b59decf9e5e3f5d48f984364ebb76cc2d169b..a99a6aeb71185efdf0bd6425c9b81d2dfe0355df 100644 (file)
@@ -6,6 +6,7 @@ CORE_OBJS := tools/ccanlint/ccanlint.o \
        tools/ccanlint/file_analysis.o \
        tools/doc_extract-core.o \
        tools/depends.o \
+       tools/tools.o \
        ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o \
        ccan/talloc/talloc.o ccan/noerr/noerr.o
 
index f88b58dc8e8ba3b416bb10a1469684258782c1bb..be6829c049d8420d5cefd76931ca09bf77a49825 100644 (file)
@@ -68,6 +68,19 @@ bool ask(const char *question)
                && toupper(reply[0]) == 'Y';
 }
 
+static const char *should_skip(struct manifest *m, struct ccanlint *i)
+{
+       if (i->skip_fail)
+               return "dependency failed";
+
+       if (i->skip)
+               return "dependency was skipped";
+
+       if (i->can_run)
+               return i->can_run(m);
+       return NULL;
+}
+
 static bool run_test(struct ccanlint *i,
                     bool summary,
                     unsigned int *score,
@@ -77,18 +90,38 @@ static bool run_test(struct ccanlint *i,
        void *result;
        unsigned int this_score;
        const struct dependent *d;
+       const char *skip;
 
-       if (i->total_score)
-               *total_score += i->total_score;
        //one less test to run through
        list_for_each(&i->dependencies, d, node)
                d->dependent->num_depends--;
+
+       skip = should_skip(m, i);
+       if (skip) {
+               if (verbose)
+                       printf("  %s: skipped (%s)\n", i->name, skip);
+
+               /* If we're skipping this because a prereq failed, we fail. */
+               if (i->skip_fail)
+                       *total_score += i->total_score;
+                       
+               list_del(&i->list);
+               list_add_tail(&finished_tests, &i->list);
+               list_for_each(&i->dependencies, d, node) {
+                       d->dependent->skip = true;
+                       d->dependent->skip_fail = i->skip_fail;
+               }
+               return true;
+       }
+
        result = i->check(m);
        if (!result) {
                if (verbose)
                        printf("  %s: OK\n", i->name);
-               if (i->total_score)
+               if (i->total_score) {
                        *score += i->total_score;
+                       *total_score += i->total_score;
+               }
 
                list_del(&i->list);
                list_add_tail(&finished_tests, &i->list);
@@ -103,6 +136,7 @@ static bool run_test(struct ccanlint *i,
        list_del(&i->list);
        list_add_tail(&finished_tests, &i->list);
 
+       *total_score += i->total_score;
        *score += this_score;
        if (summary) {
                printf("%s FAILED (%u/%u)\n",
@@ -119,11 +153,8 @@ static bool run_test(struct ccanlint *i,
 
        /* Skip any tests which depend on this one. */
        list_for_each(&i->dependencies, d, node) {
-               list_del(&d->dependent->list);
-               list_add(&finished_tests, &d->dependent->list);
-               if (verbose)
-                       printf("  -> skipping %s\n", d->dependent->name);
-               *total_score += d->dependent->total_score;
+               d->dependent->skip = true;
+               d->dependent->skip_fail = true;
        }
 
        return false;
index 9644185f5af8fba4191bdc1570800535ef6bdd30..d832332dc757197280b6ac3c4eb79427781a0b75 100644 (file)
@@ -26,8 +26,10 @@ struct manifest {
 
        struct list_head other_files;
 
-       /* From tests/check_depends.c */
-       struct list_head dep_obj_files;
+       /* From tests/check_depends_exist.c */
+       struct list_head dep_dirs;
+       /* From tests/check_depends_built.c */
+       struct list_head dep_objs;
 };
 
 struct manifest *get_manifest(const void *ctx);
@@ -41,6 +43,9 @@ struct ccanlint {
        /* Total score that this test is worth.  0 means compulsory tests. */
        unsigned int total_score;
 
+       /* Can we run this test?  Return string explaining why, if not. */
+       const char *(*can_run)(struct manifest *m);
+
        /* If this returns non-NULL, it means the check failed. */
        void *(*check)(struct manifest *m);
 
@@ -60,6 +65,10 @@ struct ccanlint {
        struct list_head dependencies;
        /* How many things do we (still) depend on? */
        unsigned int num_depends;
+       /* Did we skip a dependency?  If so, must skip this, too. */
+       bool skip;
+       /* Did we fail a dependency?  If so, skip and mark as fail. */
+       bool skip_fail;
 };
 
 /* Ask the user a yes/no question: the answer is NO if there's an error. */
index 3c8930f3d847b10b5dd6ad1ad5a4eca19af47bdf..44ea5c2dab9fd8a2760bc96a25f50a033cae22d9 100644 (file)
@@ -164,6 +164,8 @@ struct manifest *get_manifest(const void *ctx)
        list_head_init(&m->compile_fail_tests);
        list_head_init(&m->other_test_files);
        list_head_init(&m->other_files);
+       list_head_init(&m->dep_dirs);
+       list_head_init(&m->dep_objs);
 
        m->basename = talloc_getcwd(m);
        if (!m->basename)
diff --git a/tools/ccanlint/tests/build.c b/tools/ccanlint/tests/build.c
new file mode 100644 (file)
index 0000000..c3b570e
--- /dev/null
@@ -0,0 +1,60 @@
+#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 (list_empty(&m->c_files))
+               return "No C files in module";
+       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;
+
+       /* Object from all the C files. */
+       list_for_each(&m->c_files, i, list)
+               list = talloc_asprintf_append(list, "%.*s.o ",
+                                             strlen(i->name) - 2, i->name);
+
+       return list;
+}
+
+/* We leave this object file around after ccanlint runs, all built. */
+static void *do_build(struct manifest *m)
+{
+       return run_command(m, "ld -r -o ../%s.o %s", m->basename, obj_list(m));
+}
+
+static const char *describe_build(struct manifest *m, void *check_result)
+{
+       return talloc_asprintf(check_result, 
+                              "The object file for the module didn't build:\n"
+                              "%s", (char *)check_result);
+}
+
+struct ccanlint build = {
+       .name = "Module can be built",
+       .total_score = 1,
+       .check = do_build,
+       .describe = describe_build,
+       .can_run = can_build,
+};
+
+REGISTER_TEST(build, &depends_built, NULL);
diff --git a/tools/ccanlint/tests/build_objs.c b/tools/ccanlint/tests/build_objs.c
new file mode 100644 (file)
index 0000000..199772a
--- /dev/null
@@ -0,0 +1,82 @@
+#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 (list_empty(&m->c_files))
+               return "No C files in module";
+       if (safe_mode)
+               return "Safe mode enabled";
+       return NULL;
+}
+
+static bool compile_obj(struct ccan_file *c_file, char *objfile, char **report)
+{
+       char *contents;
+
+       contents = run_command(objfile, "cc " CFLAGS " -o %s -c %s",
+                              objfile, c_file->name);
+       if (contents) {
+               if (*report)
+                       *report = talloc_append_string(*report, contents);
+               else
+                       *report = contents;
+               return false;
+       }
+       return true;
+}
+
+static int cleanup_obj(char *objfile)
+{
+       unlink(objfile);
+       return 0;
+}
+
+static void *check_objs_build(struct manifest *m)
+{
+       char *report = NULL;
+       struct ccan_file *i;
+
+       /* One point for each obj file. */
+       build_objs.total_score = 0;
+       list_for_each(&m->c_files, i, list)
+               build_objs.total_score++;
+
+       list_for_each(&m->c_files, i, list) {
+               char *objfile = talloc_strdup(m, i->name);
+               objfile[strlen(objfile)-1] = 'o';
+
+               if (compile_obj(i, objfile, &report))
+                       talloc_set_destructor(objfile, cleanup_obj);
+       }
+       return report;
+}
+
+static const char *describe_objs_build(struct manifest *m, void *check_result)
+{
+       return talloc_asprintf(check_result, 
+                              "%s", (char *)check_result);
+}
+
+struct ccanlint build_objs = {
+       .name = "Module object files can be built",
+       .total_score = 1,
+       .check = check_objs_build,
+       .describe = describe_objs_build,
+       .can_run = can_build,
+};
+
+REGISTER_TEST(build_objs, &depends_exist, NULL);
diff --git a/tools/ccanlint/tests/check_build.c b/tools/ccanlint/tests/check_build.c
new file mode 100644 (file)
index 0000000..03e3233
--- /dev/null
@@ -0,0 +1,101 @@
+#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 int cleanup_testfile(char *testfile)
+{
+       unlink(testfile);
+       return 0;
+}
+
+static char *obj_list(const struct manifest *m)
+{
+       char *list = talloc_strdup(m, "");
+       struct ccan_file *i;
+
+       /* Other CCAN deps. */
+       list_for_each(&m->dep_objs, i, list)
+               list = talloc_asprintf_append(list, "%s ", i->name);
+
+       return list;
+}
+
+static char *lib_list(const struct manifest *m)
+{
+       unsigned int i, num;
+       char **libs = get_libs(m, ".", ".", &num);
+       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)
+{
+       char *contents;
+       char *tmpfile, *outfile;
+       int fd;
+
+       tmpfile = talloc_strdup(m, tempnam("/tmp", "ccanlint"));
+       talloc_set_destructor(tmpfile, cleanup_testfile);
+       outfile = talloc_strdup(m, tempnam("/tmp", "ccanlint"));
+       talloc_set_destructor(outfile, cleanup_testfile);
+
+       fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
+       if (fd < 0)
+               return talloc_asprintf(m, "Creating temporary file: %s",
+                                      strerror(errno));
+
+       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)) {
+               close(fd);
+               return "Failure writing to temporary file";
+       }
+       close(fd);
+
+       return run_command(m, "cc " CFLAGS " -o %s -x c %s -x none %s %s",
+                          outfile, tmpfile, obj_list(m), lib_list(m));
+}
+
+static const char *describe_use_build(struct manifest *m, void *check_result)
+{
+       return talloc_asprintf(check_result, 
+                              "Linking against module:\n"
+                              "%s", (char *)check_result);
+}
+
+struct ccanlint check_build = {
+       .name = "Module can be used",
+       .total_score = 1,
+       .check = check_use_build,
+       .describe = describe_use_build,
+       .can_run = can_build,
+};
+
+REGISTER_TEST(check_build, &build, NULL);
diff --git a/tools/ccanlint/tests/check_depends.c b/tools/ccanlint/tests/check_depends.c
deleted file mode 100644 (file)
index fecce0b..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-#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 expect_obj_file(const char *dir)
-{
-       char *olddir;
-       struct manifest *dep_man;
-       bool has_c_files;
-
-       olddir = talloc_getcwd(dir);
-       if (!olddir)
-               err(1, "Getting current directory");
-
-       /* We will fail below if this doesn't exist. */
-       if (chdir(dir) != 0)
-               return false;
-
-       dep_man = get_manifest(dir);
-       if (chdir(olddir) != 0)
-               err(1, "Returning to original directory '%s'", olddir);
-       talloc_free(olddir);
-
-       /* If it has C files, we expect an object file built from them. */
-       has_c_files = !list_empty(&dep_man->c_files);
-       talloc_free(dep_man);
-       return has_c_files;
-}
-
-/* FIXME: recursive ccanlint if they ask for it. */
-static char *add_dep(char *sofar, struct manifest *m, const char *dep)
-{
-       char *file, *dir;
-       struct stat st;
-       bool need_obj;
-
-       dir = talloc_asprintf(m, "../%s", dep);
-       need_obj = expect_obj_file(dir);
-
-       if (need_obj) {
-               file = talloc_asprintf(m, "../%s.o", dep);
-               if (stat(file, &st) == 0) {
-                       struct ccan_file *f = new_ccan_file(m, file);
-                       list_add_tail(&m->dep_obj_files, &f->list);
-                       return sofar;
-               }
-       }
-
-       if (stat(dir, &st) == 0) {
-               if (!need_obj)
-                       return sofar;
-
-               return talloc_asprintf_append(sofar,
-                                             "ccan/%s: isn't built (no %s)\n",
-                                             dep, file);
-       }
-
-       return talloc_asprintf_append(sofar,
-                                     "ccan/%s: could not find directory %s\n",
-                                     dep, dir);
-}
-
-static void *check_depends(struct manifest *m)
-{
-       unsigned int i;
-       char *report = NULL;
-       char **deps;
-
-       if (safe_mode)
-               deps = get_safe_ccan_deps(m, "..", m->basename, true);
-       else
-               deps = get_deps(m, "..", m->basename, true);
-
-       for (i = 0; deps[i]; i++) {
-               if (!strstarts(deps[i], "ccan/"))
-                       continue;
-
-               report = add_dep(report, m, deps[i] + strlen("ccan/"));
-       }
-       return report;
-}
-
-static const char *describe_depends(struct manifest *m, void *check_result)
-{
-       return talloc_asprintf(check_result, 
-                              "The following dependencies are needed:\n"
-                              "%s\n", (char *)check_result);
-}
-
-struct ccanlint depends = {
-       .name = "CCAN dependencies are built",
-       .total_score = 1,
-       .check = check_depends,
-       .describe = describe_depends,
-};
-
-REGISTER_TEST(depends, NULL);
diff --git a/tools/ccanlint/tests/check_depends_built.c b/tools/ccanlint/tests/check_depends_built.c
new file mode 100644 (file)
index 0000000..02fa83b
--- /dev/null
@@ -0,0 +1,92 @@
+#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;
+}
+
+/* FIXME: recursive ccanlint if they ask for it. */
+static bool expect_obj_file(const char *dir)
+{
+       char *olddir;
+       struct manifest *dep_man;
+       bool has_c_files;
+
+       olddir = talloc_getcwd(dir);
+       if (!olddir)
+               err(1, "Getting current directory");
+
+       /* We will fail below if this doesn't exist. */
+       if (chdir(dir) != 0)
+               return false;
+
+       dep_man = get_manifest(dir);
+       if (chdir(olddir) != 0)
+               err(1, "Returning to original directory '%s'", olddir);
+       talloc_free(olddir);
+
+       /* If it has C files, we expect an object file built from them. */
+       has_c_files = !list_empty(&dep_man->c_files);
+       talloc_free(dep_man);
+       return has_c_files;
+}
+
+static void *check_depends_built(struct manifest *m)
+{
+       struct ccan_file *i;
+       char *report = NULL;
+
+       list_for_each(&m->dep_dirs, i, list) {
+               char *objfile;
+               struct stat st;
+
+               if (!expect_obj_file(i->name))
+                       continue;
+
+               objfile = talloc_asprintf(m, "%s.o", i->name);
+               if (stat(objfile, &st) != 0) {
+                       report = talloc_asprintf_append(report,
+                                                       "object file %s\n",
+                                                       objfile);
+               } else {
+                       struct ccan_file *f = new_ccan_file(m, objfile);
+                       list_add_tail(&m->dep_objs, &f->list);
+               }
+                       
+       }
+       return talloc_steal(m, report);
+}
+
+static const char *describe_depends_built(struct manifest *m,
+                                         void *check_result)
+{
+       return talloc_asprintf(check_result, 
+                              "The following dependencies are not built:\n"
+                              "%s", (char *)check_result);
+}
+
+struct ccanlint depends_built = {
+       .name = "CCAN dependencies are built",
+       .total_score = 1,
+       .check = check_depends_built,
+       .describe = describe_depends_built,
+       .can_run = can_build,
+};
+
+REGISTER_TEST(depends_built, &depends_exist, NULL);
diff --git a/tools/ccanlint/tests/check_depends_exist.c b/tools/ccanlint/tests/check_depends_exist.c
new file mode 100644 (file)
index 0000000..0978340
--- /dev/null
@@ -0,0 +1,71 @@
+#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 char *add_dep(char *sofar, struct manifest *m, const char *dep)
+{
+       char *dir;
+       struct stat st;
+       struct ccan_file *f;
+
+       dir = talloc_asprintf(m, "../%s", dep);
+       if (stat(dir, &st) != 0) {
+               return talloc_asprintf_append(sofar,
+                                             "ccan/%s: expected it in"
+                                             " directory %s\n",
+                                             dep, dir);
+       }
+
+       f = new_ccan_file(m, dir);
+       list_add_tail(&m->dep_dirs, &f->list);
+       return sofar;
+}
+
+static void *check_depends_exist(struct manifest *m)
+{
+       unsigned int i;
+       char *report = NULL;
+       char **deps;
+
+       if (safe_mode)
+               deps = get_safe_ccan_deps(m, "..", m->basename, true);
+       else
+               deps = get_deps(m, "..", m->basename, true);
+
+       for (i = 0; deps[i]; i++) {
+               if (!strstarts(deps[i], "ccan/"))
+                       continue;
+
+               report = add_dep(report, m, deps[i] + strlen("ccan/"));
+       }
+       return report;
+}
+
+static const char *describe_depends_exist(struct manifest *m,
+                                         void *check_result)
+{
+       return talloc_asprintf(check_result,
+                              "The following dependencies are are expected:\n"
+                              "%s", (char *)check_result);
+}
+
+struct ccanlint depends_exist = {
+       .name = "CCAN dependencies are present",
+       .total_score = 1,
+       .check = check_depends_exist,
+       .describe = describe_depends_exist,
+};
+
+REGISTER_TEST(depends_exist, NULL);
diff --git a/tools/ccanlint/tests/check_includes_build.c b/tools/ccanlint/tests/check_includes_build.c
new file mode 100644 (file)
index 0000000..33668ed
--- /dev/null
@@ -0,0 +1,75 @@
+#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 int cleanup_testfile(char *testfile)
+{
+       unlink(testfile);
+       return 0;
+}
+
+static void *check_includes_build(struct manifest *m)
+{
+       char *contents;
+       char *tmpfile, *objfile;
+       int fd;
+
+       tmpfile = talloc_strdup(m, tempnam("/tmp", "ccanlint"));
+       talloc_set_destructor(tmpfile, cleanup_testfile);
+       objfile = talloc_strdup(m, tempnam("/tmp", "ccanlint"));
+       talloc_set_destructor(objfile, cleanup_testfile);
+
+       fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
+       if (fd < 0)
+               return talloc_asprintf(m, "Creating temporary file: %s",
+                                      strerror(errno));
+
+       contents = talloc_asprintf(tmpfile, "#include <ccan/%s/%s.h>\n",
+                                  m->basename, m->basename);
+       if (write(fd, contents, strlen(contents)) != strlen(contents)) {
+               close(fd);
+               return "Failure writing to temporary file";
+       }
+       close(fd);
+
+       return run_command(m, "cc " CFLAGS " -o %s -c -x c %s",
+                          objfile, tmpfile);
+}
+
+static const char *describe_includes_build(struct manifest *m,
+                                          void *check_result)
+{
+       return talloc_asprintf(check_result, 
+                              "#include of the main header file:\n"
+                              "%s", (char *)check_result);
+}
+
+struct ccanlint includes_build = {
+       .name = "Can compile against main header",
+       .total_score = 1,
+       .check = check_includes_build,
+       .describe = describe_includes_build,
+       .can_run = can_build,
+};
+
+REGISTER_TEST(includes_build, &depends_exist, NULL);
index 1360cc3931de3767415fa7c6021ec2b8935abc1f..203788fe9bed29dff55021eb6cf23bef442bfa54 100644 (file)
@@ -185,6 +185,22 @@ get_all_deps(const void *ctx, const char *dir, const char *name,
        return deps;
 }
 
+char **get_libs(const void *ctx, const char *dir,
+               const char *name, unsigned int *num)
+{
+       char **libs, *cmd, *infofile;
+
+       infofile = compile_info(ctx, dir, name);
+       if (!infofile)
+               errx(1, "Could not compile _info for '%s'", name);
+
+       cmd = talloc_asprintf(ctx, "%s libs", infofile);
+       libs = lines_from_cmd(cmd, num, "%s", cmd);
+       if (!libs)
+               err(1, "Could not run '%s'", cmd);
+       return libs;
+}
+
 char **get_deps(const void *ctx, const char *dir, const char *name,
                bool recurse)
 {
@@ -204,40 +220,3 @@ char **get_safe_ccan_deps(const void *ctx, const char *dir,
        }
        return get_all_deps(ctx, dir, name, get_one_safe_deps);
 }
-       
-char *talloc_basename(const void *ctx, const char *dir)
-{
-       char *p = strrchr(dir, '/');
-
-       if (!p)
-               return (char *)dir;
-       return talloc_strdup(ctx, p+1);
-}
-
-char *talloc_dirname(const void *ctx, const char *dir)
-{
-       char *p = strrchr(dir, '/');
-
-       if (!p)
-               return talloc_strdup(ctx, ".");
-       return talloc_strndup(ctx, dir, p - dir);
-}
-
-char *talloc_getcwd(const void *ctx)
-{
-       unsigned int len;
-       char *cwd;
-
-       /* *This* is why people hate C. */
-       len = 32;
-       cwd = talloc_array(ctx, char, len);
-       while (!getcwd(cwd, len)) {
-               if (errno != ERANGE) {
-                       talloc_free(cwd);
-                       return NULL;
-               }
-               cwd = talloc_realloc(ctx, cwd, char, len *= 2);
-       }
-       return cwd;
-}
-
diff --git a/tools/tools.c b/tools/tools.c
new file mode 100644 (file)
index 0000000..d50a67f
--- /dev/null
@@ -0,0 +1,69 @@
+#include <ccan/talloc/talloc.h>
+#include <ccan/grab_file/grab_file.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include "tools.h"
+
+char *talloc_basename(const void *ctx, const char *dir)
+{
+       char *p = strrchr(dir, '/');
+
+       if (!p)
+               return (char *)dir;
+       return talloc_strdup(ctx, p+1);
+}
+
+char *talloc_dirname(const void *ctx, const char *dir)
+{
+       char *p = strrchr(dir, '/');
+
+       if (!p)
+               return talloc_strdup(ctx, ".");
+       return talloc_strndup(ctx, dir, p - dir);
+}
+
+char *talloc_getcwd(const void *ctx)
+{
+       unsigned int len;
+       char *cwd;
+
+       /* *This* is why people hate C. */
+       len = 32;
+       cwd = talloc_array(ctx, char, len);
+       while (!getcwd(cwd, len)) {
+               if (errno != ERANGE) {
+                       talloc_free(cwd);
+                       return NULL;
+               }
+               cwd = talloc_realloc(ctx, cwd, char, len *= 2);
+       }
+       return cwd;
+}
+
+char *run_command(const void *ctx, const char *fmt, ...)
+{
+       va_list ap;
+       char *cmd, *contents;
+       FILE *pipe;
+
+       va_start(ap, fmt);
+       cmd = talloc_vasprintf(ctx, fmt, ap);
+       va_end(ap);
+
+       /* Ensure stderr gets to us too. */
+       cmd = talloc_asprintf_append(cmd, " 2>&1");
+       
+       pipe = popen(cmd, "r");
+       if (!pipe)
+               return talloc_asprintf(ctx, "Failed to run '%s'", cmd);
+
+       contents = grab_fd(cmd, fileno(pipe), NULL);
+       if (pclose(pipe) != 0)
+               return talloc_asprintf(ctx, "Running '%s':\n%s",
+                                      cmd, contents);
+
+       talloc_free(cmd);
+       return NULL;
+}
index 6ac4e66fcbc78b865e655d0182f4a4e7ead6cb0f..9fd1e1f6b908e4f7a004d5b89b74198fcef84f04 100644 (file)
@@ -19,7 +19,13 @@ char **get_deps(const void *ctx, const char *dir, const char *name,
 char **get_safe_ccan_deps(const void *ctx, const char *dir, const char *name,
                          bool recurse);
 
+/* This also needs to compile the info file. */
+char **get_libs(const void *ctx, const char *dir,
+               const char *name, unsigned int *num);
+
+/* From tools.c */
 char *talloc_basename(const void *ctx, const char *dir);
 char *talloc_dirname(const void *ctx, const char *dir);
 char *talloc_getcwd(const void *ctx);
+char *run_command(const void *ctx, const char *fmt, ...);
 #endif /* CCAN_TOOLS_H */