]> git.ozlabs.org Git - ccan/blobdiff - tools/ccanlint/ccanlint.c
ccanlint: fix compilation of API tests with coverage
[ccan] / tools / ccanlint / ccanlint.c
index 6bfc6d15bd1debf7ad7355da0ce3ba2181e2dde8..e3d99c79a7b72bda874466b48d427b5df2dad65c 100644 (file)
@@ -31,6 +31,8 @@
 #include <ccan/talloc/talloc.h>
 #include <ccan/opt/opt.h>
 #include <ccan/foreach/foreach.h>
+#include <ccan/grab_file/grab_file.h>
+#include <ccan/cast/cast.h>
 
 int verbose = 0;
 static LIST_HEAD(compulsory_tests);
@@ -41,6 +43,12 @@ static struct btree *cmdline_exclude;
 static struct btree *info_exclude;
 static unsigned int timeout;
 
+/* These are overridden at runtime if we can find config.h */
+const char *compiler = NULL;
+const char *cflags = NULL;
+
+const char *config_header;
+
 #if 0
 static void indent_print(const char *string)
 {
@@ -149,29 +157,13 @@ static bool run_test(struct ccanlint *i,
        }
 
        if ((!quiet && !score->pass) || verbose) {
-               struct file_error *f;
-               unsigned int lines = 1;
-
-               if (score->error)
-                       printf("%s%s\n", score->error,
-                              list_empty(&score->per_file_errors) ? "" : ":");
-
-               list_for_each(&score->per_file_errors, f, list) {
-                       if (f->line)
-                               printf("%s:%u:%s\n",
-                                      f->file->fullname, f->line, f->error);
-                       else if (f->file)
-                               printf("%s:%s\n", f->file->fullname, f->error);
-                       else
-                               printf("%s\n", f->error);
-                       if (verbose < 2 && ++lines > 5) {
-                               printf("... more (use -vv to see them all)\n");
-                               break;
-                       }
+               if (score->error) {
+                       printf("%s%s", score->error,
+                              strends(score->error, "\n") ? "" : "\n");
                }
-               if (!quiet && !score->pass && i->handle)
-                       i->handle(m, score);
        }
+       if (!quiet && score->score < score->total && i->handle)
+               i->handle(m, score);
 
        *running_score += score->score;
        *running_total += score->total;
@@ -256,7 +248,7 @@ static void init_tests(void)
        /* Resolve dependencies. */
        foreach_ptr(list, &compulsory_tests, &normal_tests) {
                list_for_each(list, c, list) {
-                       char **deps = strsplit(NULL, c->needs, " ", NULL);
+                       char **deps = strsplit(NULL, c->needs, " ");
                        unsigned int i;
 
                        for (i = 0; deps[i]; i++) {
@@ -310,12 +302,31 @@ static void init_tests(void)
        }
 }
 
+static int show_tmpdir(const char *dir)
+{
+       printf("You can find ccanlint working files in '%s'\n", dir);
+       return 0;
+}
+
 static char *keep_test(const char *testname, void *unused)
 {
-       struct ccanlint *i = find_test(testname);
-       if (!i)
-               errx(1, "No test %s to --keep", testname);
-       i->keep_results = true;
+       struct ccanlint *i;
+
+       if (streq(testname, "all")) {
+               struct list_head *list;
+               foreach_ptr(list, &compulsory_tests, &normal_tests) {
+                       list_for_each(list, i, list)
+                               i->keep_results = true;
+               }
+       } else {
+               i = find_test(testname);
+               if (!i)
+                       errx(1, "No test %s to --keep", testname);
+               i->keep_results = true;
+       }
+
+       /* Don't automatically destroy temporary dir. */
+       talloc_set_destructor(temp_dir(NULL), show_tmpdir);
        return NULL;
 }
 
@@ -409,8 +420,8 @@ static void add_info_options(struct ccan_file *info, bool mark_fails)
                        continue;
 
                for (i = 0; i < d->num_lines; i++) {
-                       char **words = collapse(strsplit(d, d->lines[i], " \t",
-                                                        NULL), NULL);
+                       char **words = collapse(strsplit(d, d->lines[i], " \t"),
+                                               NULL);
                        if (!words[0])
                                continue;
 
@@ -472,9 +483,108 @@ static void skip_unrelated_tests(struct ccanlint *target)
                                i->skip = "not relevant to target";
 }
 
+static char *demangle_string(char *string)
+{
+       unsigned int i;
+       const char mapfrom[] = "abfnrtv";
+       const char mapto[] = "\a\b\f\n\r\t\v";
+
+       if (!strchr(string, '"'))
+               return NULL;
+       string = strchr(string, '"') + 1;
+       if (!strrchr(string, '"'))
+               return NULL;
+       *strrchr(string, '"') = '\0';
+
+       for (i = 0; i < strlen(string); i++) {
+               if (string[i] == '\\') {
+                       char repl;
+                       unsigned len = 0;
+                       const char *p = strchr(mapfrom, string[i+1]);
+                       if (p) {
+                               repl = mapto[p - mapfrom];
+                               len = 1;
+                       } else if (strlen(string+i+1) >= 3) {
+                               if (string[i+1] == 'x') {
+                                       repl = (string[i+2]-'0')*16
+                                               + string[i+3]-'0';
+                                       len = 3;
+                               } else if (cisdigit(string[i+1])) {
+                                       repl = (string[i+2]-'0')*8*8
+                                               + (string[i+3]-'0')*8
+                                               + (string[i+4]-'0');
+                                       len = 3;
+                               }
+                       }
+                       if (len == 0) {
+                               repl = string[i+1];
+                               len = 1;
+                       }
+
+                       string[i] = repl;
+                       memmove(string + i + 1, string + i + len + 1,
+                               strlen(string + i + len + 1) + 1);
+               }
+       }
+
+       return string;
+}
+
+
+static void read_config_header(void)
+{
+       char *fname = talloc_asprintf(NULL, "%s/config.h", ccan_dir);
+       char **lines;
+       unsigned int i;
+
+       config_header = grab_file(NULL, fname, NULL);
+       if (!config_header) {
+               talloc_free(fname);
+               return;
+       }
+
+       lines = strsplit(config_header, config_header, "\n");
+       for (i = 0; i < talloc_array_length(lines) - 1; i++) {
+               char *sym;
+               const char **line = (const char **)&lines[i];
+
+               if (!get_token(line, "#"))
+                       continue;
+               if (!get_token(line, "define"))
+                       continue;
+               sym = get_symbol_token(lines, line);
+               if (streq(sym, "CCAN_COMPILER") && !compiler) {
+                       compiler = demangle_string(lines[i]);
+                       if (!compiler)
+                               errx(1, "%s:%u:could not parse CCAN_COMPILER",
+                                    fname, i+1);
+                       if (verbose > 1)
+                               printf("%s: compiler set to '%s'\n",
+                                      fname, compiler);
+               } else if (streq(sym, "CCAN_CFLAGS") && !cflags) {
+                       cflags = demangle_string(lines[i]);
+                       if (!cflags)
+                               errx(1, "%s:%u:could not parse CCAN_CFLAGS",
+                                    fname, i+1);
+                       if (verbose > 1)
+                               printf("%s: compiler flags set to '%s'\n",
+                                      fname, cflags);
+               }
+       }
+       if (!compiler)
+               compiler = CCAN_COMPILER;
+       if (!cflags)
+               compiler = CCAN_CFLAGS;
+}
+
+static char *opt_set_const_charp(const char *arg, const char **p)
+{
+       return opt_set_charp(arg, cast_const2(char **, p));
+}
+
 int main(int argc, char *argv[])
 {
-       bool summary = false;
+       bool summary = false, pass = true;
        unsigned int score = 0, total_score = 0;
        struct manifest *m;
        struct ccanlint *i;
@@ -495,7 +605,8 @@ int main(int argc, char *argv[])
        opt_register_noarg("--test-dep-graph", test_dependency_graph, NULL,
                         "print dependency graph of tests in Graphviz .dot format");
        opt_register_arg("-k|--keep <testname>", keep_test, NULL, NULL,
-                          "keep results of <testname> (can be used multiple times)");
+                        "keep results of <testname>"
+                        " (can be used multiple times, or 'all')");
        opt_register_noarg("--summary|-s", opt_set_bool, &summary,
                           "simply give one line summary");
        opt_register_noarg("--verbose|-v", opt_inc_intval, &verbose,
@@ -508,15 +619,25 @@ int main(int argc, char *argv[])
        opt_register_arg("--target <testname>", opt_set_charp,
                         NULL, &target,
                         "only run one test (and its prerequisites)");
+       opt_register_arg("--compiler <compiler>", opt_set_const_charp,
+                        NULL, &compiler, "set the compiler");
+       opt_register_arg("--cflags <flags>", opt_set_const_charp,
+                        NULL, &cflags, "set the compiler flags");
        opt_register_noarg("-?|-h|--help", opt_usage_and_exit,
                           "\nA program for checking and guiding development"
                           " of CCAN modules.",
                           "This usage message");
 
+       /* We move into temporary directory, so gcov dumps its files there. */
+       if (chdir(temp_dir(talloc_autofree_context())) != 0)
+               err(1, "Error changing to %s temporary dir", temp_dir(NULL));
+
        opt_parse(&argc, argv, opt_log_stderr_exit);
 
        if (dir[0] != '/')
                dir = talloc_asprintf_append(NULL, "%s/%s", base_dir, dir);
+       while (strends(dir, "/"))
+               dir[strlen(dir)-1] = '\0';
        if (dir != base_dir)
                prefix = talloc_append_string(talloc_basename(NULL, dir), ": ");
        if (verbose >= 3)
@@ -524,11 +645,8 @@ int main(int argc, char *argv[])
        if (verbose >= 4)
                tools_verbose = true;
 
-       /* We move into temporary directory, so gcov dumps its files there. */
-       if (chdir(temp_dir(talloc_autofree_context())) != 0)
-               err(1, "Error changing to %s temporary dir", temp_dir(NULL));
-
        m = get_manifest(talloc_autofree_context(), dir);
+       read_config_header();
 
        /* Create a symlink from temp dir back to src dir's test directory. */
        if (symlink(talloc_asprintf(m, "%s/test", dir),
@@ -553,11 +671,12 @@ int main(int argc, char *argv[])
        }
 
        /* --target overrides known FAIL from _info */
-       add_info_options(m->info_file, !target);
+       if (m->info_file)
+               add_info_options(m->info_file, !target);
 
        while ((i = get_next_test(&normal_tests)) != NULL)
-               run_test(i, summary, &score, &total_score, m);
+               pass &= run_test(i, summary, &score, &total_score, m);
 
        printf("%sTotal score: %u/%u\n", prefix, score, total_score);
-       return 0;
+       return pass ? 0 : 1;
 }