ccanlint: create reduce-feature config.h
authorRusty Russell <rusty@rustcorp.com.au>
Tue, 1 Mar 2011 07:18:11 +0000 (17:48 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Tue, 1 Mar 2011 07:18:11 +0000 (17:48 +1030)
A common mistake is not to try compiling with features disabled in
config.h.  The ideal case would determine how features interact and
test all combinations of them: this simply disables any features
mentioned in the code which were previously enabled.

tools/ccanlint/tests/reduce_features.c [new file with mode: 0644]
tools/ccanlint/tests/reduce_features.h [new file with mode: 0644]

diff --git a/tools/ccanlint/tests/reduce_features.c b/tools/ccanlint/tests/reduce_features.c
new file mode 100644 (file)
index 0000000..2a389ce
--- /dev/null
@@ -0,0 +1,179 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/htable/htable_type.h>
+#include <ccan/foreach/foreach.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/grab_file/grab_file.h>
+#include <ccan/str/str.h>
+#include <ccan/str_talloc/str_talloc.h>
+#include <ccan/hash/hash.h>
+#include <ccan/read_write_all/read_write_all.h>
+#include <errno.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "reduce_features.h"
+
+bool features_were_reduced;
+
+static const char *can_run(struct manifest *m)
+{
+       if (!config_header)
+               return talloc_strdup(m, "Could not read config.h");
+       return NULL;
+}
+
+static size_t option_hash(const char *name)
+{
+       return hash(name, strlen(name), 0);
+}
+
+static const char *option_name(const char *name)
+{
+       return name;
+}
+
+static bool option_cmp(const char *name1, const char *name2)
+{
+       return streq(name1, name2);
+}
+
+HTABLE_DEFINE_TYPE(char, option_name, option_hash, option_cmp, option);
+
+static unsigned int add_options(struct htable_option *opts,
+                               struct pp_conditions *cond)
+{
+       unsigned int num = 0;
+       if (cond->parent)
+               num += add_options(opts, cond->parent);
+       if (cond->type == PP_COND_IF || cond->type == PP_COND_IFDEF) {
+               if (strstarts(cond->symbol, "HAVE_")) {
+                       if (!htable_option_get(opts, cond->symbol)) {
+                               htable_option_add(opts, cond->symbol);
+                               num++;
+                       }
+               }
+       }
+       return num;
+}
+
+static struct htable_option *get_used_options(struct manifest *m)
+{
+       struct list_head *list;
+       struct ccan_file *f;
+       unsigned int i, num;
+       struct htable_option *opts = htable_option_new();
+       struct line_info *info;
+
+       num = 0;
+       foreach_ptr(list, &m->c_files, &m->h_files) {
+               list_for_each(list, f, list) {
+                       info = get_ccan_line_info(f);
+                       struct pp_conditions *prev = NULL;
+
+                       for (i = 0; i < f->num_lines; i++) {
+                               if (info[i].cond && info[i].cond != prev) {
+                                       num += add_options(opts, info[i].cond);
+                                       prev = info[i].cond;
+                               }
+                       }
+               }
+       }
+
+       if (!num) {
+               htable_option_free(opts);
+               opts = NULL;
+       }
+       return opts;
+}
+
+static struct htable_option *get_config_options(struct manifest *m)
+{
+       const char **lines = (const char **)strsplit(m, config_header, "\n");
+       unsigned int i;
+       struct htable_option *opts = htable_option_new();
+
+       for (i = 0; i < talloc_array_length(lines) - 1; i++) {
+               char *sym;
+
+               if (!get_token(&lines[i], "#"))
+                       continue;
+               if (!get_token(&lines[i], "define"))
+                       continue;
+               sym = get_symbol_token(lines, &lines[i]);
+               if (!strstarts(sym, "HAVE_"))
+                       continue;
+               /* Don't override endian... */
+               if (strends(sym, "_ENDIAN"))
+                       continue;
+               if (!get_token(&lines[i], "1"))
+                       continue;
+               htable_option_add(opts, sym);
+       }
+       return opts;
+}
+
+static void do_reduce_features(struct manifest *m,
+                              bool keep,
+                              unsigned int *timeleft, struct score *score)
+{
+       struct htable_option *options_used, *options_avail, *options;
+       struct htable_option_iter i;
+       int fd;
+       const char *sym;
+       char *hdr;
+
+       /* This isn't really a test, as such. */
+       score->total = 0;
+       score->pass = true;
+
+       options_used = get_used_options(m);
+       if (!options_used) {
+               return;
+       }
+       options_avail = get_config_options(m);
+
+       options = NULL;
+       for (sym = htable_option_first(options_used, &i);
+            sym;
+            sym = htable_option_next(options_used, &i)) {
+               if (htable_option_get(options_avail, sym)) {
+                       if (!options)
+                               options = htable_option_new();
+                       htable_option_add(options, sym);
+               }
+       }
+       htable_option_free(options_avail);
+       htable_option_free(options_used);
+
+       if (!options)
+               return;
+
+       /* Now make our own config.h variant, with our own options. */
+       hdr = talloc_strdup(m, "/* Modified by reduce_features */\n");
+       hdr = talloc_append_string(hdr, config_header);
+       for (sym = htable_option_first(options, &i);
+            sym;
+            sym = htable_option_next(options, &i)) {
+               hdr = talloc_asprintf_append
+                       (hdr, "#undef %s\n#define %s 0\n", sym, sym);
+       }
+       fd = open("config.h", O_EXCL|O_CREAT|O_RDWR, 0600);
+       if (fd < 0)
+               err(1, "Creating config.h");
+       if (!write_all(fd, hdr, strlen(hdr)))
+               err(1, "Writing config.h");
+       close(fd);
+       features_were_reduced = true;
+}
+
+struct ccanlint reduce_features = {
+       .key = "reduce_features",
+       .name = "Produce config.h with reduced features",
+       .can_run = can_run,
+       .check = do_reduce_features,
+       .needs = "tests_compile"
+};
+REGISTER_TEST(reduce_features);
diff --git a/tools/ccanlint/tests/reduce_features.h b/tools/ccanlint/tests/reduce_features.h
new file mode 100644 (file)
index 0000000..f646941
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef CCANLINT_REDUCE_FEATURES_H
+#define CCANLINT_REDUCE_FEATURES_H
+extern bool features_were_reduced;
+#endif /* CCANLINT_REDUCE_FEATURES_H */