]> git.ozlabs.org Git - ccan/blob - tools/depends.c
New polynomial module (not quite CCAN format).
[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 %s/_info.c",
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.c", 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                 /* Start of line, or after ". */
116                 if (strstarts(lines[i], "ccan/"))
117                         str = lines[i];
118                 else {
119                         str = strstr(lines[i], "\"ccan/");
120                         if (!str)
121                                 continue;
122                         str++;
123                 }
124                 
125                 len = strspn(str, "/abcdefghijklmnopqrstuvxwyz12345678980_");
126                 if (len == 5)
127                         continue;
128                 deps[n++] = talloc_strndup(deps, str, len);
129         }
130         deps[n] = NULL;
131         talloc_free(fname);
132         if (num)
133                 *num = n;
134         return deps;
135 }
136
137 static bool have_dep(char **deps, unsigned int num, const char *dep)
138 {
139         unsigned int i;
140
141         for (i = 0; i < num; i++)
142                 if (streq(deps[i], dep))
143                         return true;
144         return false;
145 }
146
147 /* Gets all the dependencies, recursively. */
148 static char **
149 get_all_deps(const void *ctx, const char *dir,
150              char **(*get_one)(const void *, const char *, unsigned int *))
151 {
152         char **deps;
153         unsigned int i, num;
154
155         deps = get_one(ctx, dir, &num);
156         for (i = 0; i < num; i++) {
157                 char **newdeps;
158                 unsigned int j, newnum;
159
160                 if (!strstarts(deps[i], "ccan/"))
161                         continue;
162
163                 newdeps = get_one(ctx, deps[i], &newnum);
164
165                 /* Should be short, so brute-force out dups. */
166                 for (j = 0; j < newnum; j++) {
167                         if (have_dep(deps, num, newdeps[j]))
168                                 continue;
169
170                         deps = talloc_realloc(NULL, deps, char *, num + 2);
171                         deps[num++] = newdeps[j];
172                         deps[num] = NULL;
173                 }
174         }
175         return deps;
176 }
177
178 char **get_deps(const void *ctx, const char *dir, bool recurse)
179 {
180         if (!recurse) {
181                 unsigned int num;
182                 return get_one_deps(ctx, dir, &num);
183         }
184         return get_all_deps(ctx, dir, get_one_deps);
185 }
186
187 char **get_safe_ccan_deps(const void *ctx, const char *dir, bool recurse)
188 {
189         if (!recurse) {
190                 unsigned int num;
191                 return get_one_safe_deps(ctx, dir, &num);
192         }
193         return get_all_deps(ctx, dir, get_one_safe_deps);
194 }
195