1/* ************************************************************************ *\ 2 * * 3 * File: md.c * 4 * * 5 * Updates makefiles from the .n dependency files generated by the * 6 * -MD option to "cc" (and "cpp"). * 7 * * 8 * Abstract: * 9 * * 10 * Basically, "md" does two things: * 11 * 1) It processes the raw dependency files produced by the cpp -MD * 12 * option. There is one line in the file for every #include * 13 * encountered, but there are repeats and patterns like * 14 * .../dir1/../dir2 appear which should reduce to .../dir2 * 15 * Md canonicalizes and flushes repeats from the dependency * 16 * list. It also sorts the file names and "fills" them to a 78 * 17 * character line. * 18 * 2) Md also updates the makefile directly with the dependency * 19 * information, so the .d file can be thrown away (-- -d option) * 20 * This is done to save space. Md assumes that dependency * 21 * information in the makefile is sorted by .o file name and it * 22 * procedes to merge in (add/or replace [as appropriate]) the new * 23 * dependency lines that it has generated. For time effeciency, * 24 * Md assumes that any .d files it is given that were created * 25 * before the creation date of the "makefile" were processed * 26 * already. It ignores them unless the force flag (-f) is given. * 27 * * 28 * Arguments: * 29 * * 30 * -d delete the .d file after it is processed * 31 * -f force an update of the dependencies in the makefile * 32 * even though the makefile is more recent than the .n file * 33 * (This implies that md has been run already.) * 34 * -m specify the makefile to be upgraded. The defaults are * 35 * "makefile" and then "Makefile". * 36 * -u like -m above, but the file will be created if necessary * 37 * -o specify an output file for the dependencies other than a * 38 * makefile * 39 * -v set the verbose flag * 40 * -x expunge old dependency info from makefile * 41 * -D subswitch for debugging. can be followed by any of * 42 * "c", "d", "m", "o", "t", "D" meaning: * 43 * c show file contents * 44 * d show new dependency crunching * 45 * m show generation of makefile * 46 * o show files being opened * 47 * t show time comparisons * 48 * D show very low level debugging * 49 * * 50 * Author: Robert V. Baron * 51 * Copyright (c) 1986 by Robert V. Baron * 52 * * 53 * HISTORY * 54 * 29-Apr-87 Robert Baron (rvb) at Carnegie-Mellon University 55 * If specified -u file does not exist, assume it is empty and 56 * generate one. As a sanity check, it must be possible to create 57 * the output file. 58 * Also, generalized fix below to handle any case of . as a 59 * file name. 60 * 61 * 25-Mar-87 Mary Thompson (mrt) at Carnegie Mellon 62 * Fixed up pathnamecanonicalization to recognize .// and 63 * drop the second / as well. mmax cpp generates this form. 64 * 65 * 6-Jan-87 Robert Baron (rvb) at Carnegie-Mellon University 66 * Fixed up pathname canonicalization to that ../../, etc would be 67 * handled correctly. 68 * Also made "force" on by default. 69 * 70 * 16-Mar-86 Robert Baron (rvb) at Carnegie-Mellon University 71 * Created 4/16/86 * 72 * * 73\* ************************************************************************ */ 74 75 76#include <sys/types.h> 77#include <sys/stat.h> 78#include <stdio.h> 79#include <stdlib.h> 80#include <string.h> 81 82#define LINESIZE 65536 // NeXT_MOD 83 84#define OUTLINELEN 79 85#define IObuffer 50000 86#define SALUTATION "# Dependencies for File:" 87#define SALUTATIONLEN (sizeof SALUTATION - 1) 88#define OLDSALUTATION "# DO NOT DELETE THIS LINE" 89#define OLDSALUTATIONLEN (sizeof OLDSALUTATION - 1) 90 91char file_array[IObuffer]; /* read file and store crunched names */ 92char dep_line[LINESIZE]; /* line being processed */ 93char dot_o[LINESIZE]; /* <foo.o>: prefix */ 94char *path_component[100]; /* stores components for a path while being 95 crunched */ 96 97struct dep { /* stores paths that a file depends on */ 98 int len; 99 char *str; 100} dep_files[1000]; 101int dep_file_index; 102 103qsort_strcmp(a, b) 104struct dep *a, *b; 105{ 106extern int strcmp(); 107 return strcmp(a->str, b->str); 108} 109 110char *outfile = (char *) 0; /* generate dependency file */ 111FILE *out; 112 113char *makefile = (char *) 0; /* user supplied makefile name */ 114char *real_mak_name; /* actual makefile name (if not supplied) */ 115char shadow_mak_name[LINESIZE]; /* changes done here then renamed */ 116FILE *mak; /* for reading makefile */ 117FILE *makout; /* for writing shadow */ 118char makbuf[LINESIZE]; /* one line buffer for makefile */ 119struct stat makstat; /* stat of makefile for time comparisons */ 120int mak_eof = 0; /* eof seen on makefile */ 121FILE *find_mak(), *temp_mak(); 122 123int delete = 0; /* -d delete dependency file */ 124int debug = 0; 125int D_contents = 0; /* print file contents */ 126int D_depend = 0; /* print dependency processing info */ 127int D_make = 0; /* print makefile processing info */ 128int D_open = 0; /* print after succesful open */ 129int D_time = 0; /* print time comparison info */ 130int force = 1; /* always update dependency info */ 131int update = 0; /* it's ok if the -m file does not exist */ 132int verbose = 0; /* tell me something */ 133int expunge = 0; /* first flush dependency stuff from makefile */ 134 135 136char *name; 137 138static void scan_mak(FILE *, FILE *, char *); 139static void finish_mak(FILE *, FILE *); 140 141main(argc,argv) 142register char **argv; 143{ 144int size; 145 146 name = *argv; 147 {register char *cp =name; 148 while (*cp) if (*cp++ == '/') name = cp; 149 } 150 151 for ( argv++ ; --argc ; argv++ ) { register char *token = *argv; 152 if (*token++ != '-' || !*token) 153 break; 154 else { register int flag; 155 for ( ; flag = *token++ ; ) { 156 switch (flag) { 157 case 'd': 158 delete++; 159 break; 160 case 'f': 161 force++; 162 break; 163 case 'u': 164 update++; 165 case 'm': 166 makefile = *++argv; 167 if (--argc < 0) goto usage; 168 break; 169 case 'o': 170 outfile = *++argv; 171 if (--argc < 0) goto usage; 172 break; 173 case 'v': 174 verbose++; 175 break; 176 case 'x': 177 expunge++; 178 break; 179 case 'D': 180 for ( ; flag = *token++ ; ) 181 switch (flag) { 182 case 'c': 183 D_contents++; 184 break; 185 case 'd': 186 D_depend++; 187 break; 188 case 'm': 189 D_make++; 190 break; 191 case 'o': 192 D_open++; 193 break; 194 case 't': 195 D_time++; 196 break; 197 case 'D': 198 debug++; 199 break; 200 default: 201 goto letters; 202 } 203 goto newtoken; 204 default: 205 goto usage; 206 } 207letters: ; 208 } 209 } 210newtoken: ; 211 } 212 213 if (!expunge && argc < 1) goto usage; 214 if ((int) outfile && (int) makefile) /* not both */ 215 goto usage; 216 217 if ((int) outfile) { 218 /* 219 * NeXT_MOD, For SGS stuff, in case still linked to master version 220 */ 221 unlink(outfile); 222 223 if ((out = fopen(outfile, "w")) == NULL) { 224 fprintf(stderr, "%s: outfile = \"%s\" ", name, outfile); 225 perror("fopen"); 226 fflush(stdout), fflush(stderr); 227 exit(1); 228 } else if (D_open) 229 printf("%s: opened outfile \"%s\"\n", name, outfile); 230 } else if (mak = find_mak(makefile)) { 231 makout = temp_mak(); 232 out = makout; 233 if (expunge) 234 expunge_mak(mak, makout); 235 else 236 skip_mak(mak, makout); 237 } else if (mak_eof && /* non existent file == mt file */ 238 (int)(makout = temp_mak())) { /* but we need to be able */ 239 out = makout; /* to write here */ 240 } else if (makefile) { 241 fprintf(stderr, "%s: makefile \"%s\" can not be opened or stat'ed\n", 242 name, makefile); 243 exit(2); 244 } 245 246 for (; argc--; argv++) { 247 dep_file_index = 0; 248 249 if (size = read_dep(*argv)) { 250 251 save_dot_o(); 252 if (D_depend) printf("%s: dot_o = \"%s\"\n", name, dot_o); 253 254 parse_dep(); 255 if (mak) scan_mak(mak, makout, dot_o); 256 if (out) output_dep(out); 257 258 if (delete) 259 unlink(*argv); 260 } 261 } 262 263 if (mak) finish_mak(mak, makout); 264 rename(shadow_mak_name, real_mak_name); 265 exit(0); 266usage: 267 fprintf(stderr, "usage: md -f -Dcdmot -m makefile -o outputfile -v <file1> ... <filen>\n"); 268 exit(1); 269} 270 271 272read_dep(file) 273register char *file; 274{ 275register int fd; 276register int size; 277struct stat statbuf; 278 279 if ((fd = open(file, 0)) < 0) { 280 fprintf(stderr, "%s: file = \"%s\" ", name, file); 281 perror("open"); 282 fflush(stdout), fflush(stderr); 283 return 0; 284 } 285 if (D_open) 286 printf("%s: opened dependency file \"%s\"\n", name, file); 287 288 if (fstat(fd, &statbuf) < 0) { 289 fprintf(stderr, "%s: file = \"%s\" ", name, file); 290 perror("stat"); 291 fflush(stdout), fflush(stderr); 292 goto out; 293 } 294 switch(statbuf.st_mode & S_IFMT) { 295 case S_IFREG: 296 if (D_time) 297 printf("%s: file time = %d\n", name, statbuf.st_mtime); 298 299 if (statbuf.st_size > IObuffer) { 300 fprintf(stderr, "%s: file \"%s\" tooo big for IObuffer\n", 301 name, file); 302 goto out; 303 } else if (force) 304 break; 305 else if ((int) mak && statbuf.st_mtime < makstat.st_mtime) { 306 if (verbose || D_time) 307 fprintf(stderr, "%s: skipping \"%s\" %d < %d \"%s\"\n", 308 name, file, statbuf.st_mtime, makstat.st_mtime, 309 real_mak_name); 310 goto out; 311 } else /* >= =>ok */ 312 break; 313 case S_IFDIR: 314 case S_IFLNK: 315 case S_IFCHR: 316 case S_IFBLK: 317 case S_IFSOCK: 318 default: 319 fprintf(stderr, "%s: bad mode: 0%o on \"%s\"\n", 320 name, statbuf.st_mode, file); 321 fflush(stdout), fflush(stderr); 322 goto out; 323 } 324 325 if ((size = read(fd, file_array, sizeof (file_array))) < 0) { 326 fprintf(stderr, "%s: file = \"%s\" ", name, file); 327 perror("read"); 328 fflush(stdout), fflush(stderr); 329 goto out; 330 } 331 file_array[size] = 0; 332 333 if (close(fd) < 0) { 334 fprintf(stderr, "%s: file = \"%s\" ", name, file); 335 perror("close"); 336 fflush(stdout), fflush(stderr); 337 return 0; 338 } 339 340 if (D_depend && D_contents) 341 printf("file_array: \"%s\"\n", file_array); 342 return size; 343out: ; 344 close(fd); 345 return 0; 346} 347 348save_dot_o() 349{ 350register char *cp = file_array; 351register char *svp = dot_o; 352register int c; 353 354 while ((*svp++ = (c = *cp++)) && c != ':'); 355 *svp = 0; 356} 357 358parse_dep() 359{ 360register char *lp = file_array; 361register int c; 362 363 while (*lp) {register char *tlp = lp; 364 register char *cp = dep_line; 365 register int i = 0; 366 int abspath = 0; 367 char oldc; 368 char *oldcp; 369 370 /* get a line to process */ 371 while ((c = *lp++) && c != '\n') 372 { 373 if (c == '\\') 374 lp++; /* skip backslash newline */ 375 else 376 *cp++ = c; 377 } 378 if (!c) 379 break; 380 *cp = 0; 381 cp = dep_line; 382 lp[-1] = 0; 383 /* skip .o file name */ 384 while ((c = *cp++) && c != ':'); if (!c) continue; 385next_filename: 386 i = 0; 387 abspath = 0; 388 while ((c = *cp) && (c == ' ' || c == '\t')) cp++; if (!c) continue; 389 390 /* canonicalization processing */ 391 392 /* initial / is remembered */ 393 if (c == '/') 394 abspath++; 395 396 while (c && c != ' ' && c != '\t') { 397 if (D_depend) printf("i = %d going \"%s\"\n", i, cp); 398 /* kill \'s */ 399 while ((c = *cp) && c == '/') cp++; if (!c) break; 400 path_component[i] = cp; 401 /* swallow chars till next / or null */ 402 while ((c = *cp++) && c != '/' && c != ' ' && c != '\t'); 403 if (c) cp[-1]=0;/* end component C style */ 404 405 /* ignore . */; 406 if (!strcmp(path_component[i], ".")) 407 ; /* if "component" != .. */ 408 else /* don't reduce /component/.. to nothing */ 409 i++; /* there could be symbolic links! */ 410 } 411 /* reassemble components */ 412 oldc = c; /* save c */ 413 oldcp = cp; /* save cp */ 414 cp = tlp; /* overwrite line in buffer */ 415 if (abspath) 416 *cp++ = '/'; 417 for (c=0; c<i; c++) {register char *ccp = path_component[c]; 418 while (*cp++ = *ccp++); 419 *--cp = '/'; 420 cp++; 421 } 422 *--cp = 0; 423 424 c=dep_file_index++; 425 dep_files[c].str = tlp; 426 dep_files[c].len = cp - tlp; 427 if (D_depend) 428 printf("%s: dep_file[%d] = \"%s\" Len %d\n", 429 name, dep_file_index - 1, tlp, cp - tlp); 430 tlp = cp + 1; 431 if (oldc) 432 { 433 cp = oldcp; 434 goto next_filename; 435 } 436 } 437} 438 439output_dep(out) 440FILE *out; 441{ 442register int j; 443register int size = 1000; 444register int dot_o_len = strlen(dot_o); 445register struct dep *dp = dep_files; 446int written = 0; 447 448 if (D_depend && debug) 449 for(j = 0; j < dep_file_index; j++) { 450 printf("dep_files[%d] = %s\n", j, dep_files[j].str); 451 } 452 453 qsort(dep_files, dep_file_index, sizeof (struct dep), qsort_strcmp); 454 455 if (D_depend && debug) 456 for(j = 0; j < dep_file_index; j++) { 457 printf("dep_files[%d] = %s\n", j, dep_files[j].str); 458 } 459 460 fprintf(out, "%s %s", SALUTATION, dot_o); 461 for(j = 0; j < dep_file_index; j++, dp++) 462 {register int len = dp->len; 463 register char *str = dp->str; 464 if (j && len == (dp-1)->len && !strcmp(str, (dp-1)->str)) 465 continue; 466 written++; 467 if (size + len + 1 > OUTLINELEN) { 468 fprintf(out, "\n%s %s", dot_o, str); 469 size = dot_o_len + len + 1; 470 } else { 471 fprintf(out, " %s", str); 472 size += len + 1; 473 } 474 } 475 fprintf(out, "\n"); 476 if (verbose) 477 fprintf(stdout, "%s: \"%s\" %d => %d\n", name, dot_o, dep_file_index, written); 478} 479 480 /* process makefile */ 481FILE * 482find_mak(file) 483char *file; 484{ 485FILE *mak; 486 487 if ((int) file) { 488 if ((mak = fopen(file, "r")) != NULL) { 489 real_mak_name = file; 490 } else if (update) { 491 mak_eof = 1; 492 real_mak_name = file; 493 return NULL; 494 } else { 495 fprintf(stderr, "%s: file = \"%s\" ", name, file); 496 perror("fopen"); 497 fflush(stdout), fflush(stderr); 498 return NULL; 499 } 500 } else { 501 if ((mak = fopen("makefile", "r")) != NULL) { 502 real_mak_name = "makefile"; 503 } else if ((mak = fopen("Makefile", "r")) != NULL) { 504 real_mak_name = "Makefile"; 505 } else return NULL; 506 } 507 508 if (fstat(fileno(mak), &makstat) < 0) { 509 fprintf(stderr, "%s: file = \"%s\" ", name, real_mak_name); 510 perror("stat"); 511 fflush(stdout), fflush(stderr); 512 return NULL; 513 } 514 if (D_open) 515 printf("%s: opened makefile \"%s\"\n", name, real_mak_name); 516 if (D_time) 517 printf("%s: makefile time = %d\n", name, makstat.st_mtime); 518 519 return mak; 520} 521 522FILE * 523temp_mak() 524{ 525FILE *mak; 526 527 strcpy(shadow_mak_name, real_mak_name); 528 strcat(shadow_mak_name, ".md"); 529 530 /* 531 * For SGS stuff, in case still linked to master version 532 */ 533 unlink(shadow_mak_name); 534 if ((mak = fopen(shadow_mak_name, "w")) == NULL) { 535 fprintf(stderr, "%s: file = \"%s\" ", name, shadow_mak_name); 536 perror("fopen"); 537 fflush(stdout), fflush(stderr); 538 return NULL; 539 } 540 if (D_open) 541 printf("%s: opened makefile.md \"%s\"\n", name, shadow_mak_name); 542 543 return mak; 544} 545 546skip_mak(makin, makout) 547register FILE *makin, *makout; 548{ 549register int len = SALUTATIONLEN; 550 551 if (D_make) 552 printf("skipping in \"%s\" ", real_mak_name); 553 554 while (fgets(makbuf, LINESIZE, makin) != NULL) { 555 if (D_make && D_contents) 556 printf("%s: \"%s\"\n", real_mak_name, makbuf); 557 if (strncmp(makbuf, SALUTATION, len)) { 558 fputs(makbuf, makout); 559 } else 560 break; 561 } 562 mak_eof = feof(makin); 563 if (mak_eof) 564 fclose(makin); 565 if (D_make) 566 printf("eof = %d str = \"%s\"", mak_eof, makbuf); 567} 568 569expunge_mak(makin, makout) 570register FILE *makin, *makout; 571{ 572register int len = SALUTATIONLEN; 573register int oldlen = OLDSALUTATIONLEN; 574 575 if (D_make) 576 printf("expunging in \"%s\" ", real_mak_name); 577 578 while (fgets(makbuf, LINESIZE, makin) != NULL) { 579 if (D_make && D_contents) 580 printf("%s: \"%s\"\n", real_mak_name, makbuf); 581 if (! strncmp(makbuf, SALUTATION, len) || 582 ! strncmp(makbuf, OLDSALUTATION, oldlen)) 583 break; 584 else 585 fputs(makbuf, makout); 586 } 587 mak_eof = 1; 588 if (mak_eof) 589 fclose(makin); 590 if (D_make) 591 printf("eof = %d str = \"%s\"", mak_eof, makbuf); 592} 593 594static void 595scan_mak(FILE *makin, FILE *makout, char *file) 596{ 597register char *cp = &makbuf[SALUTATIONLEN+1]; 598register int len = strlen(file); 599register int ret; 600 601 if (D_make) 602 printf("scanning in \"%s\" for \"%s\"\n", real_mak_name, file); 603 604 do { 605 if (mak_eof) /* don't scan any more */ 606 return; 607 608 ret = strncmp(cp, file, len); 609 if (D_make) 610 printf("saw \"%s\" ret = %d\n", cp, ret); 611 612 if (ret < 0) { /* skip forward till match or greater */ 613 fputs(makbuf, makout); /* line we're looking at */ 614 while (fgets(makbuf, LINESIZE, makin) != NULL) { 615 if (strncmp(makbuf, SALUTATION, SALUTATIONLEN)) { 616 fputs(makbuf, makout); 617 } else 618 break; 619 } 620 mak_eof = feof(makin); 621 if (mak_eof) 622 fclose(makin); 623 continue; 624 } else if (ret == 0) { /* flush match */ 625 while (fgets(makbuf, LINESIZE, makin) != NULL) { 626 if (strncmp(makbuf, SALUTATION, SALUTATIONLEN)) { 627 ; /* flush old stuff */ 628 } else 629 break; 630 } 631 mak_eof = feof(makin); 632 if (mak_eof) 633 fclose(makin); 634 break; 635 } else { /* no luck this time */ 636 break; 637 } 638 } while (1); 639} 640 641static void 642finish_mak(FILE *makin, FILE *makout) 643{ 644 if (mak_eof) /* don't scan any more */ 645 return; 646 647 if (D_make) 648 printf("finishing in \"%s\"\n", real_mak_name); 649 650 fputs(makbuf, makout); /* line we're looking at */ 651 while (fgets(makbuf, LINESIZE, makin) != NULL) { 652 fputs(makbuf, makout); 653 } 654} 655