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