From: Rusty Russell Date: Thu, 1 Dec 2011 06:15:51 +0000 (+1030) Subject: ccanlint: get rid of separate class of compulsory tests. X-Git-Url: https://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=60824f8a0f758dc7808eb31cc83434ea78f44ed1 ccanlint: get rid of separate class of compulsory tests. It's reduced to a flag which means we stop processing the module. We have to enhance our dependencies a bit, but they've always been quite good. This simplifies things quite a bit. --- diff --git a/.gitignore b/.gitignore index 6237a284..6b2b9fba 100644 --- a/.gitignore +++ b/.gitignore @@ -9,8 +9,7 @@ tools/doc_extract tools/namespacize tools/run_tests tools/ccanlint/ccanlint -tools/ccanlint/generated-compulsory-tests -tools/ccanlint/generated-normal-tests +tools/ccanlint/generated-testlist inter-depends test-depends lib-depends diff --git a/tools/ccanlint/Makefile b/tools/ccanlint/Makefile index ee371e05..11d09e87 100644 --- a/tools/ccanlint/Makefile +++ b/tools/ccanlint/Makefile @@ -1,6 +1,5 @@ -COMPULSORY_TEST_CFILES := $(wildcard tools/ccanlint/compulsory_tests/*.c) -NORMAL_TEST_CFILES := $(wildcard tools/ccanlint/tests/*.c) -TEST_OBJS := $(NORMAL_TEST_CFILES:.c=.o) $(COMPULSORY_TEST_CFILES:.c=.o) +TEST_CFILES := $(wildcard tools/ccanlint/tests/*.c) +TEST_OBJS := $(TEST_CFILES:.c=.o) CORE_OBJS := \ ccan/asort/asort.o \ @@ -33,20 +32,16 @@ CORE_OBJS := \ OBJS := $(CORE_OBJS) $(TEST_OBJS) # FIXME: write a trivial C program to do this -tools/ccanlint/generated-normal-tests: $(NORMAL_TEST_CFILES) - cat $^ | grep ^REGISTER_TEST > $@ -tools/ccanlint/generated-compulsory-tests: $(COMPULSORY_TEST_CFILES) +tools/ccanlint/generated-testlist: $(TEST_CFILES) cat $^ | grep ^REGISTER_TEST > $@ -$(TEST_OBJS): tools/ccanlint/generated-normal-tests tools/ccanlint/generated-compulsory-tests +$(TEST_OBJS): tools/ccanlint/generated-testlist # Otherwise, ccanlint.c et al. may fail to build -$(CORE_OBJS): tools/ccanlint/generated-normal-tests tools/ccanlint/generated-compulsory-tests config.h +$(CORE_OBJS): tools/ccanlint/generated-testlist config.h tools/ccanlint/ccanlint: $(OBJS) ccanlint-clean: - $(RM) tools/ccanlint/generated-compulsory-tests - $(RM) tools/ccanlint/generated-normal-tests + $(RM) tools/ccanlint/generated-testlist $(RM) tools/ccanlint/ccanlint - diff --git a/tools/ccanlint/ccanlint.c b/tools/ccanlint/ccanlint.c index 63724d2e..b35f11a7 100644 --- a/tools/ccanlint/ccanlint.c +++ b/tools/ccanlint/ccanlint.c @@ -33,10 +33,13 @@ #include #include #include +#include + +/* Defines struct tlist_ccanlint. */ +TLIST_TYPE(ccanlint, struct ccanlint); int verbose = 0; -static struct list_head compulsory_tests; -static struct list_head normal_tests; +static struct tlist_ccanlint tests = TLIST_INIT(tests); bool safe_mode = false; static struct btree *cmdline_exclude; static struct btree *info_exclude; @@ -182,9 +185,9 @@ static bool run_test(struct ccanlint *i, return score->pass; } -static void register_test(struct list_head *h, struct ccanlint *test) +static void register_test(struct ccanlint *test) { - list_add(h, &test->list); + tlist_add(&tests, test, list); test->options = talloc_array(NULL, char *, 1); test->options[0] = NULL; test->skip = NULL; @@ -194,14 +197,14 @@ static void register_test(struct list_head *h, struct ccanlint *test) /** * get_next_test - retrieves the next test to be processed **/ -static inline struct ccanlint *get_next_test(struct list_head *test) +static inline struct ccanlint *get_next_test(void) { struct ccanlint *i; - if (list_empty(test)) + if (tlist_empty(&tests)) return NULL; - list_for_each(test, i, list) { + tlist_for_each(&tests, i, list) { if (i->num_depends == 0) return i; } @@ -212,11 +215,7 @@ static struct ccanlint *find_test(const char *key) { struct ccanlint *i; - list_for_each(&compulsory_tests, i, list) - if (streq(i->key, key)) - return i; - - list_for_each(&normal_tests, i, list) + tlist_for_each(&tests, i, list) if (streq(i->key, key)) return i; @@ -232,91 +231,74 @@ bool is_excluded(const char *name) #undef REGISTER_TEST #define REGISTER_TEST(name, ...) extern struct ccanlint name -#include "generated-normal-tests" -#include "generated-compulsory-tests" +#include "generated-testlist" static void init_tests(void) { struct ccanlint *c; struct btree *keys, *names; - struct list_head *list; - list_head_init(&normal_tests); - list_head_init(&compulsory_tests); + tlist_init(&tests); #undef REGISTER_TEST -#define REGISTER_TEST(name) register_test(&normal_tests, &name) -#include "generated-normal-tests" -#undef REGISTER_TEST -#define REGISTER_TEST(name) register_test(&compulsory_tests, &name) -#include "generated-compulsory-tests" +#define REGISTER_TEST(name) register_test(&name) +#include "generated-testlist" /* Initialize dependency lists. */ - foreach_ptr(list, &compulsory_tests, &normal_tests) { - list_for_each(list, c, list) { - list_head_init(&c->dependencies); - c->num_depends = 0; - } + tlist_for_each(&tests, c, list) { + list_head_init(&c->dependencies); + c->num_depends = 0; } /* Resolve dependencies. */ - foreach_ptr(list, &compulsory_tests, &normal_tests) { - list_for_each(list, c, list) { - char **deps = strsplit(NULL, c->needs, " "); - unsigned int i; - - for (i = 0; deps[i]; i++) { - struct ccanlint *dep; - struct dependent *dchild; - - dep = find_test(deps[i]); - if (!dep) - errx(1, "BUG: unknown dep '%s' for %s", - deps[i], c->key); - dchild = talloc(NULL, struct dependent); - dchild->dependent = c; - list_add_tail(&dep->dependencies, - &dchild->node); - c->num_depends++; - } - talloc_free(deps); + tlist_for_each(&tests, c, list) { + char **deps = strsplit(NULL, c->needs, " "); + unsigned int i; + + for (i = 0; deps[i]; i++) { + struct ccanlint *dep; + struct dependent *dchild; + + dep = find_test(deps[i]); + if (!dep) + errx(1, "BUG: unknown dep '%s' for %s", + deps[i], c->key); + dchild = talloc(NULL, struct dependent); + dchild->dependent = c; + list_add_tail(&dep->dependencies, &dchild->node); + c->num_depends++; } + talloc_free(deps); } /* Self-consistency check: make sure no two tests have the same key or name. */ keys = btree_new(btree_strcmp); names = btree_new(btree_strcmp); - foreach_ptr(list, &compulsory_tests, &normal_tests) { - list_for_each(list, c, list) { - if (!btree_insert(keys, c->key)) - errx(1, "BUG: Duplicate test key '%s'", - c->key); - if (!btree_insert(names, c->name)) - errx(1, "BUG: Duplicate test name '%s'", - c->name); - } + tlist_for_each(&tests, c, list) { + if (!btree_insert(keys, c->key)) + errx(1, "BUG: Duplicate test key '%s'", + c->key); + if (!btree_insert(names, c->name)) + errx(1, "BUG: Duplicate test name '%s'", + c->name); } + btree_delete(keys); btree_delete(names); } static void print_test_depends(void) { - struct list_head *list; - - foreach_ptr(list, &compulsory_tests, &normal_tests) { - struct ccanlint *c; - printf("\%s Tests\n", - list == &compulsory_tests ? "Compulsory" : "Normal"); - - list_for_each(list, c, list) { - if (!list_empty(&c->dependencies)) { - const struct dependent *d; - printf("These depend on %s:\n", c->key); - list_for_each(&c->dependencies, d, node) - printf("\t%s\n", d->dependent->key); - } + struct ccanlint *c; + printf("Tests:\n"); + + tlist_for_each(&tests, c, list) { + if (!list_empty(&c->dependencies)) { + const struct dependent *d; + printf("These depend on %s:\n", c->key); + list_for_each(&c->dependencies, d, node) + printf("\t%s\n", d->dependent->key); } } } @@ -333,11 +315,8 @@ static char *keep_test(const char *testname, void *unused) init_tests(); 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; - } + tlist_for_each(&tests, i, list) + i->keep_results = true; } else { i = find_test(testname); if (!i) @@ -356,34 +335,29 @@ static char *skip_test(const char *testname, void *unused) return NULL; } -static void print_tests(struct list_head *tests, const char *type) +static char *list_tests(void *arg) { struct ccanlint *i; - printf("%s tests:\n", type); + init_tests(); + + printf("Tests:\n"); /* This makes them print in topological order. */ - while ((i = get_next_test(tests)) != NULL) { + while ((i = get_next_test()) != NULL) { const struct dependent *d; printf(" %-25s %s\n", i->key, i->name); list_del(&i->list); list_for_each(&i->dependencies, d, node) d->dependent->num_depends--; } -} - -static char *list_tests(void *arg) -{ - init_tests(); - print_tests(&compulsory_tests, "Compulsory"); - print_tests(&normal_tests, "Normal"); exit(0); } -static void test_dgraph_vertices(struct list_head *tests, const char *style) +static void test_dgraph_vertices(const char *style) { const struct ccanlint *i; - list_for_each(tests, i, list) { + tlist_for_each(&tests, i, list) { /* * todo: escape labels in case ccanlint test keys have * characters interpreted as GraphViz syntax. @@ -392,12 +366,12 @@ static void test_dgraph_vertices(struct list_head *tests, const char *style) } } -static void test_dgraph_edges(struct list_head *tests) +static void test_dgraph_edges(void) { const struct ccanlint *i; const struct dependent *d; - list_for_each(tests, i, list) + tlist_for_each(&tests, i, list) list_for_each(&i->dependencies, d, node) printf("\t\"%p\" -> \"%p\"\n", d->dependent, i); } @@ -407,11 +381,8 @@ static char *test_dependency_graph(void *arg) init_tests(); puts("digraph G {"); - test_dgraph_vertices(&compulsory_tests, ", style=filled, fillcolor=yellow"); - test_dgraph_vertices(&normal_tests, ""); - - test_dgraph_edges(&compulsory_tests); - test_dgraph_edges(&normal_tests); + test_dgraph_vertices(""); + test_dgraph_edges(); puts("}"); @@ -552,12 +523,10 @@ static bool depends_on(struct ccanlint *i, struct ccanlint *target) static void skip_unrelated_tests(struct ccanlint *target) { struct ccanlint *i; - struct list_head *list; - foreach_ptr(list, &compulsory_tests, &normal_tests) - list_for_each(list, i, list) - if (!depends_on(i, target)) - i->skip = "not relevant to target"; + tlist_for_each(&tests, i, list) + if (!depends_on(i, target)) + i->skip = "not relevant to target"; } static char *demangle_string(char *string) @@ -726,6 +695,8 @@ int main(int argc, char *argv[]) for (i = 1; i < argc; i++) { unsigned int score, total_score; + bool added_info_options = false; + dir = argv[i]; if (dir[0] != '/') @@ -762,26 +733,25 @@ int main(int argc, char *argv[]) if (symlink(talloc_asprintf(m, "%s/test", dir), testlink) != 0) err(1, "Creating test symlink in %s", temp_dir(NULL)); - /* If you don't pass the compulsory tests, score is 0. */ score = total_score = 0; - while ((t = get_next_test(&compulsory_tests)) != NULL) { + while ((t = get_next_test()) != NULL) { if (!run_test(t, summary, &score, &total_score, m, prefix)) { - warnx("%s%s failed", prefix, t->name); - printf("%sTotal score: 0/%u\n", - prefix, total_score); pass = false; - goto next; + if (t->compulsory) { + warnx("%s%s failed", prefix, t->name); + printf("%sTotal score: 0/%u\n", + prefix, total_score); + goto next; + } } - } - /* --target overrides known FAIL from _info */ - if (m->info_file) - add_info_options(m->info_file, !target); - - while ((t = get_next_test(&normal_tests)) != NULL) - pass &= run_test(t, summary, &score, &total_score, m, - prefix); + /* --target overrides known FAIL from _info */ + if (!added_info_options && m->info_file) { + add_info_options(m->info_file, !target); + added_info_options = true; + } + } printf("%sTotal score: %u/%u\n", prefix, score, total_score); next: ; diff --git a/tools/ccanlint/ccanlint.h b/tools/ccanlint/ccanlint.h index 452b44f7..16600cff 100644 --- a/tools/ccanlint/ccanlint.h +++ b/tools/ccanlint/ccanlint.h @@ -91,6 +91,9 @@ struct ccanlint { /* Can we run this test? Return string explaining why, if not. */ const char *(*can_run)(struct manifest *m); + /* Should we stop immediately if test fails? */ + bool compulsory; + /* keep is set if you should keep the results. * If timeleft is set to 0, means it timed out. * score is the result, and a talloc context freed after all our diff --git a/tools/ccanlint/compulsory_tests/build.h b/tools/ccanlint/compulsory_tests/build.h deleted file mode 100644 index 04140bf9..00000000 --- a/tools/ccanlint/compulsory_tests/build.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef CCANLINT_BUILD_H -#define CCANLINT_BUILD_H -char *build_module(struct manifest *m, bool keep, enum compile_type ctype, - char **errstr); -char *build_submodule(struct manifest *m, const char *flags, - enum compile_type ctype); -void build_objects(struct manifest *m, - bool keep, struct score *score, const char *flags, - enum compile_type ctype); -#endif /* CCANLINT_BUILD_H */ diff --git a/tools/ccanlint/compulsory_tests/depends_exist.c b/tools/ccanlint/compulsory_tests/depends_exist.c deleted file mode 100644 index b1694058..00000000 --- a/tools/ccanlint/compulsory_tests/depends_exist.c +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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; -} - -/* FIXME: check this is still true once we reduce features. */ -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); - bool needs_tap; - - if (strrchr(updir, '/')) - *strrchr(updir, '/') = '\0'; - - /* We may need libtap for testing, unless we're "tap" */ - if (streq(m->basename, "tap")) { - needs_tap = false; - } else if (list_empty(&m->run_tests) && list_empty(&m->api_tests)) { - needs_tap = false; - } else { - needs_tap = true; - } - - if (safe_mode) - deps = get_safe_ccan_deps(m, m->dir, true); - else - deps = get_deps(m, m->dir, true, - &m->info_file->compiled[COMPILE_NORMAL]); - - for (i = 0; deps[i]; i++) { - if (!strstarts(deps[i], "ccan/")) - continue; - - if (!add_dep(m, deps[i], score)) - return; - - if (streq(deps[i], "ccan/tap")) { - needs_tap = false; - } - } - - if (needs_tap && !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); diff --git a/tools/ccanlint/compulsory_tests/info_exists.c b/tools/ccanlint/compulsory_tests/info_exists.c deleted file mode 100644 index ac840119..00000000 --- a/tools/ccanlint/compulsory_tests/info_exists.c +++ /dev/null @@ -1,86 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 = talloc_strdup(score, - "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 \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); diff --git a/tools/ccanlint/compulsory_tests/module_builds.c b/tools/ccanlint/compulsory_tests/module_builds.c deleted file mode 100644 index 23985d2c..00000000 --- a/tools/ccanlint/compulsory_tests/module_builds.c +++ /dev/null @@ -1,90 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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, enum compile_type ctype) -{ - 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[ctype]); - - return list; -} - -char *build_module(struct manifest *m, bool keep, - enum compile_type ctype, char **errstr) -{ - char *name = link_objects(m, m->basename, false, obj_list(m, ctype), - errstr); - if (name) { - if (keep) { - char *realname = talloc_asprintf(m, "%s.o", m->dir); - assert(ctype == COMPILE_NORMAL); - /* 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[COMPILE_NORMAL] - = build_module(m, keep, COMPILE_NORMAL, &errstr); - if (!m->compiled[COMPILE_NORMAL]) { - score_file_error(score, NULL, 0, "%s", 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); - diff --git a/tools/ccanlint/compulsory_tests/objects_build.c b/tools/ccanlint/compulsory_tests/objects_build.c deleted file mode 100644 index 2132b173..00000000 --- a/tools/ccanlint/compulsory_tests/objects_build.c +++ /dev/null @@ -1,79 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "build.h" - -static const char *can_build(struct manifest *m) -{ - if (safe_mode) - return "Safe mode enabled"; - return NULL; -} - -void build_objects(struct manifest *m, - bool keep, struct score *score, const char *flags, - enum compile_type ctype) -{ - 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[ctype] = maybe_temp_file(m, "", keep, fullfile); - if (!compile_object(score, fullfile, ccan_dir, compiler, flags, - i->compiled[ctype], &output)) { - talloc_free(i->compiled[ctype]); - score_file_error(score, i, 0, - "Compiling object files:\n%s", - output); - errors = true; - } else if (!streq(output, "")) { - score_file_error(score, i, 0, - "Compiling object files gave" - " warnings:\n%s", - output); - warnings = true; - } - } - - if (!errors) { - score->pass = true; - score->score = score->total - warnings; - } -} - -static void check_objs_build(struct manifest *m, - bool keep, - unsigned int *timeleft, struct score *score) -{ - build_objects(m, keep, score, cflags, COMPILE_NORMAL); -} - -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); diff --git a/tools/ccanlint/tests/build.h b/tools/ccanlint/tests/build.h new file mode 100644 index 00000000..04140bf9 --- /dev/null +++ b/tools/ccanlint/tests/build.h @@ -0,0 +1,10 @@ +#ifndef CCANLINT_BUILD_H +#define CCANLINT_BUILD_H +char *build_module(struct manifest *m, bool keep, enum compile_type ctype, + char **errstr); +char *build_submodule(struct manifest *m, const char *flags, + enum compile_type ctype); +void build_objects(struct manifest *m, + bool keep, struct score *score, const char *flags, + enum compile_type ctype); +#endif /* CCANLINT_BUILD_H */ diff --git a/tools/ccanlint/tests/depends_build.c b/tools/ccanlint/tests/depends_build.c index e7faa1f1..ea5c3926 100644 --- a/tools/ccanlint/tests/depends_build.c +++ b/tools/ccanlint/tests/depends_build.c @@ -13,7 +13,7 @@ #include #include #include -#include "../compulsory_tests/build.h" +#include "build.h" static const char *can_build(struct manifest *m) { diff --git a/tools/ccanlint/tests/depends_build_without_features.c b/tools/ccanlint/tests/depends_build_without_features.c index 9982d27b..dfa96d72 100644 --- a/tools/ccanlint/tests/depends_build_without_features.c +++ b/tools/ccanlint/tests/depends_build_without_features.c @@ -14,7 +14,7 @@ #include #include #include "reduce_features.h" -#include "../compulsory_tests/build.h" +#include "build.h" static const char *can_build(struct manifest *m) { @@ -55,7 +55,7 @@ struct ccanlint depends_build_without_features = { .name = "Module's CCAN dependencies can be found or built (reduced features)", .check = check_depends_built_without_features, .can_run = can_build, - .needs = "depends_exist reduce_features" + .needs = "depends_build reduce_features" }; REGISTER_TEST(depends_build_without_features); diff --git a/tools/ccanlint/tests/depends_exist.c b/tools/ccanlint/tests/depends_exist.c new file mode 100644 index 00000000..ff67857f --- /dev/null +++ b/tools/ccanlint/tests/depends_exist.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} + +/* FIXME: check this is still true once we reduce features. */ +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); + bool needs_tap; + + if (strrchr(updir, '/')) + *strrchr(updir, '/') = '\0'; + + /* We may need libtap for testing, unless we're "tap" */ + if (streq(m->basename, "tap")) { + needs_tap = false; + } else if (list_empty(&m->run_tests) && list_empty(&m->api_tests)) { + needs_tap = false; + } else { + needs_tap = true; + } + + if (safe_mode) + deps = get_safe_ccan_deps(m, m->dir, true); + else + deps = get_deps(m, m->dir, true, + &m->info_file->compiled[COMPILE_NORMAL]); + + for (i = 0; deps[i]; i++) { + if (!strstarts(deps[i], "ccan/")) + continue; + + if (!add_dep(m, deps[i], score)) + return; + + if (streq(deps[i], "ccan/tap")) { + needs_tap = false; + } + } + + if (needs_tap && !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", + .compulsory = true, + .check = check_depends_exist, + .needs = "info_exists" +}; + +REGISTER_TEST(depends_exist); diff --git a/tools/ccanlint/tests/examples_compile.c b/tools/ccanlint/tests/examples_compile.c index d52ee789..3cbb23fd 100644 --- a/tools/ccanlint/tests/examples_compile.c +++ b/tools/ccanlint/tests/examples_compile.c @@ -12,7 +12,7 @@ #include #include #include -#include "../compulsory_tests/build.h" +#include "build.h" static const char *can_run(struct manifest *m) { diff --git a/tools/ccanlint/tests/hash_if.c b/tools/ccanlint/tests/hash_if.c index 35608ef1..9e10c304 100644 --- a/tools/ccanlint/tests/hash_if.c +++ b/tools/ccanlint/tests/hash_if.c @@ -71,7 +71,7 @@ struct ccanlint hash_if = { .key = "hash_if", .name = "Features are checked with #if not #ifdef", .check = check_hash_if, - .needs = "" + .needs = "info_exists" }; REGISTER_TEST(hash_if); diff --git a/tools/ccanlint/tests/headers_idempotent.c b/tools/ccanlint/tests/headers_idempotent.c index f23784e4..4b98b246 100644 --- a/tools/ccanlint/tests/headers_idempotent.c +++ b/tools/ccanlint/tests/headers_idempotent.c @@ -195,7 +195,7 @@ struct ccanlint headers_idempotent = { .name = "Module headers are #ifndef/#define wrapped", .check = check_idempotent, .handle = handle_idem, - .needs = "" + .needs = "info_exists main_header_exists" }; REGISTER_TEST(headers_idempotent); diff --git a/tools/ccanlint/tests/info_exists.c b/tools/ccanlint/tests/info_exists.c new file mode 100644 index 00000000..80edb5aa --- /dev/null +++ b/tools/ccanlint/tests/info_exists.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 = talloc_strdup(score, + "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 \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", + .compulsory = true, + .check = check_has_info, + .handle = create_info_template, + .needs = "" +}; + +REGISTER_TEST(info_exists); diff --git a/tools/ccanlint/tests/module_builds.c b/tools/ccanlint/tests/module_builds.c new file mode 100644 index 00000000..52059af7 --- /dev/null +++ b/tools/ccanlint/tests/module_builds.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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, enum compile_type ctype) +{ + 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[ctype]); + + return list; +} + +char *build_module(struct manifest *m, bool keep, + enum compile_type ctype, char **errstr) +{ + char *name = link_objects(m, m->basename, false, obj_list(m, ctype), + errstr); + if (name) { + if (keep) { + char *realname = talloc_asprintf(m, "%s.o", m->dir); + assert(ctype == COMPILE_NORMAL); + /* 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[COMPILE_NORMAL] + = build_module(m, keep, COMPILE_NORMAL, &errstr); + if (!m->compiled[COMPILE_NORMAL]) { + score_file_error(score, NULL, 0, "%s", errstr); + return; + } + + score->pass = true; + score->score = score->total; +} + +struct ccanlint module_builds = { + .key = "module_builds", + .name = "Module can be built from object files", + .compulsory = true, + .check = do_build, + .can_run = can_build, + .needs = "objects_build" +}; + +REGISTER_TEST(module_builds); diff --git a/tools/ccanlint/tests/no_trailing_whitespace.c b/tools/ccanlint/tests/no_trailing_whitespace.c index f046a248..5d3b5ca1 100644 --- a/tools/ccanlint/tests/no_trailing_whitespace.c +++ b/tools/ccanlint/tests/no_trailing_whitespace.c @@ -53,7 +53,7 @@ struct ccanlint no_trailing_whitespace = { .key = "no_trailing_whitespace", .name = "Module's source code has no trailing whitespace", .check = check_trailing_whitespace, - .needs = "" + .needs = "info_exists" }; diff --git a/tools/ccanlint/tests/objects_build.c b/tools/ccanlint/tests/objects_build.c new file mode 100644 index 00000000..8fc11266 --- /dev/null +++ b/tools/ccanlint/tests/objects_build.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "build.h" + +static const char *can_build(struct manifest *m) +{ + if (safe_mode) + return "Safe mode enabled"; + return NULL; +} + +void build_objects(struct manifest *m, + bool keep, struct score *score, const char *flags, + enum compile_type ctype) +{ + 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[ctype] = maybe_temp_file(m, "", keep, fullfile); + if (!compile_object(score, fullfile, ccan_dir, compiler, flags, + i->compiled[ctype], &output)) { + talloc_free(i->compiled[ctype]); + score_file_error(score, i, 0, + "Compiling object files:\n%s", + output); + errors = true; + } else if (!streq(output, "")) { + score_file_error(score, i, 0, + "Compiling object files gave" + " warnings:\n%s", + output); + warnings = true; + } + } + + if (!errors) { + score->pass = true; + score->score = score->total - warnings; + } +} + +static void check_objs_build(struct manifest *m, + bool keep, + unsigned int *timeleft, struct score *score) +{ + build_objects(m, keep, score, cflags, COMPILE_NORMAL); +} + +struct ccanlint objects_build = { + .key = "objects_build", + .name = "Module object files can be built", + .compulsory = true, + .check = check_objs_build, + .can_run = can_build, + .needs = "depends_exist" +}; + +REGISTER_TEST(objects_build); diff --git a/tools/ccanlint/tests/objects_build_without_features.c b/tools/ccanlint/tests/objects_build_without_features.c index 477d3025..770dd475 100644 --- a/tools/ccanlint/tests/objects_build_without_features.c +++ b/tools/ccanlint/tests/objects_build_without_features.c @@ -1,7 +1,7 @@ #include #include #include "reduce_features.h" -#include "../compulsory_tests/build.h" +#include "build.h" static void check_objs_build_without_features(struct manifest *m, bool keep, diff --git a/tools/ccanlint/tests/tests_compile.c b/tools/ccanlint/tests/tests_compile.c index bfa412a6..ee1a6b2c 100644 --- a/tools/ccanlint/tests/tests_compile.c +++ b/tools/ccanlint/tests/tests_compile.c @@ -221,6 +221,6 @@ struct ccanlint tests_compile_without_features = { .name = "Module tests compile (without features)", .check = do_compile_tests_without_features, .can_run = features_reduced, - .needs = "tests_helpers_compile_without_features objects_build_without_features" + .needs = "module_builds tests_helpers_compile_without_features objects_build_without_features" }; REGISTER_TEST(tests_compile_without_features); diff --git a/tools/ccanlint/tests/tests_compile_coverage.c b/tools/ccanlint/tests/tests_compile_coverage.c index a1028110..accb9ffd 100644 --- a/tools/ccanlint/tests/tests_compile_coverage.c +++ b/tools/ccanlint/tests/tests_compile_coverage.c @@ -14,7 +14,7 @@ #include #include #include -#include "../compulsory_tests/build.h" +#include "build.h" #include "tests_compile.h" /* Note: we already test safe_mode in run_tests.c */ diff --git a/tools/ccanlint/tests/tests_exist.c b/tools/ccanlint/tests/tests_exist.c index 523fe99f..57fd4e11 100644 --- a/tools/ccanlint/tests/tests_exist.c +++ b/tools/ccanlint/tests/tests_exist.c @@ -132,6 +132,6 @@ struct ccanlint tests_exist = { .key = "tests_exist", .name = "Module has test directory with tests in it", .check = check_tests_exist, - .needs = "" + .needs = "info_exists" };