ccanlint: create reduce-feature config.h
[ccan] / tools / ccanlint / tests / reduce_features.c
1 #include <tools/ccanlint/ccanlint.h>
2 #include <tools/tools.h>
3 #include <ccan/htable/htable_type.h>
4 #include <ccan/foreach/foreach.h>
5 #include <ccan/talloc/talloc.h>
6 #include <ccan/grab_file/grab_file.h>
7 #include <ccan/str/str.h>
8 #include <ccan/str_talloc/str_talloc.h>
9 #include <ccan/hash/hash.h>
10 #include <ccan/read_write_all/read_write_all.h>
11 #include <errno.h>
12 #include <err.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include "reduce_features.h"
18
19 bool features_were_reduced;
20
21 static const char *can_run(struct manifest *m)
22 {
23         if (!config_header)
24                 return talloc_strdup(m, "Could not read config.h");
25         return NULL;
26 }
27
28 static size_t option_hash(const char *name)
29 {
30         return hash(name, strlen(name), 0);
31 }
32
33 static const char *option_name(const char *name)
34 {
35         return name;
36 }
37
38 static bool option_cmp(const char *name1, const char *name2)
39 {
40         return streq(name1, name2);
41 }
42
43 HTABLE_DEFINE_TYPE(char, option_name, option_hash, option_cmp, option);
44
45 static unsigned int add_options(struct htable_option *opts,
46                                 struct pp_conditions *cond)
47 {
48         unsigned int num = 0;
49         if (cond->parent)
50                 num += add_options(opts, cond->parent);
51         if (cond->type == PP_COND_IF || cond->type == PP_COND_IFDEF) {
52                 if (strstarts(cond->symbol, "HAVE_")) {
53                         if (!htable_option_get(opts, cond->symbol)) {
54                                 htable_option_add(opts, cond->symbol);
55                                 num++;
56                         }
57                 }
58         }
59         return num;
60 }
61
62 static struct htable_option *get_used_options(struct manifest *m)
63 {
64         struct list_head *list;
65         struct ccan_file *f;
66         unsigned int i, num;
67         struct htable_option *opts = htable_option_new();
68         struct line_info *info;
69
70         num = 0;
71         foreach_ptr(list, &m->c_files, &m->h_files) {
72                 list_for_each(list, f, list) {
73                         info = get_ccan_line_info(f);
74                         struct pp_conditions *prev = NULL;
75
76                         for (i = 0; i < f->num_lines; i++) {
77                                 if (info[i].cond && info[i].cond != prev) {
78                                         num += add_options(opts, info[i].cond);
79                                         prev = info[i].cond;
80                                 }
81                         }
82                 }
83         }
84
85         if (!num) {
86                 htable_option_free(opts);
87                 opts = NULL;
88         }
89         return opts;
90 }
91
92 static struct htable_option *get_config_options(struct manifest *m)
93 {
94         const char **lines = (const char **)strsplit(m, config_header, "\n");
95         unsigned int i;
96         struct htable_option *opts = htable_option_new();
97
98         for (i = 0; i < talloc_array_length(lines) - 1; i++) {
99                 char *sym;
100
101                 if (!get_token(&lines[i], "#"))
102                         continue;
103                 if (!get_token(&lines[i], "define"))
104                         continue;
105                 sym = get_symbol_token(lines, &lines[i]);
106                 if (!strstarts(sym, "HAVE_"))
107                         continue;
108                 /* Don't override endian... */
109                 if (strends(sym, "_ENDIAN"))
110                         continue;
111                 if (!get_token(&lines[i], "1"))
112                         continue;
113                 htable_option_add(opts, sym);
114         }
115         return opts;
116 }
117
118 static void do_reduce_features(struct manifest *m,
119                                bool keep,
120                                unsigned int *timeleft, struct score *score)
121 {
122         struct htable_option *options_used, *options_avail, *options;
123         struct htable_option_iter i;
124         int fd;
125         const char *sym;
126         char *hdr;
127
128         /* This isn't really a test, as such. */
129         score->total = 0;
130         score->pass = true;
131
132         options_used = get_used_options(m);
133         if (!options_used) {
134                 return;
135         }
136         options_avail = get_config_options(m);
137
138         options = NULL;
139         for (sym = htable_option_first(options_used, &i);
140              sym;
141              sym = htable_option_next(options_used, &i)) {
142                 if (htable_option_get(options_avail, sym)) {
143                         if (!options)
144                                 options = htable_option_new();
145                         htable_option_add(options, sym);
146                 }
147         }
148         htable_option_free(options_avail);
149         htable_option_free(options_used);
150
151         if (!options)
152                 return;
153
154         /* Now make our own config.h variant, with our own options. */
155         hdr = talloc_strdup(m, "/* Modified by reduce_features */\n");
156         hdr = talloc_append_string(hdr, config_header);
157         for (sym = htable_option_first(options, &i);
158              sym;
159              sym = htable_option_next(options, &i)) {
160                 hdr = talloc_asprintf_append
161                         (hdr, "#undef %s\n#define %s 0\n", sym, sym);
162         }
163         fd = open("config.h", O_EXCL|O_CREAT|O_RDWR, 0600);
164         if (fd < 0)
165                 err(1, "Creating config.h");
166         if (!write_all(fd, hdr, strlen(hdr)))
167                 err(1, "Writing config.h");
168         close(fd);
169         features_were_reduced = true;
170 }
171
172 struct ccanlint reduce_features = {
173         .key = "reduce_features",
174         .name = "Produce config.h with reduced features",
175         .can_run = can_run,
176         .check = do_reduce_features,
177         .needs = "tests_compile"
178 };
179 REGISTER_TEST(reduce_features);