Non-compiling version of ccan_depends by default.
[ccan] / tools / depends.c
1 #include "talloc/talloc.h"
2 #include "string/string.h"
3 #include "tools.h"
4 #include <err.h>
5 #include <stdbool.h>
6 #include <unistd.h>
7
8 static char ** __attribute__((format(printf, 3, 4)))
9 lines_from_cmd(const void *ctx, unsigned int *num, char *format, ...)
10 {
11         va_list ap;
12         char *cmd, *buffer;
13         FILE *p;
14
15         va_start(ap, format);
16         cmd = talloc_vasprintf(ctx, format, ap);
17         va_end(ap);
18
19         p = popen(cmd, "r");
20         if (!p)
21                 err(1, "Executing '%s'", cmd);
22
23         buffer = grab_fd(ctx, fileno(p), NULL);
24         if (!buffer)
25                 err(1, "Reading from '%s'", cmd);
26         pclose(p);
27
28         return strsplit(ctx, buffer, "\n", num);
29 }
30
31 static int unlink_info(char *infofile)
32 {
33         unlink(infofile);
34         return 0;
35 }
36
37 /* Be careful about trying to compile over running programs (parallel make) */
38 static char *compile_info(const void *ctx, const char *dir)
39 {
40         char *infofile = talloc_asprintf(ctx, "%s/_info.%u", dir, getpid());
41         char *cmd = talloc_asprintf(ctx, "cc " CFLAGS " -o %s %s/_info.c",
42                                     infofile, dir);
43         talloc_set_destructor(infofile, unlink_info);
44         if (system(cmd) != 0)
45                 return NULL;
46
47         return infofile;
48 }
49
50 static char **get_one_deps(const void *ctx, const char *dir, unsigned int *num)
51 {
52         char **deps, *cmd, *infofile;
53
54         infofile = compile_info(ctx, dir);
55         if (!infofile)
56                 errx(1, "Could not compile _info for '%s'", dir);
57
58         cmd = talloc_asprintf(ctx, "%s depends", infofile);
59         deps = lines_from_cmd(cmd, num, "%s", cmd);
60         if (!deps)
61                 err(1, "Could not run '%s'", cmd);
62         return deps;
63 }
64
65 /* Make copy of src, replacing "from" with "to". */
66 static char *replace(const void *ctx, const char *src,
67                      const char *from, const char *to)
68 {
69         char *ret = talloc_strdup(ctx, "");
70         unsigned int rlen, len, add;
71
72         rlen = len = 0;
73         for (;;) {
74                 const char *next = strstr(src+len, from);
75                 if (!next)
76                         add = strlen(src+len) + 1;
77                 else
78                         add = next - (src+len);
79
80                 ret = talloc_realloc(ctx, ret, char, rlen + add + strlen(to)+1);
81                 memcpy(ret+rlen, src+len, add);
82                 if (!next)
83                         return ret;
84                 len += add;
85                 rlen += add;
86                 strcpy(ret+rlen, to);
87                 rlen += strlen(to);
88                 len += strlen(from);
89         }
90 }
91
92 /* This is a terrible hack.  We scan for ccan/ strings. */
93 static char **get_one_safe_deps(const void *ctx,
94                                 const char *dir, unsigned int *num)
95 {
96         char **deps, **lines, *raw, *fname;
97         unsigned int i, n = 0;
98
99         fname = talloc_asprintf(ctx, "%s/_info.c", dir);
100         raw = grab_file(fname, fname, NULL);
101         if (!raw)
102                 errx(1, "Could not open %s", fname);
103
104         /* Replace \n by actual line breaks, and split it. */
105         lines = strsplit(raw, replace(raw, raw, "\\n", "\n"), "\n", &n);
106
107         deps = talloc_array(ctx, char *, n+1);
108
109         for (n = i = 0; lines[i]; i++) {
110                 char *str;
111                 unsigned int len;
112
113                 /* Start of line, or after ". */
114                 if (strstarts(lines[i], "ccan/"))
115                         str = lines[i];
116                 else {
117                         str = strstr(lines[i], "\"ccan/");
118                         if (!str)
119                                 continue;
120                         str++;
121                 }
122                 
123                 len = strspn(str, "/abcdefghijklmnopqrstuvxwyz12345678980_");
124                 if (len == 5)
125                         continue;
126                 deps[n++] = talloc_strndup(deps, str, len);
127         }
128         deps[n] = NULL;
129         talloc_free(fname);
130         if (num)
131                 *num = n;
132         return deps;
133 }
134
135 static bool have_dep(char **deps, unsigned int num, const char *dep)
136 {
137         unsigned int i;
138
139         for (i = 0; i < num; i++)
140                 if (streq(deps[i], dep))
141                         return true;
142         return false;
143 }
144
145 /* Gets all the dependencies, recursively. */
146 static char **
147 get_all_deps(const void *ctx, const char *dir,
148              char **(*get_one)(const void *, const char *, unsigned int *))
149 {
150         char **deps;
151         unsigned int i, num;
152
153         deps = get_one(ctx, dir, &num);
154         for (i = 0; i < num; i++) {
155                 char **newdeps;
156                 unsigned int j, newnum;
157
158                 if (!strstarts(deps[i], "ccan/"))
159                         continue;
160
161                 newdeps = get_one(ctx, deps[i], &newnum);
162
163                 /* Should be short, so brute-force out dups. */
164                 for (j = 0; j < newnum; j++) {
165                         if (have_dep(deps, num, newdeps[j]))
166                                 continue;
167
168                         deps = talloc_realloc(NULL, deps, char *, num + 2);
169                         deps[num++] = newdeps[j];
170                         deps[num] = NULL;
171                 }
172         }
173         return deps;
174 }
175
176 char **get_deps(const void *ctx, const char *dir)
177 {
178         return get_all_deps(ctx, dir, get_one_deps);
179 }
180
181 char **get_safe_ccan_deps(const void *ctx, const char *dir)
182 {
183         return get_all_deps(ctx, dir, get_one_safe_deps);
184 }
185