tools: new "configurator" tool.
authorRusty Russell <rusty@rustcorp.com.au>
Wed, 3 Nov 2010 00:21:07 +0000 (10:51 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Wed, 3 Nov 2010 02:01:31 +0000 (12:31 +1030)
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).

Makefile
Makefile-ccan
config.h
tools/Makefile
tools/ccanlint/Makefile
tools/compile.c
tools/configurator/configurator.c [new file with mode: 0644]
tools/tools.c
tools/tools.h

index c1f5ea8f6d78a17d8c5dd4cb087e4b4092726fb0..ae01880b4af02596fafed39461dd47219e5fb937 100644 (file)
--- a/Makefile
+++ b/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
index 9b248a53fc27bb7392140a1fd1910203dbb392a4..6911dffc28b73a1de92e0823308f2c87516c7e7d 100644 (file)
@@ -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
 
index 52c6fdd4f848ba471960c4ac4a33966c23898e5d..bf6849097280a307f7fb51f4fd03943197f4f8b7 100644 (file)
--- a/config.h
+++ b/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
index 4607d41ac9f42c318e70a1d173247ac632520892..d2c18aa15356dbb765588e15268e9350856f8226 100644 (file)
@@ -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)
 
index b84b8353fa2da22c8692963acfd8919e6854f304..fb0526db52821375560f3eec65f486192a1add79 100644 (file)
@@ -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)
 
index 6707c4a68c99af055efe47a5428d6a6cf74faf5a..8f80734b095d5ad06f0aec2f696b3197eaf0a4c2 100644 (file)
@@ -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);
 }
diff --git a/tools/configurator/configurator.c b/tools/configurator/configurator.c
new file mode 100644 (file)
index 0000000..4b2ffe7
--- /dev/null
@@ -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;
+}
index b93448278648034f8835668db5476cc6abe038c5..20fcc9bb5bfba7fd142e5735e3a8841c9c3e50b2 100644 (file)
@@ -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,
index 0ea4a6cdcdc6c26b84f436e9f3036cb2a9f3b509..168a47bd28e0c595b3162c1c58d6a376d428c524 100644 (file)
@@ -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. */