]> git.saurik.com Git - apple/xnu.git/blob - SETUP/md/md.c
f253bf5fe90c86442ac5615b16de0028d33c35d4
[apple/xnu.git] / SETUP / md / md.c
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
91 char file_array[IObuffer]; /* read file and store crunched names */
92 char dep_line[LINESIZE]; /* line being processed */
93 char dot_o[LINESIZE]; /* <foo.o>: prefix */
94 char *path_component[100]; /* stores components for a path while being
95 crunched */
96
97 struct dep { /* stores paths that a file depends on */
98 int len;
99 char *str;
100 } dep_files[1000];
101 int dep_file_index;
102
103 qsort_strcmp(a, b)
104 struct dep *a, *b;
105 {
106 extern int strcmp();
107 return strcmp(a->str, b->str);
108 }
109
110 char *outfile = (char *) 0; /* generate dependency file */
111 FILE *out;
112
113 char *makefile = (char *) 0; /* user supplied makefile name */
114 char *real_mak_name; /* actual makefile name (if not supplied) */
115 char shadow_mak_name[LINESIZE]; /* changes done here then renamed */
116 FILE *mak; /* for reading makefile */
117 FILE *makout; /* for writing shadow */
118 char makbuf[LINESIZE]; /* one line buffer for makefile */
119 struct stat makstat; /* stat of makefile for time comparisons */
120 int mak_eof = 0; /* eof seen on makefile */
121 FILE *find_mak(), *temp_mak();
122
123 int delete = 0; /* -d delete dependency file */
124 int debug = 0;
125 int D_contents = 0; /* print file contents */
126 int D_depend = 0; /* print dependency processing info */
127 int D_make = 0; /* print makefile processing info */
128 int D_open = 0; /* print after succesful open */
129 int D_time = 0; /* print time comparison info */
130 int force = 1; /* always update dependency info */
131 int update = 0; /* it's ok if the -m file does not exist */
132 int verbose = 0; /* tell me something */
133 int expunge = 0; /* first flush dependency stuff from makefile */
134
135
136 char *name;
137
138 static void scan_mak(FILE *, FILE *, char *);
139 static void finish_mak(FILE *, FILE *);
140
141 main(argc,argv)
142 register char **argv;
143 {
144 int 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 }
207 letters: ;
208 }
209 }
210 newtoken: ;
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);
266 usage:
267 fprintf(stderr, "usage: md -f -Dcdmot -m makefile -o outputfile -v <file1> ... <filen>\n");
268 exit(1);
269 }
270
271
272 read_dep(file)
273 register char *file;
274 {
275 register int fd;
276 register int size;
277 struct 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;
343 out: ;
344 close(fd);
345 return 0;
346 }
347
348 save_dot_o()
349 {
350 register char *cp = file_array;
351 register char *svp = dot_o;
352 register int c;
353
354 while ((*svp++ = (c = *cp++)) && c != ':');
355 *svp = 0;
356 }
357
358 parse_dep()
359 {
360 register char *lp = file_array;
361 register 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;
385 next_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
439 output_dep(out)
440 FILE *out;
441 {
442 register int j;
443 register int size = 1000;
444 register int dot_o_len = strlen(dot_o);
445 register struct dep *dp = dep_files;
446 int 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 */
481 FILE *
482 find_mak(file)
483 char *file;
484 {
485 FILE *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
522 FILE *
523 temp_mak()
524 {
525 FILE *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
546 skip_mak(makin, makout)
547 register FILE *makin, *makout;
548 {
549 register 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
569 expunge_mak(makin, makout)
570 register FILE *makin, *makout;
571 {
572 register int len = SALUTATIONLEN;
573 register 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
594 static void
595 scan_mak(FILE *makin, FILE *makout, char *file)
596 {
597 register char *cp = &makbuf[SALUTATIONLEN+1];
598 register int len = strlen(file);
599 register 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
641 static void
642 finish_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 }