📄 job.c
字号:
* A new Job node is created and added to the list of running * jobs. PMake is forked and a child shell created. *----------------------------------------------------------------------- */static intJobStart (gn, flags, previous) GNode *gn; /* target to create */ short flags; /* flags for the job to override normal ones. * e.g. JOB_SPECIAL or JOB_IGNDOTS */ Job *previous; /* The previous Job structure for this node, * if any. */{ register Job *job; /* new job descriptor */ char *argv[4]; /* Argument vector to shell */ static int jobno = 0; /* job number of catching output in a file */ Boolean cmdsOK; /* true if the nodes commands were all right */ Boolean local; /* Set true if the job was run locally */ Boolean noExec; /* Set true if we decide not to run the job */ if (previous != (Job *)NULL) { previous->flags &= ~ (JOB_FIRST|JOB_IGNERR|JOB_SILENT|JOB_REMOTE); job = previous; } else { job = (Job *) emalloc (sizeof (Job)); if (job == (Job *)NULL) { Punt("JobStart out of memory"); } flags |= JOB_FIRST; } job->node = gn; job->tailCmds = NILLNODE; /* * Set the initial value of the flags for this job based on the global * ones and the node's attributes... Any flags supplied by the caller * are also added to the field. */ job->flags = 0; if (Targ_Ignore (gn)) { job->flags |= JOB_IGNERR; } if (Targ_Silent (gn)) { job->flags |= JOB_SILENT; } job->flags |= flags; /* * Check the commands now so any attributes from .DEFAULT have a chance * to migrate to the node */ if (job->flags & JOB_FIRST) { cmdsOK = Job_CheckCommands(gn, Error); } else { cmdsOK = TRUE; } /* * If the -n flag wasn't given, we open up OUR (not the child's) * temporary file to stuff commands in it. The thing is rd/wr so we don't * need to reopen it to feed it to the shell. If the -n flag *was* given, * we just set the file to be stdout. Cute, huh? */ if ((gn->type & OP_MAKE) || (!noExecute && !touchFlag)) { /* * We're serious here, but if the commands were bogus, we're * also dead... */ if (!cmdsOK) { DieHorribly(); } job->cmdFILE = fopen (tfile, "w+"); if (job->cmdFILE == (FILE *) NULL) { Punt ("Could not open %s", tfile); } fcntl(fileno(job->cmdFILE), F_SETFD, 1); /* * Send the commands to the command file, flush all its buffers then * rewind and remove the thing. */ noExec = FALSE; /* * used to be backwards; replace when start doing multiple commands * per shell. */ if (compatMake) { /* * Be compatible: If this is the first time for this node, * verify its commands are ok and open the commands list for * sequential access by later invocations of JobStart. * Once that is done, we take the next command off the list * and print it to the command file. If the command was an * ellipsis, note that there's nothing more to execute. */ if ((job->flags&JOB_FIRST) && (Lst_Open(gn->commands) != SUCCESS)){ cmdsOK = FALSE; } else { LstNode ln = Lst_Next (gn->commands); if ((ln == NILLNODE) || JobPrintCommand ((char *)Lst_Datum (ln), job)) { noExec = TRUE; Lst_Close (gn->commands); } if (noExec && !(job->flags & JOB_FIRST)) { /* * If we're not going to execute anything, the job * is done and we need to close down the various * file descriptors we've opened for output, then * call JobDoOutput to catch the final characters or * send the file to the screen... Note that the i/o streams * are only open if this isn't the first job. * Note also that this could not be done in * Job_CatchChildren b/c it wasn't clear if there were * more commands to execute or not... */ if (usePipes) {#ifdef RMT_WILL_WATCH Rmt_Ignore(job->inPipe);#else FD_CLR(job->inPipe, &outputs);#endif if (job->outPipe != job->inPipe) { (void)close (job->outPipe); } JobDoOutput (job, TRUE); (void)close (job->inPipe); } else { (void)close (job->outFd); JobDoOutput (job, TRUE); } } } } else { /* * We can do all the commands at once. hooray for sanity */ numCommands = 0; Lst_ForEach (gn->commands, JobPrintCommand, (ClientData)job); /* * If we didn't print out any commands to the shell script, * there's not much point in executing the shell, is there? */ if (numCommands == 0) { noExec = TRUE; } } } else if (noExecute) { /* * Not executing anything -- just print all the commands to stdout * in one fell swoop. This will still set up job->tailCmds correctly. */ if (lastNode != gn) { printf (targFmt, gn->name); lastNode = gn; } job->cmdFILE = stdout; /* * Only print the commands if they're ok, but don't die if they're * not -- just let the user know they're bad and keep going. It * doesn't do any harm in this case and may do some good. */ if (cmdsOK) { Lst_ForEach(gn->commands, JobPrintCommand, (ClientData)job); } /* * Don't execute the shell, thank you. */ noExec = TRUE; } else { /* * Just touch the target and note that no shell should be executed. * Set cmdFILE to stdout to make life easier. Check the commands, too, * but don't die if they're no good -- it does no harm to keep working * up the graph. */ job->cmdFILE = stdout; Job_Touch (gn, job->flags&JOB_SILENT); noExec = TRUE; } /* * If we're not supposed to execute a shell, don't. */ if (noExec) { /* * Unlink and close the command file if we opened one */ if (job->cmdFILE != stdout) { (void) unlink (tfile); fclose(job->cmdFILE); } else { fflush (stdout); } /* * We only want to work our way up the graph if we aren't here because * the commands for the job were no good. */ if (cmdsOK) { if (aborting == 0) { if (job->tailCmds != NILLNODE) { Lst_ForEachFrom(job->node->commands, job->tailCmds, JobSaveCommand, (ClientData)job->node); } Make_Update(job->node); } free((Address)job); return(JOB_FINISHED); } else { free((Address)job); return(JOB_ERROR); } } else { fflush (job->cmdFILE); (void) unlink (tfile); } /* * Set up the control arguments to the shell. This is based on the flags * set earlier for this job. */ JobMakeArgv(job, argv); /* * If we're using pipes to catch output, create the pipe by which we'll * get the shell's output. If we're using files, print out that we're * starting a job and then set up its temporary-file name. This is just * tfile with two extra digits tacked on -- jobno. */ if (job->flags & JOB_FIRST) { if (usePipes) { int fd[2]; (void) pipe(fd); job->inPipe = fd[0]; job->outPipe = fd[1]; (void)fcntl (job->inPipe, F_SETFD, 1); (void)fcntl (job->outPipe, F_SETFD, 1); } else { printf ("Remaking `%s'\n", gn->name); fflush (stdout); sprintf (job->outFile, "%s%02d", tfile, jobno); jobno = (jobno + 1) % 100; job->outFd = open(job->outFile,O_WRONLY|O_CREAT|O_APPEND,0600); (void)fcntl (job->outFd, F_SETFD, 1); } } local = TRUE; if (local && (((nLocal >= maxLocal) && !(job->flags & JOB_SPECIAL) && (maxLocal != 0)))) { /* * The job can only be run locally, but we've hit the limit of * local concurrency, so put the job on hold until some other job * finishes. Note that the special jobs (.BEGIN, .INTERRUPT and .END) * may be run locally even when the local limit has been reached * (e.g. when maxLocal == 0), though they will be exported if at * all possible. */ jobFull = TRUE; if (DEBUG(JOB)) { printf("Can only run job locally.\n"); } job->flags |= JOB_RESTART; (void)Lst_AtEnd(stoppedJobs, (ClientData)job); } else { if ((nLocal >= maxLocal) && local) { /* * If we're running this job locally as a special case (see above), * at least say the table is full. */ jobFull = TRUE; if (DEBUG(JOB)) { printf("Local job queue is full.\n"); } } JobExec(job, argv); } return(JOB_RUNNING);}/*- *----------------------------------------------------------------------- * JobDoOutput -- * This function is called at different times depending on * whether the user has specified that output is to be collected * via pipes or temporary files. In the former case, we are called * whenever there is something to read on the pipe. We collect more * output from the given job and store it in the job's outBuf. If * this makes up a line, we print it tagged by the job's identifier, * as necessary. * If output has been collected in a temporary file, we open the * file and read it line by line, transfering it to our own * output channel until the file is empty. At which point we * remove the temporary file. * In both cases, however, we keep our figurative eye out for the * 'noPrint' line for the shell from which the output came. If * we recognize a line, we don't print it. If the command is not * alone on the line (the character after it is not \0 or \n), we * do print whatever follows it. * * Results: * None * * Side Effects: * curPos may be shifted as may the contents of outBuf. *----------------------------------------------------------------------- */static voidJobDoOutput (job, finish) register Job *job; /* the job whose output needs printing */ Boolean finish; /* TRUE if this is the last time we'll be * called for this job */{ Boolean gotNL = FALSE; /* true if got a newline */ register int nr; /* number of bytes read */ register int i; /* auxiliary index into outBuf */ register int max; /* limit for i (end of current data) */ int nRead; /* (Temporary) number of bytes read */ FILE *oFILE; /* Stream pointer to shell's output file */ char inLine[132]; if (usePipes) { /* * Read as many bytes as will fit in the buffer. */end_loop: nRead = read (job->inPipe, &job->outBuf[job->curPos], JOB_BUFSIZE - job->curPos); if (nRead < 0) { if (DEBUG(JOB)) { perror("JobDoOutput(piperead)"); } nr = 0; } else { nr = nRead; } /* * If we hit the end-of-file (the job is dead), we must flush its * remaining output, so pretend we read a newline if there's any * output remaining in the buffer. * Also clear the 'finish' flag so we stop looping. */ if ((nr == 0) && (job->curPos != 0)) { job->outBuf[job->curPos] = '\n'; nr = 1; finish = FALSE; } else if (nr == 0) { finish = FALSE; } /* * Look for the last newline in the bytes we just got. If there is * one, break out of the loop with 'i' as its index and gotNL set * TRUE. */ max = job->curPos + nr; for (i = job->curPos + nr - 1; i >= job->curPos; i--) { if (job->outBuf[i] == '\n') { gotNL = TRUE; break; } else if (job->outBuf[i] == '\0') { /* * Why? */ job->outBuf[i] = ' '; } } if (!gotNL) { job->curPos += nr; if (job->curPos == JOB_BUFSIZE) { /* * If we've run out of buffer space, we have no choice * but to print the stuff. sigh. */ gotNL = TRUE; i = job->curPos; } } if (gotNL) { /* * Need to send the output to the screen. Null terminate it * first, overwriting the newline character if there was one. * So long as the line isn't one we should filter (according * to the shell description), we print the line, preceeded * by a target banner if this target isn't the same as the * one for which we last printed something. * The rest of the data in the buffer are then shifted down * to the start of the buffer and curPos is set accordingly. */ job->outBuf[i] = '\0'; if (i >= job->curPos) { register char *cp, *ecp; cp = job->outBuf; if (commandShell->noPrint) { ecp = Str_FindSubstring(job->outBuf, commandShell->noPrint); while (ecp != (char *)NULL) { if (cp != ecp) { *ecp = '\0'; if (job->node != lastNode) { printf (targFmt, job->node->name); lastNode = job->node; } /* * The only way there wouldn't be a newline after * this line is if it were the last in the buffer. * however, since the non-printable comes after it, * there must be a newline, so we don't print one. */ printf ("%s", cp); } cp = ecp + commandShell->noPLen; if (cp != &job->outBuf[i]) { /* * Still more to print, look again after skipping * the whitespace following the non-printable * command.... */ cp++; while (*cp == ' ' || *cp == '\t' || *cp == '\n') { cp++; } ecp = Str_FindSubstring (cp, commandShell->noPrint); } else { break; } } } /* * There's still more in that thar buffer. This time, though, * we know there's no newline at the end, so we add one of * our own free will. */ if (*cp != '\0') {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -