Browse Source

First cut, some hacks, three simple modules.

Rusty Russell 18 years ago
commit
fdfcdafbfa

+ 2 - 0
.bzrignore

@@ -0,0 +1,2 @@
+_info
+.depends

+ 23 - 0
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

+ 38 - 0
build_assert/_info.c

@@ -0,0 +1,38 @@
+#include <stdio.h>
+#include <string.h>
+#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;
+}

+ 37 - 0
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 */

+ 10 - 0
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
+}

+ 9 - 0
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;
+}

+ 7 - 0
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;
+}

+ 9 - 0
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();
+}

+ 30 - 0
check_type/_info.c

@@ -0,0 +1,30 @@
+#include <stdio.h>
+#include <string.h>
+#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;
+}

+ 63 - 0
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 */

+ 9 - 0
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;
+}

+ 14 - 0
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;
+}

+ 10 - 0
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;
+}

+ 22 - 0
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;
+}

+ 3 - 0
config.h

@@ -0,0 +1,3 @@
+/* Simple config.h for gcc. */
+#define HAVE_TYPEOF 1
+#define HAVE_STATEMENT_EXPR 1

+ 47 - 0
container_of/_info.c

@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <string.h>
+#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;
+}

+ 14 - 0
container_of/container_of.h

@@ -0,0 +1,14 @@
+#ifndef CCAN_CONTAINER_OF_H
+#define CCAN_CONTAINER_OF_H
+#include <stddef.h>
+
+#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 */

+ 21 - 0
container_of/test/compile_fail-bad-type.c

@@ -0,0 +1,21 @@
+#include "container_of/container_of.h"
+#include <stdlib.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 *p;
+
+#ifdef FAIL
+	p = container_of(intp, struct foo, a);
+#else
+	p = (char *)intp;
+#endif
+	return p == NULL;
+}

+ 20 - 0
container_of/test/compile_fail-types.c

@@ -0,0 +1,20 @@
+#include "container_of/container_of.h"
+#include <stdlib.h>
+
+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;
+}

+ 19 - 0
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();
+}

+ 210 - 0
run_tests.c

@@ -0,0 +1,210 @@
+#include <err.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <assert.h>
+#include <unistd.h>
+#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] <dir>");
+
+	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());
+}

+ 22 - 0
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