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