rbuf, tools: clean up rbuf usage.
[ccan] / tools / depends.c
1 #include <ccan/str/str.h>
2 #include <ccan/read_write_all/read_write_all.h>
3 #include <ccan/rbuf/rbuf.h>
4 #include <ccan/tal/path/path.h>
5 #include <ccan/tal/grab_file/grab_file.h>
6 #include <ccan/compiler/compiler.h>
7 #include <ccan/err/err.h>
8 #include "tools.h"
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <fcntl.h>
12 #include <stdbool.h>
13 #include <unistd.h>
14 #include <errno.h>
15
16 static char ** PRINTF_FMT(2, 3)
17 lines_from_cmd(const void *ctx, const char *format, ...)
18 {
19         va_list ap;
20         char *cmd;
21         FILE *p;
22         struct rbuf in;
23         char *ret;
24
25         va_start(ap, format);
26         cmd = tal_vfmt(ctx, format, ap);
27         va_end(ap);
28
29         p = popen(cmd, "r");
30         if (!p)
31                 err(1, "Executing '%s'", cmd);
32
33         /* FIXME: Use rbuf_read_str(&in, '\n') rather than strsplit! */
34         rbuf_init(&in, fileno(p), tal_arr(ctx, char, 0), 0,
35                   tal_rbuf_enlarge);
36         ret = rbuf_read_str(&in, 0);
37         if (!ret && errno)
38                 err(1, "Reading from '%s'", cmd);
39         pclose(p);
40
41         return tal_strsplit(ctx, ret, "\n", STR_EMPTY_OK);
42 }
43
44 /* Be careful about trying to compile over running programs (parallel make).
45  * temp_file helps here. */
46 char *compile_info(const void *ctx, const char *dir)
47 {
48         char *info_c_file, *info, *compiled, *output;
49         int fd;
50
51         /* Copy it to a file with proper .c suffix. */
52         info = grab_file(ctx, tal_fmt(ctx, "%s/_info", dir));
53         if (!info)
54                 return NULL;
55
56         info_c_file = temp_file(ctx, ".c", "_info");
57         fd = open(info_c_file, O_WRONLY|O_CREAT|O_EXCL, 0600);
58         if (fd < 0)
59                 return NULL;
60         if (!write_all(fd, info, tal_count(info)-1)) {
61                 close(fd);
62                 return NULL;
63         }
64
65         if (close(fd) != 0)
66                 return NULL;
67
68         compiled = temp_file(ctx, "", "info");
69         if (compile_and_link(ctx, info_c_file, find_ccan_dir(dir), "",
70                              compiler, cflags, "", compiled, &output))
71                 return compiled;
72         return NULL;
73 }
74
75 static char **get_one_deps(const void *ctx, const char *dir, const char *style,
76                            char *(*get_info)(const void *ctx, const char *dir))
77 {
78         char **deps, *cmd;
79         char *infofile = get_info(ctx, dir);
80
81         if (!infofile)
82                 return NULL;
83
84         cmd = tal_fmt(ctx, "%s %s", infofile, style);
85         deps = lines_from_cmd(cmd, "%s", cmd);
86         if (!deps) {
87                 /* You must understand depends, maybe not testdepends. */
88                 if (streq(style, "depends"))
89                         err(1, "Could not run '%s'", cmd);
90                 deps = tal(ctx, char *);
91                 deps[0] = NULL;
92         }
93         return deps;
94 }
95
96 /* Make copy of src, replacing "from" with "to". */
97 static char *replace(const void *ctx, const char *src,
98                      const char *from, const char *to)
99 {
100         char *ret = tal_strdup(ctx, "");
101         unsigned int rlen, len, add;
102
103         rlen = len = 0;
104         for (;;) {
105                 const char *next = strstr(src+len, from);
106                 if (!next)
107                         add = strlen(src+len) + 1;
108                 else
109                         add = next - (src+len);
110
111                 tal_resize(&ret, rlen + add + strlen(to)+1);
112                 memcpy(ret+rlen, src+len, add);
113                 if (!next)
114                         return ret;
115                 len += add;
116                 rlen += add;
117                 strcpy(ret+rlen, to);
118                 rlen += strlen(to);
119                 len += strlen(from);
120         }
121 }
122
123 /* This is a terrible hack.  We scan for ccan/ strings. */
124 static char **get_one_safe_deps(const void *ctx,
125                                 const char *dir,
126                                 const char *style,
127                                 char *(*unused)(const void *, const char *))
128 {
129         char **deps, **lines, *raw, *fname;
130         unsigned int i, n;
131         bool correct_style = false;
132
133         fname = path_join(ctx, dir, "_info");
134         raw = grab_file(fname, fname);
135         if (!raw)
136                 errx(1, "Could not open %s", fname);
137
138         /* Replace \n by actual line breaks, and split it. */
139         lines = tal_strsplit(raw, replace(raw, raw, "\\n", "\n"), "\n",
140                              STR_EMPTY_OK);
141
142         deps = tal_arr(ctx, char *, tal_count(lines));
143
144         for (n = i = 0; lines[i]; i++) {
145                 char *str;
146                 unsigned int len;
147
148                 /* Ignore lines starting with # (e.g. #include) */
149                 if (lines[i][0] == '#')
150                         continue;
151
152                 if (strstr(lines[i], "\"testdepends\""))
153                         correct_style = streq(style, "testdepends");
154                 else if (strstr(lines[i], "\"depends\""))
155                         correct_style = streq(style, "depends");
156
157                 if (!correct_style)
158                         continue;
159
160                 /* Start of line, or after ". */
161                 if (strstarts(lines[i], "ccan/"))
162                         str = lines[i];
163                 else {
164                         str = strstr(lines[i], "\"ccan/");
165                         if (!str)
166                                 continue;
167                         str++;
168                 }
169                 
170                 len = strspn(str, "/abcdefghijklmnopqrstuvxwyz12345678980_");
171                 if (len == 5)
172                         continue;
173                 deps[n++] = tal_strndup(deps, str, len);
174         }
175         deps[n] = NULL;
176         tal_free(fname);
177
178         /* Make sure tal_array_length() works */
179         tal_resize(&deps, n + 1);
180         return deps;
181 }
182
183 static bool have_dep(char **deps, const char *dep)
184 {
185         unsigned int i;
186
187         for (i = 0; deps[i]; i++)
188                 if (streq(deps[i], dep))
189                         return true;
190         return false;
191 }
192
193
194
195 /* Gets all the dependencies, recursively. */
196 static char **
197 get_all_deps(const void *ctx, const char *dir, const char *style,
198              char *(*get_info)(const void *ctx, const char *dir),
199              char **(*get_one)(const void *, const char *, const char *,
200                                char *(*get_info)(const void *, const char *)))
201 {
202         char **deps;
203         unsigned int i;
204
205         deps = get_one(ctx, dir, style, get_info);
206         if (!deps)
207                 return NULL;
208         for (i = 0; i < tal_count(deps)-1; i++) {
209                 char **newdeps;
210                 unsigned int j;
211                 char *subdir;
212
213                 if (!strstarts(deps[i], "ccan/"))
214                         continue;
215
216                 subdir = path_join(ctx, find_ccan_dir(dir), deps[i]);
217                 newdeps = get_one(ctx, subdir, "depends", get_info);
218
219                 /* Should be short, so brute-force out dups. */
220                 for (j = 0; j < tal_count(newdeps)-1; j++) {
221                         unsigned int num;
222
223                         if (have_dep(deps, newdeps[j]))
224                                 continue;
225
226                         num = tal_count(deps)-1;
227                         tal_resize(&deps, num + 2);
228                         deps[num] = newdeps[j];
229                         deps[num+1] = NULL;
230                 }
231         }
232         return deps;
233 }
234
235 /* Can return NULL: _info may not support prop. */
236 static char **get_one_prop(const void *ctx, const char *dir, const char *prop,
237                            char *(*get_info)(const void *ctx, const char *dir))
238 {
239         char *cmd, **lines;
240
241         cmd = tal_fmt(ctx, "%s %s", get_info(ctx, dir), prop);
242         lines = lines_from_cmd(cmd, "%s", cmd);
243         /* Strip final NULL. */
244         if (lines)
245                 tal_resize(&lines, tal_count(lines)-1);
246         return lines;
247 }
248
249 static char **get_one_libs(const void *ctx, const char *dir,
250                            char *(*get_info)(const void *ctx, const char *dir))
251 {
252         return get_one_prop(ctx, dir, "libs", get_info);
253 }
254
255 static char **get_one_cflags(const void *ctx, const char *dir,
256                            char *(*get_info)(const void *ctx, const char *dir))
257 {
258         return get_one_prop(ctx, dir, "cflags", get_info);
259 }
260
261 static char **get_one_ccanlint(const void *ctx, const char *dir,
262                                char *(*get_info)(const void *ctx, const char *dir))
263 {
264         return get_one_prop(ctx, dir, "ccanlint", get_info);
265 }
266
267 /* O(n^2) but n is small. */
268 static char **add_deps(char **deps1, char **deps2)
269 {
270         unsigned int i, len;
271
272         len = tal_count(deps1);
273
274         for (i = 0; deps2[i]; i++) {
275                 if (have_dep(deps1, deps2[i]))
276                         continue;
277                 tal_resize(&deps1, len + 1);
278                 deps1[len-1] = tal_strdup(deps1, deps2[i]);
279                 deps1[len++] = NULL;
280         }
281         return deps1;
282 }
283
284 char **get_cflags(const void *ctx, const char *dir,
285         char *(*get_info)(const void *ctx, const char *dir))
286 {
287         char **flags;
288         unsigned int len;
289         flags = get_one_cflags(ctx, dir, get_info);
290         len = tal_count(flags);
291         tal_resize(&flags, len + 1);
292         flags[len] = NULL;
293         return flags;
294 }
295
296 char **get_ccanlint(const void *ctx, const char *dir,
297                     char *(*get_info)(const void *ctx, const char *dir))
298 {
299         char **ccanlint;
300         unsigned int len;
301         ccanlint = get_one_ccanlint(ctx, dir, get_info);
302         len = tal_count(ccanlint);
303         tal_resize(&ccanlint, len + 1);
304         ccanlint[len] = NULL;
305         return ccanlint;
306 }
307
308 static char *get_one_ported(const void *ctx, const char *dir,
309                             char *(*get_info)(const void *ctx, const char *dir))
310 {
311         char **ported = get_one_prop(ctx, dir, "ported", get_info);
312
313         /* No news is good news. */
314         if (tal_count(ported) == 0)
315                 return NULL;
316
317         if (tal_count(ported) != 1)
318                 errx(1, "%s/_info ported gave %zu lines, not one",
319                      dir, tal_count(ported));
320
321         if (streq(ported[0], ""))
322                 return NULL;
323         else
324                 return ported[0];
325 }
326
327 char *get_ported(const void *ctx, const char *dir, bool recurse,
328                 char *(*get_info)(const void *ctx, const char *dir))
329 {
330         char *msg;
331
332         msg = get_one_ported(ctx, dir, get_info);
333         if (msg)
334                 return msg;
335
336         if (recurse) {
337                 size_t i;
338                 char **deps = get_deps(ctx, dir, "depends", true, get_info);
339                 for (i = 0; deps[i]; i++) {
340                         char *subdir;
341                         if (!strstarts(deps[i], "ccan/"))
342                                 continue;
343
344                         subdir = path_join(ctx, find_ccan_dir(dir), deps[i]);
345                         msg = get_one_ported(ctx, subdir, get_info);
346                         if (msg)
347                                 return msg;
348                 }
349         }
350         return NULL;
351 }
352
353 char **get_libs(const void *ctx, const char *dir, const char *style,
354                 char *(*get_info)(const void *ctx, const char *dir))
355 {
356         char **deps, **libs;
357         unsigned int i, len;
358
359         libs = get_one_libs(ctx, dir, get_info);
360         len = tal_count(libs);
361
362         if (style) {
363                 deps = get_deps(ctx, dir, style, true, get_info);
364                 if (streq(style, "testdepends"))
365                         deps = add_deps(deps,
366                                         get_deps(ctx, dir, "depends", true,
367                                                  get_info));
368
369                 for (i = 0; deps[i]; i++) {
370                         char **newlibs, *subdir;
371                         size_t newlen;
372
373                         if (!strstarts(deps[i], "ccan/"))
374                                 continue;
375
376                         subdir = path_join(ctx, find_ccan_dir(dir), deps[i]);
377
378                         newlibs = get_one_libs(ctx, subdir, get_info);
379                         newlen = tal_count(newlibs);
380                         tal_resize(&libs, len + newlen);
381                         memcpy(&libs[len], newlibs,
382                                sizeof(newlibs[0])*newlen);
383                         len += newlen;
384                 }
385         }
386
387         /* Append NULL entry. */
388         tal_resize(&libs, len + 1);
389         libs[len] = NULL;
390         return libs;
391 }
392
393 /* FIXME: This is O(n^2), which is dumb. */
394 static char **uniquify_deps(char **deps)
395 {
396         unsigned int i, j, num;
397
398         if (!deps)
399                 return NULL;
400
401         num = tal_count(deps) - 1;
402         for (i = 0; i < num; i++) {
403                 for (j = i + 1; j < num; j++) {
404                         if (streq(deps[i], deps[j])) {
405                                 memmove(&deps[j], &deps[j+1],
406                                         (num - j - 1) * sizeof(char *));
407                                 num--;
408                         }
409                 }
410         }
411         deps[num] = NULL;
412         /* Make sure tal_count() works */
413         tal_resize(&deps, num + 1);
414         return deps;
415 }
416
417 char **get_deps(const void *ctx, const char *dir, const char *style,
418                 bool recurse,
419                 char *(*get_info)(const void *ctx, const char *dir))
420 {
421         char **ret;
422
423         if (!recurse) {
424                 ret = get_one_deps(ctx, dir, style, get_info);
425         } else
426                 ret = get_all_deps(ctx, dir, style, get_info, get_one_deps);
427
428         return uniquify_deps(ret);
429 }
430
431 char **get_safe_ccan_deps(const void *ctx, const char *dir, const char *style,
432                           bool recurse)
433 {
434         char **ret;
435         if (!recurse) {
436                 ret = get_one_safe_deps(ctx, dir, style, NULL);
437         } else {
438                 ret = get_all_deps(ctx, dir, style, NULL, get_one_safe_deps);
439         }
440         return uniquify_deps(ret);
441 }