Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/nmake/command.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1984-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* *
19
***********************************************************************/
20
#pragma prototyped
21
/*
22
* Glenn Fowler
23
* AT&T Research
24
*
25
* make command execution routines
26
*/
27
28
#include "make.h"
29
30
#include <sig.h>
31
32
#if !_all_shells_the_same
33
#undef EXIT_CODE
34
#define EXIT_CODE(x) ((x)&0177)
35
#endif
36
37
#define AFTER 0 /* done -- making after prereqs */
38
#define BEFORE 001 /* done -- before after prereqs */
39
#define BLOCKED 002 /* waiting for prereqs */
40
#define INTERMEDIATE 003 /* waiting for parent cancel */
41
#define READY 004 /* job ready to run */
42
#define RUNNING 005 /* job action sent to coshell */
43
44
#define PUSHED 010 /* currently push'd */
45
#define STATUS (~PUSHED) /* status mask */
46
47
#define MAMNAME(r) ((state.mam.dynamic||(r)!=state.frame->target||((r)->property&P_after))?mamname(r):(char*)0)
48
49
#if DEBUG
50
51
static char* statusname[] =
52
{
53
"AFTER", "BEFORE", "BLOCKED", "INTERMEDIATE", "READY", "RUNNING",
54
};
55
56
#define jobstatus() do { if (state.test & 0x00001000) dumpjobs(2, JOB_status); else if (error_info.trace <= CMDTRACE) dumpjobs(CMDTRACE, JOB_status); } while (0)
57
58
#else
59
60
#define jobstatus()
61
62
#endif
63
64
struct Context_s; typedef struct Context_s Context_t;
65
struct Joblist_s; typedef struct Joblist_s Joblist_t;
66
67
struct Context_s /* job target context */
68
{
69
Context_t* next; /* for free list link */
70
Frame_t* frame; /* active target frames */
71
Frame_t* last; /* last active target frame */
72
int targetview; /* state.targetview */
73
};
74
75
struct Joblist_s /* job list cell */
76
{
77
Joblist_t* next; /* next in list */
78
Joblist_t* prev; /* prev in list */
79
Cojob_t* cojob; /* coshell job info */
80
Rule_t* target; /* target for job */
81
List_t* prereqs; /* these must be done */
82
Context_t* context; /* job target context */
83
char* action; /* unexpanded action */
84
int status; /* job status */
85
Flags_t flags; /* job flags */
86
};
87
88
typedef struct Jobstate_s /* job state */
89
{
90
Joblist_t* firstjob; /* first job */
91
Joblist_t* lastjob; /* last job */
92
Joblist_t* freejob; /* free jobs */
93
Frame_t* freeframe; /* free target frames */
94
Context_t* freecontext; /* free job context headers */
95
int intermediate; /* # INTERMEDIATE jobs */
96
Sfio_t* tmp; /* tmp stream */
97
Rule_t* triggered; /* triggered but not yet a job */
98
} Jobstate_t;
99
100
static Jobstate_t jobs;
101
102
/*
103
* accept r as up to date
104
*/
105
106
static void
107
accept(register Rule_t* r)
108
{
109
if (r->property & P_state)
110
{
111
if (!state.silent)
112
error(0, "touch %s", r->name);
113
if (state.exec)
114
state.savestate = 1;
115
goto done;
116
}
117
if (r->property & P_archive)
118
{
119
if (state.exec)
120
{
121
artouch(r->name, NiL);
122
statetime(r, 0);
123
}
124
if (!state.silent)
125
error(0, "touch %s", r->name);
126
goto done;
127
}
128
if (r->active && (r->active->parent->target->property & P_archive) && !(r->property & (P_after|P_before|P_use)))
129
{
130
char* t;
131
132
if (t = strrchr(r->name, '/'))
133
{
134
if (!r->uname)
135
r->uname = r->name;
136
r->name = maprule(t + 1, r);
137
}
138
if (state.exec)
139
artouch(r->active->parent->target->name, r->name);
140
else if (!state.silent)
141
error(0, "touch %s/%s", r->active->parent->target->name, r->name);
142
if (!(r->dynamic & D_regular))
143
goto done;
144
}
145
if (!(r->property & P_virtual))
146
{
147
if (!state.silent)
148
error(0, "touch %s", r->name);
149
if (state.exec)
150
{
151
Stat_t st;
152
153
if (stat(r->name, &st) || tmxgetmtime(&st) < state.start && tmxtouch(r->name, (Time_t)0, (Time_t)0, (Time_t)0, 0))
154
error(ERROR_SYSTEM|1, "cannot touch %s", r->name);
155
statetime(r, 0);
156
}
157
}
158
done:
159
if (r->status != TOUCH)
160
r->status = r->time ? EXISTS : (r->property & P_dontcare) ? IGNORE : FAILED;
161
}
162
163
/*
164
* apply operator or action with attributes in r given lhs, rhs and job flags
165
* blocks until action is complete
166
*/
167
168
int
169
apply(register Rule_t* r, char* lhs, char* rhs, char* act, Flags_t flags)
170
{
171
register Rule_t* x;
172
int oop;
173
int errors;
174
Rule_t lhs_rule;
175
Rule_t rhs_rule;
176
List_t lhs_prereqs;
177
Frame_t lhs_frame;
178
Frame_t* oframe;
179
180
zero(lhs_rule);
181
zero(rhs_rule);
182
lhs_prereqs.rule = &rhs_rule;
183
lhs_prereqs.next = 0;
184
x = &lhs_rule;
185
x->prereqs = &lhs_prereqs;
186
zero(lhs_frame);
187
lhs_frame.parent = state.frame;
188
lhs_frame.target = x;
189
x->active = &lhs_frame;
190
x->property = r->property & (P_make|P_operator);
191
if (x->property & P_operator)
192
{
193
x->action = act;
194
act = r->action;
195
}
196
else
197
x->action = r->action;
198
x->name = lhs;
199
x->statedata = r->name;
200
x->time = state.start;
201
rhs_rule.name = rhs;
202
rhs_rule.time = x->time + 1;
203
oframe = state.frame;
204
state.frame = &lhs_frame;
205
if ((r->property & (P_make|P_operator)) == (P_make|P_operator))
206
{
207
if (r->prereqs)
208
{
209
Time_t t;
210
char* oaction;
211
212
oaction = r->action;
213
r->action = null;
214
make(r, &t, NiL, 0);
215
r->action = oaction;
216
}
217
oop = state.op;
218
state.op = 1;
219
errors = parse(NiL, act, r->name, NiL) == FAILED;
220
state.op = oop;
221
}
222
else
223
{
224
r->status = UPDATE;
225
trigger(r, NiL, act, flags);
226
errors = complete(r, NiL, NiL, 0);
227
}
228
state.frame = oframe;
229
return errors;
230
}
231
232
/*
233
* apply() returning (temporary) CO_DATAFILE pointer to stdout of action
234
*/
235
236
Sfio_t*
237
fapply(Rule_t* r, char* lhs, char* rhs, char* act, Flags_t flags)
238
{
239
Sfio_t* fp;
240
241
fp = 0;
242
if (!apply(r, lhs, rhs, act, flags|CO_DATAFILE))
243
{
244
if (fp = sfopen(NiL, state.tmpfile, "re"))
245
remove(state.tmpfile);
246
else
247
error(2, "%s: cannot read temporary data output file %s", r->name, state.tmpfile);
248
}
249
state.tmpfile = 0;
250
return fp;
251
}
252
253
/*
254
* call functional r with args
255
* functional return value returned
256
* 0 returned if not functional or empty return
257
*/
258
259
char*
260
call(register Rule_t* r, char* args)
261
{
262
register Var_t* v;
263
264
if (r->property & P_functional)
265
{
266
maketop(r, 0, args);
267
if ((v = getvar(r->name)) && *v->value)
268
return v->value;
269
}
270
return 0;
271
}
272
273
/*
274
* commit target/virtual directory
275
*/
276
277
static void
278
commit(Joblist_t* job, register char* s)
279
{
280
register char* t;
281
register char* v;
282
register Rule_t* r;
283
Stat_t st;
284
285
if (t = strrchr(s, '/'))
286
{
287
*t = 0;
288
if (r = bindfile(NiL, s, 0))
289
{
290
if (!r->view)
291
{
292
*t = '/';
293
return;
294
}
295
if (*(v = r->name) != '/')
296
{
297
sfprintf(internal.nam, "%s/%s", state.view[r->view].root, v);
298
v = sfstruse(internal.nam);
299
}
300
if (stat(v, &st))
301
r = 0;
302
}
303
if (r || state.targetcontext && (!r || !r->time) && (st.st_mode = (S_IRWXU|S_IRWXG|S_IRWXO)) && tmxsetmtime(&st, state.start))
304
{
305
/*
306
* why not mkdir -p here?
307
*/
308
309
commit(job, s);
310
if (((job->flags & CO_ALWAYS) || state.exec && state.touch) && (mkdir(s, st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) || stat(s, &st)))
311
error(1, "%s: cannot create target directory %s", job->target->name, s);
312
if (state.mam.out)
313
{
314
Sfio_t* tmp = sfstropen();
315
316
sfprintf(tmp, "mkdir %s", s);
317
dumpaction(state.mam.out, MAMNAME(job->target), sfstruse(tmp), NiL);
318
sfstrclose(tmp);
319
}
320
r = makerule(s);
321
if (r->dynamic & D_alias)
322
oldname(r);
323
r->view = 0;
324
r->time = tmxgetmtime(&st);
325
if (r->dynamic & D_scanned)
326
unbind(NiL, (char*)r, NiL);
327
}
328
*t = '/';
329
}
330
}
331
332
/*
333
* push job context
334
*/
335
336
static void
337
push(Joblist_t* job)
338
{
339
register Context_t* z;
340
register Frame_t* p;
341
register Rule_t* r;
342
int n;
343
Time_t tm;
344
345
job->status |= PUSHED;
346
if (z = job->context)
347
{
348
p = z->frame;
349
for (;;)
350
{
351
if (!(r = getrule(p->context.name)))
352
r = makerule(p->context.name);
353
p->target = r;
354
tm = r->time;
355
r->time = p->context.time;
356
p->context.time = tm;
357
p->context.frame = r->active;
358
r->active = p;
359
if (p == p->parent)
360
break;
361
p = p->parent;
362
}
363
n = state.targetview;
364
state.targetview = z->targetview;
365
z->targetview = n;
366
p = state.frame;
367
state.frame = z->frame;
368
z->frame = p;
369
}
370
}
371
372
/*
373
* pop job context
374
*/
375
376
static void
377
pop(Joblist_t* job)
378
{
379
register Context_t* z;
380
register Frame_t* p;
381
register Rule_t* r;
382
int n;
383
Time_t tm;
384
385
if (z = job->context)
386
{
387
n = state.targetview;
388
state.targetview = z->targetview;
389
z->targetview = n;
390
p = state.frame;
391
state.frame = z->frame;
392
z->frame = p;
393
for (;;)
394
{
395
if (!(r = getrule(p->context.name)))
396
r = makerule(p->context.name);
397
r->active = p->context.frame;
398
tm = r->time;
399
r->time = p->context.time;
400
p->context.time = tm;
401
if (p == p->parent)
402
break;
403
p = p->parent;
404
}
405
}
406
job->status &= ~PUSHED;
407
}
408
409
/*
410
* discard (free) job context
411
*/
412
413
static void
414
discard(register Joblist_t* job)
415
{
416
register Context_t* z;
417
register List_t* p;
418
419
if (job->flags & CO_SEMAPHORES)
420
for (p = job->prereqs; p; p = p->next)
421
if (p->rule->semaphore)
422
{
423
p->rule->semaphore++;
424
p->rule->status = EXISTS;
425
}
426
if (job->flags & CO_PRIMARY)
427
{
428
job->prereqs->next = 0;
429
freelist(job->prereqs);
430
}
431
if (z = job->context)
432
{
433
z->last->parent = jobs.freeframe;
434
jobs.freeframe = z->frame;
435
z->next = jobs.freecontext;
436
jobs.freecontext = z;
437
job->context = 0;
438
}
439
if (job->next)
440
job->next->prev = job->prev;
441
else
442
jobs.lastjob = job->prev;
443
if (job->prev)
444
job->prev->next = job->next;
445
else
446
jobs.firstjob = job->next;
447
job->next = jobs.freejob;
448
jobs.freejob = job;
449
}
450
451
/*
452
* save job context for later execute()
453
*/
454
455
static void
456
save(Joblist_t* job)
457
{
458
register Frame_t* o;
459
register Frame_t* p;
460
register Frame_t* x;
461
Context_t* z;
462
463
if (job->action && !job->context)
464
{
465
if (z = jobs.freecontext)
466
jobs.freecontext = jobs.freecontext->next;
467
else
468
z = newof(0, Context_t, 1, 0);
469
z->targetview = state.targetview;
470
o = state.frame;
471
p = 0;
472
for (;;)
473
{
474
if (x = jobs.freeframe)
475
jobs.freeframe = jobs.freeframe->parent;
476
else
477
x = newof(0, Frame_t, 1, 0);
478
if (p)
479
p->parent = x;
480
else
481
z->frame = x;
482
p = x;
483
p->original = o->original;
484
p->primary = o->primary;
485
p->stem = o->stem ? strdup(o->stem) : 0;
486
p->context.name = o->target->name;
487
p->context.time = o->target->time;
488
if (o->parent == o)
489
break;
490
o = o->parent;
491
}
492
z->last = p->parent = p;
493
job->context = z;
494
}
495
}
496
497
/*
498
* restore action by expanding into buf using original context
499
* coexec attributes placed in att
500
*/
501
502
static void
503
restore(register Joblist_t* job, Sfio_t* buf, Sfio_t* att)
504
{
505
register char* s;
506
register char* b;
507
char* u;
508
char* down;
509
char* back;
510
char* sep;
511
int downlen;
512
int localview;
513
void* pos;
514
Var_t* v;
515
Sfio_t* opt;
516
Sfio_t* tmp;
517
Sfio_t* context;
518
519
push(job);
520
localview = state.localview;
521
state.localview = state.mam.statix && !state.expandview && state.user && !(job->flags & CO_ALWAYS);
522
if ((job->flags & CO_LOCALSTACK) || (job->target->dynamic & D_hasscope))
523
{
524
register Rule_t* r;
525
register List_t* p;
526
527
job->flags |= CO_LOCALSTACK;
528
pos = pushlocal();
529
opt = sfstropen();
530
if (job->target->dynamic & D_hasscope)
531
for (p = job->prereqs; p; p = p->next)
532
if ((r = p->rule)->dynamic & D_scope)
533
{
534
if (*r->name == '-')
535
set(r->name, 1, opt);
536
else
537
parse(NiL, r->name, r->name, opt);
538
}
539
else if ((r->property & (P_make|P_local|P_use)) == (P_make|P_local) && r->action)
540
parse(NiL, r->action, r->name, opt);
541
}
542
context = state.context;
543
if (state.targetcontext && *(u = unbound(job->target)) != '/' && (s = strrchr(u, '/')))
544
{
545
size_t n;
546
int c;
547
548
tmp = sfstropen();
549
downlen = s - u;
550
*s = 0;
551
sfprintf(tmp, "%s%c", u, 0);
552
n = sfstrtell(tmp);
553
c = '/';
554
do
555
{
556
if (u = strchr(u, '/'))
557
u++;
558
else
559
c = 0;
560
sfputr(tmp, "..", c);
561
} while (c);
562
*s = '/';
563
back = (down = sfstrbase(tmp)) + n;
564
state.context = buf;
565
buf = sfstropen();
566
state.localview++;
567
}
568
else
569
state.context = 0;
570
if (job->action)
571
expand(buf, job->action);
572
if (state.context)
573
{
574
s = sfstruse(buf);
575
sep = strchr(s, '\n') ? "\n" : "; ";
576
sfprintf(state.context, "{ cd %s%s", down, sep);
577
while (b = strchr(s, MARK_CONTEXT))
578
{
579
sfwrite(state.context, s, b - s);
580
if (!(s = strchr(++b, MARK_CONTEXT)))
581
error(PANIC, "unbalanced MARK_CONTEXT");
582
*s++ = 0;
583
if (*b == '/' || (u = getbound(b)) && *u == '/')
584
sfputr(state.context, b, -1);
585
else if (*b)
586
{
587
if (strneq(b, down, downlen))
588
switch (*(b + downlen))
589
{
590
case 0:
591
sfputc(state.context, '.');
592
continue;
593
case '/':
594
sfputr(state.context, b + downlen + 1, -1);
595
continue;
596
}
597
if (streq(b, "."))
598
sfputr(state.context, back, -1);
599
else if (isspace(*b))
600
sfputr(state.context, b, -1);
601
else
602
sfprintf(state.context, "%s/%s", back, b);
603
}
604
}
605
sfprintf(state.context, "%s%s}", s, sep);
606
sfstrclose(tmp);
607
sfstrclose(buf);
608
}
609
state.context = context;
610
sfprintf(att, "label=%s", job->target->name);
611
if ((v = getvar(CO_ENV_ATTRIBUTES)) && !(v->property & V_import))
612
sfprintf(att, ",%s", v->value);
613
if (job->flags & CO_LOCALSTACK)
614
{
615
poplocal(pos);
616
if (*(s = sfstruse(opt)))
617
set(s, 1, NiL);
618
sfclose(opt);
619
}
620
state.localview = localview;
621
pop(job);
622
}
623
624
static int done(Joblist_t* job, int, Cojob_t*);
625
626
/*
627
* send a job to the coshell for execution
628
*/
629
630
static void
631
execute(register Joblist_t* job)
632
{
633
register List_t* p;
634
char* s;
635
char* t;
636
int flags;
637
Rule_t* r;
638
Var_t* v;
639
Sfio_t* tmp;
640
Sfio_t* att;
641
Sfio_t* sp;
642
643
att = sfstropen();
644
tmp = sfstropen();
645
restore(job, tmp, att);
646
job->status = RUNNING;
647
job->target->mark &= ~M_waiting;
648
if (state.targetcontext || state.maxview && !state.fsview && *job->target->name != '/' && (!(job->target->dynamic & D_regular) || job->target->view))
649
commit(job, job->target->name);
650
if ((state.mam.dynamic || state.mam.regress) && state.user && !(job->target->property & (P_after|P_before|P_dontcare|P_make|P_state|P_virtual)))
651
sfprintf(state.mam.out, "%sinit %s %s\n", state.mam.label, mamname(job->target), timefmt(NiL, CURTIME));
652
t = sfstruse(tmp);
653
if (!(job->flags & CO_ALWAYS))
654
{
655
if (state.touch)
656
{
657
if (state.virtualdot)
658
{
659
state.virtualdot = 0;
660
lockstate(1);
661
}
662
if (!(job->target->property & (P_attribute|P_virtual)))
663
{
664
accept(job->target);
665
if ((job->target->property & (P_joint|P_target)) == (P_joint|P_target))
666
for (p = job->target->prereqs->rule->prereqs; p; p = p->next)
667
if (p->rule != job->target)
668
accept(p->rule);
669
}
670
}
671
else if (*t && (!state.silent || state.mam.regress))
672
dumpaction(state.mam.out ? state.mam.out : sfstdout, NiL, t, NiL);
673
done(job, 0, NiL);
674
}
675
else
676
{
677
if (state.virtualdot && !notfile(job->target))
678
{
679
state.virtualdot = 0;
680
lockstate(1);
681
}
682
if (!state.coshell)
683
{
684
#if !O_cloexec
685
if (internal.openfile)
686
fcntl(internal.openfd, F_SETFD, FD_CLOEXEC);
687
#endif
688
sp = sfstropen();
689
sfprintf(sp, "label=%s", idname);
690
expand(sp, " $(" CO_ENV_OPTIONS ")");
691
flags = CO_ANY;
692
if (state.cross)
693
flags |= CO_CROSS;
694
if (state.serialize && state.jobs > 1)
695
flags |= CO_SERIALIZE;
696
if (!(state.coshell = coopen(getval(CO_ENV_SHELL, VAL_PRIMARY), flags, sfstruse(sp))))
697
error(ERROR_SYSTEM|3, "coshell open error");
698
sfstrclose(sp);
699
}
700
if (p = internal.exports->prereqs)
701
{
702
Sfio_t* exp;
703
704
exp = sfstropen();
705
do
706
{
707
if (v = getvar(p->rule->name))
708
{
709
expand(exp, v->value);
710
coexport(state.coshell, p->rule->name, sfstruse(exp));
711
}
712
else if (s = strchr(p->rule->name, '='))
713
{
714
*s = 0;
715
expand(exp, s + 1);
716
coexport(state.coshell, p->rule->name, sfstruse(exp));
717
*s = '=';
718
}
719
} while (p = p->next);
720
sfstrclose(exp);
721
#if 0
722
freelist(internal.exports->prereqs);
723
#endif
724
internal.exports->prereqs = 0;
725
}
726
if (job->flags & CO_DATAFILE)
727
{
728
static char* dot;
729
static char* tmp;
730
731
if (job->target->property & P_read)
732
{
733
if (!dot)
734
dot = pathtemp(NiL, 0, null, idname, NiL);
735
state.tmpfile = dot;
736
}
737
else
738
{
739
if (!tmp)
740
tmp = pathtemp(NiL, 0, NiL, idname, NiL);
741
state.tmpfile = tmp;
742
}
743
}
744
#if !_HUH_1992_02_29 /* i386 and ftx m68k dump without this statement -- help */
745
message((-99, "execute: %s: t=0x%08x &t=0x%08x", job->target->name, t, &t));
746
#endif
747
if (state.mam.out)
748
dumpaction(state.mam.out, MAMNAME(job->target), t, NiL);
749
if (r = getrule(external.makerun))
750
maketop(r, P_dontcare|P_foreground, NiL);
751
#if _WINIX
752
if (internal.openfile)
753
#else
754
if ((state.test & 0x00020000) && internal.openfile)
755
#endif
756
{
757
internal.openfile = 0;
758
close(internal.openfd);
759
}
760
if (!(job->cojob = coexec(state.coshell, t, job->flags, state.tmpfile, NiL, sfstruse(att))))
761
error(3, "%s: cannot send action to coshell", job->target->name);
762
job->cojob->local = (void*)job;
763
764
/*
765
* grab semaphores
766
*/
767
768
if (job->target->dynamic & D_hassemaphore)
769
{
770
job->flags |= CO_SEMAPHORES;
771
for (p = job->prereqs; p; p = p->next)
772
if (p->rule->semaphore && --p->rule->semaphore == 1)
773
p->rule->status = MAKING;
774
}
775
776
/*
777
* check status and sync
778
*/
779
780
if (job->target->dynamic & D_hasafter)
781
save(job);
782
if (job->flags & (CO_DATAFILE|CO_FOREGROUND))
783
{
784
complete(job->target, NiL, NiL, 0);
785
if (job->target->property & (P_functional|P_read))
786
{
787
if (sp = sfopen(NiL, state.tmpfile, "r"))
788
{
789
remove(state.tmpfile);
790
if (job->target->property & P_read)
791
parse(sp, NiL, job->target->name, NiL);
792
else
793
{
794
char* e;
795
796
sfmove(sp, tmp, SF_UNBOUND, -1);
797
t = sfstrbase(tmp);
798
e = sfstrseek(tmp, 0, SEEK_CUR);
799
while (e > t && *(e - 1) == '\n')
800
e--;
801
sfstrseek(tmp, e - t, SEEK_SET);
802
setvar(job->target->name, sfstruse(tmp), 0);
803
}
804
sfclose(sp);
805
}
806
else
807
error(2, "%s: cannot read temporary data output file %s", job->target->name, state.tmpfile);
808
state.tmpfile = 0;
809
}
810
}
811
}
812
sfstrclose(att);
813
sfstrclose(tmp);
814
}
815
816
/*
817
* check if job for r with completed prereqs p can be cancelled
818
*/
819
820
static int
821
cancel(register Rule_t* r, register List_t* p)
822
{
823
register Rule_t* a;
824
register Rule_t* s;
825
register Rule_t* t;
826
827
if (r->must)
828
{
829
s = staterule(RULE, r, NiL, 0);
830
for (; p; p = p->next)
831
{
832
if ((a = p->rule)->dynamic & D_alias)
833
a = makerule(a->name);
834
if ((a->dynamic & D_same) && (!s || !(t = staterule(RULE, a, NiL, 0)) || s->event >= t->event))
835
r->must--;
836
}
837
if (!r->must)
838
{
839
if (error_info.trace || state.explain)
840
error(state.explain ? 0 : -1, "cancelling %s action", r->name);
841
r->status = EXISTS;
842
r->dynamic |= D_same;
843
r->dynamic &= ~D_triggered;
844
if (t = staterule(PREREQS, r, NiL, 0))
845
t->time = CURTIME;
846
return 1;
847
}
848
}
849
return 0;
850
}
851
852
/*
853
* all actions on behalf of job are done
854
* with the possible exception of after prereqs
855
* clear!=0 to clear job queue
856
* 0 returned if further blocking required
857
*/
858
859
static int
860
done(register Joblist_t* job, int clear, Cojob_t* cojob)
861
{
862
register List_t* p;
863
register Rule_t* a;
864
Time_t tm;
865
int n;
866
int semaphore;
867
Rule_t* jammed;
868
Rule_t* waiting;
869
870
if (clear && jobs.triggered && (((a = jobs.triggered)->property & P_state) || (a = staterule(RULE, a, NiL, 0))) && (a->property & P_force))
871
{
872
a->time = 0;
873
state.savestate = 1;
874
}
875
another:
876
jobstatus();
877
if (job->status == INTERMEDIATE)
878
state.intermediate--;
879
else if (!clear && job->status == RUNNING && (job->target->dynamic & D_hasafter) && hasafter(job->target, (job->flags & CO_ERRORS) ? P_failure : P_after))
880
{
881
job->status = BEFORE;
882
for (p = job->target->prereqs; p; p = p->next)
883
{
884
if ((a = p->rule)->dynamic & D_alias)
885
a = makerule(a->name);
886
if (a->status == MAKING && !a->semaphore || !(a->property & P_make) && a->status == UPDATE)
887
return 0;
888
}
889
after:
890
#if __GNUC__ == 3 && ( sparc || _sparc || __sparc ) && !_HUH_2008_10_25 /* gcc code generation bug -- first hit on solaris -- not sure if for all gcc 3.* */
891
#ifndef __GNUC_MINOR__
892
#define __GNUC_MINOR__ 0
893
#endif
894
#ifndef __GNUC_PATCHLEVEL__
895
#define __GNUC_PATCHLEVEL__ 1
896
#endif
897
if (!jobs.firstjob)
898
{
899
static int warned = 0;
900
901
if (!warned)
902
{
903
warned = 1;
904
error(state.mam.regress || state.regress ? -1 : 1, "command.c:%d: gcc %d.%d.%d code generation bug workaround -- pass this on to the vendor", __LINE__, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
905
}
906
return !clear;
907
}
908
#endif
909
push(job);
910
n = makeafter(job->target, (job->flags & CO_ERRORS) ? P_failure : P_after);
911
pop(job);
912
if (n)
913
job->flags |= CO_ERRORS;
914
else
915
{
916
job->flags &= ~CO_ERRORS;
917
for (p = job->prereqs; p; p = p->next)
918
{
919
if ((a = p->rule)->dynamic & D_alias)
920
a = makerule(a->name);
921
if (!a->semaphore && a->status == MAKING)
922
{
923
job->status = AFTER;
924
return !state.coshell || cojobs(state.coshell) < state.jobs;
925
}
926
}
927
}
928
}
929
930
/*
931
* update rule times and status
932
*/
933
934
if (job->target->status != TOUCH)
935
job->target->status = (job->flags & CO_ERRORS) ? ((job->target->property & P_dontcare) ? IGNORE : FAILED) : EXISTS;
936
tm = statetime(job->target, 0);
937
if (n = cojob && (state.mam.dynamic || state.mam.regress) && state.user && !(job->target->property & (P_after|P_before|P_dontcare|P_make|P_state|P_virtual)))
938
sfprintf(state.mam.out, "%scode %s %d %s %s%s%s\n", state.mam.label, (job->target != state.frame->target || (job->target->property & P_after)) ? mamname(job->target) : "-", EXIT_CODE(cojob->status), timefmt(NiL, tm), timefmt(NiL, CURTIME), (job->target->dynamic & D_same) ? " same" : null, cojob->status && (job->flags & CO_IGNORE) ? " ignore" : null);
939
if ((job->target->property & (P_joint|P_target)) == (P_joint|P_target))
940
for (p = job->target->prereqs->rule->prereqs; p; p = p->next)
941
if (p->rule != job->target)
942
{
943
if (p->rule->status != TOUCH)
944
p->rule->status = (job->flags & CO_ERRORS) ? ((p->rule->property & P_dontcare) ? IGNORE : FAILED) : EXISTS;
945
tm = statetime(p->rule, 0);
946
if (n)
947
sfprintf(state.mam.out, "%scode %s %d %s%s%s\n", state.mam.label, mamname(p->rule), EXIT_CODE(cojob->status), timefmt(NiL, tm), (p->rule->dynamic & D_same) ? " same" : null, cojob->status && (job->flags & CO_IGNORE) ? " ignore" : null);
948
}
949
950
/*
951
* update the job list
952
*/
953
954
discard(job);
955
again:
956
jammed = 0;
957
if (job = jobs.firstjob)
958
for (;;)
959
{
960
switch (job->status)
961
{
962
case AFTER:
963
case BEFORE:
964
case BLOCKED:
965
case READY:
966
n = READY;
967
semaphore = 1;
968
waiting = 0;
969
for (p = job->prereqs; p; p = p->next)
970
{
971
if ((a = p->rule)->dynamic & D_alias)
972
a = makerule(a->name);
973
if ((a->property & P_after) && job->status != BEFORE && job->status != AFTER)
974
continue;
975
switch (a->status)
976
{
977
case FAILED:
978
if (a->property & P_repeat)
979
continue;
980
job->flags |= CO_ERRORS;
981
goto another;
982
case MAKING:
983
if (!jammed && (a->mark & M_waiting) && !(a->property & P_archive))
984
{
985
waiting = a;
986
continue;
987
}
988
waiting = 0;
989
n = BLOCKED;
990
if (!a->semaphore)
991
semaphore = 0;
992
break;
993
default:
994
continue;
995
}
996
break;
997
}
998
if (waiting)
999
jammed = waiting;
1000
else if (!clear && job->status == AFTER)
1001
{
1002
if (n == READY || semaphore)
1003
goto another;
1004
}
1005
else if (!clear && job->status == BEFORE)
1006
{
1007
if (n == READY || semaphore)
1008
goto after;
1009
}
1010
else if ((job->status = n) == READY)
1011
{
1012
unjam:
1013
if (clear || cancel(job->target, job->prereqs))
1014
goto another;
1015
if ((job->target->dynamic & D_intermediate) && job->target->must == 1)
1016
{
1017
job->status = INTERMEDIATE;
1018
jobs.intermediate++;
1019
}
1020
else if ((job->target->dynamic & (D_hasbefore|D_triggered)) == (D_hasbefore|D_triggered))
1021
{
1022
push(job);
1023
n = makebefore(job->target);
1024
pop(job);
1025
if (n)
1026
{
1027
job->flags |= CO_ERRORS;
1028
goto another;
1029
}
1030
}
1031
else if (!state.coshell || cojobs(state.coshell) < state.jobs)
1032
{
1033
execute(job);
1034
goto again;
1035
}
1036
}
1037
break;
1038
case RUNNING:
1039
if (clear && job->cojob && (job->cojob->flags & CO_SERVICE))
1040
{
1041
job->status = FAILED;
1042
job->flags |= CO_ERRORS;
1043
cokill(state.coshell, job->cojob, 0);
1044
}
1045
break;
1046
}
1047
if (!(job = job->next))
1048
{
1049
/*
1050
* jammed is the first discovered member
1051
* of a possible deadlock and we arbitrarily
1052
* break it here
1053
*/
1054
1055
if (jammed)
1056
{
1057
if (error_info.trace || state.explain)
1058
error(state.explain ? 0 : -1, "breaking possible job deadlock at %s", jammed->name);
1059
for (job = jobs.firstjob; job; job = job->next)
1060
#if __GNUC__ >= 4 && !_HUH_2006_01_11 /* gcc code generation bug -- first hit on macos -- not sure if for all gcc 4.* */
1061
#ifndef __GNUC_MINOR__
1062
#define __GNUC_MINOR__ 0
1063
#endif
1064
#ifndef __GNUC_PATCHLEVEL__
1065
#define __GNUC_PATCHLEVEL__ 1
1066
#endif
1067
1068
if (!job)
1069
{
1070
static int warned = 0;
1071
1072
if (!warned)
1073
{
1074
warned = 1;
1075
error(state.mam.regress || state.regress ? -1 : 1, "command.c:%d: gcc %d.%d.%d code generation bug workaround -- pass this on to the vendor", __LINE__, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
1076
}
1077
break;
1078
}
1079
else
1080
#endif
1081
if (job->target == jammed)
1082
{
1083
if (job->status == AFTER)
1084
goto another;
1085
if (job->status != RUNNING)
1086
{
1087
jammed = 0;
1088
job->status = READY;
1089
state.jobs++;
1090
goto unjam;
1091
}
1092
}
1093
}
1094
break;
1095
}
1096
}
1097
return !clear;
1098
}
1099
1100
/*
1101
* block until one job completes
1102
* update the job list
1103
* clear job list on any unexpected action error
1104
*/
1105
1106
int
1107
block(int check)
1108
{
1109
register Cojob_t* cojob;
1110
register Joblist_t* job;
1111
Rule_t* r;
1112
int n;
1113
int clear = 0;
1114
int resume = 0;
1115
1116
if (!state.coshell || !copending(state.coshell))
1117
{
1118
if (jobs.intermediate)
1119
{
1120
/*
1121
* mark the jobs that must be generated
1122
*/
1123
1124
n = 0;
1125
for (job = jobs.firstjob; job; job = job->next)
1126
if (job->target->must > ((unsigned int)(job->target->dynamic & D_intermediate) != 0))
1127
{
1128
n = 1;
1129
break;
1130
}
1131
if (n)
1132
{
1133
/*
1134
* some intermediates must be generated
1135
*/
1136
1137
1138
error(2, "some intermediates must be generated");
1139
}
1140
else
1141
{
1142
/*
1143
* accept missing intermediates
1144
*/
1145
1146
while (job = jobs.firstjob)
1147
{
1148
if (error_info.trace || state.explain)
1149
error(state.explain ? 0 : -1, "cancelling %s action -- %s", job->target->name, job->status == INTERMEDIATE ? "intermediate not needed" : "missing intermediates accepted");
1150
job->target->status = EXISTS;
1151
discard(job);
1152
}
1153
jobs.intermediate = 0;
1154
return 1;
1155
}
1156
}
1157
return 0;
1158
}
1159
for (;;)
1160
{
1161
state.waiting = 1;
1162
if ((cojob = cowait(state.coshell, check ? (Cojob_t*)state.coshell : (Cojob_t*)0, -1)) && (job = (Joblist_t*)cojob->local))
1163
job->cojob = 0;
1164
if (trap())
1165
{
1166
if (state.interpreter)
1167
clear = resume = 1;
1168
if (!cojob)
1169
continue;
1170
}
1171
state.waiting = 0;
1172
if (!cojob)
1173
{
1174
if (check)
1175
return 0;
1176
break;
1177
}
1178
if (r = getrule(external.jobdone))
1179
{
1180
if (!jobs.tmp)
1181
jobs.tmp = sfstropen();
1182
sfprintf(jobs.tmp, "%s %d %s %s", job->target->name, cojob->status, fmtelapsed(cojob->user, CO_QUANT), fmtelapsed(cojob->sys, CO_QUANT));
1183
call(r, sfstruse(jobs.tmp));
1184
}
1185
if (cojob->status)
1186
{
1187
if (n = !EXITED_TERM(cojob->status) || EXIT_CODE(cojob->status))
1188
{
1189
if ((job->target->dynamic & D_hasafter) && hasafter(job->target, P_failure))
1190
n = 0;
1191
error(n ? 2 : state.explain ? 0 : -1, "%s%s code %d making %s%s", n ? "*** " : null, ERROR_translate(NiL, NiL, NiL, EXITED_TERM(cojob->status) ? "termination" : "exit"), EXIT_CODE(cojob->status), job->target->name, (job->flags & CO_IGNORE) ? ERROR_translate(NiL, NiL, NiL, " ignored") : null);
1192
}
1193
if (!(job->flags & CO_IGNORE))
1194
{
1195
job->flags |= CO_ERRORS;
1196
if (state.keepgoing || !n)
1197
{
1198
if (n)
1199
state.errors++;
1200
job->flags |= CO_KEEPGOING;
1201
}
1202
}
1203
if (state.interrupt || !(job->flags & (CO_IGNORE|CO_KEEPGOING)))
1204
clear = 1;
1205
}
1206
message((-3, "job: %s: interrupt=%d clear=%d status=%d flags=%08x", job->target->name, state.interrupt, clear, cojob->status, job->flags));
1207
1208
/*
1209
* job is done
1210
*/
1211
1212
if (done(job, clear, cojob))
1213
return 1;
1214
}
1215
if (resume)
1216
longjmp(state.resume.label, 1);
1217
if (!state.finish)
1218
{
1219
if (!copending(state.coshell))
1220
{
1221
if (clear)
1222
finish(1);
1223
}
1224
else if (!state.interrupt)
1225
error(3, "lost contact with coshell");
1226
}
1227
return 0;
1228
}
1229
1230
/*
1231
* wait until all actions for r and/or the rules in list p have completed
1232
* r==0 and p==0 waits for all actions
1233
* the number of FAILED actions is returned
1234
*/
1235
1236
int
1237
complete(register Rule_t* r, register List_t* p, Time_t* tm, Flags_t flags)
1238
{
1239
register int errors = 0;
1240
int check = 0;
1241
int recent;
1242
List_t tmp;
1243
List_t* q;
1244
Time_t tprereqs;
1245
1246
if (r)
1247
{
1248
tmp.next = p;
1249
p = &tmp;
1250
p->rule = r;
1251
}
1252
else
1253
{
1254
if (p && streq(p->rule->name, "-"))
1255
{
1256
p = p->next;
1257
check = 1;
1258
}
1259
if (!p)
1260
{
1261
while (block(check));
1262
return 0;
1263
}
1264
}
1265
for (tprereqs = 0; p; p = p->next)
1266
{
1267
if ((r = p->rule)->dynamic & D_alias)
1268
r = makerule(r->name);
1269
if (recent = r->status == MAKING)
1270
{
1271
message((-1, "waiting for %s%s", r->semaphore ? "semaphore " : null, r->name));
1272
r->mark |= M_waiting;
1273
do
1274
{
1275
if (!block(check))
1276
{
1277
if (recent = r->status == MAKING)
1278
{
1279
r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
1280
error(1, "%s did not complete", r->name);
1281
}
1282
break;
1283
}
1284
} while (r->status == MAKING);
1285
r->mark &= ~M_waiting;
1286
}
1287
if (r->status == UPDATE && !(r->property & P_make) && !(flags & P_implicit))
1288
r->status = EXISTS;
1289
if (recent && (r->property & (P_joint|P_target)) == (P_joint|P_target))
1290
{
1291
register Rule_t* x;
1292
register Rule_t* s;
1293
1294
s = staterule(RULE, r, NiL, 1);
1295
for (q = r->prereqs->rule->prereqs; q; q = q->next)
1296
if ((x = q->rule) != r)
1297
{
1298
if (x->status != r->status)
1299
{
1300
x->status = r->status;
1301
x->time = r->time;
1302
}
1303
if (s && (x = staterule(RULE, x, NiL, 1)))
1304
{
1305
x->dynamic |= D_built;
1306
x->action = s->action;
1307
x->prereqs = s->prereqs;
1308
}
1309
}
1310
}
1311
if (r->status == FAILED)
1312
errors++;
1313
if (!(r->property & (P_after|P_before|P_ignore)) && r->time > tprereqs)
1314
tprereqs = r->time;
1315
}
1316
if (tm)
1317
*tm = tprereqs;
1318
return errors;
1319
}
1320
1321
/*
1322
* terminate all jobs
1323
*/
1324
1325
void
1326
terminate(void)
1327
{
1328
if (state.coshell && cokill(state.coshell, NiL, SIGTERM))
1329
error(2, "coshell job kill error");
1330
}
1331
1332
/*
1333
* complete all jobs and drop the coshell
1334
*/
1335
1336
void
1337
drop(void)
1338
{
1339
if (state.coshell)
1340
{
1341
while (block(0));
1342
message((-1, "jobs %d user %s sys %s", state.coshell->total, fmtelapsed(state.coshell->user, CO_QUANT), fmtelapsed(state.coshell->sys, CO_QUANT)));
1343
coclose(state.coshell);
1344
state.coshell = 0;
1345
}
1346
}
1347
1348
/*
1349
* trigger action to build r
1350
* a contains the action attributes
1351
*
1352
* NOTE: the prereqs cons() may not be freed
1353
*/
1354
1355
void
1356
trigger(register Rule_t* r, Rule_t* a, char* action, Flags_t flags)
1357
{
1358
register Joblist_t* job;
1359
register List_t* p;
1360
List_t* prereqs;
1361
int n;
1362
1363
/*
1364
* update flags
1365
*/
1366
1367
if (!a)
1368
a = r;
1369
if (state.exec && !state.touch || (a->property & P_always) && (!state.never || (flags & CO_URGENT)))
1370
flags |= CO_ALWAYS;
1371
if ((a->property | r->property) & P_local)
1372
flags |= CO_LOCAL;
1373
if (!state.jobs || (r->property & P_foreground) || (r->property & (P_make|P_functional)) == P_functional || (r->dynamic & D_hasmake))
1374
flags |= CO_FOREGROUND|CO_LOCAL;
1375
if (state.keepgoing || state.unwind)
1376
flags |= CO_KEEPGOING;
1377
if (state.silent)
1378
flags |= CO_SILENT;
1379
if (state.ignore)
1380
flags |= CO_IGNORE;
1381
if (r->property & (P_functional|P_read))
1382
flags |= CO_DATAFILE;
1383
if (action)
1384
{
1385
message((-1, "triggering %s action%s%s", r->name, r == a ? null : " using ", r == a ? null : a->name));
1386
if (state.exec)
1387
jobs.triggered = r;
1388
r->dynamic |= D_triggered;
1389
if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
1390
for (p = r->prereqs->rule->prereqs; p; p = p->next)
1391
p->rule->dynamic |= D_triggered;
1392
if (!*action)
1393
action = 0;
1394
}
1395
if (state.coshell && (action && !(r->property & P_make) || (flags & CO_FOREGROUND)))
1396
{
1397
/*
1398
* the make thread blocks when too many jobs are outstanding
1399
*/
1400
1401
n = (flags & CO_FOREGROUND) ? 0 : (state.jobs - 1);
1402
while ((cozombie(state.coshell) || cojobs(state.coshell) > n) && block(0));
1403
}
1404
prereqs = r->prereqs;
1405
if (r->active && r->active->primary)
1406
{
1407
prereqs = cons(getrule(r->active->primary), prereqs);
1408
flags |= CO_PRIMARY;
1409
}
1410
if (r->property & P_make)
1411
{
1412
if (r->property & P_local)
1413
{
1414
r->status = EXISTS;
1415
return;
1416
}
1417
1418
/*
1419
* make actions are done immediately, bypassing the job queue
1420
*/
1421
1422
if (prereqs && complete(NiL, prereqs, NiL, 0))
1423
r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
1424
else
1425
{
1426
if (action && cancel(r, prereqs))
1427
r->status = EXISTS;
1428
else if ((r->dynamic & (D_hasbefore|D_triggered)) == (D_hasbefore|D_triggered) && (makebefore(r) || complete(NiL, prereqs, NiL, 0)))
1429
r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
1430
else
1431
{
1432
if (r->property & P_functional)
1433
setvar(r->name, null, 0);
1434
if (action)
1435
switch (parse(NiL, action, r->name, NiL))
1436
{
1437
case EXISTS:
1438
if (!(r->property & (P_state|P_virtual)))
1439
statetime(r, 0);
1440
break;
1441
case FAILED:
1442
r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
1443
break;
1444
case TOUCH:
1445
r->time = internal.internal->time;
1446
break;
1447
case UPDATE:
1448
if ((r->property & (P_state|P_virtual)) != (P_state|P_virtual))
1449
r->time = CURTIME;
1450
break;
1451
}
1452
if (r->status == UPDATE)
1453
r->status = EXISTS;
1454
}
1455
}
1456
if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
1457
for (p = r->prereqs->rule->prereqs; p; p = p->next)
1458
if (p->rule != r)
1459
{
1460
p->rule->status = r->status;
1461
p->rule->time = r->time;
1462
}
1463
if ((r->dynamic & (D_hasafter|D_triggered)) == (D_hasafter|D_triggered))
1464
{
1465
if (r->status == FAILED)
1466
{
1467
if (hasafter(r, P_failure) && !makeafter(r, P_failure) && !complete(NiL, prereqs, NiL, 0))
1468
r->status = EXISTS;
1469
}
1470
else if (hasafter(r, P_after) && (makeafter(r, P_after) || complete(NiL, prereqs, NiL, 0)))
1471
r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
1472
}
1473
}
1474
else
1475
{
1476
/*
1477
* only one repeat action at a time
1478
*/
1479
1480
if ((r->property & P_repeat) && (r->property & (P_before|P_after)) && !(r->dynamic & D_hassemaphore))
1481
{
1482
a = catrule(internal.semaphore->name, ".", r->name, 1);
1483
a->semaphore = 2;
1484
r->prereqs = append(r->prereqs, cons(a, NiL));
1485
r->dynamic |= D_hassemaphore;
1486
}
1487
1488
/*
1489
* check if any prerequisites are blocking execution
1490
* FAILED prerequisites cause the target to fail too
1491
*/
1492
1493
n = READY;
1494
for (;;)
1495
{
1496
for (p = prereqs; p; p = p->next)
1497
{
1498
if ((a = p->rule)->dynamic & D_alias)
1499
a = makerule(a->name);
1500
if (a->property & P_after)
1501
continue;
1502
switch (a->status)
1503
{
1504
case FAILED:
1505
if (a->property & P_repeat)
1506
continue;
1507
r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
1508
if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
1509
for (p = r->prereqs->rule->prereqs; p; p = p->next)
1510
if (p->rule != r)
1511
p->rule->status = (p->rule->property & P_dontcare) ? IGNORE : FAILED;
1512
return;
1513
case MAKING:
1514
if (a->active)
1515
error(1, "%s: prerequisite %s is active", r->name, a->name);
1516
else
1517
n = BLOCKED;
1518
break;
1519
}
1520
}
1521
if (n != READY)
1522
break;
1523
if (action)
1524
{
1525
if (cancel(r, prereqs))
1526
return;
1527
if ((r->dynamic & D_intermediate) && r->must == 1)
1528
{
1529
n = INTERMEDIATE;
1530
jobs.intermediate++;
1531
break;
1532
}
1533
}
1534
if ((r->dynamic & (D_hasbefore|D_triggered)) != (D_hasbefore|D_triggered))
1535
break;
1536
if (makebefore(r))
1537
{
1538
r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
1539
if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
1540
for (p = r->prereqs->rule->prereqs; p; p = p->next)
1541
if (p->rule != r)
1542
p->rule->status = (p->rule->property & P_dontcare) ? IGNORE : FAILED;
1543
return;
1544
}
1545
}
1546
if (action || n != READY)
1547
{
1548
/*
1549
* allocate a job cell and add to job list
1550
* the first READY job from the top is executed next
1551
*/
1552
1553
if (job = jobs.freejob)
1554
jobs.freejob = jobs.freejob->next;
1555
else
1556
job = newof(0, Joblist_t, 1, 0);
1557
if (flags & CO_URGENT)
1558
{
1559
job->prev = 0;
1560
if (job->next = jobs.firstjob)
1561
jobs.firstjob->prev = job;
1562
else
1563
jobs.lastjob = job;
1564
jobs.firstjob = job;
1565
}
1566
else
1567
{
1568
job->next = 0;
1569
if (job->prev = jobs.lastjob)
1570
jobs.lastjob->next = job;
1571
else
1572
jobs.firstjob = job;
1573
jobs.lastjob = job;
1574
}
1575
1576
/*
1577
* fill in the info
1578
*/
1579
1580
job->target = r;
1581
job->prereqs = prereqs;
1582
job->status = n;
1583
job->flags = flags;
1584
job->action = action;
1585
r->status = MAKING;
1586
if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
1587
for (p = r->prereqs->rule->prereqs; p; p = p->next)
1588
if (p->rule != r)
1589
p->rule->status = r->status;
1590
if (n == READY)
1591
{
1592
execute(job);
1593
if (r->dynamic & D_hasafter)
1594
save(job);
1595
}
1596
else
1597
save(job);
1598
jobstatus();
1599
}
1600
else
1601
{
1602
if (r->status == UPDATE)
1603
r->status = EXISTS;
1604
if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
1605
for (p = r->prereqs->rule->prereqs; p; p = p->next)
1606
if (p->rule->status == UPDATE)
1607
p->rule->status = EXISTS;
1608
if ((r->dynamic & (D_hasafter|D_triggered)) == (D_hasafter|D_triggered))
1609
{
1610
if (r->status == FAILED)
1611
{
1612
if (hasafter(r, P_failure) && !makeafter(r, P_failure) && !complete(NiL, prereqs, NiL, 0))
1613
r->status = EXISTS;
1614
}
1615
else if (hasafter(r, P_after) && (makeafter(r, P_after) || complete(NiL, prereqs, NiL, 0)))
1616
r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
1617
if (r->status == EXISTS)
1618
{
1619
char* t;
1620
Sfio_t* tmp;
1621
1622
tmp = sfstropen();
1623
edit(tmp, r->name, KEEP, DELETE, DELETE);
1624
if (*(t = sfstruse(tmp)))
1625
newfile(r, t, r->time);
1626
sfstrclose(tmp);
1627
}
1628
}
1629
}
1630
if (r->dynamic & D_triggered)
1631
{
1632
r->time = CURTIME;
1633
if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
1634
for (p = r->prereqs->rule->prereqs; p; p = p->next)
1635
p->rule->time = r->time;
1636
}
1637
}
1638
}
1639
1640
/*
1641
* resolve any cached info on file opened on fd
1642
*/
1643
1644
int
1645
resolve(char* file, int fd, int mode)
1646
{
1647
return state.coshell ? cosync(state.coshell, file, fd, mode) : 0;
1648
}
1649
1650
#if DEBUG
1651
/*
1652
* dump the job table
1653
*/
1654
1655
void
1656
dumpjobs(int level, int op)
1657
{
1658
register Joblist_t* job;
1659
register List_t* p;
1660
register Rule_t* a;
1661
int indent;
1662
int line;
1663
1664
if (state.coshell && error_info.trace <= level)
1665
{
1666
indent = error_info.indent;
1667
error_info.indent = 0;
1668
line = error_info.line;
1669
error_info.line = 0;
1670
switch (op)
1671
{
1672
case JOB_blocked:
1673
for (job = jobs.firstjob; job; job = job->next)
1674
if (job->status == BLOCKED)
1675
{
1676
sfprintf(sfstderr, "%8s %s :", statusname[job->status & STATUS], job->target->name);
1677
for (p = job->prereqs; p; p = p->next)
1678
{
1679
if ((a = p->rule)->dynamic & D_alias)
1680
a = makerule(a->name);
1681
if ((a->property & P_after) && job->status != BEFORE && job->status != AFTER)
1682
continue;
1683
if (a->status == MAKING)
1684
sfprintf(sfstderr, " %s", a->name);
1685
}
1686
sfprintf(sfstderr, "\n");
1687
}
1688
else if (job->status == READY)
1689
sfprintf(sfstderr, "%8s %s\n", statusname[job->status & STATUS], job->target->name);
1690
break;
1691
case JOB_status:
1692
sfprintf(sfstderr, "JOB STATUS TARGET tot=%d pend=%d done=%d\n", state.coshell->total, copending(state.coshell), cozombie(state.coshell));
1693
for (job = jobs.firstjob; job; job = job->next)
1694
{
1695
if (job->cojob)
1696
sfsprintf(tmpname, MAXNAME, "[%d]%s", job->cojob->id, job->cojob->id > 99 ? null : job->cojob->id > 9 ? " " : " ");
1697
else
1698
sfsprintf(tmpname, MAXNAME, "[-] ");
1699
sfprintf(sfstderr, "%s %-13s%s\t%s%s%s\n"
1700
, tmpname
1701
, job->status == RUNNING && !job->cojob ? "DONE" : statusname[job->status & STATUS]
1702
, job->target->name
1703
, (job->target->must > ((unsigned int)(job->target->dynamic & D_intermediate) != 0)) ? " [must]" : null
1704
, job->context ? null : " [popped]"
1705
, (job->target->mark & M_waiting) ? " [waiting]" : null
1706
);
1707
}
1708
break;
1709
default:
1710
error(1, "%d: unknown op index", op);
1711
break;
1712
}
1713
error_info.indent = indent;
1714
error_info.line = line;
1715
}
1716
}
1717
#endif
1718
1719