1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1990-2011 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 <gsf@research.att.com> * 18 * * 19 ***********************************************************************/ 20 #pragma prototyped 21 22 /* 23 * mamake -- MAM make 24 * 25 * coded for portability 26 */ 27 28 static char id[] = "\n@(#)$Id: mamake (AT&T Research) 2011-08-31 $\0\n"; 29 30 #if _PACKAGE_ast 31 32 #include <ast.h> 33 #include <error.h> 34 35 static const char usage[] = 36 "[-?\n@(#)$Id: mamake (AT&T Research) 2011-08-31 $\n]" 37 USAGE_LICENSE 38 "[+NAME?mamake - make abstract machine make]" 39 "[+DESCRIPTION?\bmamake\b reads \amake abstract machine\a target and" 40 " prerequisite file descriptions from a mamfile (see \b-f\b) and executes" 41 " actions to update targets that are older than their prerequisites." 42 " Mamfiles are generated by the \b--mam\b option of \bnmake\b(1) and" 43 " \bgmake\b(1) and are portable to environments that only have" 44 " \bsh\b(1) and \bcc\b(1).]" 45 "[+?In practice \bmamake\b is used to bootstrap build \bnmake\b(1) and" 46 " \bksh\b(1) in new environments. Mamfiles are used rather than" 47 " old-\bmake\b makefiles because some features are not reliably supported" 48 " across all \bmake\b variants:]{" 49 " [+action execution?Multi-line actions are executed as a" 50 " unit by \b$SHELL\b. There are some shell constructs" 51 " that cannot be expressed in an old-\bmake\b makefile.]" 52 " [+viewpathing?\bVPATH\b is properly interpreted. This allows" 53 " source to be separate from generated files.]" 54 " [+recursion?Ordered subdirectory recursion over unrelated" 55 " makefiles.]" 56 " }" 57 "[+?\bmamprobe\b(1) is called to probe and generate system specific variable" 58 " definitions. The probe information is regenerated when it is older" 59 " than the \bmamprobe\b command.]" 60 "[+?For compatibility with \bnmake\b(1) the \b-K\b option and the" 61 " \brecurse\b and \bcc-*\b command line targets are ignored.]" 62 "[e:?Explain reason for triggering action. Ignored if -F is on.]" 63 "[f:?Read \afile\a instead of the default.]:[file:=Mamfile]" 64 "[i:?Ignore action errors.]" 65 "[k:?Continue after error with sibling prerequisites.]" 66 "[n:?Print actions but do not execute. Recursion actions (see \b-r\b) are still" 67 " executed. Use \b-N\b to disable recursion actions too.]" 68 "[r:?Recursively make leaf directories matching \apattern\a. Only leaf" 69 " directories containing a makefile named \bNmakefile\b, \bnmakefile\b," 70 " \bMakefile\b or \bmakefile\b are considered. The first makefile" 71 " found in each leaf directory is scanned for leaf directory" 72 " prerequisites; the recusion order is determined by a topological sort" 73 " of these prerequisites.]:[pattern]" 74 "[C:?Do all work in \adirectory\a. All messages will mention" 75 " \adirectory\a.]:[directory]" 76 "[D:?Set the debug trace level to \alevel\a. Higher levels produce more" 77 " output.]#[level]" 78 "[F:?Force all targets to be out of date.]" 79 "[K:?Ignored.]" 80 "[N:?Like \b-n\b but recursion actions (see \b-r\b) are also disabled.]" 81 "[V:?Print the program version and exit.]" 82 "[G:debug-symbols?Compile and link with debugging symbol options enabled.]" 83 "[S:strip-symbols?Strip link-time static symbols from executables.]" 84 85 "\n" 86 "\n[ target ... ] [ name=value ... ]\n" 87 "\n" 88 89 "[+SEE ALSO?\bgmake\b(1), \bmake\b(1), \bmamprobe\b(1)," 90 " \bnmake\b(1), \bsh\b(1)]" 91 ; 92 93 #else 94 95 #define elementsof(x) (sizeof(x)/sizeof(x[0])) 96 #define newof(p,t,n,x) ((p)?(t*)realloc((char*)(p),sizeof(t)*(n)+(x)):(t*)calloc(1,sizeof(t)*(n)+(x))) 97 98 #define NiL ((char*)0) 99 100 #endif 101 102 #include <stdio.h> 103 #include <unistd.h> 104 #include <ctype.h> 105 #include <sys/types.h> 106 #include <sys/stat.h> 107 #include <time.h> 108 109 #if !_PACKAGE_ast && defined(__STDC__) 110 #include <stdlib.h> 111 #include <string.h> 112 #endif 113 114 #define delimiter(c) ((c)==' '||(c)=='\t'||(c)=='\n'||(c)==';'||(c)=='('||(c)==')'||(c)=='`'||(c)=='|'||(c)=='&'||(c)=='=') 115 116 #define add(b,c) (((b)->nxt >= (b)->end) ? append(b, "") : NiL, *(b)->nxt++ = (c)) 117 #define get(b) ((b)->nxt-(b)->buf) 118 #define set(b,o) ((b)->nxt=(b)->buf+(o)) 119 #define use(b) (*(b)->nxt=0,(b)->nxt=(b)->buf) 120 121 #define CHUNK 1024 122 #define KEY(a,b,c,d) ((((unsigned long)(a))<<15)|(((unsigned long)(b))<<10)|(((unsigned long)(c))<<5)|(((unsigned long)(d)))) 123 #define NOW ((unsigned long)time((time_t*)0)) 124 #define ROTATE(p,l,r,t) ((t)=(p)->l,(p)->l=(t)->r,(t)->r=(p),(p)=(t)) 125 126 #define RULE_active 0x0001 /* active target */ 127 #define RULE_dontcare 0x0002 /* ok if not found */ 128 #define RULE_error 0x0004 /* not found or not generated */ 129 #define RULE_exists 0x0008 /* target file exists */ 130 #define RULE_generated 0x0010 /* generated target */ 131 #define RULE_ignore 0x0020 /* ignore time */ 132 #define RULE_implicit 0x0040 /* implicit prerequisite */ 133 #define RULE_made 0x0080 /* already made */ 134 #define RULE_virtual 0x0100 /* not a file */ 135 136 #define STREAM_KEEP 0x0001 /* don't fclose() on pop() */ 137 #define STREAM_MUST 0x0002 /* push() file must exist */ 138 #define STREAM_PIPE 0x0004 /* pclose() on pop() */ 139 140 #ifndef S_IXUSR 141 #define S_IXUSR 0100 /* owner execute permission */ 142 #endif 143 #ifndef S_IXGRP 144 #define S_IXGRP 0010 /* group execute permission */ 145 #endif 146 #ifndef S_IXOTH 147 #define S_IXOTH 0001 /* other execute permission */ 148 #endif 149 150 struct Rule_s; 151 152 typedef struct stat Stat_t; 153 typedef FILE Stdio_t; 154 155 typedef struct Buf_s /* buffer stream */ 156 { 157 struct Buf_s* old; /* next dropped buffer */ 158 char* end; /* 1 past end of buffer */ 159 char* nxt; /* next char to add */ 160 char* buf; /* buffer space */ 161 } Buf_t; 162 163 typedef struct Dict_item_s /* dictionary item */ 164 { 165 struct Dict_item_s* left; /* left child */ 166 struct Dict_item_s* right; /* right child */ 167 void* value; /* user defined value */ 168 char name[1];/* 0 terminated name */ 169 } Dict_item_t; 170 171 typedef struct Dict_s /* dictionary handle */ 172 { 173 Dict_item_t* root; /* root item */ 174 } Dict_t; 175 176 typedef struct List_s /* Rule_t list */ 177 { 178 struct List_s* next; /* next in list */ 179 struct Rule_s* rule; /* list item */ 180 } List_t; 181 182 typedef struct Rule_s /* rule item */ 183 { 184 char* name; /* unbound name */ 185 char* path; /* bound path */ 186 List_t* prereqs; /* prerequisites */ 187 struct Rule_s* leaf; /* recursion leaf alias */ 188 int flags; /* RULE_* flags */ 189 int making; /* currently make()ing */ 190 unsigned long time; /* modification time */ 191 } Rule_t; 192 193 typedef struct Stream_s /* input file stream stack */ 194 { 195 Stdio_t* fp; /* read stream */ 196 char* file; /* stream path */ 197 unsigned long line; /* stream line */ 198 int flags; /* stream flags */ 199 } Stream_t; 200 201 typedef struct View_s /* viewpath level */ 202 { 203 struct View_s* next; /* next level in viewpath */ 204 int node; /* viewpath node path length */ 205 char dir[1]; /* viewpath level dir prefix */ 206 } View_t; 207 208 static struct /* program state */ 209 { 210 Buf_t* buf; /* work buffer */ 211 Buf_t* old; /* dropped buffers */ 212 Buf_t* opt; /* option buffer */ 213 214 Dict_t* leaf; /* recursion leaf dictionary */ 215 Dict_t* libs; /* library dictionary */ 216 Dict_t* rules; /* rule dictionary */ 217 Dict_t* vars; /* variable dictionary */ 218 219 View_t* view; /* viewpath levels */ 220 221 char* directory; /* work in this directory */ 222 char* id; /* command name */ 223 char* file; /* first input file */ 224 char* pwd; /* current directory */ 225 char* recurse; /* recursion pattern */ 226 char* shell; /* ${SHELL} */ 227 228 int active; /* targets currently active */ 229 int debug; /* negative of debug level */ 230 int errors; /* some error(s) occurred */ 231 int exec; /* execute actions */ 232 int explain; /* explain actions */ 233 int force; /* all targets out of date */ 234 int ignore; /* ignore command errors */ 235 int indent; /* debug indent */ 236 int keepgoing; /* do siblings on error */ 237 int never; /* never execute */ 238 int peek; /* next line already in input */ 239 int probed; /* probe already done */ 240 int verified; /* don't bother with verify() */ 241 242 Stream_t streams[4]; /* input file stream stack */ 243 Stream_t* sp; /* input stream stack pointer */ 244 245 char input[8*CHUNK]; /* input buffer */ 246 } state; 247 248 static unsigned long make(Rule_t*); 249 250 static char mamfile[] = "Mamfile"; 251 static char sh[] = "/bin/sh"; 252 253 extern char** environ; 254 255 #if !_PACKAGE_ast 256 257 #if defined(NeXT) || defined(__NeXT) 258 #define getcwd(a,b) getwd(a) 259 #endif 260 261 /* 262 * emit usage message and exit 263 */ 264 265 static void 266 usage() 267 { 268 fprintf(stderr, "Usage: %s [-iknFKNV] [-f mamfile] [-r pattern] [-C directory] [-D level] [target ...] [name=value ...]\n", state.id); 269 exit(2); 270 } 271 272 #endif 273 274 /* 275 * output error message identification 276 */ 277 278 static void 279 identify(Stdio_t* sp) 280 { 281 if (state.directory) 282 fprintf(sp, "%s [%s]: ", state.id, state.directory); 283 else 284 fprintf(sp, "%s: ", state.id); 285 } 286 287 /* 288 * emit error message 289 * level: 290 * <0 debug 291 * 0 info 292 * 1 warning 293 * 2 error 294 * >2 exit(level-2) 295 */ 296 297 static void 298 report(int level, char* text, char* item, unsigned long stamp) 299 { 300 int i; 301 302 if (level >= state.debug) 303 { 304 if (level) 305 identify(stderr); 306 if (level < 0) 307 { 308 fprintf(stderr, "debug%d: ", level); 309 for (i = 1; i < state.indent; i++) 310 fprintf(stderr, " "); 311 } 312 else 313 { 314 if (state.sp && state.sp->line) 315 { 316 if (state.sp->file) 317 fprintf(stderr, "%s: ", state.sp->file); 318 fprintf(stderr, "%ld: ", state.sp->line); 319 } 320 if (level == 1) 321 fprintf(stderr, "warning: "); 322 else if (level > 1) 323 state.errors = 1; 324 } 325 if (item) 326 fprintf(stderr, "%s: ", item); 327 fprintf(stderr, "%s", text); 328 if (stamp && state.debug <= -2) 329 fprintf(stderr, " %10lu", stamp); 330 fprintf(stderr, "\n"); 331 if (level > 2) 332 exit(level - 2); 333 } 334 } 335 336 /* 337 * don't know how to make or exit code making 338 */ 339 340 static void 341 dont(Rule_t* r, int code, int keepgoing) 342 { 343 identify(stderr); 344 if (!code) 345 fprintf(stderr, "don't know how to make %s\n", r->name); 346 else 347 { 348 fprintf(stderr, "*** exit code %d making %s%s\n", code, r->name, state.ignore ? " ignored" : ""); 349 unlink(r->name); 350 if (state.ignore) 351 return; 352 } 353 if (!keepgoing) 354 exit(1); 355 state.errors++; 356 r->flags |= RULE_error; 357 } 358 359 /* 360 * local strrchr() 361 */ 362 363 static char* 364 last(register char* s, register int c) 365 { 366 register char* r = 0; 367 368 for (r = 0; *s; s++) 369 if (*s == c) 370 r = s; 371 return r; 372 } 373 374 /* 375 * open a buffer stream 376 */ 377 378 static Buf_t* 379 buffer(void) 380 { 381 register Buf_t* buf; 382 383 if (buf = state.old) 384 state.old = state.old->old; 385 else if (!(buf = newof(0, Buf_t, 1, 0)) || !(buf->buf = newof(0, char, CHUNK, 0))) 386 report(3, "out of space [buffer]", NiL, (unsigned long)0); 387 buf->end = buf->buf + CHUNK; 388 buf->nxt = buf->buf; 389 return buf; 390 } 391 392 /* 393 * close a buffer stream 394 */ 395 396 static void 397 drop(Buf_t* buf) 398 { 399 buf->old = state.old; 400 state.old = buf; 401 } 402 403 /* 404 * append str length n to buffer and return the buffer base 405 */ 406 407 static char* 408 appendn(Buf_t* buf, char* str, int n) 409 { 410 int m; 411 int i; 412 413 if ((n + 1) >= (buf->end - buf->nxt)) 414 { 415 i = buf->nxt - buf->buf; 416 m = (((buf->end - buf->buf) + n + CHUNK + 1) / CHUNK) * CHUNK; 417 if (!(buf->buf = newof(buf->buf, char, m, 0))) 418 report(3, "out of space [buffer resize]", NiL, (unsigned long)0); 419 buf->end = buf->buf + m; 420 buf->nxt = buf->buf + i; 421 } 422 memcpy(buf->nxt, str, n + 1); 423 buf->nxt += n; 424 return buf->buf; 425 } 426 427 /* 428 * append str to buffer and return the buffer base 429 * if str==0 then next pointer reset to base 430 */ 431 432 static char* 433 append(Buf_t* buf, char* str) 434 { 435 if (str) 436 return appendn(buf, str, strlen(str)); 437 buf->nxt = buf->buf; 438 return buf->buf; 439 } 440 441 /* 442 * allocate space for s and return the copy 443 */ 444 445 static char* 446 duplicate(char* s) 447 { 448 char* t; 449 int n; 450 451 n = strlen(s); 452 if (!(t = newof(0, char, n, 1))) 453 report(3, "out of space [duplicate]", s, (unsigned long)0); 454 strcpy(t, s); 455 return t; 456 } 457 458 /* 459 * open a new dictionary 460 */ 461 462 static Dict_t* 463 dictionary(void) 464 { 465 Dict_t* dict; 466 467 if (!(dict = newof(0, Dict_t, 1, 0))) 468 report(3, "out of space [dictionary]", NiL, (unsigned long)0); 469 return dict; 470 } 471 472 /* 473 * return the value for item name in dictionary dict 474 * if value!=0 then name entry value is created if necessary and set 475 * uses top-down splaying (ala Tarjan and Sleator) 476 */ 477 478 static void* 479 search(register Dict_t* dict, char* name, void* value) 480 { 481 register int cmp; 482 register Dict_item_t* root; 483 register Dict_item_t* t; 484 register Dict_item_t* left; 485 register Dict_item_t* right; 486 register Dict_item_t* lroot; 487 register Dict_item_t* rroot; 488 489 root = dict->root; 490 left = right = lroot = rroot = 0; 491 while (root) 492 { 493 if (!(cmp = strcmp(name, root->name))) 494 break; 495 else if (cmp < 0) 496 { 497 if (root->left && (cmp = strcmp(name, root->left->name)) <= 0) 498 { 499 ROTATE(root, left, right, t); 500 if (!cmp) 501 break; 502 } 503 if (right) 504 right->left = root; 505 else 506 rroot = root; 507 right = root; 508 root = root->left; 509 right->left = 0; 510 } 511 else 512 { 513 if (root->right && (cmp = strcmp(name, root->right->name)) >= 0) 514 { 515 ROTATE(root, right, left, t); 516 if (!cmp) 517 break; 518 } 519 if (left) 520 left->right = root; 521 else 522 lroot = root; 523 left = root; 524 root = root->right; 525 left->right = 0; 526 } 527 } 528 if (root) 529 { 530 if (right) 531 right->left = root->right; 532 else 533 rroot = root->right; 534 if (left) 535 left->right = root->left; 536 else 537 lroot = root->left; 538 } 539 else if (value) 540 { 541 if (!(root = newof(0, Dict_item_t, 1, strlen(name)))) 542 report(3, "out of space [dictionary]", name, (unsigned long)0); 543 strcpy(root->name, name); 544 } 545 if (root) 546 { 547 if (value) 548 root->value = value; 549 root->left = lroot; 550 root->right = rroot; 551 dict->root = root; 552 return value ? (void*)root->name : root->value; 553 } 554 if (left) 555 { 556 left->right = rroot; 557 dict->root = lroot; 558 } 559 else if (right) 560 { 561 right->left = lroot; 562 dict->root = rroot; 563 } 564 return 0; 565 } 566 567 /* 568 * low level for walk() 569 */ 570 571 static int 572 apply(Dict_t* dict, Dict_item_t* item, int (*func)(Dict_item_t*, void*), void* handle) 573 { 574 register Dict_item_t* right; 575 576 do 577 { 578 right = item->right; 579 if (item->left && apply(dict, item->left, func, handle)) 580 return -1; 581 if ((*func)(item, handle)) 582 return -1; 583 } while (item = right); 584 return 0; 585 } 586 587 /* 588 * apply func to each dictionary item 589 */ 590 591 static int 592 walk(Dict_t* dict, int (*func)(Dict_item_t*, void*), void* handle) 593 { 594 return dict->root ? apply(dict, dict->root, func, handle) : 0; 595 } 596 597 /* 598 * return a rule pointer for name 599 */ 600 601 static Rule_t* 602 rule(char* name) 603 { 604 Rule_t* r; 605 606 if (!(r = (Rule_t*)search(state.rules, name, NiL))) 607 { 608 if (!(r = newof(0, Rule_t, 1, 0))) 609 report(3, "out of space [rule]", name, (unsigned long)0); 610 r->name = (char*)search(state.rules, name, (void*)r); 611 } 612 return r; 613 } 614 615 /* 616 * prepend p onto rule r prereqs 617 */ 618 619 static void 620 cons(Rule_t* r, Rule_t* p) 621 { 622 register List_t* x; 623 624 for (x = r->prereqs; x && x->rule != p; x = x->next); 625 if (!x) 626 { 627 if (!(x = newof(0, List_t, 1, 0))) 628 report(3, "out of space [list]", r->name, (unsigned long)0); 629 x->rule = p; 630 x->next = r->prereqs; 631 r->prereqs = x; 632 } 633 } 634 635 /* 636 * initialize the viewpath 637 */ 638 639 static void 640 view(void) 641 { 642 register char* s; 643 register char* t; 644 register char* p; 645 register View_t* vp; 646 647 View_t* zp; 648 int c; 649 int n; 650 651 Stat_t st; 652 Stat_t ts; 653 654 char buf[CHUNK]; 655 656 if (stat(".", &st)) 657 report(3, "cannot stat", ".", (unsigned long)0); 658 if ((s = (char*)search(state.vars, "PWD", NiL)) && !stat(s, &ts) && 659 ts.st_dev == st.st_dev && ts.st_ino == st.st_ino) 660 state.pwd = s; 661 if (!state.pwd) 662 { 663 if (!getcwd(buf, sizeof(buf) - 1)) 664 report(3, "cannot determine PWD", NiL, (unsigned long)0); 665 state.pwd = duplicate(buf); 666 search(state.vars, "PWD", state.pwd); 667 } 668 if ((s = (char*)search(state.vars, "VPATH", NiL)) && *s) 669 { 670 zp = 0; 671 for (;;) 672 { 673 for (t = s; *t && *t != ':'; t++); 674 if (c = *t) 675 *t = 0; 676 if (!state.view) 677 { 678 /* 679 * determine the viewpath offset 680 */ 681 682 if (stat(s, &st)) 683 report(3, "cannot stat top view", s, (unsigned long)0); 684 if (stat(state.pwd, &ts)) 685 report(3, "cannot stat", state.pwd, (unsigned long)0); 686 if (ts.st_dev == st.st_dev && ts.st_ino == st.st_ino) 687 p = "."; 688 else 689 { 690 p = state.pwd + strlen(state.pwd); 691 while (p > state.pwd) 692 if (*--p == '/') 693 { 694 if (p == state.pwd) 695 report(3, ". not under VPATH", s, (unsigned long)0); 696 *p = 0; 697 if (stat(state.pwd, &ts)) 698 report(3, "cannot stat", state.pwd, (unsigned long)0); 699 *p = '/'; 700 if (ts.st_dev == st.st_dev && ts.st_ino == st.st_ino) 701 { 702 p++; 703 break; 704 } 705 } 706 if (p <= state.pwd) 707 report(3, "cannot determine viewpath offset", s, (unsigned long)0); 708 } 709 } 710 n = strlen(s); 711 if (!(vp = newof(0, View_t, 1, strlen(p) + n + 1))) 712 report(3, "out of space [view]", s, (unsigned long)0); 713 vp->node = n + 1; 714 strcpy(vp->dir, s); 715 *(vp->dir + n) = '/'; 716 strcpy(vp->dir + n + 1, p); 717 report(-4, vp->dir, "view", (unsigned long)0); 718 if (!state.view) 719 state.view = zp = vp; 720 else 721 zp = zp->next = vp; 722 if (!c) 723 break; 724 *t++ = c; 725 s = t; 726 } 727 } 728 } 729 730 /* 731 * return next '?' or '}' in nested '}' 732 */ 733 734 static char* 735 cond(register char* s) 736 { 737 register int n; 738 739 if (*s == '?') 740 s++; 741 n = 0; 742 for (;;) 743 { 744 switch (*s++) 745 { 746 case 0: 747 break; 748 case '{': 749 n++; 750 continue; 751 case '}': 752 if (!n--) 753 break; 754 continue; 755 case '?': 756 if (!n) 757 break; 758 continue; 759 default: 760 continue; 761 } 762 break; 763 } 764 return s - 1; 765 } 766 767 /* 768 * expand var refs from s into buf 769 */ 770 771 static void 772 substitute(Buf_t* buf, register char* s) 773 { 774 register char* t; 775 register char* v; 776 register char* q; 777 register char* b; 778 register int c; 779 register int n; 780 int a = 0; 781 int i; 782 783 while (c = *s++) 784 { 785 if (c == '$' && *s == '{') 786 { 787 b = s - 1; 788 i = 1; 789 for (n = *(t = ++s) == '-' ? 0 : '-'; (c = *s) && c != '?' && c != '+' && c != n && c != ':' && c != '=' && c != '[' && c != '}'; s++) 790 if (!isalnum(c) && c != '_') 791 i = 0; 792 *s = 0; 793 if (c == '[') 794 { 795 append(buf, b); 796 *s = c; 797 continue; 798 } 799 v = (char*)search(state.vars, t, NiL); 800 if ((c == ':' || c == '=') && (!v || c == ':' && !*v)) 801 { 802 append(buf, b); 803 *s = c; 804 continue; 805 } 806 if (t[0] == 'A' && t[1] == 'R' && t[2] == 0) 807 a = 1; 808 *s = c; 809 if (c && c != '}') 810 { 811 n = 1; 812 for (t = ++s; *s; s++) 813 if (*s == '{') 814 n++; 815 else if (*s == '}' && !--n) 816 break; 817 } 818 switch (c) 819 { 820 case '?': 821 q = cond(t - 1); 822 if (v) 823 { 824 if (((q - t) != 1 || *t != '*') && strncmp(v, t, q - t)) 825 v = 0; 826 } 827 else if (q == t) 828 v = s; 829 t = cond(q); 830 if (v) 831 { 832 if (t > q) 833 { 834 c = *t; 835 *t = 0; 836 substitute(buf, q + 1); 837 *t = c; 838 } 839 } 840 else 841 { 842 q = cond(t); 843 if (q > t) 844 { 845 c = *q; 846 *q = 0; 847 substitute(buf, t + 1); 848 *q = c; 849 } 850 } 851 break; 852 case '+': 853 case '-': 854 if ((v == 0 || *v == 0) == (c == '-')) 855 { 856 c = *s; 857 *s = 0; 858 substitute(buf, t); 859 *s = c; 860 break; 861 } 862 if (c != '-') 863 break; 864 /*FALLTHROUGH*/ 865 case 0: 866 case '=': 867 case '}': 868 if (v) 869 { 870 if (a && t[0] == 'm' && t[1] == 'a' && t[2] == 'm' && t[3] == '_' && t[4] == 'l' && t[5] == 'i' && t[6] == 'b') 871 { 872 for (t = v; *t == ' '; t++); 873 for (; *t && *t != ' '; t++); 874 if (*t) 875 *t = 0; 876 else 877 t = 0; 878 substitute(buf, v); 879 if (t) 880 *t = ' '; 881 } 882 else 883 substitute(buf, v); 884 } 885 else if (i) 886 { 887 c = *s; 888 *s = 0; 889 append(buf, b); 890 *s = c; 891 continue; 892 } 893 break; 894 } 895 if (*s) 896 s++; 897 } 898 else 899 add(buf, c); 900 } 901 } 902 903 /* 904 * expand var refs from s into buf and return buf base 905 */ 906 907 static char* 908 expand(Buf_t* buf, char* s) 909 { 910 substitute(buf, s); 911 return use(buf); 912 } 913 914 /* 915 * stat() with .exe check 916 */ 917 918 static char* 919 status(Buf_t* buf, int off, char* path, struct stat* st) 920 { 921 int r; 922 char* s; 923 Buf_t* tmp; 924 925 if (!stat(path, st)) 926 return path; 927 if (!(tmp = buf)) 928 { 929 tmp = buffer(); 930 off = 0; 931 } 932 if (off) 933 set(tmp, off); 934 else 935 append(tmp, path); 936 append(tmp, ".exe"); 937 s = use(tmp); 938 r = stat(s, st); 939 if (!buf) 940 { 941 drop(tmp); 942 s = path; 943 } 944 if (r) 945 { 946 if (off) 947 s[off] = 0; 948 s = 0; 949 } 950 return s; 951 } 952 953 /* 954 * return path to file 955 */ 956 957 static char* 958 find(Buf_t* buf, char* file, struct stat* st) 959 { 960 char* s; 961 View_t* vp; 962 int node; 963 int c; 964 int o; 965 966 if (s = status(buf, 0, file, st)) 967 { 968 report(-3, s, "find", (unsigned long)0); 969 return s; 970 } 971 if (vp = state.view) 972 { 973 node = 0; 974 if (*file == '/') 975 { 976 do 977 { 978 if (!strncmp(file, vp->dir, vp->node)) 979 { 980 file += vp->node; 981 node = 2; 982 break; 983 } 984 } while (vp = vp->next); 985 } 986 else 987 vp = vp->next; 988 if (vp) 989 do 990 { 991 if (node) 992 { 993 c = vp->dir[vp->node]; 994 vp->dir[vp->node] = 0; 995 append(buf, vp->dir); 996 vp->dir[vp->node] = c; 997 } 998 else 999 { 1000 append(buf, vp->dir); 1001 append(buf, "/"); 1002 } 1003 append(buf, file); 1004 o = get(buf); 1005 s = use(buf); 1006 if (s = status(buf, o, s, st)) 1007 { 1008 report(-3, s, "find", (unsigned long)0); 1009 return s; 1010 } 1011 } while (vp = vp->next); 1012 } 1013 return 0; 1014 } 1015 1016 /* 1017 * bind r to a file and return the modify time 1018 */ 1019 1020 static unsigned long 1021 bind(Rule_t* r) 1022 { 1023 char* s; 1024 Buf_t* buf; 1025 struct stat st; 1026 1027 buf = buffer(); 1028 if (s = find(buf, r->name, &st)) 1029 { 1030 if (s != r->name) 1031 r->path = duplicate(s); 1032 r->time = st.st_mtime; 1033 r->flags |= RULE_exists; 1034 } 1035 drop(buf); 1036 return r->time; 1037 } 1038 1039 /* 1040 * pop the current input file 1041 */ 1042 1043 static int 1044 pop(void) 1045 { 1046 int r; 1047 1048 if (!state.sp) 1049 report(3, "input stack underflow", NiL, (unsigned long)0); 1050 if (!state.sp->fp || (state.sp->flags & STREAM_KEEP)) 1051 r = 0; 1052 else if (state.sp->flags & STREAM_PIPE) 1053 r = pclose(state.sp->fp); 1054 else 1055 r = fclose(state.sp->fp); 1056 if (state.sp == state.streams) 1057 state.sp = 0; 1058 else 1059 state.sp--; 1060 return r; 1061 } 1062 1063 /* 1064 * push file onto the input stack 1065 */ 1066 1067 static int 1068 push(char* file, Stdio_t* fp, int flags) 1069 { 1070 char* path; 1071 Buf_t* buf; 1072 struct stat st; 1073 1074 if (!state.sp) 1075 state.sp = state.streams; 1076 else if (++state.sp >= &state.streams[elementsof(state.streams)]) 1077 report(3, "input stream stack overflow", NiL, (unsigned long)0); 1078 if (state.sp->fp = fp) 1079 state.sp->file = "pipeline"; 1080 else if (flags & STREAM_PIPE) 1081 report(3, "pipe error", file, (unsigned long)0); 1082 else if (!file || !strcmp(file, "-") || !strcmp(file, "/dev/stdin")) 1083 { 1084 flags |= STREAM_KEEP; 1085 state.sp->file = "/dev/stdin"; 1086 state.sp->fp = stdin; 1087 } 1088 else 1089 { 1090 buf = buffer(); 1091 if (path = find(buf, file, &st)) 1092 { 1093 if (!(state.sp->fp = fopen(path, "r"))) 1094 report(3, "cannot read", path, (unsigned long)0); 1095 state.sp->file = duplicate(path); 1096 drop(buf); 1097 } 1098 else 1099 { 1100 drop(buf); 1101 pop(); 1102 if (flags & STREAM_MUST) 1103 report(3, "not found", file, (unsigned long)0); 1104 return 0; 1105 } 1106 } 1107 state.sp->flags = flags; 1108 state.sp->line = 0; 1109 return 1; 1110 } 1111 1112 /* 1113 * return the next input line 1114 */ 1115 1116 static char* 1117 input(void) 1118 { 1119 char* e; 1120 1121 if (!state.sp) 1122 report(3, "no input file stream", NiL, (unsigned long)0); 1123 if (state.peek) 1124 state.peek = 0; 1125 else if (!fgets(state.input, sizeof(state.input), state.sp->fp)) 1126 return 0; 1127 else if (*state.input && *(e = state.input + strlen(state.input) - 1) == '\n') 1128 *e = 0; 1129 state.sp->line++; 1130 return state.input; 1131 } 1132 1133 /* 1134 * pass shell action s to ${SHELL:-/bin/sh} 1135 * the -c wrapper ensures that scripts are run in the selected shell 1136 * even on systems that otherwise demand #! magic (can you say cygwin) 1137 */ 1138 1139 static int 1140 execute(register char* s) 1141 { 1142 register int c; 1143 Buf_t* buf; 1144 1145 if (!state.shell && (!(state.shell = (char*)search(state.vars, "SHELL", NiL)) || !strcmp(state.shell, sh))) 1146 state.shell = sh; 1147 buf = buffer(); 1148 append(buf, state.shell); 1149 append(buf, " -c '"); 1150 while (c = *s++) 1151 { 1152 if (c == '\'') 1153 { 1154 add(buf, c); 1155 for (s--; *s == c; s++) 1156 { 1157 add(buf, '\\'); 1158 add(buf, c); 1159 } 1160 } 1161 add(buf, c); 1162 } 1163 add(buf, '\''); 1164 s = use(buf); 1165 report(-5, s, "exec", (unsigned long)0); 1166 if ((c = system(s)) > 255) 1167 c >>= 8; 1168 drop(buf); 1169 return c; 1170 } 1171 1172 /* 1173 * run action s to update r 1174 */ 1175 1176 static unsigned long 1177 run(Rule_t* r, register char* s) 1178 { 1179 register Rule_t* q; 1180 register char* t; 1181 register int c; 1182 register View_t* v; 1183 int i; 1184 int j; 1185 int x; 1186 Stat_t st; 1187 Buf_t* buf; 1188 1189 if (r->flags & RULE_error) 1190 return r->time; 1191 buf = buffer(); 1192 if (!strncmp(s, "mamake -r ", 10)) 1193 { 1194 state.verified = 1; 1195 x = !state.never; 1196 } 1197 else 1198 x = state.exec; 1199 if (x) 1200 append(buf, "trap - 1 2 3 15\nPATH=.:$PATH\nset -x\n"); 1201 if (state.view) 1202 { 1203 do 1204 { 1205 for (; delimiter(*s); s++) 1206 add(buf, *s); 1207 for (t = s; *s && !delimiter(*s); s++); 1208 c = *s; 1209 *s = 0; 1210 if (c == '=') 1211 { 1212 append(buf, t); 1213 continue; 1214 } 1215 if ((q = (Rule_t*)search(state.rules, t, NiL)) && q->path && !(q->flags & RULE_generated)) 1216 append(buf, q->path); 1217 else 1218 { 1219 append(buf, t); 1220 if (*t == '-' && *(t + 1) == 'I' && (*(t + 2) || c)) 1221 { 1222 if (*(t + 2)) 1223 i = 2; 1224 else 1225 { 1226 for (i = 3; *(t + i) == ' ' || *(t + i) == '\t'; i++); 1227 *s = c; 1228 for (s = t + i; *s && *s != ' ' && *s != '\t' && *s != '\n'; s++); 1229 c = *s; 1230 *s = 0; 1231 append(buf, t + 2); 1232 } 1233 if (*(t + i) && *(t + i) != '/') 1234 { 1235 v = state.view; 1236 while (v = v->next) 1237 { 1238 add(buf, ' '); 1239 for (j = 0; j < i; j++) 1240 add(buf, *(t + j)); 1241 append(buf, v->dir); 1242 if (*(t + i) != '.' || *(t + i + 1)) 1243 { 1244 add(buf, '/'); 1245 append(buf, t + i); 1246 } 1247 } 1248 } 1249 } 1250 } 1251 } while (*s = c); 1252 s = use(buf); 1253 } 1254 else if (x) 1255 { 1256 append(buf, s); 1257 s = use(buf); 1258 } 1259 if (x) 1260 { 1261 if (c = execute(s)) 1262 dont(r, c, state.keepgoing); 1263 if (status((Buf_t*)0, 0, r->name, &st)) 1264 { 1265 r->time = st.st_mtime; 1266 r->flags |= RULE_exists; 1267 } 1268 else 1269 r->time = NOW; 1270 } 1271 else 1272 { 1273 fprintf(stdout, "%s\n", s); 1274 if (state.debug) 1275 fflush(stdout); 1276 r->time = NOW; 1277 r->flags |= RULE_exists; 1278 } 1279 drop(buf); 1280 return r->time; 1281 } 1282 1283 /* 1284 * return the full path for s using buf workspace 1285 */ 1286 1287 static char* 1288 path(Buf_t* buf, char* s, int must) 1289 { 1290 register char* p; 1291 register char* d; 1292 register char* x; 1293 char* e; 1294 register int c; 1295 int t; 1296 int o; 1297 Stat_t st; 1298 1299 for (e = s; *e && *e != ' ' && *e != '\t'; e++); 1300 t = *e; 1301 if ((x = status(buf, 0, s, &st)) && (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) 1302 return x; 1303 if (!(p = (char*)search(state.vars, "PATH", NiL))) 1304 report(3, "variable not defined", "PATH", (unsigned long)0); 1305 do 1306 { 1307 for (d = p; *p && *p != ':'; p++); 1308 c = *p; 1309 *p = 0; 1310 if (*d && (*d != '.' || *(d + 1))) 1311 { 1312 append(buf, d); 1313 add(buf, '/'); 1314 } 1315 *p = c; 1316 if (t) 1317 *e = 0; 1318 append(buf, s); 1319 if (t) 1320 *e = t; 1321 o = get(buf); 1322 x = use(buf); 1323 if ((x = status(buf, o, x, &st)) && (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) 1324 return x; 1325 } while (*p++); 1326 if (must) 1327 report(3, "command not found", s, (unsigned long)0); 1328 return 0; 1329 } 1330 1331 /* 1332 * generate (if necessary) and read the MAM probe information 1333 * done on the first `setv CC ...' 1334 */ 1335 1336 static void 1337 probe(void) 1338 { 1339 register char* cc; 1340 register char* s; 1341 unsigned long h; 1342 unsigned long q; 1343 Buf_t* buf; 1344 Buf_t* pro; 1345 Buf_t* tmp; 1346 struct stat st; 1347 1348 static char let[] = "ABCDEFGHIJKLMNOP"; 1349 static char cmd[] = "mamprobe"; 1350 1351 if (!(cc = (char*)search(state.vars, "CC", NiL))) 1352 cc = "cc"; 1353 buf = buffer(); 1354 s = path(buf, cmd, 1); 1355 q = stat(s, &st) ? (unsigned long)0 : (unsigned long)st.st_mtime; 1356 pro = buffer(); 1357 s = cc = path(pro, cc, 1); 1358 for (h = 0; *s; s++) 1359 h = h * 0x63c63cd9L + *s + 0x9c39c33dL; 1360 if (!(s = (char*)search(state.vars, "INSTALLROOT", NiL))) 1361 report(3, "variable must be defined", "INSTALLROOT", (unsigned long)0); 1362 append(buf, s); 1363 append(buf, "/lib/probe/C/mam/"); 1364 for (h &= 0xffffffffL; h; h >>= 4) 1365 add(buf, let[h & 0xf]); 1366 s = use(buf); 1367 h = stat(s, &st) ? (unsigned long)0 : (unsigned long)st.st_mtime; 1368 if (h < q || !push(s, (Stdio_t*)0, 0)) 1369 { 1370 tmp = buffer(); 1371 append(tmp, cmd); 1372 add(tmp, ' '); 1373 append(tmp, s); 1374 add(tmp, ' '); 1375 append(tmp, cc); 1376 if (execute(use(tmp))) 1377 report(3, "cannot generate probe info", s, (unsigned long)0); 1378 drop(tmp); 1379 if (!push(s, (Stdio_t*)0, 0)) 1380 report(3, "cannot read probe info", s, (unsigned long)0); 1381 } 1382 drop(pro); 1383 drop(buf); 1384 make(rule("")); 1385 pop(); 1386 } 1387 1388 /* 1389 * add attributes in s to r 1390 */ 1391 1392 static void 1393 attributes(register Rule_t* r, register char* s) 1394 { 1395 register char* t; 1396 register int n; 1397 1398 for (;;) 1399 { 1400 for (; *s == ' '; s++); 1401 for (t = s; *s && *s != ' '; s++); 1402 if (!(n = s - t)) 1403 break; 1404 switch (*t) 1405 { 1406 case 'd': 1407 if (n == 8 && !strncmp(t, "dontcare", n)) 1408 r->flags |= RULE_dontcare; 1409 break; 1410 case 'g': 1411 if (n == 9 && !strncmp(t, "generated", n)) 1412 r->flags |= RULE_generated; 1413 break; 1414 case 'i': 1415 if (n == 6 && !strncmp(t, "ignore", n)) 1416 r->flags |= RULE_ignore; 1417 else if (n == 8 && !strncmp(t, "implicit", n)) 1418 r->flags |= RULE_implicit; 1419 break; 1420 case 'v': 1421 if (n == 7 && !strncmp(t, "virtual", n)) 1422 r->flags |= RULE_virtual; 1423 break; 1424 } 1425 } 1426 } 1427 1428 /* 1429 * define ${mam_libX} for library reference lib 1430 */ 1431 1432 static char* 1433 require(char* lib, int dontcare) 1434 { 1435 register int c; 1436 char* s; 1437 char* r; 1438 FILE* f; 1439 Buf_t* buf; 1440 Buf_t* tmp; 1441 struct stat st; 1442 1443 static int dynamic = -1; 1444 1445 if (dynamic < 0) 1446 dynamic = (s = search(state.vars, "mam_cc_L", NiL)) ? atoi(s) : 0; 1447 if (!(r = search(state.vars, lib, NiL))) 1448 { 1449 buf = buffer(); 1450 tmp = buffer(); 1451 s = 0; 1452 for (;;) 1453 { 1454 if (s) 1455 append(buf, s); 1456 if (r = search(state.vars, "mam_cc_PREFIX_ARCHIVE", NiL)) 1457 append(buf, r); 1458 append(buf, lib + 2); 1459 if (r = search(state.vars, "mam_cc_SUFFIX_ARCHIVE", NiL)) 1460 append(buf, r); 1461 r = expand(tmp, use(buf)); 1462 if (!stat(r, &st)) 1463 break; 1464 if (s) 1465 { 1466 r = lib; 1467 break; 1468 } 1469 s = "${INSTALLROOT}/lib/"; 1470 if (dynamic) 1471 { 1472 append(buf, s); 1473 if (r = search(state.vars, "mam_cc_PREFIX_SHARED", NiL)) 1474 append(buf, r); 1475 append(buf, lib + 2); 1476 if (r = search(state.vars, "mam_cc_SUFFIX_SHARED", NiL)) 1477 append(buf, r); 1478 r = expand(tmp, use(buf)); 1479 if (!stat(r, &st)) 1480 { 1481 r = lib; 1482 break; 1483 } 1484 } 1485 } 1486 if (r != lib) 1487 r = duplicate(r); 1488 search(state.vars, lib, r); 1489 append(tmp, lib + 2); 1490 append(tmp, ".req"); 1491 if (!(f = fopen(use(tmp), "r"))) 1492 { 1493 append(tmp, "${INSTALLROOT}/lib/lib/"); 1494 append(tmp, lib + 2); 1495 f = fopen(expand(buf, use(tmp)), "r"); 1496 } 1497 if (f) 1498 { 1499 for (;;) 1500 { 1501 while ((c = fgetc(f)) == ' ' || c == '\t' || c == '\n'); 1502 if (c == EOF) 1503 break; 1504 do 1505 { 1506 add(tmp, c); 1507 } while ((c = fgetc(f)) != EOF && c != ' ' && c != '\t' && c != '\n'); 1508 s = use(tmp); 1509 if (s[0] && (s[0] != '-' || s[1])) 1510 { 1511 add(buf, ' '); 1512 append(buf, require(s, 0)); 1513 } 1514 } 1515 fclose(f); 1516 r = use(buf); 1517 } 1518 else if (dontcare) 1519 { 1520 append(tmp, "set -\n"); 1521 append(tmp, "cd /tmp\n"); 1522 append(tmp, "echo 'int main(){return 0;}' > x.${!-$$}.c\n"); 1523 append(tmp, "${CC} ${CCFLAGS} -o x.${!-$$}.x x.${!-$$}.c "); 1524 append(tmp, r); 1525 append(tmp, " >/dev/null 2>&1\n"); 1526 append(tmp, "c=$?\n"); 1527 append(tmp, "rm -f x.${!-$$}.[cox]\n"); 1528 append(tmp, "exit $c\n"); 1529 if (execute(expand(buf, use(tmp)))) 1530 r = ""; 1531 } 1532 r = duplicate(r); 1533 search(state.vars, lib, r); 1534 append(tmp, "mam_lib"); 1535 append(tmp, lib + 2); 1536 search(state.vars, use(tmp), r); 1537 drop(tmp); 1538 drop(buf); 1539 } 1540 return r; 1541 } 1542 1543 /* 1544 * input() until `done r' 1545 */ 1546 1547 static unsigned long 1548 make(Rule_t* r) 1549 { 1550 register char* s; 1551 register char* t; 1552 register char* u; 1553 register char* v; 1554 register Rule_t* q; 1555 unsigned long z; 1556 unsigned long x; 1557 Buf_t* buf; 1558 Buf_t* cmd; 1559 1560 r->making++; 1561 if (r->flags & RULE_active) 1562 state.active++; 1563 if (*r->name) 1564 { 1565 z = bind(r); 1566 state.indent++; 1567 report(-1, r->name, "make", r->time); 1568 } 1569 else 1570 z = 0; 1571 buf = buffer(); 1572 cmd = 0; 1573 while (s = input()) 1574 { 1575 for (; *s == ' '; s++); 1576 for (; isdigit(*s); s++); 1577 for (; *s == ' '; s++); 1578 for (u = s; *s && *s != ' '; s++); 1579 if (*s) 1580 { 1581 for (*s++ = 0; *s == ' '; s++); 1582 for (t = s; *s && *s != ' '; s++); 1583 if (*s) 1584 for (*s++ = 0; *s == ' '; s++); 1585 v = s; 1586 } 1587 else 1588 t = v = s; 1589 switch (KEY(u[0], u[1], u[2], u[3])) 1590 { 1591 case KEY('b','i','n','d'): 1592 if ((t[0] == '-' || t[0] == '+') && t[1] == 'l' && (s = require(t, !strcmp(v, "dontcare"))) && strncmp(r->name, "FEATURE/", 8) && strcmp(r->name, "configure.h")) 1593 for (;;) 1594 { 1595 for (t = s; *s && *s != ' '; s++); 1596 if (*s) 1597 *s = 0; 1598 else 1599 s = 0; 1600 if (*t) 1601 { 1602 q = rule(expand(buf, t)); 1603 attributes(q, v); 1604 x = bind(q); 1605 if (z < x) 1606 z = x; 1607 if (q->flags & RULE_error) 1608 r->flags |= RULE_error; 1609 } 1610 if (!s) 1611 break; 1612 for (*s++ = ' '; *s == ' '; s++); 1613 } 1614 continue; 1615 case KEY('d','o','n','e'): 1616 q = rule(expand(buf, t)); 1617 if (q != r) 1618 report(2, "improper done statement", t, (unsigned long)0); 1619 attributes(r, v); 1620 if (cmd && state.active && (state.force || r->time < z || !r->time && !z)) 1621 { 1622 if (state.explain && !state.force) 1623 { 1624 if (!r->time) 1625 fprintf(stderr, "%s [not found]\n", r->name); 1626 else 1627 fprintf(stderr, "%s [%lu] older than prerequisites [%lu]\n", r->name, r->time, z); 1628 } 1629 substitute(buf, use(cmd)); 1630 x = run(r, use(buf)); 1631 if (z < x) 1632 z = x; 1633 } 1634 r->flags |= RULE_made; 1635 if (!(r->flags & (RULE_dontcare|RULE_error|RULE_exists|RULE_generated|RULE_implicit|RULE_virtual))) 1636 dont(r, 0, state.keepgoing); 1637 break; 1638 case KEY('e','x','e','c'): 1639 r->flags |= RULE_generated; 1640 if (r->path) 1641 { 1642 free(r->path); 1643 r->path = 0; 1644 r->time = 0; 1645 } 1646 if (state.active) 1647 { 1648 if (cmd) 1649 add(cmd, '\n'); 1650 else 1651 cmd = buffer(); 1652 append(cmd, v); 1653 } 1654 continue; 1655 case KEY('m','a','k','e'): 1656 q = rule(expand(buf, t)); 1657 if (!q->making) 1658 { 1659 attributes(q, v); 1660 x = make(q); 1661 if (!(q->flags & RULE_ignore) && z < x) 1662 z = x; 1663 if (q->flags & RULE_error) 1664 r->flags |= RULE_error; 1665 } 1666 continue; 1667 case KEY('p','r','e','v'): 1668 q = rule(expand(buf, t)); 1669 if (!q->making) 1670 { 1671 if (!(q->flags & RULE_ignore) && z < q->time) 1672 z = q->time; 1673 if (q->flags & RULE_error) 1674 r->flags |= RULE_error; 1675 state.indent++; 1676 report(-2, q->name, "prev", q->time); 1677 state.indent--; 1678 } 1679 continue; 1680 case KEY('s','e','t','v'): 1681 if (!search(state.vars, t, NiL)) 1682 { 1683 if (*v == '"') 1684 { 1685 s = v + strlen(v) - 1; 1686 if (*s == '"') 1687 { 1688 *s = 0; 1689 v++; 1690 } 1691 } 1692 search(state.vars, t, duplicate(expand(buf, v))); 1693 } 1694 if (!state.probed && t[0] == 'C' && t[1] == 'C' && !t[2]) 1695 { 1696 state.probed = 1; 1697 probe(); 1698 } 1699 continue; 1700 default: 1701 continue; 1702 } 1703 break; 1704 } 1705 drop(buf); 1706 if (cmd) 1707 drop(cmd); 1708 if (*r->name) 1709 { 1710 report(-1, r->name, "done", z); 1711 state.indent--; 1712 } 1713 if (r->flags & RULE_active) 1714 state.active--; 1715 r->making--; 1716 return r->time = z; 1717 } 1718 1719 /* 1720 * verify that active targets were made 1721 */ 1722 1723 static int 1724 verify(Dict_item_t* item, void* handle) 1725 { 1726 Rule_t* r = (Rule_t*)item->value; 1727 1728 if ((r->flags & (RULE_active|RULE_error|RULE_made)) == RULE_active) 1729 dont(r, 0, 1); 1730 return 0; 1731 } 1732 1733 /* 1734 * return 1 if name is an initializer 1735 */ 1736 1737 static int 1738 initializer(char* name) 1739 { 1740 register char* s; 1741 1742 if (s = last(name, '/')) 1743 s++; 1744 else 1745 s = name; 1746 return s[0] == 'I' && s[1] == 'N' && s[2] == 'I' && s[3] == 'T'; 1747 } 1748 1749 /* 1750 * update recursion leaf r and its prerequisites 1751 */ 1752 1753 static int 1754 update(register Rule_t* r) 1755 { 1756 register List_t* x; 1757 Buf_t* buf; 1758 1759 static char cmd[] = "${MAMAKE} -C "; 1760 static char arg[] = " ${MAMAKEARGS}"; 1761 1762 r->flags |= RULE_made; 1763 if (r->leaf) 1764 r->leaf->flags |= RULE_made; 1765 for (x = r->prereqs; x; x = x->next) 1766 if (x->rule->leaf && !(x->rule->flags & RULE_made)) 1767 update(x->rule); 1768 buf = buffer(); 1769 substitute(buf, cmd); 1770 append(buf, r->name); 1771 substitute(buf, arg); 1772 run(r, use(buf)); 1773 drop(buf); 1774 return 0; 1775 } 1776 1777 /* 1778 * scan makefile prereqs 1779 */ 1780 1781 static int 1782 scan(Dict_item_t* item, void* handle) 1783 { 1784 register Rule_t* r = (Rule_t*)item->value; 1785 register char* s; 1786 register char* t; 1787 register char* u; 1788 register char* w; 1789 Rule_t* q; 1790 int i; 1791 int j; 1792 int k; 1793 int p; 1794 Buf_t* buf; 1795 1796 static char* files[] = 1797 { 1798 "Nmakefile", 1799 "nmakefile", 1800 "Makefile", 1801 "makefile" 1802 }; 1803 1804 /* 1805 * drop non-leaf rules 1806 */ 1807 1808 if (!r->leaf) 1809 return 0; 1810 1811 /* 1812 * always make initializers 1813 */ 1814 1815 if (initializer(r->name)) 1816 { 1817 if (!(r->flags & RULE_made)) 1818 update(r); 1819 return 0; 1820 } 1821 buf = buffer(); 1822 for (i = 0; i < elementsof(files); i++) 1823 { 1824 append(buf, r->name); 1825 add(buf, '/'); 1826 append(buf, files[i]); 1827 if (push(use(buf), (Stdio_t*)0, 0)) 1828 { 1829 while (s = input()) 1830 { 1831 j = p = 0; 1832 while (*s) 1833 { 1834 for (k = 1; (i = *s) == ' ' || i == '\t' || i == '"' || i == '\''; s++); 1835 for (t = s; (i = *s) && i != ' ' && i != '\t' && i != '"' && i != '\'' && i != '\\' && i != ':'; s++) 1836 if (i == '/') 1837 t = s + 1; 1838 else if (i == '.' && *(s + 1) != 'c' && *(s + 1) != 'C' && *(s + 1) != 'h' && *(s + 1) != 'H' && t[0] == 'l' && t[1] == 'i' && t[2] == 'b') 1839 *s = 0; 1840 if (*s) 1841 *s++ = 0; 1842 if (!t[0]) 1843 k = 0; 1844 else if ((t[0] == '-' || t[0] == '+') && t[1] == 'l' && t[2]) 1845 { 1846 append(buf, "lib"); 1847 append(buf, t + 2); 1848 t = use(buf); 1849 } 1850 else if (p) 1851 { 1852 if (t[0] == '+' && !t[1]) 1853 p = 2; 1854 else if (p == 1) 1855 { 1856 if (i != ':' || strncmp(s, "command", 7)) 1857 { 1858 append(buf, "lib"); 1859 append(buf, t); 1860 t = use(buf); 1861 } 1862 if (i == ':') 1863 while (*s && (*s == ' ' || *s == '\t')) 1864 s++; 1865 } 1866 } 1867 else if (i == ':') 1868 { 1869 if (j != ':' || !isupper(*t)) 1870 k = 0; 1871 else if (!strcmp(t, "PACKAGE")) 1872 { 1873 p = 1; 1874 k = 0; 1875 } 1876 else 1877 for (u = t; *u; u++) 1878 if (isupper(*u)) 1879 *u = tolower(*u); 1880 else if (!isalnum(*u)) 1881 { 1882 k = 0; 1883 break; 1884 } 1885 } 1886 else if (t[0] != 'l' || t[1] != 'i' || t[2] != 'b') 1887 k = 0; 1888 else 1889 for (u = t + 3; *u; u++) 1890 if (!isalnum(*u)) 1891 { 1892 k = 0; 1893 break; 1894 } 1895 if (k && ((q = (Rule_t*)search(state.leaf, t, NiL)) && q != r || *t++ == 'l' && *t++ == 'i' && *t++ == 'b' && *t && (q = (Rule_t*)search(state.leaf, t, NiL)) && q != r)) 1896 { 1897 for (t = w = r->name; *w; w++) 1898 if (*w == '/') 1899 t = w + 1; 1900 if (t[0] == 'l' && t[1] == 'i' && t[2] == 'b') 1901 t += 3; 1902 for (u = w = q->name; *w; w++) 1903 if (*w == '/') 1904 u = w + 1; 1905 if (strcmp(t, u)) 1906 cons(r, q); 1907 } 1908 j = i; 1909 } 1910 } 1911 pop(); 1912 for (s = 0, w = r->name; *w; w++) 1913 if (*w == '/') 1914 s = w; 1915 if (s) 1916 { 1917 if ((s - r->name) > 3 && *(s - 1) == 'b' && *(s - 2) == 'i' && *(s - 3) == 'l' && *(s - 4) != '/') 1918 { 1919 /* 1920 * foolib : foo : libfoo 1921 */ 1922 1923 *(s - 3) = 0; 1924 q = (Rule_t*)search(state.leaf, r->name, NiL); 1925 if (q && q != r) 1926 cons(r, q); 1927 for (t = w = r->name; *w; w++) 1928 if (*w == '/') 1929 t = w + 1; 1930 append(buf, "lib"); 1931 append(buf, t); 1932 q = (Rule_t*)search(state.leaf, use(buf), NiL); 1933 if (q && q != r) 1934 cons(r, q); 1935 *(s - 3) = 'l'; 1936 } 1937 else if (((s - r->name) != 3 || *(s - 1) != 'b' || *(s - 2) != 'i' || *(s - 3) != 'l') && (*(s + 1) != 'l' || *(s + 2) != 'i' || *(s + 3) != 'b')) 1938 { 1939 /* 1940 * huh/foobar : lib/libfoo 1941 */ 1942 1943 s++; 1944 t = s + strlen(s); 1945 while (--t > s) 1946 { 1947 append(buf, "lib/lib"); 1948 appendn(buf, s, t - s); 1949 q = (Rule_t*)search(state.leaf, use(buf), NiL); 1950 if (q && q != r) 1951 cons(r, q); 1952 } 1953 } 1954 } 1955 break; 1956 } 1957 } 1958 drop(buf); 1959 return 0; 1960 } 1961 1962 /* 1963 * descend into op and its prereqs 1964 */ 1965 1966 static int 1967 descend(Dict_item_t* item, void* handle) 1968 { 1969 Rule_t* r = (Rule_t*)item->value; 1970 1971 if (!state.active && (!(r->flags & RULE_active) || !(r = (Rule_t*)search(state.leaf, r->name, NiL)))) 1972 return 0; 1973 return r->leaf && !(r->flags & RULE_made) ? update(r) : 0; 1974 } 1975 1976 /* 1977 * append the non-leaf active targets to state.opt 1978 */ 1979 1980 static int 1981 active(Dict_item_t* item, void* handle) 1982 { 1983 Rule_t* r = (Rule_t*)item->value; 1984 1985 if (r->flags & RULE_active) 1986 { 1987 if (r->leaf || search(state.leaf, r->name, NiL)) 1988 state.active = 0; 1989 else 1990 { 1991 add(state.opt, ' '); 1992 append(state.opt, r->name); 1993 } 1994 } 1995 return 0; 1996 } 1997 1998 /* 1999 * recurse on mamfiles in subdirs matching pattern 2000 */ 2001 2002 static int 2003 recurse(char* pattern) 2004 { 2005 register char* s; 2006 register char* t; 2007 Rule_t* r; 2008 Buf_t* buf; 2009 Buf_t* tmp; 2010 struct stat st; 2011 2012 /* 2013 * first determine the MAM subdirs 2014 */ 2015 2016 tmp = buffer(); 2017 buf = buffer(); 2018 state.exec = !state.never; 2019 state.leaf = dictionary(); 2020 append(buf, "ls -d "); 2021 append(buf, pattern); 2022 s = use(buf); 2023 push("recurse", popen(s, "r"), STREAM_PIPE); 2024 while (s = input()) 2025 { 2026 append(buf, s); 2027 add(buf, '/'); 2028 append(buf, mamfile); 2029 if (find(tmp, use(buf), &st)) 2030 { 2031 r = rule(s); 2032 if (t = last(r->name, '/')) 2033 t++; 2034 else 2035 t = r->name; 2036 r->leaf = rule(t); 2037 search(state.leaf, t, r); 2038 } 2039 } 2040 pop(); 2041 drop(buf); 2042 drop(tmp); 2043 2044 /* 2045 * grab the non-leaf active targets 2046 */ 2047 2048 if (!state.active) 2049 { 2050 state.active = 1; 2051 walk(state.rules, active, NiL); 2052 } 2053 search(state.vars, "MAMAKEARGS", duplicate(use(state.opt) + 1)); 2054 2055 /* 2056 * scan the makefile and descend 2057 */ 2058 2059 walk(state.rules, scan, NiL); 2060 state.view = 0; 2061 walk(state.rules, descend, NiL); 2062 return 0; 2063 } 2064 2065 int 2066 main(int argc, char** argv) 2067 { 2068 register char** e; 2069 register char* s; 2070 register char* t; 2071 register char* v; 2072 Buf_t* tmp; 2073 int c; 2074 2075 /* 2076 * initialize the state 2077 */ 2078 2079 state.id = "mamake"; 2080 state.active = 1; 2081 state.exec = 1; 2082 state.file = mamfile; 2083 state.opt = buffer(); 2084 state.rules = dictionary(); 2085 state.vars = dictionary(); 2086 search(state.vars, "MAMAKE", *argv); 2087 2088 /* 2089 * parse the options 2090 */ 2091 2092 #if _PACKAGE_ast 2093 error_info.id = state.id; 2094 for (;;) 2095 { 2096 switch (optget(argv, usage)) 2097 { 2098 case 'e': 2099 append(state.opt, " -e"); 2100 state.explain = 1; 2101 continue; 2102 case 'i': 2103 append(state.opt, " -i"); 2104 state.ignore = 1; 2105 continue; 2106 case 'k': 2107 append(state.opt, " -k"); 2108 state.keepgoing = 1; 2109 continue; 2110 case 'N': 2111 state.never = 1; 2112 /*FALLTHROUGH*/ 2113 case 'n': 2114 append(state.opt, " -n"); 2115 state.exec = 0; 2116 continue; 2117 case 'F': 2118 append(state.opt, " -F"); 2119 state.force = 1; 2120 continue; 2121 case 'K': 2122 continue; 2123 case 'V': 2124 fprintf(stdout, "%s\n", id + 10); 2125 exit(0); 2126 case 'f': 2127 append(state.opt, " -f "); 2128 append(state.opt, opt_info.arg); 2129 state.file = opt_info.arg; 2130 continue; 2131 case 'r': 2132 state.recurse = opt_info.arg; 2133 continue; 2134 case 'C': 2135 state.directory = opt_info.arg; 2136 continue; 2137 case 'D': 2138 append(state.opt, " -D"); 2139 append(state.opt, opt_info.arg); 2140 state.debug = -opt_info.num; 2141 continue; 2142 case 'G': 2143 append(state.opt, " -G"); 2144 search(state.vars, "-debug-symbols", "1"); 2145 continue; 2146 case 'S': 2147 append(state.opt, " -S"); 2148 search(state.vars, "-strip-symbols", "1"); 2149 continue; 2150 case '?': 2151 error(ERROR_USAGE|4, "%s", opt_info.arg); 2152 continue; 2153 case ':': 2154 error(2, "%s", opt_info.arg); 2155 continue; 2156 } 2157 break; 2158 } 2159 if (error_info.errors) 2160 error(ERROR_USAGE|4, "%s", optusage(NiL)); 2161 argv += opt_info.index; 2162 #else 2163 while ((s = *++argv) && *s == '-') 2164 { 2165 if (*(s + 1) == '-') 2166 { 2167 if (!*(s + 2)) 2168 { 2169 append(state.opt, " --"); 2170 argv++; 2171 break; 2172 } 2173 for (t = s += 2; *t && *t != '='; t++); 2174 if (!strncmp(s, "debug-symbols", t - s) && append(state.opt, " -G") || !strncmp(s, "strip-symbols", t - s) && append(state.opt, " -S")) 2175 { 2176 if (*t) 2177 { 2178 v = t + 1; 2179 if (t > s && *(t - 1) == '+') 2180 t--; 2181 c = *t; 2182 *t = 0; 2183 } 2184 else 2185 { 2186 c = 0; 2187 v = "1"; 2188 } 2189 search(state.vars, s - 1, v); 2190 if (c) 2191 *t = c; 2192 continue; 2193 } 2194 usage(); 2195 break; 2196 } 2197 for (;;) 2198 { 2199 switch (*++s) 2200 { 2201 case 0: 2202 break; 2203 case 'e': 2204 append(state.opt, " -e"); 2205 state.explain = 1; 2206 continue; 2207 case 'i': 2208 append(state.opt, " -i"); 2209 state.ignore = 1; 2210 continue; 2211 case 'k': 2212 append(state.opt, " -k"); 2213 state.keepgoing = 1; 2214 continue; 2215 case 'N': 2216 state.never = 1; 2217 /*FALLTHROUGH*/ 2218 case 'n': 2219 append(state.opt, " -n"); 2220 state.exec = 0; 2221 continue; 2222 case 'F': 2223 append(state.opt, " -F"); 2224 state.force = 1; 2225 continue; 2226 case 'G': 2227 append(state.opt, " -G"); 2228 search(state.vars, "-debug-symbols", "1"); 2229 continue; 2230 case 'K': 2231 continue; 2232 case 'S': 2233 append(state.opt, " -S"); 2234 search(state.vars, "-strip-symbols", "1"); 2235 continue; 2236 case 'V': 2237 fprintf(stdout, "%s\n", id + 10); 2238 exit(0); 2239 case 'f': 2240 case 'r': 2241 case 'C': 2242 case 'D': 2243 t = s; 2244 if (!*++s && !(s = *++argv)) 2245 { 2246 report(2, "option value expected", t, (unsigned long)0); 2247 usage(); 2248 } 2249 else 2250 switch (*t) 2251 { 2252 case 'f': 2253 append(state.opt, " -f "); 2254 append(state.opt, s); 2255 state.file = s; 2256 break; 2257 case 'r': 2258 state.recurse = s; 2259 break; 2260 case 'C': 2261 state.directory = s; 2262 break; 2263 case 'D': 2264 append(state.opt, " -D"); 2265 append(state.opt, s); 2266 state.debug = -atoi(s); 2267 break; 2268 } 2269 break; 2270 default: 2271 report(2, "unknown option", s, (unsigned long)0); 2272 case '?': 2273 usage(); 2274 break; 2275 } 2276 break; 2277 } 2278 } 2279 #endif 2280 2281 /* 2282 * load the environment 2283 */ 2284 2285 for (e = environ; s = *e; e++) 2286 for (t = s; *t; t++) 2287 if (*t == '=') 2288 { 2289 *t = 0; 2290 search(state.vars, s, t + 1); 2291 *t = '='; 2292 break; 2293 } 2294 2295 /* 2296 * grab the command line targets and variable definitions 2297 */ 2298 2299 while (s = *argv++) 2300 { 2301 for (t = s; *t; t++) 2302 if (*t == '=') 2303 { 2304 v = t + 1; 2305 if (t > s && *(t - 1) == '+') 2306 t--; 2307 c = *t; 2308 *t = 0; 2309 search(state.vars, s, v); 2310 tmp = buffer(); 2311 append(tmp, s); 2312 append(tmp, ".FORCE"); 2313 search(state.vars, use(tmp), v); 2314 drop(tmp); 2315 *t = c; 2316 break; 2317 } 2318 if (!*t) 2319 { 2320 /* 2321 * handle a few targets for nmake compatibility 2322 */ 2323 2324 if (*s == 'e' && !strncmp(s, "error 0 $(MAKEVERSION:", 22)) 2325 exit(1); 2326 if (*s == 'r' && !strcmp(s, "recurse") || *s == 'c' && !strncmp(s, "cc-", 3)) 2327 continue; 2328 rule(s)->flags |= RULE_active; 2329 state.active = 0; 2330 if (state.recurse) 2331 continue; 2332 } 2333 add(state.opt, ' '); 2334 add(state.opt, '\''); 2335 append(state.opt, s); 2336 add(state.opt, '\''); 2337 } 2338 2339 /* 2340 * initialize the views 2341 */ 2342 2343 if (state.directory && chdir(state.directory)) 2344 report(3, "cannot change working directory", NiL, (unsigned long)0); 2345 view(); 2346 2347 /* 2348 * recursion drops out here 2349 */ 2350 2351 if (state.recurse) 2352 return recurse(state.recurse); 2353 2354 /* 2355 * read the mamfile(s) and bring the targets up to date 2356 */ 2357 2358 search(state.vars, "MAMAKEARGS", duplicate(use(state.opt) + 1)); 2359 push(state.file, (Stdio_t*)0, STREAM_MUST); 2360 make(rule("")); 2361 pop(); 2362 2363 /* 2364 * verify that active targets were made 2365 */ 2366 2367 if (!state.active && !state.verified) 2368 walk(state.rules, verify, NiL); 2369 2370 /* 2371 * done 2372 */ 2373 2374 return state.errors != 0; 2375 } 2376