/* ************************************************************************ *\
* *
* 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 *
* *
\* ************************************************************************ */
#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]; /* <foo.o>: 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 *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 */
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);
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; c<i; c++) {register char *ccp = path_component[c];
while (*cp++ = *ccp++);
*--cp = '/';
cp++;
}
*--cp = 0;
output_dep(out)
FILE *out;
{
register int j;
register int size = 1000;
register int dot_o_len = strlen(dot_o);
register struct dep *dp = dep_files;
int written = 0;
/*
* 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);
}
}