Skip to content

Commit 8315591

Browse files
committed
dyndep: add initial dyndep support
1 parent ed567a1 commit 8315591

File tree

8 files changed

+331
-4
lines changed

8 files changed

+331
-4
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ ALL_CFLAGS=$(CFLAGS) -std=c99 -Wall -Wextra -Wpedantic
66
OBJ=\
77
build.o\
88
deps.o\
9+
dyndep.o\
910
env.o\
1011
graph.o\
1112
htab.o\

build.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <unistd.h>
1414
#include "build.h"
1515
#include "deps.h"
16+
#include "dyndep.h"
1617
#include "env.h"
1718
#include "graph.h"
1819
#include "log.h"
@@ -241,6 +242,8 @@ buildadd(struct node *n)
241242
++ntotal;
242243
}
243244
e->flags &= ~FLAG_CYCLE;
245+
if (e->dyndep)
246+
dyndepload(e->dyndep, false);
244247
}
245248

246249
static size_t
@@ -406,6 +409,10 @@ nodedone(struct node *n, bool prune)
406409
/* mark node clean for computedirty of edges with dyndeps */
407410
n->dirty = false;
408411

412+
/* if this node is a dyndep it can be loaded now */
413+
if (n->dyndep)
414+
dyndepload(n->dyndep, false);
415+
409416
for (i = 0; i < n->nuse; ++i) {
410417
e = n->use[i];
411418
/* skip edges not used in this build */

build.ninja

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ rule link
1010

1111
build build.o: cc build.c
1212
build deps.o: cc deps.c
13+
build dyndep.o: cc dyndep.c
1314
build env.o: cc env.c
1415
build graph.o: cc graph.c
1516
build htab.o: cc htab.c
@@ -20,6 +21,6 @@ build scan.o: cc scan.c
2021
build tool.o: cc tool.c
2122
build tree.o: cc tree.c
2223
build util.o: cc util.c
23-
build samu: link build.o deps.o env.o graph.o htab.o log.o parse.o samu.o scan.o tool.o tree.o util.o
24+
build samu: link build.o deps.o dyndep.o env.o graph.o htab.o log.o parse.o samu.o scan.o tool.o tree.o util.o
2425

2526
default samu

dyndep.c

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
#include <inttypes.h>
2+
#include <stdbool.h>
3+
#include <stddef.h>
4+
#include <stdlib.h>
5+
#include <stdio.h>
6+
#include <string.h>
7+
#include <unistd.h>
8+
#include "build.h"
9+
#include "dyndep.h"
10+
#include "env.h"
11+
#include "graph.h"
12+
#include "htab.h"
13+
#include "scan.h"
14+
#include "util.h"
15+
16+
struct nodearray {
17+
struct node **node;
18+
size_t len;
19+
};
20+
21+
const char *dyndepversion = "1.0";
22+
23+
static struct dyndep *alldyndeps;
24+
25+
void
26+
dyndepinit(void)
27+
{
28+
struct dyndep *d;
29+
30+
/* delete old dyndeps in case we rebuilt the manifest */
31+
while (alldyndeps) {
32+
d = alldyndeps;
33+
alldyndeps = d->allnext;
34+
free(d->use);
35+
free(d);
36+
}
37+
}
38+
39+
struct dyndep *
40+
mkdyndep(struct node *n)
41+
{
42+
struct dyndep *d;
43+
44+
if (n->dyndep)
45+
return n->dyndep;
46+
47+
d = xmalloc(sizeof(*d));
48+
d->node = n;
49+
d->use = NULL;
50+
d->nuse = 0;
51+
d->update = NULL;
52+
d->nupdate = 0;
53+
d->done = false;
54+
d->allnext = alldyndeps;
55+
alldyndeps = d;
56+
n->dyndep = d;
57+
58+
return d;
59+
}
60+
61+
void
62+
dyndepuse(struct dyndep *d, struct edge *e)
63+
{
64+
e->dyndep = d;
65+
66+
/* allocate in powers of two */
67+
if (!(d->nuse & (d->nuse - 1)))
68+
d->use = xreallocarray(d->use, d->nuse ? d->nuse * 2 : 1, sizeof(e));
69+
d->use[d->nuse++] = e;
70+
}
71+
72+
static void
73+
dyndepupdate(struct dyndep *d, struct node *n)
74+
{
75+
/* allocate in powers of two */
76+
if (!(d->nupdate & (d->nupdate - 1)))
77+
d->update = xreallocarray(d->update, d->nupdate ? d->nupdate * 2 : 1, sizeof(n));
78+
d->update[d->nupdate++] = n;
79+
}
80+
81+
static void
82+
parselet(struct scanner *s, struct evalstring **val)
83+
{
84+
scanchar(s, '=');
85+
*val = scanstring(s, false);
86+
scannewline(s);
87+
}
88+
89+
static void
90+
checkversion(const char *ver)
91+
{
92+
if (strcmp(ver, dyndepversion) > 0)
93+
fatal("ninja_dyndep_version %s is newer than %s", ver, dyndepversion);
94+
}
95+
96+
static void
97+
dyndepparseedge(struct scanner *s, struct environment *env)
98+
{
99+
struct dyndep *d;
100+
struct edge *e;
101+
struct node *n;
102+
static struct nodearray nodes;
103+
static size_t nodescap;
104+
struct evalstring *str;
105+
char *name;
106+
struct string *val;
107+
108+
str = scanstring(s, true);
109+
if (!str)
110+
scanerror(s, "expected explicit output");
111+
112+
val = enveval(env, str);
113+
n = nodeget(val->s, val->n);
114+
if (!n)
115+
fatal("unknown target '%s'", val->s);
116+
e = n->gen;
117+
if (!e)
118+
fatal("no action to generate '%s' in dyndep", val->s);
119+
free(val);
120+
d = e->dyndep;
121+
if (!d)
122+
fatal("target '%s' does not mention this dyndep", n->path->s);
123+
124+
dyndepupdate(d, n);
125+
126+
nodes.len = 0;
127+
if (scanpipe(s, 1)) {
128+
for (; (str = scanstring(s, true)); ) {
129+
val = enveval(e->env, str);
130+
canonpath(val);
131+
if (nodes.len == nodescap) {
132+
nodescap = nodes.node ? nodescap * 2 : 32;
133+
nodes.node = xreallocarray(nodes.node, nodescap, sizeof(nodes.node[0]));
134+
}
135+
nodes.node[nodes.len++] = mknode(val);
136+
}
137+
}
138+
if (nodes.len)
139+
edgeadddynouts(e, nodes.node, nodes.len);
140+
141+
scanchar(s, ':');
142+
name = scanname(s);
143+
if (strcmp(name, "dyndep") != 0)
144+
fatal("unexpected rule '%s' in dyndep", name);
145+
free(name);
146+
147+
nodes.len = 0;
148+
if (scanpipe(s, 1)) {
149+
for (; (str = scanstring(s, true)); ) {
150+
val = enveval(e->env, str);
151+
canonpath(val);
152+
if (nodes.len == nodescap) {
153+
nodescap = nodes.node ? nodescap * 2 : 32;
154+
nodes.node = xreallocarray(nodes.node, nodescap, sizeof(nodes.node[0]));
155+
}
156+
nodes.node[nodes.len++] = mknode(val);
157+
}
158+
}
159+
if (nodes.len)
160+
edgeadddyndeps(e, nodes.node, nodes.len);
161+
162+
scannewline(s);
163+
while (scanindent(s)) {
164+
name = scanname(s);
165+
if (strcmp(name, "restat") != 0)
166+
fatal("unexpected variable '%s' in dyndep", name);
167+
parselet(s, &str);
168+
envaddvar(e->env, "restat", enveval(env, str));
169+
}
170+
171+
e->flags |= FLAG_DYNDEP;
172+
}
173+
174+
static void
175+
dyndepparse(const char *name)
176+
{
177+
struct scanner s;
178+
struct environment *env;
179+
struct evalstring *str;
180+
struct string *val;
181+
bool version = false;
182+
char *var;
183+
184+
scaninit(&s, name);
185+
186+
env = mkenv(NULL);
187+
for (;;) {
188+
switch (scankeyword(&s, &var)) {
189+
case BUILD:
190+
if (!version)
191+
goto version;
192+
dyndepparseedge(&s, env);
193+
break;
194+
case VARIABLE:
195+
if (version)
196+
scanerror(&s, "unexpected variable '%s'", var);
197+
parselet(&s, &str);
198+
val = enveval(env, str);
199+
if (strcmp(var, "ninja_dyndep_version") == 0)
200+
checkversion(val->s);
201+
envaddvar(env, var, val);
202+
version = true;
203+
break;
204+
case EOF:
205+
if (!version)
206+
goto version;
207+
scanclose(&s);
208+
return;
209+
default:
210+
scanerror(&s, "unexpected keyword");
211+
}
212+
}
213+
version:
214+
scanerror(&s, "expected 'ninja_dyndep_version'");
215+
}
216+
217+
bool
218+
dyndepload(struct dyndep *d, bool prune)
219+
{
220+
const char *name;
221+
struct edge *e;
222+
struct node *n;
223+
size_t i;
224+
225+
if (d->done)
226+
return true;
227+
228+
name = d->node->path->s;
229+
230+
if (!d->node->gen)
231+
fatal("dyndep not created by any action: '%s'", name);
232+
233+
if (!prune) {
234+
/* only load dyndep file if its clean and nothing is blocking it */
235+
if (d->node->gen->flags & FLAG_DIRTY || d->node->gen->nblock > 0)
236+
return false;
237+
} else {
238+
nodestat(d->node);
239+
if (d->node->mtime == MTIME_MISSING)
240+
return false;
241+
}
242+
243+
if (buildopts.explain)
244+
warn("loading dyndep file: '%s'", name);
245+
246+
dyndepparse(name);
247+
248+
d->done = true;
249+
250+
if (prune)
251+
return true;
252+
253+
/* verify that all nodes with this dyndep are loaded */
254+
for (i = 0; i < d->nuse; i++) {
255+
e = d->use[i];
256+
if (!(e->flags & FLAG_DYNDEP))
257+
fatal("target '%s' not mentioned in dyndep file '%s'",
258+
e->out[0]->path->s, d->node->path->s);
259+
}
260+
/* (re)-schedule the nodes updated by this dyndep */
261+
for (i = 0; i < d->nupdate; i++) {
262+
n = d->update[i];
263+
/* only update the node if it was previously added to the build */
264+
if (n->gen->flags & FLAG_WORK)
265+
buildupdate(n);
266+
else
267+
buildadd(n);
268+
n->gen->flags &= ~FLAG_DYNDEP;
269+
}
270+
271+
return true;
272+
}

dyndep.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
struct node;
2+
3+
struct dyndep {
4+
/* the node building the dyndep file */
5+
struct node *node;
6+
7+
/* edges using this dyndep */
8+
struct edge **use;
9+
size_t nuse;
10+
11+
/* nodes this dyndep updated */
12+
struct node **update;
13+
size_t nupdate;
14+
15+
/* is this dyndep file already loaded */
16+
_Bool done;
17+
18+
/* used for alldyndeps linked list */
19+
struct dyndep *allnext;
20+
};
21+
22+
void dyndepinit(void);
23+
24+
/* create a new dyndep or return existing dyndep */
25+
struct dyndep *mkdyndep(struct node *);
26+
/* load the dyndep */
27+
bool dyndepload(struct dyndep *, bool);
28+
/* record the usage of a dyndep by an edge */
29+
void dyndepuse(struct dyndep *, struct edge *);

0 commit comments

Comments
 (0)