/* ************************************************************************ *\ * * * File: md.c * * * * Updates makefiles from the .n dependency files generated by the * * -MD option to "cc" (and "cpp"). * * * * Abstract: * * * * Basically, "md" does two things: * * 1) It processes the raw dependency files produced by the cpp -MD * * option. There is one line in the file for every #include * * encountered, but there are repeats and patterns like * * .../dir1/../dir2 appear which should reduce to .../dir2 * * Md canonicalizes and flushes repeats from the dependency * * list. It also sorts the file names and "fills" them to a 78 * * character line. * * 2) Md also updates the makefile directly with the dependency * * information, so the .d file can be thrown away (-- -d option) * * This is done to save space. Md assumes that dependency * * information in the makefile is sorted by .o file name and it * * procedes to merge in (add/or replace [as appropriate]) the new * * dependency lines that it has generated. For time effeciency, * * Md assumes that any .d files it is given that were created * * before the creation date of the "makefile" were processed * * already. It ignores them unless the force flag (-f) is given. * * * * Arguments: * * * * -d delete the .d file after it is processed * * -f force an update of the dependencies in the makefile * * even though the makefile is more recent than the .n file * * (This implies that md has been run already.) * * -m specify the makefile to be upgraded. The defaults are * * "makefile" and then "Makefile". * * -u like -m above, but the file will be created if necessary * * -o specify an output file for the dependencies other than a * * makefile * * -v set the verbose flag * * -x expunge old dependency info from makefile * * -D subswitch for debugging. can be followed by any of * * "c", "d", "m", "o", "t", "D" meaning: * * c show file contents * * d show new dependency crunching * * m show generation of makefile * * o show files being opened * * t show time comparisons * * D show very low level debugging * * * * Author: Robert V. Baron * * Copyright (c) 1986 by Robert V. Baron * * * * HISTORY * * 29-Apr-87 Robert Baron (rvb) at Carnegie-Mellon University * If specified -u file does not exist, assume it is empty and * generate one. As a sanity check, it must be possible to create * the output file. * Also, generalized fix below to handle any case of . as a * file name. * * 25-Mar-87 Mary Thompson (mrt) at Carnegie Mellon * Fixed up pathnamecanonicalization to recognize .// and * drop the second / as well. mmax cpp generates this form. * * 6-Jan-87 Robert Baron (rvb) at Carnegie-Mellon University * Fixed up pathname canonicalization to that ../../, etc would be * handled correctly. * Also made "force" on by default. * * 16-Mar-86 Robert Baron (rvb) at Carnegie-Mellon University * Created 4/16/86 * * * \* ************************************************************************ */ #include #include #include #include #include #define LINESIZE 65536 // NeXT_MOD #define OUTLINELEN 79 #define IObuffer 50000 #define SALUTATION "# Dependencies for File:" #define SALUTATIONLEN (sizeof SALUTATION - 1) #define OLDSALUTATION "# DO NOT DELETE THIS LINE" #define OLDSALUTATIONLEN (sizeof OLDSALUTATION - 1) char file_array[IObuffer]; /* read file and store crunched names */ char dep_line[LINESIZE]; /* line being processed */ char dot_o[LINESIZE]; /* : prefix */ char *path_component[100]; /* stores components for a path while being crunched */ struct dep { /* stores paths that a file depends on */ int len; char *str; } dep_files[1000]; int dep_file_index; qsort_strcmp(a, b) struct dep *a, *b; { extern int strcmp(); return strcmp(a->str, b->str); } char *outfile = (char *) 0; /* generate dependency file */ FILE *out; char *makefile = (char *) 0; /* user supplied makefile name */ char *real_mak_name; /* actual makefile name (if not supplied) */ char shadow_mak_name[LINESIZE]; /* changes done here then renamed */ FILE *mak; /* for reading makefile */ FILE *makout; /* for writing shadow */ char makbuf[LINESIZE]; /* one line buffer for makefile */ struct stat makstat; /* stat of makefile for time comparisons */ int mak_eof = 0; /* eof seen on makefile */ FILE *find_mak(), *temp_mak(); int delete = 0; /* -d delete dependency file */ int debug = 0; int D_contents = 0; /* print file contents */ int D_depend = 0; /* print dependency processing info */ int D_make = 0; /* print makefile processing info */ int D_open = 0; /* print after succesful open */ int D_time = 0; /* print time comparison info */ int force = 1; /* always update dependency info */ int update = 0; /* it's ok if the -m file does not exist */ int verbose = 0; /* tell me something */ int expunge = 0; /* first flush dependency stuff from makefile */ char *name; static void scan_mak(FILE *, FILE *, char *); static void finish_mak(FILE *, FILE *); main(argc,argv) register char **argv; { int size; name = *argv; {register char *cp =name; while (*cp) if (*cp++ == '/') name = cp; } for ( argv++ ; --argc ; argv++ ) { register char *token = *argv; if (*token++ != '-' || !*token) break; else { register int flag; for ( ; flag = *token++ ; ) { switch (flag) { case 'd': delete++; break; case 'f': force++; break; case 'u': update++; case 'm': makefile = *++argv; if (--argc < 0) goto usage; break; case 'o': outfile = *++argv; if (--argc < 0) goto usage; break; case 'v': verbose++; break; case 'x': expunge++; break; case 'D': for ( ; flag = *token++ ; ) switch (flag) { case 'c': D_contents++; break; case 'd': D_depend++; break; case 'm': D_make++; break; case 'o': D_open++; break; case 't': D_time++; break; case 'D': debug++; break; default: goto letters; } goto newtoken; default: goto usage; } letters: ; } } newtoken: ; } if (!expunge && argc < 1) goto usage; if ((int) outfile && (int) makefile) /* not both */ goto usage; if ((int) outfile) { /* * NeXT_MOD, For SGS stuff, in case still linked to master version */ unlink(outfile); if ((out = fopen(outfile, "w")) == NULL) { fprintf(stderr, "%s: outfile = \"%s\" ", name, outfile); perror("fopen"); fflush(stdout), fflush(stderr); exit(1); } else if (D_open) printf("%s: opened outfile \"%s\"\n", name, outfile); } else if (mak = find_mak(makefile)) { makout = temp_mak(); out = makout; if (expunge) expunge_mak(mak, makout); else skip_mak(mak, makout); } else if (mak_eof && /* non existent file == mt file */ (int)(makout = temp_mak())) { /* but we need to be able */ out = makout; /* to write here */ } else if (makefile) { fprintf(stderr, "%s: makefile \"%s\" can not be opened or stat'ed\n", name, makefile); exit(2); } for (; argc--; argv++) { dep_file_index = 0; if (size = read_dep(*argv)) { save_dot_o(); if (D_depend) printf("%s: dot_o = \"%s\"\n", name, dot_o); parse_dep(); if (mak) scan_mak(mak, makout, dot_o); if (out) output_dep(out); if (delete) unlink(*argv); } } if (mak) finish_mak(mak, makout); rename(shadow_mak_name, real_mak_name); exit(0); usage: fprintf(stderr, "usage: md -f -Dcdmot -m makefile -o outputfile -v ... \n"); exit(1); } read_dep(file) register char *file; { register int fd; register int size; struct stat statbuf; if ((fd = open(file, 0)) < 0) { fprintf(stderr, "%s: file = \"%s\" ", name, file); perror("open"); fflush(stdout), fflush(stderr); return 0; } if (D_open) printf("%s: opened dependency file \"%s\"\n", name, file); if (fstat(fd, &statbuf) < 0) { fprintf(stderr, "%s: file = \"%s\" ", name, file); perror("stat"); fflush(stdout), fflush(stderr); goto out; } switch(statbuf.st_mode & S_IFMT) { case S_IFREG: if (D_time) printf("%s: file time = %d\n", name, statbuf.st_mtime); if (statbuf.st_size > IObuffer) { fprintf(stderr, "%s: file \"%s\" tooo big for IObuffer\n", name, file); goto out; } else if (force) break; else if ((int) mak && statbuf.st_mtime < makstat.st_mtime) { if (verbose || D_time) fprintf(stderr, "%s: skipping \"%s\" %d < %d \"%s\"\n", name, file, statbuf.st_mtime, makstat.st_mtime, real_mak_name); goto out; } else /* >= =>ok */ break; case S_IFDIR: case S_IFLNK: case S_IFCHR: case S_IFBLK: case S_IFSOCK: default: fprintf(stderr, "%s: bad mode: 0%o on \"%s\"\n", name, statbuf.st_mode, file); fflush(stdout), fflush(stderr); goto out; } if ((size = read(fd, file_array, sizeof (file_array))) < 0) { fprintf(stderr, "%s: file = \"%s\" ", name, file); perror("read"); fflush(stdout), fflush(stderr); goto out; } file_array[size] = 0; if (close(fd) < 0) { fprintf(stderr, "%s: file = \"%s\" ", name, file); perror("close"); fflush(stdout), fflush(stderr); return 0; } if (D_depend && D_contents) printf("file_array: \"%s\"\n", file_array); return size; out: ; close(fd); return 0; } save_dot_o() { register char *cp = file_array; register char *svp = dot_o; register int c; while ((*svp++ = (c = *cp++)) && c != ':'); *svp = 0; } parse_dep() { register char *lp = file_array; register int c; while (*lp) {register char *tlp = lp; register char *cp = dep_line; register int i = 0; int abspath = 0; char oldc; char *oldcp; /* get a line to process */ while ((c = *lp++) && c != '\n') { if (c == '\\') lp++; /* skip backslash newline */ else *cp++ = c; } if (!c) break; *cp = 0; cp = dep_line; lp[-1] = 0; /* skip .o file name */ while ((c = *cp++) && c != ':'); if (!c) continue; next_filename: i = 0; abspath = 0; while ((c = *cp) && (c == ' ' || c == '\t')) cp++; if (!c) continue; /* canonicalization processing */ /* initial / is remembered */ if (c == '/') abspath++; while (c && c != ' ' && c != '\t') { if (D_depend) printf("i = %d going \"%s\"\n", i, cp); /* kill \'s */ while ((c = *cp) && c == '/') cp++; if (!c) break; path_component[i] = cp; /* swallow chars till next / or null */ while ((c = *cp++) && c != '/' && c != ' ' && c != '\t'); if (c) cp[-1]=0;/* end component C style */ /* ignore . */; if (!strcmp(path_component[i], ".")) ; /* if "component" != .. */ else /* don't reduce /component/.. to nothing */ i++; /* there could be symbolic links! */ } /* reassemble components */ oldc = c; /* save c */ oldcp = cp; /* save cp */ cp = tlp; /* overwrite line in buffer */ if (abspath) *cp++ = '/'; for (c=0; clen; register char *str = dp->str; if (j && len == (dp-1)->len && !strcmp(str, (dp-1)->str)) continue; written++; if (size + len + 1 > OUTLINELEN) { fprintf(out, "\n%s %s", dot_o, str); size = dot_o_len + len + 1; } else { fprintf(out, " %s", str); size += len + 1; } } fprintf(out, "\n"); if (verbose) fprintf(stdout, "%s: \"%s\" %d => %d\n", name, dot_o, dep_file_index, written); } /* process makefile */ FILE * find_mak(file) char *file; { FILE *mak; if ((int) file) { if ((mak = fopen(file, "r")) != NULL) { real_mak_name = file; } else if (update) { mak_eof = 1; real_mak_name = file; return NULL; } else { fprintf(stderr, "%s: file = \"%s\" ", name, file); perror("fopen"); fflush(stdout), fflush(stderr); return NULL; } } else { if ((mak = fopen("makefile", "r")) != NULL) { real_mak_name = "makefile"; } else if ((mak = fopen("Makefile", "r")) != NULL) { real_mak_name = "Makefile"; } else return NULL; } if (fstat(fileno(mak), &makstat) < 0) { fprintf(stderr, "%s: file = \"%s\" ", name, real_mak_name); perror("stat"); fflush(stdout), fflush(stderr); return NULL; } if (D_open) printf("%s: opened makefile \"%s\"\n", name, real_mak_name); if (D_time) printf("%s: makefile time = %d\n", name, makstat.st_mtime); return mak; } FILE * temp_mak() { FILE *mak; strcpy(shadow_mak_name, real_mak_name); strcat(shadow_mak_name, ".md"); /* * For SGS stuff, in case still linked to master version */ unlink(shadow_mak_name); if ((mak = fopen(shadow_mak_name, "w")) == NULL) { fprintf(stderr, "%s: file = \"%s\" ", name, shadow_mak_name); perror("fopen"); fflush(stdout), fflush(stderr); return NULL; } if (D_open) printf("%s: opened makefile.md \"%s\"\n", name, shadow_mak_name); return mak; } skip_mak(makin, makout) register FILE *makin, *makout; { register int len = SALUTATIONLEN; if (D_make) printf("skipping in \"%s\" ", real_mak_name); while (fgets(makbuf, LINESIZE, makin) != NULL) { if (D_make && D_contents) printf("%s: \"%s\"\n", real_mak_name, makbuf); if (strncmp(makbuf, SALUTATION, len)) { fputs(makbuf, makout); } else break; } mak_eof = feof(makin); if (mak_eof) fclose(makin); if (D_make) printf("eof = %d str = \"%s\"", mak_eof, makbuf); } expunge_mak(makin, makout) register FILE *makin, *makout; { register int len = SALUTATIONLEN; register int oldlen = OLDSALUTATIONLEN; if (D_make) printf("expunging in \"%s\" ", real_mak_name); while (fgets(makbuf, LINESIZE, makin) != NULL) { if (D_make && D_contents) printf("%s: \"%s\"\n", real_mak_name, makbuf); if (! strncmp(makbuf, SALUTATION, len) || ! strncmp(makbuf, OLDSALUTATION, oldlen)) break; else fputs(makbuf, makout); } mak_eof = 1; if (mak_eof) fclose(makin); if (D_make) printf("eof = %d str = \"%s\"", mak_eof, makbuf); } static void scan_mak(FILE *makin, FILE *makout, char *file) { register char *cp = &makbuf[SALUTATIONLEN+1]; register int len = strlen(file); register int ret; if (D_make) printf("scanning in \"%s\" for \"%s\"\n", real_mak_name, file); do { if (mak_eof) /* don't scan any more */ return; ret = strncmp(cp, file, len); if (D_make) printf("saw \"%s\" ret = %d\n", cp, ret); if (ret < 0) { /* skip forward till match or greater */ fputs(makbuf, makout); /* line we're looking at */ while (fgets(makbuf, LINESIZE, makin) != NULL) { if (strncmp(makbuf, SALUTATION, SALUTATIONLEN)) { fputs(makbuf, makout); } else break; } mak_eof = feof(makin); if (mak_eof) fclose(makin); continue; } else if (ret == 0) { /* flush match */ while (fgets(makbuf, LINESIZE, makin) != NULL) { if (strncmp(makbuf, SALUTATION, SALUTATIONLEN)) { ; /* flush old stuff */ } else break; } mak_eof = feof(makin); if (mak_eof) fclose(makin); break; } else { /* no luck this time */ break; } } while (1); } static void finish_mak(FILE *makin, FILE *makout) { if (mak_eof) /* don't scan any more */ return; if (D_make) printf("finishing in \"%s\"\n", real_mak_name); fputs(makbuf, makout); /* line we're looking at */ while (fgets(makbuf, LINESIZE, makin) != NULL) { fputs(makbuf, makout); } }