Browse Source

tools: new "configurator" tool.

A simple C program to generate config.h.  It also saves the compiler and
flags for use by ccanlint (though they have to accept -c and -o).
Rusty Russell 15 years ago
parent
commit
e73b0b3718
9 changed files with 367 additions and 11 deletions
  1. 4 1
      Makefile
  2. 4 2
      Makefile-ccan
  3. 4 1
      config.h
  4. 3 1
      tools/Makefile
  5. 1 1
      tools/ccanlint/Makefile
  6. 4 2
      tools/compile.c
  7. 338 0
      tools/configurator/configurator.c
  8. 1 1
      tools/tools.c
  9. 8 2
      tools/tools.h

+ 4 - 1
Makefile

@@ -88,7 +88,7 @@ summary-fastcheck-%: tools/ccanlint/ccanlint $(OBJFILES)
 	tools/ccanlint/ccanlint -t $(FASTTIMEOUT) -s -d ccan/$*
 
 ccan/%/info: ccan/%/_info
-	$(CC) $(CFLAGS) -o $@ -x c $<
+	$(CC) $(CCAN_CFLAGS) -o $@ -x c $<
 
 libccan.a(%.o): ccan/%.o
 	$(AR) r $@ $<
@@ -106,6 +106,9 @@ inter-depends: $(ALL_DEPENDS) Makefile
 test-depends: $(ALL_DEPENDS) Makefile
 	for f in $(ALL_DEPENDS); do echo check-`basename \`dirname $$f\``: `sed -n 's,ccan/\(.*\),check-\1,p' < $$f`; done > $@
 
+config.h: tools/configurator/configurator Makefile Makefile-ccan
+	@tools/configurator/configurator $(CC) $(CCAN_CFLAGS) > config.h
+
 include tools/Makefile
 -include inter-depends
 -include test-depends

+ 4 - 2
Makefile-ccan

@@ -2,8 +2,10 @@
 # For simple projects you could just do:
 #	SRCFILES += $(wildcard ccan/*/*.c)
 
-CFLAGS=-g -O3 -Wall -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -I. $(DEPGEN)
-#CFLAGS=-g -Wall -Wstrict-prototypes -Wold-style-definition -Werror -I. $(DEPGEN)
+CCAN_CFLAGS=-g -O3 -Wall -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror
+CFLAGS = $(CCAN_CFLAGS) -I. $(DEPGEN)
+
+#CFLAGS=-g -Wall -Wstrict-prototypes -Wold-style-definition -Werror $(DEPGEN)
 
 default: libccan.a
 

+ 4 - 1
config.h

@@ -1,4 +1,7 @@
-/* Simple config.h for recent gcc. */
+/* Generated by CCAN configurator */
+#define CCAN_COMPILER "cc"
+#define CCAN_CFLAGS "-g -O3 -Wall -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror"
+
 #define HAVE_ALIGNOF 1
 #define HAVE_ATTRIBUTE_COLD 1
 #define HAVE_ATTRIBUTE_CONST 1

+ 3 - 1
tools/Makefile

@@ -1,4 +1,4 @@
-ALL_TOOLS = tools/ccan_depends tools/doc_extract tools/namespacize tools/ccanlint/ccanlint
+ALL_TOOLS = tools/configurator/configurator tools/ccan_depends tools/doc_extract tools/namespacize tools/ccanlint/ccanlint
 
 DEP_OBJS = tools/depends.o tools/compile.o tools/tools.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/talloc/talloc.o ccan/noerr/noerr.o ccan/read_write_all/read_write_all.o
 
@@ -13,6 +13,8 @@ tools/namespacize: tools/namespacize.o $(DEP_OBJS)
 
 tools/namespacize.o tools/depends.o: tools/tools.h
 
+tools/configurator/configurator: tools/configurator/configurator.c
+
 tools-clean: ccanlint-clean
 	$(RM) $(ALL_TOOLS)
 

+ 1 - 1
tools/ccanlint/Makefile

@@ -25,7 +25,7 @@ tools/ccanlint/generated-compulsory-tests: $(COMPULSORY_TEST_CFILES)
 $(TEST_OBJS): tools/ccanlint/generated-normal-tests tools/ccanlint/generated-compulsory-tests
 
 # Otherwise, ccanlint.c et al. may fail to build
