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