ccanlint: add testdepends support.
[ccan] / tools / ccanlint / tests / tests_compile.c
1 #include <tools/ccanlint/ccanlint.h>
2 #include <tools/tools.h>
3 #include <ccan/talloc/talloc.h>
4 #include <ccan/str/str.h>
5 #include <ccan/foreach/foreach.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <limits.h>
11 #include <errno.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <err.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include "reduce_features.h"
18 #include "tests_compile.h"
19
20 static const char *can_build(struct manifest *m)
21 {
22         if (safe_mode)
23                 return "Safe mode enabled";
24         return NULL;
25 }
26
27 char *test_obj_list(const struct manifest *m, bool link_with_module,
28                     enum compile_type ctype, enum compile_type own_ctype)
29 {
30         char *list = talloc_strdup(m, "");
31         struct ccan_file *i;
32         struct manifest *subm;
33
34         /* Objects from any other C files. */
35         list_for_each(&m->other_test_c_files, i, list)
36                 list = talloc_asprintf_append(list, " %s",
37                                               i->compiled[ctype]);
38
39         /* Our own object files. */
40         if (link_with_module)
41                 list_for_each(&m->c_files, i, list)
42                         list = talloc_asprintf_append(list, " %s",
43                                                       i->compiled[own_ctype]);
44
45         /* Other ccan modules (normal depends). */
46         list_for_each(&m->deps, subm, list) {
47                 if (subm->compiled[ctype])
48                         list = talloc_asprintf_append(list, " %s",
49                                                       subm->compiled[ctype]);
50         }
51
52         /* Other ccan modules (test depends). */
53         list_for_each(&m->test_deps, subm, list) {
54                 if (subm->compiled[ctype])
55                         list = talloc_asprintf_append(list, " %s",
56                                                       subm->compiled[ctype]);
57         }
58
59         return list;
60 }
61
62 char *test_lib_list(const struct manifest *m, enum compile_type ctype)
63 {
64         unsigned int i;
65         char **libs;
66         char *ret = talloc_strdup(m, "");
67
68         libs = get_libs(m, m->dir, "testdepends", get_or_compile_info);
69         for (i = 0; libs[i]; i++)
70                 ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
71         return ret;
72 }
73
74 static bool compile(const void *ctx,
75                     struct manifest *m,
76                     struct ccan_file *file,
77                     bool fail,
78                     bool link_with_module,
79                     enum compile_type ctype,
80                     char **output)
81 {
82         char *fname, *flags;
83
84         flags = talloc_asprintf(ctx, "%s%s%s",
85                                 fail ? "-DFAIL " : "",
86                                 cflags,
87                                 ctype == COMPILE_NOFEAT
88                                 ? " "REDUCE_FEATURES_FLAGS : "");
89
90         fname = temp_file(ctx, "", file->fullname);
91         if (!compile_and_link(ctx, file->fullname, ccan_dir,
92                               test_obj_list(m, link_with_module,
93                                             ctype, ctype),
94                               compiler, flags, test_lib_list(m, ctype), fname,
95                               output)) {
96                 talloc_free(fname);
97                 return false;
98         }
99
100         file->compiled[ctype] = fname;
101         return true;
102 }
103
104 static void compile_async(const void *ctx,
105                           struct manifest *m,
106                           struct ccan_file *file,
107                           bool link_with_module,
108                           enum compile_type ctype,
109                           unsigned int time_ms)
110 {
111         char *flags;
112
113         file->compiled[ctype] = temp_file(ctx, "", file->fullname);
114         flags = talloc_asprintf(ctx, "%s%s",
115                                 cflags,
116                                 ctype == COMPILE_NOFEAT
117                                 ? " "REDUCE_FEATURES_FLAGS : "");
118
119         compile_and_link_async(file, time_ms, file->fullname, ccan_dir,
120                                test_obj_list(m, link_with_module, ctype, ctype),
121                                compiler, flags, test_lib_list(m, ctype),
122                                file->compiled[ctype]);
123 }
124
125 static void compile_tests(struct manifest *m,
126                           struct score *score,
127                           enum compile_type ctype,
128                           unsigned int time_ms)
129 {
130         char *cmdout;
131         struct ccan_file *i;
132         struct list_head *list;
133         bool errors = false, warnings = false, ok;
134
135         foreach_ptr(list, &m->compile_ok_tests, &m->run_tests, &m->api_tests) {
136                 list_for_each(list, i, list) {
137                         compile_async(score, m, i,
138                                       list == &m->api_tests,
139                                       ctype, time_ms);
140                 }
141         }
142
143         while ((i = collect_command(&ok, &cmdout)) != NULL) {
144                 if (!ok) {
145                         score_file_error(score, i, 0,
146                                          "Compile failed:\n%s",
147                                          cmdout);
148                         errors = true;
149                 } else if (!streq(cmdout, "")) {
150                         score_file_error(score, i, 0,
151                                          "Compile gave warnings:\n%s",
152                                          cmdout);
153                         warnings = true;
154                 }
155         }
156
157         /* The compile fail tests are a bit weird, handle them separately */
158         if (errors)
159                 return;
160
161         /* For historical reasons, "fail" often means "gives warnings" */
162         list_for_each(&m->compile_fail_tests, i, list) {
163                 if (!compile(score, m, i, false, false, ctype, &cmdout)) {
164                         score_file_error(score, i, 0,
165                                          "Compile without -DFAIL failed:\n%s",
166                                          cmdout);
167                         return;
168                 }
169                 if (!streq(cmdout, "")) {
170                         score_file_error(score, i, 0,
171                                          "Compile gave warnings"
172                                          " without -DFAIL:\n%s",
173                                          cmdout);
174                         return;
175                 }
176                 if (compile(score, m, i, true, false, ctype, &cmdout)
177                     && streq(cmdout, "")) {
178                         score_file_error(score, i, 0,
179                                          "Compiled successfully with -DFAIL?");
180                         return;
181                 }
182                 score->total++;
183         }
184
185         score->pass = true;
186         score->score = score->total - warnings;
187 }
188
189 /* FIXME: If we time out, set *timeleft to 0 */
190 static void do_compile_tests(struct manifest *m,
191                              unsigned int *timeleft, struct score *score)
192 {
193         compile_tests(m, score, COMPILE_NORMAL, *timeleft);
194 }
195
196 struct ccanlint tests_compile = {
197         .key = "tests_compile",
198         .name = "Module tests compile",
199         .check = do_compile_tests,
200         .can_run = can_build,
201         .needs = "tests_helpers_compile objects_build"
202 };
203
204 REGISTER_TEST(tests_compile);
205
206 static const char *features_reduced(struct manifest *m)
207 {
208         if (features_were_reduced)
209                 return NULL;
210         return "No features to turn off";
211 }
212
213 static void do_compile_tests_without_features(struct manifest *m,
214                                               unsigned int *timeleft,
215                                               struct score *score)
216 {
217         compile_tests(m, score, COMPILE_NOFEAT, *timeleft);
218 }
219
220 struct ccanlint tests_compile_without_features = {
221         .key = "tests_compile_without_features",
222         .name = "Module tests compile (without features)",
223         .check = do_compile_tests_without_features,
224         .can_run = features_reduced,
225         .needs = "module_builds tests_helpers_compile_without_features objects_build_without_features"
226 };
227 REGISTER_TEST(tests_compile_without_features);