-$(CORE_OBJS): tools/ccanlint/generated-normal-tests tools/ccanlint/generated-compulsory-tests
+$(CORE_OBJS): tools/ccanlint/generated-normal-tests tools/ccanlint/generated-compulsory-tests config.h
 
 tools/ccanlint/ccanlint: $(OBJS)
 

+ 4 - 2
tools/compile.c

@@ -28,7 +28,8 @@ char *compile_object(const void *ctx, const char *cfile, const char *ccandir,
 {
 	if (compile_verbose)
 		printf("Compiling %s\n", outfile);
-	return run_command(ctx, NULL, "cc " CFLAGS " -I%s %s -c -o %s %s",
+	return run_command(ctx, NULL, CCAN_COMPILER " " CCAN_CFLAGS
+			   " -I%s %s -c -o %s %s",
 			   ccandir, extra_cflags, outfile, cfile);
 }
 
@@ -40,6 +41,7 @@ char *compile_and_link(const void *ctx, const char *cfile, const char *ccandir,
 {
 	if (compile_verbose)
 		printf("Compiling and linking %s\n", outfile);
-	return run_command(ctx, NULL, "cc " CFLAGS " -I%s %s -o %s %s %s %s",
+	return run_command(ctx, NULL, CCAN_COMPILER " " CCAN_CFLAGS
+			   " -I%s %s -o %s %s %s %s",
 			   ccandir, extra_cflags, outfile, cfile, objs, libs);
 }

+ 338 - 0
tools/configurator/configurator.c

@@ -0,0 +1,338 @@
+/* Simple tool to create config.h.
+ * Would be much easier with ccan modules, but deliberately standalone. */
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#define DEFAULT_COMPILER "cc"
+#define DEFAULT_FLAGS "-g -Wall -Wundef -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes -Wold-style-definition -Werror"
+
+#define OUTPUT_FILE "configurator.out"
+#define INPUT_FILE "configuratortest.c"
+
+static int verbose;
+
+enum test_style {
+	EXECUTE,
+	OUTSIDE_MAIN,
+	DEFINES_FUNC,
+	INSIDE_MAIN
+};
+
+struct test {
+	const char *name;
+	enum test_style style;
+	const char *depends;
+	const char *fragment;
+	bool done;
+	bool answer;
+};
+
+static struct test tests[] = {
+	{ "HAVE_ALIGNOF", INSIDE_MAIN, NULL,
+	  "return __alignof__(double) > 0 ? 0 : 1;" },
+	{ "HAVE_ATTRIBUTE_COLD", DEFINES_FUNC, NULL,
+	  "static int __attribute__((cold)) func(int x) { return x; }" },
+	{ "HAVE_ATTRIBUTE_CONST", DEFINES_FUNC, NULL,
+	  "static int __attribute__((const)) func(int x) { return x; }" },
+	{ "HAVE_ATTRIBUTE_MAY_ALIAS", OUTSIDE_MAIN, NULL,
+	  "typedef short __attribute__((__may_alias__)) short_a;" },
+	{ "HAVE_ATTRIBUTE_PRINTF", DEFINES_FUNC, NULL,
+	  "static void __attribute__((format(__printf__, 1, 2))) func(const char *fmt, ...) { }" },
+	{ "HAVE_ATTRIBUTE_UNUSED", OUTSIDE_MAIN, NULL,
+	  "static int __attribute__((unused)) func(int x) { return x; }" },
+	{ "HAVE_ATTRIBUTE_USED", OUTSIDE_MAIN, NULL,
+	  "static int __attribute__((used)) func(int x) { return x; }" },
+	{ "HAVE_BIG_ENDIAN", EXECUTE, NULL,
+	  "union { int i; char c[sizeof(int)]; } u;\n"
+	  "u.i = 0x01020304;\n"
+	  "return u.c[0] == 0x01 && u.c[1] == 0x02 && u.c[2] == 0x03 && u.c[3] == 0x04 ? 0 : 1;" },
+	{ "HAVE_BSWAP_64", DEFINES_FUNC, "HAVE_BYTESWAP_H",
+	  "#include <byteswap.h>\n"
+	  "static int func(int x) { return bswap_64(x); }" },
+	{ "HAVE_BUILTIN_CHOOSE_EXPR", INSIDE_MAIN, NULL,
+	  "return __builtin_choose_expr(1, 0, \"garbage\");" },
+	{ "HAVE_BUILTIN_CLZ", INSIDE_MAIN, NULL,
+	  "return __builtin_clz(1) == (sizeof(int)*8 - 1) ? 0 : 1;" },
+	{ "HAVE_BUILTIN_CLZL", INSIDE_MAIN, NULL,
+	  "return __builtin_clzl(1) == (sizeof(long)*8 - 1) ? 0 : 1;" },
+	{ "HAVE_BUILTIN_CLZLL", INSIDE_MAIN, NULL,
+	  "return __builtin_clzll(1) == (sizeof(long long)*8 - 1) ? 0 : 1;" },
+	{ "HAVE_BUILTIN_CONSTANT_P", INSIDE_MAIN, NULL,
+	  "return __builtin_constant_p(1) ? 0 : 1;" },
+	{ "HAVE_BUILTIN_EXPECT", INSIDE_MAIN, NULL,
+	  "return __builtin_expect(argc == 1, 1) ? 0 : 1;" },
+	{ "HAVE_BUILTIN_FFSL", INSIDE_MAIN, NULL,
+	  "return __builtin_ffsl(0L) == 0 ? 0 : 1;" },
+	{ "HAVE_BUILTIN_POPCOUNTL", INSIDE_MAIN, NULL,
+	  "return __builtin_popcountl(255L) == 8 ? 0 : 1;" },
+	{ "HAVE_BUILTIN_TYPES_COMPATIBLE_P", INSIDE_MAIN, NULL,
+	  "return __builtin_types_compatible_p(char *, int) ? 1 : 0;" },
+	{ "HAVE_BYTESWAP_H", OUTSIDE_MAIN, NULL,
+	  "#include <byteswap.h>\n" },
+	{ "HAVE_COMPOUND_LITERALS", INSIDE_MAIN, NULL,
+	  "char **foo = (char *[]) { \"x\", \"y\", \"z\" };\n"
+	  "return foo[0] ? 0 : 1;" },
+	{ "HAVE_FOR_LOOP_DECLARATION", INSIDE_MAIN, NULL,
+	  "for (int i = 0; i < argc; i++) { return 0; };\n"
+	  "return 1;" },
+	{ "HAVE_GETPAGESIZE", DEFINES_FUNC, NULL,
+	  "#include <unistd.h>\n"
+	  "static int func(void) { return getpagesize(); }" },
+	{ "HAVE_LITTLE_ENDIAN", EXECUTE, NULL,
+	  "union { int i; char c[sizeof(int)]; } u;\n"
+	  "u.i = 0x01020304;\n"
+	  "return u.c[0] == 0x04 && u.c[1] == 0x03 && u.c[2] == 0x02 && u.c[3] == 0x01 ? 0 : 1;" },
+	{ "HAVE_MMAP", DEFINES_FUNC, NULL,
+	  "#include <sys/mman.h>\n"
+	  "static void *func(int fd) {\n"
+	  "	return mmap(0, 65536, PROT_READ, MAP_SHARED, fd, 0);\n"
+	  "}" },
+	{ "HAVE_NESTED_FUNCTIONS", DEFINES_FUNC, NULL,
+	  "static int func(int val) {\n"
+	  "	auto void add(int val2);\n"
+	  "	void add(int val2) { val += val2; }\n"
+	  "	add(7);\n"
+	  "	return val;\n"
+	  "}" },
+	{ "HAVE_STATEMENT_EXPR", INSIDE_MAIN, NULL,
+	  "return ({ int x = argc; x == argc ? 0 : 1; });" },
+	{ "HAVE_TYPEOF", INSIDE_MAIN, NULL,
+	  "__typeof__(argc) i; i = argc; return i == argc ? 0 : 1;" },
+	{ "HAVE_UTIME", DEFINES_FUNC, NULL,
+	  "#include <sys/types.h>\n"
+	  "#include <utime.h>\n"
+	  "static int func(const char *filename) {\n"
+	  "	struct utimbuf times = { 0 };\n"
+	  "	return utime(filename, &times);\n"
+	  "}" },
+	{ "HAVE_WARN_UNUSED_RESULT", DEFINES_FUNC, NULL,
+	  "#include <sys/types.h>\n"
+	  "#include <utime.h>\n"
+	  "static __attribute__((warn_unused_result)) int func(int i) {\n"
+	  "	return i + 1;\n"
+	  "}" },
+};
+
+static char *grab_fd(int fd)
+{
+	int ret;
+	size_t max, size = 0;
+	char *buffer;
+
+	max = 16384;
+	buffer = malloc(max+1);
+	while ((ret = read(fd, buffer + size, max - size)) > 0) {
+		size += ret;
+		if (size == max)
+			buffer = realloc(buffer, max *= 2);
+	}
+	if (ret < 0)
+		err(1, "reading from command");
+	buffer[size] = '\0';
+	return buffer;
+}
+
+static char *run(const char *cmd, int *exitstatus)
+{
+	pid_t pid;
+	int p[2];
+	char *ret;
+	int status;
+
+	if (pipe(p) != 0)
+		err(1, "creating pipe");
+
+	pid = fork();
+	if (pid == -1)
+		err(1, "forking");
+
+	if (pid == 0) {
+		if (dup2(p[1], STDOUT_FILENO) != STDOUT_FILENO
+		    || dup2(p[1], STDERR_FILENO) != STDERR_FILENO
+		    || close(p[0]) != 0
+		    || close(STDIN_FILENO) != 0
+		    || open("/dev/null", O_RDONLY) != STDIN_FILENO)
+			exit(128);
+
+		status = system(cmd);
+		if (WIFEXITED(status))
+			exit(WEXITSTATUS(status));
+		/* Here's a hint... */
+		exit(128 + WTERMSIG(status));
+	}
+
+	close(p[1]);
+	ret = grab_fd(p[0]);
+	/* This shouldn't fail... */
+	if (waitpid(pid, &status, 0) != pid)
+		err(1, "Failed to wait for child");
+	close(p[0]);
+	if (WIFEXITED(status))
+		*exitstatus = WEXITSTATUS(status);
+	else
+		*exitstatus = -WTERMSIG(status);
+	return ret;
+}
+
+static char *connect_args(char *argv[], const char *extra)
+{
+	unsigned int i, len = strlen(extra) + 1;
+	char *ret;
+
+	for (i = 1; argv[i]; i++)
+		len += 1 + strlen(argv[i]);
+
+	ret = malloc(len);
+	len = 0;
+	for (i = 1; argv[i]; i++) {
+		strcpy(ret + len, argv[i]);
+		len += strlen(argv[i]);
+		ret[len++] = ' ';
+	}
+	strcpy(ret + len, extra);
+	return ret;
+}
+
+static struct test *find_test(const char *name)
+{
+	unsigned int i;
+
+	for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
+		if (strcmp(tests[i].name, name) == 0)
+			return &tests[i];
+	}
+	abort();
+}
+
+#define PRE_BOILERPLATE "/* Test program generated by configurator. */\n"
+#define MAIN_START_BOILERPLATE "int main(int argc, char *argv[]) {\n"
+#define USE_FUNC_BOILERPLATE "(void)func;\n"
+#define MAIN_BODY_BOILERPLATE "return 0;\n"
+#define MAIN_END_BOILERPLATE "}\n"
+
+static bool run_test(const char *cmd, struct test *test)
+{
+	char *output;
+	FILE *outf;
+	int status;
+
+	if (test->done)
+		return test->answer;
+
+	if (test->depends && !run_test(cmd, find_test(test->depends))) {
+		test->answer = false;
+		test->done = true;
+		return test->answer;
+	}
+
+	outf = fopen(INPUT_FILE, "w");
+	if (!outf)
+		err(1, "creating %s", INPUT_FILE);
+
+	fprintf(outf, "%s", PRE_BOILERPLATE);
+	switch (test->style) {
+	case EXECUTE:
+	case INSIDE_MAIN:
+		fprintf(outf, "%s", MAIN_START_BOILERPLATE);
+		fprintf(outf, "%s", test->fragment);
+		fprintf(outf, "%s", MAIN_END_BOILERPLATE);
+		break;
+	case OUTSIDE_MAIN:
+		fprintf(outf, "%s", test->fragment);
+		fprintf(outf, "%s", MAIN_START_BOILERPLATE);
+		fprintf(outf, "%s", MAIN_BODY_BOILERPLATE);
+		fprintf(outf, "%s", MAIN_END_BOILERPLATE);
+		break;
+	case DEFINES_FUNC:
+		fprintf(outf, "%s", test->fragment);
+		fprintf(outf, "%s", MAIN_START_BOILERPLATE);
+		fprintf(outf, "%s", USE_FUNC_BOILERPLATE);
+		fprintf(outf, "%s", MAIN_BODY_BOILERPLATE);
+		fprintf(outf, "%s", MAIN_END_BOILERPLATE);
+		break;
+	}
+	fclose(outf);
+
+	if (verbose > 1)
+		if (system("cat " INPUT_FILE) == -1);
+
+	output = run(cmd, &status);
+	if (status != 0) {
+		if (verbose)
+			printf("Compile fail for %s, status %i: %s\n",
+			       test->name, status, output);
+		if (test->style == EXECUTE)
+			errx(1, "Test for %s did not compile:\n%s",
+			     test->name, output);
+		test->answer = false;
+		free(output);
+	} else {
+		/* Compile succeeded. */
+		free(output);
+		/* We run INSIDE_MAIN tests for sanity checking. */
+		if (test->style == EXECUTE || test->style == INSIDE_MAIN) {
+			output = run("./" OUTPUT_FILE, &status);
+			if (test->style == INSIDE_MAIN && status != 0)
+				errx(1, "Test for %s failed with %i:\n%s",
+				     test->name, status, output);
+			if (verbose && status)
+				printf("%s exited %i\n", test->name, status);
+			free(output);
+		}
+		test->answer = (status == 0);
+	}
+	test->done = true;
+	return test->answer;
+}
+
+int main(int argc, char *argv[])
+{
+	char *cmd;
+	char *default_args[] = { "", DEFAULT_COMPILER, DEFAULT_FLAGS, NULL };
+	unsigned int i;
+
+	if (argc > 1) {
+		if (strcmp(argv[1], "--help") == 0) {
+			printf("Usage: configurator [-v] [<compiler> <flags>...]\n"
+			       "  <compiler> <flags> will have \"-o <outfile> <infile.c>\" appended\n"
+			       "Default: %s %s\n",
+			       DEFAULT_COMPILER, DEFAULT_FLAGS);
+			exit(0);
+		}
+		if (strcmp(argv[1], "-v") == 0) {
+			argc--;
+			argv++;
+			verbose = 1;
+		} else if (strcmp(argv[1], "-vv") == 0) {
+			argc--;
+			argv++;
+			verbose = 2;
+		}
+	}
+
+	if (argc == 1)
+		argv = default_args;
+
+	cmd = connect_args(argv, "-o " OUTPUT_FILE " " INPUT_FILE);
+	for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
+		run_test(cmd, &tests[i]);
+
+	unlink(OUTPUT_FILE);
+	unlink(INPUT_FILE);
+
+	cmd[strlen(cmd) - strlen(" -o " OUTPUT_FILE " " INPUT_FILE)] = '\0';
+	printf("/* Generated by CCAN configurator */\n");
+	printf("#define CCAN_COMPILER \"%s\"\n", argv[1]);
+	printf("#define CCAN_CFLAGS \"%s\"\n\n", cmd + strlen(argv[1]) + 1);
+	for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
+		printf("#define %s %u\n", tests[i].name, tests[i].answer);
+	return 0;
+}

+ 1 - 1
tools/tools.c

@@ -131,7 +131,7 @@ char *run_with_timeout(const void *ctx, const char *cmd,
 		*timeout_ms = 0;
 	else
 		*timeout_ms -= ms;
-
+	close(p[0]);
 	if (tools_verbose) {
 		printf("%s", ret);
 		printf("Finished: %u ms, %s %u\n", ms,

+ 8 - 2
tools/tools.h

@@ -1,6 +1,14 @@
 #ifndef CCAN_TOOLS_H
 #define CCAN_TOOLS_H
 #include <stdbool.h>
+#include "config.h"
+
+#ifndef CCAN_COMPILER
+#define CCAN_COMPILER "cc"
+#endif
+#ifndef CCAN_CFLAGS
+#define CCAN_CFLAGS "-g -Wall -Wundef -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes -Wold-style-definition -Werror"
+#endif
 
 #define IDENT_CHARS	"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
 			"abcdefghijklmnopqrstuvwxyz" \
@@ -8,8 +16,6 @@
 
 #define SPACE_CHARS	" \f\n\r\t\v"
 
-#define CFLAGS "-g -Wall -Wundef -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes -Wold-style-definition -Werror"
-
 #define COVERAGE_CFLAGS "-fprofile-arcs -ftest-coverage"
 
 /* This actually compiles and runs the info file to get dependencies. */