/************************************************************************* * * m a k e : m a k e . c * * Do the actual making for make plus system dependent stuff *======================================================================== * Edition history * * # Date Comments By * --- -------- ---------------------------------------------------- --- * 1 ?? ?? * 2 01.07.89 $<,$* bugs fixed RAL * 3 23.08.89 (time_t)time((time_t*)0) bug fixed, N_EXISTS added RAL * 4 30.08.89 leading sp. in cmd. output eliminated, indention ch. PSH,RAL * 5 03.09.89 :: time fixed, error output -> stderr, N_ERROR intr. * fixed LZ elimintaed RAL * 6 07.09.89 implmacro, DF macros,debug stuff added RAL * 7 09.09.89 tos support added PHH,RAL * 8 17.09.89 make1 arg. fixed, N_EXEC introduced RAL * ------------ Version 2.0 released ------------------------------- RAL * 18.05.90 fixed -n bug with silent rules. (Now echos them.) PAN * *************************************************************************/ #include "h.h" #include #include _PROTOTYPE(static void tellstatus, (FILE *out, char *name, int status)); static bool execflag; /* * Exec a shell that returns exit status correctly (/bin/esh). * The standard EON shell returns the process number of the last * async command, used by the debugger (ugg). * [exec on eon is like a fork+exec on unix] */ int dosh(string, shell) char *string; char *shell; { int number; #ifdef unix return system(string); #endif #ifdef tos return Tosexec(string); #endif #ifdef eon return ((number = execl(shell, shell,"-c", string, 0)) == -1) ? -1: /* couldn't start the shell */ wait(number); /* return its exit status */ #endif #ifdef os9 int status, pid; strcat(string, "\n"); if ((number = os9fork(shell, strlen(string), string, 0, 0, 0)) == -1) return -1; /* Couldn't start a shell */ do { if ((pid = wait(&status)) == -1) return -1; /* child already died!?!? */ } while (pid != number); return status; #endif } #ifdef unix /* * Make a file look very outdated after an error trying to make it. * Don't remove, this keeps hard links intact. (kjb) */ int makeold(name) char *name; { struct utimbuf a; a.actime = a.modtime = 0; /* The epoch */ return utime(name, &a); } #endif static void tellstatus(out, name, status) FILE *out; char *name; int status; { char cwd[PATH_MAX]; fprintf(out, "%s in %s: ", name, getcwd(cwd, sizeof(cwd)) == NULL ? "?" : cwd); if (WIFEXITED(status)) { fprintf(out, "Exit code %d", WEXITSTATUS(status)); } else { fprintf(out, "Signal %d%s", WTERMSIG(status), status & 0x80 ? " - core dumped" : ""); } } /* * Do commands to make a target */ void docmds1(np, lp) struct name *np; struct line *lp; { register char *q; register char *p; register struct cmd *cp; bool ssilent; bool signore; int estat; char *shell; if (*(shell = getmacro("SHELL")) == '\0') #ifdef eon shell = ":bin/esh"; #endif #ifdef unix shell = "/bin/sh"; #endif #ifdef os9 shell = "shell"; #endif #ifdef tos shell = "DESKTOP"; /* TOS has no shell */ #endif for (cp = lp->l_cmd; cp; cp = cp->c_next) { execflag = TRUE; strcpy(str1, cp->c_cmd); expmake = FALSE; expand(&str1s); q = str1; ssilent = silent; signore = ignore; while ((*q == '@') || (*q == '-')) { if (*q == '@') /* Specific silent */ ssilent = TRUE; else /* Specific ignore */ signore = TRUE; if (!domake) putchar(*q); /* Show all characters. */ q++; /* Not part of the command */ } for (p=q; *p; p++) { if (*p == '\n' && p[1] != '\0') { *p = ' '; if (!ssilent || !domake) fputs("\\\n", stdout); } else if (!ssilent || !domake) putchar(*p); } if (!ssilent || !domake) putchar('\n'); if (domake || expmake) { /* Get the shell to execute it */ fflush(stdout); if ((estat = dosh(q, shell)) != 0) { if (estat == -1) fatal("Couldn't execute %s", shell,0); else if (signore) { tellstatus(stdout, myname, estat); printf(" (Ignored)\n"); } else { tellstatus(stderr, myname, estat); fprintf(stderr, "\n"); if (!(np->n_flag & N_PREC)) #ifdef unix if (makeold(np->n_name) == 0) fprintf(stderr,"%s: made '%s' look old.\n", myname, np->n_name); #else if (unlink(np->n_name) == 0) fprintf(stderr,"%s: '%s' removed.\n", myname, np->n_name); #endif if (!conterr) exit(estat != 0); np->n_flag |= N_ERROR; return; } } } } } void docmds(np) struct name *np; { register struct line *lp; for (lp = np->n_line; lp; lp = lp->l_next) docmds1(np, lp); } #ifdef tos /* * execute the command submitted by make, * needed because TOS has no internal shell, * so we use Pexec to do the job * v 1.1 of 10/sep/89 by yeti */ #define DELM1 ';' #define DELM2 ' ' #define DELM3 ',' int Tosexec(string) char *string; { register char *help, *help2, c; register unsigned char l=1; char progname[80], command[255], plain[15]; static char **envp,*env; register int error,i; /* generate strange TOS environment (RAL) */ for ( i = 0, envp = environ; *envp; envp++) i += strlen(*envp) +1; if ((env = malloc(i+1)) == (char *)0) fatal("No memory for TOS environment",(char *)0,0); for ( envp = environ, help = env; *envp; envp++) { strcpy ( help, *envp); while ( *(help++)) ; } *help = '\0'; help = progname; while((*help++=*string++) != ' '); /* progname is command name */ *--help = '\0'; l = strlen(string); /* build option list */ command[0] = l; /* TOS likes it complicated */ strcpy(&command[1],string); if ((error = (int) Pexec(0,progname,command,env)) != -33) { free(env); return(error); } /* could'nt find program, try to search the PATH */ if((help=strrchr(progname,'\\')) != (char *) 0) /* just the */ strcpy(plain,++help); /* name */ else if((help=strrchr(progname,'/')) != (char *) 0) strcpy(plain,++help); else if((help=strrchr(progname,':')) != (char *) 0) strcpy(plain,++help); else strcpy(plain,progname); if(*(help=getmacro("PATH")) == '\0') { free(env); return(-33); } c = 1; while(c) { help2 = &progname[-1]; i = 0; while((c=*help++) != '\0' && i<80 && c != DELM1 && c != DELM2 && c != DELM3) *++help2 = c, i++; *++help2 = '\\'; strcpy(++help2,plain); if((error=(int) Pexec(0,progname,command,env))!=-33) { free(env); return(error); } } free(env); return(-33); } /* (stolen from ZOO -- thanks to Rahul Dehsi) Function mstonix() accepts an MSDOS format date and time and returns a **IX format time. No adjustment is done for timezone. */ time_t mstonix (date, time) unsigned int date, time; { int year, month, day, hour, min, sec, daycount; time_t longtime; /* no. of days to beginning of month for each month */ static int dsboy[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; if (date == 0 && time == 0) /* special case! */ return (0L); /* part of following code is common to zoolist.c */ year = (((unsigned int) date >> 9) & 0x7f) + 1980; month = ((unsigned int) date >> 5) & 0x0f; day = date & 0x1f; hour = ((unsigned int) time >> 11)& 0x1f; min = ((unsigned int) time >> 5) & 0x3f; sec = ((unsigned int) time & 0x1f) * 2; /* DEBUG and leap year fixes thanks to Mark Alexander */ #ifdef DEBUG printf ("mstonix: year=%d month=%d day=%d hour=%d min=%d sec=%d\n", year, month, day, hour, min, sec); #endif /* Calculate days since 1970/01/01 */ daycount = 365 * (year - 1970) + /* days due to whole years */ (year - 1969) / 4 + /* days due to leap years */ dsboy[month-1] + /* days since beginning of this year */ day-1; /* days since beginning of month */ if (year % 4 == 0 && year % 400 != 0 && month >= 3) /* if this is a leap year and month */ daycount++; /* is March or later, add a day */ /* Knowing the days, we can find seconds */ longtime = daycount * 24L * 60L * 60L + hour * 60L * 60L + min * 60 + sec; return (longtime); } #endif /* tos */ #ifdef os9 /* * Some stuffing around to get the modified time of a file * in an os9 file system */ void getmdate(fd, tbp) int fd; struct sgtbuf *tbp; { struct registers regs; static struct fildes fdbuf; regs.rg_a = fd; regs.rg_b = SS_FD; regs.rg_x = &fdbuf; regs.rg_y = sizeof (fdbuf); if (_os9(I_GETSTT, ®s) == -1) { errno = regs.rg_b & 0xff; return -1; } if (tbp) { _strass(tbp, fdbuf.fd_date, sizeof (fdbuf.fd_date)); tbp->t_second = 0; /* Files are only acurate to mins */ } return 0; } /* * Kludge routine to return an aproximation of how many * seconds since 1980. Dates will be in order, but will not * be lineer */ time_t cnvtime(tbp) struct sgtbuf *tbp; { long acc; acc = tbp->t_year - 80; /* Baseyear is 1980 */ acc = acc * 12 + tbp->t_month; acc = acc * 31 + tbp->t_day; acc = acc * 24 + tbp->t_hour; acc = acc * 60 + tbp->t_minute; acc = acc * 60 + tbp->t_second; return acc; } /* * Get the current time in the internal format */ void time(tp) time_t *tp; { struct sgtbuf tbuf; if (getime(&tbuf) < 0) return -1; if (tp) *tp = cnvtime(&tbuf); return 0; } #endif /* * Get the modification time of a file. If the first * doesn't exist, it's modtime is set to 0. */ void modtime(np) struct name *np; { #ifdef unix struct stat info; int r; if (is_archive_ref(np->n_name)) { r = archive_stat(np->n_name, &info); } else { r = stat(np->n_name, &info); } if (r < 0) { if (errno != ENOENT) fatal("Can't open %s: %s", np->n_name, errno); np->n_time = 0L; np->n_flag &= ~N_EXISTS; } else { np->n_time = info.st_mtime; np->n_flag |= N_EXISTS; } #endif #ifdef tos struct DOSTIME fm; int fd; if((fd=Fopen(np->n_name,0)) < 0) { np->n_time = 0L; np->n_flag &= ~N_EXISTS; } else { Fdatime(&fm,fd,0); Fclose(fd); np->n_time = mstonix((unsigned int)fm.date,(unsigned int)fm.time); np->n_flag |= N_EXISTS; } #endif #ifdef eon struct stat info; int fd; if ((fd = open(np->n_name, 0)) < 0) { if (errno != ER_NOTF) fatal("Can't open %s: %s", np->n_name, errno); np->n_time = 0L; np->n_flag &= ~N_EXISTS; } else if (getstat(fd, &info) < 0) fatal("Can't getstat %s: %s", np->n_name, errno); else { np->n_time = info.st_mod; np->n_flag |= N_EXISTS; } close(fd); #endif #ifdef os9 struct sgtbuf info; int fd; if ((fd = open(np->n_name, 0)) < 0) { if (errno != E_PNNF) fatal("Can't open %s: %s", np->n_name, errno); np->n_time = 0L; np->n_flag &= ~N_EXISTS; } else if (getmdate(fd, &info) < 0) fatal("Can't getstat %s: %s", np->n_name, errno); else { np->n_time = cnvtime(&info); np->n_flag |= N_EXISTS; } close(fd); #endif } /* * Update the mod time of a file to now. */ void touch(np) struct name *np; { char c; int fd; if (!domake || !silent) printf("touch(%s)\n", np->n_name); if (domake) { #ifdef unix struct utimbuf a; a.actime = a.modtime = time((time_t *)NULL); if (utime(np->n_name, &a) < 0) printf("%s: '%s' not touched - non-existant\n", myname, np->n_name); #endif #ifdef tos struct DOSTIME fm; int fd; if((fd=Fopen(np->n_name,0)) < 0) { printf("%s: '%s' not touched - non-existant\n", myname, np->n_name); } else { fm.date = Tgetdate(); fm.time = Tgettime(); Fdatime(&fm,fd,1); Fclose(fd); } #endif #ifdef eon if ((fd = open(np->n_name, 0)) < 0) printf("%s: '%s' not touched - non-existant\n", myname, np->n_name); else { uread(fd, &c, 1, 0); uwrite(fd, &c, 1); } close(fd); #endif #ifdef os9 /* * Strange that something almost as totally useless * as this is easy to do in os9! */ if ((fd = open(np->n_name, S_IWRITE)) < 0) printf("%s: '%s' not touched - non-existant\n", myname, np->n_name); close(fd); #endif } } /* * Recursive routine to make a target. */ int make(np, level) struct name *np; int level; { register struct depend *dp; register struct line *lp; register struct depend *qdp; time_t now, t, dtime = 0; bool dbgfirst = TRUE; char *basename = (char *) 0; char *inputname = (char *) 0; if (np->n_flag & N_DONE) { if(dbginfo) dbgprint(level,np,"already done"); return 0; } modtime(np); /* Gets modtime of this file */ while (time(&now) == np->n_time) { /* Time of target is equal to the current time. This bothers us, because * we can't tell if it needs to be updated if we update a file it depends * on within a second. So wait until the second is over. */ usleep(10000); } if (rules) { for (lp = np->n_line; lp; lp = lp->l_next) if (lp->l_cmd) break; if (!lp) dyndep(np,&basename,&inputname); } if (!(np->n_flag & (N_TARG | N_EXISTS))) { fprintf(stderr,"%s: Don't know how to make %s\n", myname, np->n_name); if (conterr) { np->n_flag |= N_ERROR; if (dbginfo) dbgprint(level,np,"don't know how to make"); return 0; } else exit(1); } for (qdp = (struct depend *)0, lp = np->n_line; lp; lp = lp->l_next) { for (dp = lp->l_dep; dp; dp = dp->d_next) { if(dbginfo && dbgfirst) { dbgprint(level,np," {"); dbgfirst = FALSE; } make(dp->d_name, level+1); if (np->n_time < dp->d_name->n_time) qdp = newdep(dp->d_name, qdp); dtime = max(dtime, dp->d_name->n_time); if (dp->d_name->n_flag & N_ERROR) np->n_flag |= N_ERROR; if (dp->d_name->n_flag & N_EXEC ) np->n_flag |= N_EXEC; } if (!quest && (np->n_flag & N_DOUBLE) && (np->n_time < dtime || !( np->n_flag & N_EXISTS))) { execflag = FALSE; make1(np, lp, qdp, basename, inputname); /* free()'s qdp */ dtime = 0; qdp = (struct depend *)0; if(execflag) np->n_flag |= N_EXEC; } } np->n_flag |= N_DONE; if (quest) { t = np->n_time; np->n_time = now; return (t < dtime); } else if ((np->n_time < dtime || !( np->n_flag & N_EXISTS)) && !(np->n_flag & N_DOUBLE)) { execflag = FALSE; make1(np, (struct line *)0, qdp, basename, inputname); /* free()'s qdp */ np->n_time = now; if ( execflag) np->n_flag |= N_EXEC; } else if ( np->n_flag & N_EXEC ) { np->n_time = now; } if (dbginfo) { if(dbgfirst) { if(np->n_flag & N_ERROR) dbgprint(level,np,"skipped because of error"); else if(np->n_flag & N_EXEC) dbgprint(level,np,"successfully made"); else dbgprint(level,np,"is up to date"); } else { if(np->n_flag & N_ERROR) dbgprint(level,(struct name *)0,"} skipped because of error"); else if(np->n_flag & N_EXEC) dbgprint(level,(struct name *)0,"} successfully made"); else dbgprint(level,(struct name *)0,"} is up to date"); } } if (level == 0 && !(np->n_flag & N_EXEC)) printf("%s: '%s' is up to date\n", myname, np->n_name); if(basename) free(basename); return 0; } void make1(np, lp, qdp, basename, inputname) struct name *np; struct line *lp; register struct depend *qdp; char *basename; char *inputname; { register struct depend *dp; size_t l1, l2; if (dotouch) touch(np); else if (!(np->n_flag & N_ERROR)) { strcpy(str1, ""); if(!inputname) { inputname = str1; /* default */ if (ambigmac) implmacros(np,lp,&basename,&inputname); } setDFmacro("<",inputname); if(!basename) basename = str1; setDFmacro("*",basename); for (dp = qdp; dp; dp = qdp) { l1= strlen(str1); l2= strlen(dp->d_name->n_name); while (l1 + 1 + l2 +1 > str1s.len) strrealloc(&str1s); if (strlen(str1)) strcat(str1, " "); strcat(str1, dp->d_name->n_name); qdp = dp->d_next; free(dp); } setmacro("?", str1); setDFmacro("@", np->n_name); if (lp) /* lp set if doing a :: rule */ docmds1(np, lp); else docmds(np); } } void implmacros(np,lp, pbasename,pinputname) struct name *np; struct line *lp; char **pbasename; /* Name without suffix */ char **pinputname; { struct line *llp; register char *p; register char *q; register char *suff; /* Old suffix */ int baselen; struct depend *dp; bool dpflag = FALSE; /* get basename out of target name */ p = str2; q = np->n_name; suff = suffix(q); while ( *q && (q < suff || !suff)) *p++ = *q++; *p = '\0'; if ((*pbasename = (char *) malloc(strlen(str2)+1)) == (char *)0 ) fatal("No memory for basename",(char *)0,0); strcpy(*pbasename,str2); baselen = strlen(str2); if ( lp) llp = lp; else llp = np->n_line; while (llp) { for (dp = llp->l_dep; dp; dp = dp->d_next) { if( strncmp(*pbasename,dp->d_name->n_name,baselen) == 0) { *pinputname = dp->d_name->n_name; return; } if( !dpflag) { *pinputname = dp->d_name->n_name; dpflag = TRUE; } } if (lp) break; llp = llp->l_next; } #if NO_WE_DO_WANT_THIS_BASENAME free(*pbasename); /* basename ambiguous or no dependency file */ *pbasename = (char *)0; #endif return; } void dbgprint(level,np,comment) int level; struct name *np; char *comment; { char *timep; if(np) { timep = ctime(&np->n_time); timep[24] = '\0'; fputs(&timep[4],stdout); } else fputs(" ",stdout); fputs(" ",stdout); while(level--) fputs(" ",stdout); if (np) { fputs(np->n_name,stdout); if (np->n_flag & N_DOUBLE) fputs(" :: ",stdout); else fputs(" : ",stdout); } fputs(comment,stdout); putchar((int)'\n'); fflush(stdout); return; }