From: Rusty Russell Date: Wed, 5 Dec 2007 13:41:39 +0000 (+1100) Subject: First cut, some hacks, three simple modules. X-Git-Tag: init X-Git-Url: http://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=refs%2Ftags%2Finit First cut, some hacks, three simple modules. --- fdfcdafbfa7957b6ca393bb4000fbaad3813a61a diff --git a/.bzrignore b/.bzrignore new file mode 100644 index 00000000..e98cb383 --- /dev/null +++ b/.bzrignore @@ -0,0 +1,2 @@ +_info +.depends diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..a8a6103a --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +CFLAGS=-O3 -Wall -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -I. + +ALL=$(patsubst %/test, %, $(wildcard */test)) +ALL_DEPENDS=$(patsubst %, %/.depends, $(ALL)) + +default: test-all + +test-all: $(ALL_DEPENDS) + @$(MAKE) `for f in $(ALL); do echo test-$$f test-$$f; while read d; do echo test-$$d test-$$f; done < $$f/.depends; done | tsort` + +$(ALL_DEPENDS): %/.depends: %/_info + @$< depends > $@ || ( rm -f $@; exit 1 ) + +test-%: FORCE run_tests + @echo Testing $*... + @if ./run_tests $* | grep ^'not ok'; then exit 1; else exit 0; fi + +FORCE: + +run_tests: run_tests.o tap/tap.o talloc/talloc.o + +clean: + rm -f run_tests run_tests.o diff --git a/build_assert/_info.c b/build_assert/_info.c new file mode 100644 index 00000000..555aae21 --- /dev/null +++ b/build_assert/_info.c @@ -0,0 +1,38 @@ +#include +#include +#include "config.h" + +/** + * build_assert - routines for build-time assertions + * + * This code provides routines which will cause compilation to fail should some + * assertion be untrue: such failures are preferable to run-time assertions, + * but much more limited since they can only depends on compile-time constants. + * + * These assertions are most useful when two parts of the code must be kept in + * sync: it is better to avoid such cases if possible, but seconds best is to + * detect invalid changes at build time. + * + * For example, a tricky piece of code might rely on a certain element being at + * the start of the structure. To ensure that future changes don't break it, + * you would catch such changes in your code like so: + * + * Example: + * char *foo_string(struct foo *foo) + * { + * // This trick requires that the string be first in the structure + * BUILD_ASSERT(offsetof(struct foo, string) == 0); + * return (char *)foo; + * } + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) + /* Nothing. */ + return 0; + + return 1; +} diff --git a/build_assert/build_assert.h b/build_assert/build_assert.h new file mode 100644 index 00000000..4b0d75e4 --- /dev/null +++ b/build_assert/build_assert.h @@ -0,0 +1,37 @@ +#ifndef CCAN_BUILD_ASSERT_H +#define CCAN_BUILD_ASSERT_H + +/** + * BUILD_ASSERT - assert a build-time dependency. + * @cond: the compile-time condition which must be true. + * + * Your compile will fail if the condition isn't true, or can't be evaluated + * by the compiler. This can only be used within a function. + * + * Example: + * char *foo_to_char(struct foo *foo) + * { + * // This code needs string to be at start of foo. + * BUILD_ASSERT(offsetof(struct foo, string) == 0); + * return (char *)foo; + * } + */ +#define BUILD_ASSERT(cond) \ + do { (void) sizeof(char [1 - 2*!(cond)]); } while(0) + +/** + * EXPR_BUILD_ASSERT - assert a build-time dependency, as an expression. + * @cond: the compile-time condition which must be true. + * + * Your compile will fail if the condition isn't true, or can't be evaluated + * by the compiler. This can be used in an expression: its value is "0". + * + * Example: + * #define foo_to_char(foo) \ + * ((char *)(foo) \ + * + EXPR_BUILD_ASSERT(offsetof(struct foo, string) == 0)) + */ +#define EXPR_BUILD_ASSERT(cond) \ + (sizeof(char [1 - 2*!(cond)]) - 1) + +#endif /* CCAN_BUILD_ASSERT_H */ diff --git a/build_assert/test/compile_fail-expr.c b/build_assert/test/compile_fail-expr.c new file mode 100644 index 00000000..41cdc0f8 --- /dev/null +++ b/build_assert/test/compile_fail-expr.c @@ -0,0 +1,10 @@ +#include "build_assert/build_assert.h" + +int main(int argc, char *argv[]) +{ +#ifdef FAIL + return EXPR_BUILD_ASSERT(1 == 0); +#else + return 0; +#endif +} diff --git a/build_assert/test/compile_fail.c b/build_assert/test/compile_fail.c new file mode 100644 index 00000000..a6867db5 --- /dev/null +++ b/build_assert/test/compile_fail.c @@ -0,0 +1,9 @@ +#include "build_assert/build_assert.h" + +int main(int argc, char *argv[]) +{ +#ifdef FAIL + BUILD_ASSERT(1 == 0); +#endif + return 0; +} diff --git a/build_assert/test/compile_ok.c b/build_assert/test/compile_ok.c new file mode 100644 index 00000000..bc5541f5 --- /dev/null +++ b/build_assert/test/compile_ok.c @@ -0,0 +1,7 @@ +#include "build_assert/build_assert.h" + +int main(int argc, char *argv[]) +{ + BUILD_ASSERT(1 == 1); + return 0; +} diff --git a/build_assert/test/run-EXPR_BUILD_ASSERT.c b/build_assert/test/run-EXPR_BUILD_ASSERT.c new file mode 100644 index 00000000..7fd0c49f --- /dev/null +++ b/build_assert/test/run-EXPR_BUILD_ASSERT.c @@ -0,0 +1,9 @@ +#include "build_assert/build_assert.h" +#include "tap/tap.h" + +int main(int argc, char *argv[]) +{ + plan_tests(1); + ok1(EXPR_BUILD_ASSERT(1 == 1) == 0); + return exit_status(); +} diff --git a/check_type/_info.c b/check_type/_info.c new file mode 100644 index 00000000..06e90eb4 --- /dev/null +++ b/check_type/_info.c @@ -0,0 +1,30 @@ +#include +#include +#include "config.h" + +/** + * check_type - routines for compile time type checking + * + * C has fairly weak typing: ints get automatically converted to longs, signed + * to unsigned, etc. There are some cases where this is best avoided, and + * these macros provide methods for evoking warnings (or build errors) when + * a precise type isn't used. + * + * On compilers which don't support typeof() these routines are less effective, + * since they have to use sizeof() which can only distiguish between types of + * different size. + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { +#if !HAVE_TYPEOF + printf("build_assert\n"); +#endif + return 0; + } + + return 1; +} diff --git a/check_type/check_type.h b/check_type/check_type.h new file mode 100644 index 00000000..e05236f8 --- /dev/null +++ b/check_type/check_type.h @@ -0,0 +1,63 @@ +#ifndef CCAN_CHECK_TYPE_H +#define CCAN_CHECK_TYPE_H +#include "config.h" + +/** + * check_type - issue a warning or build failure if type is not correct. + * @expr: the expression whose type we should check (not evaluated). + * @type: the exact type we expect the expression to be. + * + * This macro is usually used within other macros to try to ensure that a macro + * argument is of the expected type. No type promotion of the expression is + * done: an unsigned int is not the same as an int! + * + * check_type() always evaluates to 1. + * + * If your compiler does not support typeof, then the best we can do is fail + * to compile if the sizes of the types are unequal (a less complete check). + * + * Example: + * // They should always pass a 64-bit value to _set_some_value! + * #define set_some_value(expr) \ + * _set_some_value((check_type((expr), uint64_t), (expr))) + */ + +/** + * check_types_match - issue a warning or build failure if types are not same. + * @expr1: the first expression (not evaluated). + * @expr2: the second expression (not evaluated). + * + * This macro is usually used within other macros to try to ensure that + * arguments are of identical types. No type promotion of the expressions is + * done: an unsigned int is not the same as an int! + * + * check_types_match() always evaluates to 1. + * + * If your compiler does not support typeof, then the best we can do is fail + * to compile if the sizes of the types are unequal (a less complete check). + * + * Example: + * // Do subtraction to get to enclosing type, but make sure that + * // pointer is of correct type for that member. + * #define container_of(mbr_ptr, encl_type, mbr) \ + * (check_types_match((mbr_ptr), &((encl_type *)0)->mbr), \ + * ((encl_type *) \ + * ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr)))) + */ +#if HAVE_TYPEOF +#define check_type(expr, type) \ + ((typeof(expr) *)0 != (type *)0) + +#define check_types_match(expr1, expr2) \ + ((typeof(expr1) *)0 != (typeof(expr2) *)0) +#else +#include "build_assert/build_assert.h" +/* Without typeof, we can only test the sizes. */ +#define check_type(expr, type) \ + EXPR_BUILD_ASSERT(sizeof(expr) == sizeof(type)) + +#define check_types_match(expr1, expr2) \ + EXPR_BUILD_ASSERT(sizeof(expr1) == sizeof(expr2)) +#endif /* HAVE_TYPEOF */ + +#endif /* CCAN_CHECK_TYPE_H */ diff --git a/check_type/test/compile_fail-check_type.c b/check_type/test/compile_fail-check_type.c new file mode 100644 index 00000000..d19fe86f --- /dev/null +++ b/check_type/test/compile_fail-check_type.c @@ -0,0 +1,9 @@ +#include "check_type/check_type.h" + +int main(int argc, char *argv[]) +{ +#ifdef FAIL + check_type(argc, char); +#endif + return 0; +} diff --git a/check_type/test/compile_fail-check_type_unsigned.c b/check_type/test/compile_fail-check_type_unsigned.c new file mode 100644 index 00000000..6b18acb5 --- /dev/null +++ b/check_type/test/compile_fail-check_type_unsigned.c @@ -0,0 +1,14 @@ +#include "check_type/check_type.h" + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if HAVE_TYPEOF + check_type(argc, unsigned int); +#else + /* This doesn't work without typeof, so just fail */ +#error "Fail without typeof" +#endif +#endif + return 0; +} diff --git a/check_type/test/compile_fail-check_types_match.c b/check_type/test/compile_fail-check_types_match.c new file mode 100644 index 00000000..bc1f9c31 --- /dev/null +++ b/check_type/test/compile_fail-check_types_match.c @@ -0,0 +1,10 @@ +#include "check_type/check_type.h" + +int main(int argc, char *argv[]) +{ + unsigned char x = argc; +#ifdef FAIL + check_types_match(argc, x); +#endif + return x; +} diff --git a/check_type/test/run.c b/check_type/test/run.c new file mode 100644 index 00000000..acfe31e5 --- /dev/null +++ b/check_type/test/run.c @@ -0,0 +1,22 @@ +#include "check_type/check_type.h" +#include "tap/tap.h" + +int main(int argc, char *argv[]) +{ + int x = 0, y = 0; + + plan_tests(9); + + ok1(check_type(argc, int) == 0); + ok1(check_type(&argc, int *) == 0); + ok1(check_types_match(argc, argc) == 0); + ok1(check_types_match(argc, x) == 0); + ok1(check_types_match(&argc, &x) == 0); + + ok1(check_type(x++, int) == 0); + ok(x == 0, "check_type does not evaluate expression"); + ok1(check_types_match(x++, y++) == 0); + ok(x == 0 && y == 0, "check_types_match does not evaluate expressions"); + + return 0; +} diff --git a/config.h b/config.h new file mode 100644 index 00000000..6cde6a41 --- /dev/null +++ b/config.h @@ -0,0 +1,3 @@ +/* Simple config.h for gcc. */ +#define HAVE_TYPEOF 1 +#define HAVE_STATEMENT_EXPR 1 diff --git a/container_of/_info.c b/container_of/_info.c new file mode 100644 index 00000000..7705e38c --- /dev/null +++ b/container_of/_info.c @@ -0,0 +1,47 @@ +#include +#include +#include "config.h" + +/** + * container_of - routine for upcasting + * + * It is often convenient to create code where the caller registers a pointer + * to a generic structure and a callback. The callback might know that the + * pointer points to within a larger structure, and container_of gives a + * convenient and fairly type-safe way of returning to the enclosing structure. + * + * This idiom is an alternative to providing a void * pointer for every + * callback. + * + * Example: + * struct info + * { + * int my_stuff; + * struct timer timer; + * }; + * + * static void my_timer_callback(struct timer *timer) + * { + * struct info *info = container_of(timer, struct info, timer); + * printf("my_stuff is %u\n", info->my_stuff); + * } + * + * int main() + * { + * struct info info = { .my_stuff = 1 }; + * + * register_timer(&info.timer); + * ... + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("check_type\n"); + return 0; + } + + return 1; +} diff --git a/container_of/container_of.h b/container_of/container_of.h new file mode 100644 index 00000000..49f20b56 --- /dev/null +++ b/container_of/container_of.h @@ -0,0 +1,14 @@ +#ifndef CCAN_CONTAINER_OF_H +#define CCAN_CONTAINER_OF_H +#include + +#include "config.h" +#include "check_type/check_type.h" + +#define container_of(member_ptr, containing_type, member) \ + ((containing_type *) \ + ((char *)(member_ptr) - offsetof(containing_type, member)) \ + - check_types_match(*(member_ptr), ((containing_type *)0)->member)) + + +#endif /* CCAN_CONTAINER_OF_H */ diff --git a/container_of/test/compile_fail-bad-type.c b/container_of/test/compile_fail-bad-type.c new file mode 100644 index 00000000..d372fa54 --- /dev/null +++ b/container_of/test/compile_fail-bad-type.c @@ -0,0 +1,21 @@ +#include "container_of/container_of.h" +#include + +struct foo { + int a; + char b; +}; + +int main(int argc, char *argv[]) +{ + struct foo foo = { .a = 1, .b = 2 }; + int *intp = &foo.a; + char *p; + +#ifdef FAIL + p = container_of(intp, struct foo, a); +#else + p = (char *)intp; +#endif + return p == NULL; +} diff --git a/container_of/test/compile_fail-types.c b/container_of/test/compile_fail-types.c new file mode 100644 index 00000000..33d98784 --- /dev/null +++ b/container_of/test/compile_fail-types.c @@ -0,0 +1,20 @@ +#include "container_of/container_of.h" +#include + +struct foo { + int a; + char b; +}; + +int main(int argc, char *argv[]) +{ + struct foo foo = { .a = 1, .b = 2 }, *foop; + int *intp = &foo.a; + +#ifdef FAIL + foop = container_of(intp, struct foo, b); +#else + foop = NULL; +#endif + return intp == NULL; +} diff --git a/container_of/test/run.c b/container_of/test/run.c new file mode 100644 index 00000000..dab462bc --- /dev/null +++ b/container_of/test/run.c @@ -0,0 +1,19 @@ +#include "container_of/container_of.h" +#include "tap/tap.h" + +struct foo { + int a; + char b; +}; + +int main(int argc, char *argv[]) +{ + struct foo foo = { .a = 1, .b = 2 }; + int *intp = &foo.a; + char *charp = &foo.b; + + plan_tests(2); + ok1(container_of(intp, struct foo, a) == &foo); + ok1(container_of(charp, struct foo, b) == &foo); + return exit_status(); +} diff --git a/run_tests.c b/run_tests.c new file mode 100644 index 00000000..15b52ace --- /dev/null +++ b/run_tests.c @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include +#include +#include "tap/tap.h" +#include "talloc/talloc.h" +#include "string/string.h" + +#define CFLAGS "-O3 -Wall -Wundef -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -I." + +/* FIXME: Use build bug later. */ +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static struct test *tests = NULL; +static struct obj *objs = NULL; +static int verbose; + +struct test_type +{ + const char *name; + void (*testfn)(struct test_type *t, const char *name); +}; + +struct test +{ + struct test *next; + struct test_type *type; + char *name; +}; + +struct obj +{ + struct obj *next; + char *name; +}; + +static char *output_name(const char *name) +{ + char *ret; + + assert(strends(name, ".c")); + + ret = talloc_strdup(name, name); + ret[strlen(ret) - 2] = '\0'; + return ret; +} + +static char *obj_list(void) +{ + char *list = talloc_strdup(objs, ""); + struct obj *i; + + for (i = objs; i; i = i->next) + list = talloc_asprintf_append(list, "%s ", i->name); + + /* FIXME */ + list = talloc_asprintf_append(list, "tap/tap.o"); + return list; +} + +static void compile_objs(void) +{ + struct obj *i; + + for (i = objs; i; i = i->next) { + char *cmd = talloc_asprintf(i, "gcc " CFLAGS " -o %s.o -c %s%s", + output_name(i->name), i->name, + verbose ? "" : "> /dev/null 2>&1"); + ok(system(cmd) == 0, "%s", cmd); + } +} + +static void cleanup_objs(void) +{ + struct obj *i; + + for (i = objs; i; i = i->next) + unlink(talloc_asprintf(i, "%s.o", output_name(i->name))); +} + +static void add_test(const char *testdir, const char *name, struct test_type *t) +{ + struct test *test = talloc(testdir, struct test); + + test->next = tests; + test->type = t; + test->name = talloc_asprintf(test, "%s/%s", testdir, name); + tests = test; +} + +static void add_obj(const char *testdir, const char *name) +{ + struct obj *obj = talloc(testdir, struct obj); + + obj->next = objs; + obj->name = talloc_asprintf(obj, "%s/%s", testdir, name); + objs = obj; +} + +static int build(const char *name, int fail) +{ + const char *cmd; + int ret; + + cmd = talloc_asprintf(name, "gcc " CFLAGS " %s -o %s %s %s%s", + fail ? "-DFAIL" : "", + output_name(name), name, obj_list(), + verbose ? "" : "> /dev/null 2>&1"); + + if (verbose) + fprintf(stderr, "Running %s\n", cmd); + + ret = system(cmd); + if (ret == -1) + diag("cmd '%s' failed to execute", cmd); + + return ret; +} + +static void compile_ok(struct test_type *t, const char *name) +{ + ok(build(name, 0) == 0, "%s %s", t->name, name); +} + +static void compile_fail(struct test_type *t, const char *name) +{ + if (build(name, 0) != 0) + fail("non-FAIL build %s", name); + else + ok(build(name, 1) > 0, "%s %s", t->name, name); +} + +static void run(const char *name) +{ + if (system(output_name(name)) == -1) + fail("running %s had error %m", name); +} + +static void cleanup(const char *name) +{ + unlink(output_name(name)); +} + +static struct test_type test_types[] = { + { "compile_ok", compile_ok }, + { "compile_fail", compile_fail }, + { "run", compile_ok }, +}; + +int main(int argc, char *argv[]) +{ + DIR *dir; + struct dirent *d; + char *testdir; + struct test *test; + unsigned int num_tests = 0, num_objs = 0; + + if (argc > 1 && streq(argv[1], "--verbose")) { + verbose = 1; + argc--; + argv++; + } + + if (argc != 2) + errx(1, "Usage: run_tests [--verbose] "); + + testdir = talloc_asprintf(NULL, "%s/test", argv[1]); + dir = opendir(testdir); + if (!dir) + err(1, "Opening '%s'", testdir); + + while ((d = readdir(dir)) != NULL) { + unsigned int i; + if (d->d_name[0] == '.' || !strends(d->d_name, ".c")) + continue; + + for (i = 0; i < ARRAY_SIZE(test_types); i++) { + if (strstarts(d->d_name, test_types[i].name)) { + add_test(testdir, d->d_name, &test_types[i]); + num_tests++; + break; + } + } + if (i == ARRAY_SIZE(test_types)) { + add_obj(testdir, d->d_name); + num_objs++; + } + } + + plan_tests(num_tests + num_objs); + /* First all the extra object compilations. */ + compile_objs(); + + /* Do all the test compilations. */ + for (test = tests; test; test = test->next) + test->type->testfn(test->type, test->name); + + cleanup_objs(); + + /* Now run all the ones which wanted to run. */ + for (test = tests; test; test = test->next) { + if (streq(test->type->name, "run")) + run(test->name); + cleanup(test->name); + } + + exit(exit_status()); +} diff --git a/test_all.sh b/test_all.sh new file mode 100755 index 00000000..3c65c21c --- /dev/null +++ b/test_all.sh @@ -0,0 +1,22 @@ +#! /bin/sh + +# First, test normal config. +if ! make -s; then + echo Normal config failed. + exit 1 +fi + +# Now, remove one HAVE_ at a time. +cp config.h original-config.h +trap "mv original-config.h config.h && rm -f .newconfig" EXIT + +while grep -q '1$' config.h; do + tr '\012' @ < config.h | sed 's/1@/0@/' | tr @ '\012' > .newconfig + diff -u config.h .newconfig + mv .newconfig config.h + if ! make -s; then + echo Failed config: + cat config.h + exit 1 + fi +done