Annotation of src/usr.bin/make/meta.c, Revision 1.54
1.54 ! sjg 1: /* $NetBSD: meta.c,v 1.53 2016/03/07 21:45:43 christos Exp $ */
1.15 sjg 2:
1.1 sjg 3: /*
4: * Implement 'meta' mode.
5: * Adapted from John Birrell's patches to FreeBSD make.
6: * --sjg
7: */
8: /*
1.48 sjg 9: * Copyright (c) 2009-2016, Juniper Networks, Inc.
1.1 sjg 10: * Portions Copyright (c) 2009, John Birrell.
11: *
12: * Redistribution and use in source and binary forms, with or without
13: * modification, are permitted provided that the following conditions
14: * are met:
15: * 1. Redistributions of source code must retain the above copyright
16: * notice, this list of conditions and the following disclaimer.
17: * 2. Redistributions in binary form must reproduce the above copyright
18: * notice, this list of conditions and the following disclaimer in the
19: * documentation and/or other materials provided with the distribution.
20: *
21: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32: */
1.3 sjg 33: #if defined(USE_META)
1.1 sjg 34:
35: #ifdef HAVE_CONFIG_H
36: # include "config.h"
37: #endif
38: #include <sys/stat.h>
39: #include <sys/ioctl.h>
40: #include <libgen.h>
41: #include <errno.h>
1.2 sjg 42: #if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H)
1.1 sjg 43: #include <err.h>
1.2 sjg 44: #endif
1.1 sjg 45:
46: #include "make.h"
47: #include "job.h"
48:
49: #ifdef HAVE_FILEMON_H
50: # include <filemon.h>
51: #endif
52: #if !defined(USE_FILEMON) && defined(FILEMON_SET_FD)
53: # define USE_FILEMON
54: #endif
55:
56: static BuildMon Mybm; /* for compat */
1.32 sjg 57: static Lst metaBailiwick; /* our scope of control */
1.53 christos 58: static char *metaBailiwickStr; /* string storage for the list */
1.32 sjg 59: static Lst metaIgnorePaths; /* paths we deliberately ignore */
1.53 christos 60: static char *metaIgnorePathsStr; /* string storage for the list */
1.32 sjg 61:
62: #ifndef MAKE_META_IGNORE_PATHS
63: #define MAKE_META_IGNORE_PATHS ".MAKE.META.IGNORE_PATHS"
64: #endif
1.1 sjg 65:
66: Boolean useMeta = FALSE;
67: static Boolean useFilemon = FALSE;
68: static Boolean writeMeta = FALSE;
69: static Boolean metaEnv = FALSE; /* don't save env unless asked */
70: static Boolean metaVerbose = FALSE;
71: static Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */
1.12 sjg 72: static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */
1.22 sjg 73: static Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */
1.1 sjg 74:
75: extern Boolean forceJobs;
76: extern Boolean comatMake;
1.25 sjg 77: extern char **environ;
1.1 sjg 78:
79: #define MAKE_META_PREFIX ".MAKE.META.PREFIX"
80:
81: #ifndef N2U
82: # define N2U(n, u) (((n) + ((u) - 1)) / (u))
83: #endif
84: #ifndef ROUNDUP
85: # define ROUNDUP(n, u) (N2U((n), (u)) * (u))
86: #endif
87:
1.2 sjg 88: #if !defined(HAVE_STRSEP)
89: # define strsep(s, d) stresep((s), (d), 0)
90: #endif
91:
1.1 sjg 92: /*
93: * Filemon is a kernel module which snoops certain syscalls.
94: *
95: * C chdir
96: * E exec
97: * F [v]fork
98: * L [sym]link
99: * M rename
100: * R read
101: * W write
102: * S stat
103: *
104: * See meta_oodate below - we mainly care about 'E' and 'R'.
105: *
106: * We can still use meta mode without filemon, but
107: * the benefits are more limited.
108: */
109: #ifdef USE_FILEMON
110: # ifndef _PATH_FILEMON
111: # define _PATH_FILEMON "/dev/filemon"
112: # endif
113:
114: /*
115: * Open the filemon device.
116: */
117: static void
118: filemon_open(BuildMon *pbm)
119: {
120: int retry;
121:
122: pbm->mon_fd = pbm->filemon_fd = -1;
123: if (!useFilemon)
124: return;
125:
126: for (retry = 5; retry >= 0; retry--) {
127: if ((pbm->filemon_fd = open(_PATH_FILEMON, O_RDWR)) >= 0)
128: break;
129: }
130:
131: if (pbm->filemon_fd < 0) {
132: useFilemon = FALSE;
133: warn("Could not open %s", _PATH_FILEMON);
134: return;
135: }
136:
137: /*
138: * We use a file outside of '.'
139: * to avoid a FreeBSD kernel bug where unlink invalidates
140: * cwd causing getcwd to do a lot more work.
141: * We only care about the descriptor.
142: */
143: pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL);
144: if (ioctl(pbm->filemon_fd, FILEMON_SET_FD, &pbm->mon_fd) < 0) {
145: err(1, "Could not set filemon file descriptor!");
146: }
147: /* we don't need these once we exec */
1.42 christos 148: (void)fcntl(pbm->mon_fd, F_SETFD, FD_CLOEXEC);
149: (void)fcntl(pbm->filemon_fd, F_SETFD, FD_CLOEXEC);
1.1 sjg 150: }
151:
152: /*
153: * Read the build monitor output file and write records to the target's
154: * metadata file.
155: */
156: static void
157: filemon_read(FILE *mfp, int fd)
158: {
159: char buf[BUFSIZ];
1.35 sjg 160: int n;
1.1 sjg 161:
162: /* Check if we're not writing to a meta data file.*/
163: if (mfp == NULL) {
164: if (fd >= 0)
165: close(fd); /* not interested */
166: return;
167: }
168: /* rewind */
1.24 christos 169: (void)lseek(fd, (off_t)0, SEEK_SET);
1.1 sjg 170:
1.36 sjg 171: fprintf(mfp, "\n-- filemon acquired metadata --\n");
1.1 sjg 172:
1.35 sjg 173: while ((n = read(fd, buf, sizeof(buf))) > 0) {
174: fwrite(buf, 1, n, mfp);
1.1 sjg 175: }
176: fflush(mfp);
1.35 sjg 177: close(fd);
1.1 sjg 178: }
179: #endif
180:
1.8 sjg 181: /*
182: * when realpath() fails,
183: * we use this, to clean up ./ and ../
184: */
185: static void
186: eat_dots(char *buf, size_t bufsz, int dots)
187: {
188: char *cp;
189: char *cp2;
190: const char *eat;
191: size_t eatlen;
192:
193: switch (dots) {
194: case 1:
195: eat = "/./";
196: eatlen = 2;
197: break;
198: case 2:
199: eat = "/../";
200: eatlen = 3;
201: break;
202: default:
203: return;
204: }
205:
206: do {
207: cp = strstr(buf, eat);
208: if (cp) {
209: cp2 = cp + eatlen;
210: if (dots == 2 && cp > buf) {
211: do {
212: cp--;
213: } while (cp > buf && *cp != '/');
214: }
215: if (*cp == '/') {
216: strlcpy(cp, cp2, bufsz - (cp - buf));
217: } else {
218: return; /* can't happen? */
219: }
220: }
221: } while (cp);
222: }
223:
1.1 sjg 224: static char *
225: meta_name(struct GNode *gn, char *mname, size_t mnamelen,
226: const char *dname,
227: const char *tname)
228: {
229: char buf[MAXPATHLEN];
230: char cwd[MAXPATHLEN];
231: char *rp;
232: char *cp;
233: char *tp;
234: char *p[4]; /* >= number of possible uses */
235: int i;
236:
237: i = 0;
238: if (!dname)
239: dname = Var_Value(".OBJDIR", gn, &p[i++]);
240: if (!tname)
241: tname = Var_Value(TARGET, gn, &p[i++]);
242:
1.8 sjg 243: if (realpath(dname, cwd))
244: dname = cwd;
245:
1.1 sjg 246: /*
247: * Weed out relative paths from the target file name.
248: * We have to be careful though since if target is a
249: * symlink, the result will be unstable.
250: * So we use realpath() just to get the dirname, and leave the
251: * basename as given to us.
252: */
253: if ((cp = strrchr(tname, '/'))) {
254: if (realpath(tname, buf)) {
255: if ((rp = strrchr(buf, '/'))) {
256: rp++;
257: cp++;
258: if (strcmp(cp, rp) != 0)
259: strlcpy(rp, cp, sizeof(buf) - (rp - buf));
260: }
261: tname = buf;
1.8 sjg 262: } else {
263: /*
264: * We likely have a directory which is about to be made.
265: * We pretend realpath() succeeded, to have a chance
266: * of generating the same meta file name that we will
267: * next time through.
268: */
269: if (tname[0] == '/') {
270: strlcpy(buf, tname, sizeof(buf));
271: } else {
272: snprintf(buf, sizeof(buf), "%s/%s", cwd, tname);
273: }
274: eat_dots(buf, sizeof(buf), 1); /* ./ */
275: eat_dots(buf, sizeof(buf), 2); /* ../ */
276: tname = buf;
1.1 sjg 277: }
278: }
279: /* on some systems dirname may modify its arg */
280: tp = bmake_strdup(tname);
281: if (strcmp(dname, dirname(tp)) == 0)
282: snprintf(mname, mnamelen, "%s.meta", tname);
283: else {
284: snprintf(mname, mnamelen, "%s/%s.meta", dname, tname);
285:
286: /*
287: * Replace path separators in the file name after the
288: * current object directory path.
289: */
290: cp = mname + strlen(dname) + 1;
291:
292: while (*cp != '\0') {
293: if (*cp == '/')
294: *cp = '_';
295: cp++;
296: }
297: }
298: free(tp);
299: for (i--; i >= 0; i--) {
1.44 christos 300: free(p[i]);
1.1 sjg 301: }
302: return (mname);
303: }
304:
305: /*
306: * Return true if running ${.MAKE}
307: * Bypassed if target is flagged .MAKE
308: */
309: static int
310: is_submake(void *cmdp, void *gnp)
311: {
312: static char *p_make = NULL;
313: static int p_len;
314: char *cmd = cmdp;
315: GNode *gn = gnp;
316: char *mp = NULL;
317: char *cp;
318: char *cp2;
319: int rc = 0; /* keep looking */
320:
321: if (!p_make) {
322: p_make = Var_Value(".MAKE", gn, &cp);
323: p_len = strlen(p_make);
324: }
325: cp = strchr(cmd, '$');
326: if ((cp)) {
1.47 sjg 327: mp = Var_Subst(NULL, cmd, gn, VARF_WANTRES);
1.1 sjg 328: cmd = mp;
329: }
330: cp2 = strstr(cmd, p_make);
331: if ((cp2)) {
332: switch (cp2[p_len]) {
333: case '\0':
334: case ' ':
335: case '\t':
336: case '\n':
337: rc = 1;
338: break;
339: }
340: if (cp2 > cmd && rc > 0) {
341: switch (cp2[-1]) {
342: case ' ':
343: case '\t':
344: case '\n':
345: break;
346: default:
347: rc = 0; /* no match */
348: break;
349: }
350: }
351: }
1.44 christos 352: free(mp);
1.1 sjg 353: return (rc);
354: }
355:
356: typedef struct meta_file_s {
357: FILE *fp;
358: GNode *gn;
359: } meta_file_t;
360:
361: static int
362: printCMD(void *cmdp, void *mfpp)
363: {
364: meta_file_t *mfp = mfpp;
365: char *cmd = cmdp;
366: char *cp = NULL;
367:
368: if (strchr(cmd, '$')) {
1.47 sjg 369: cmd = cp = Var_Subst(NULL, cmd, mfp->gn, VARF_WANTRES);
1.1 sjg 370: }
371: fprintf(mfp->fp, "CMD %s\n", cmd);
1.44 christos 372: free(cp);
1.1 sjg 373: return 0;
374: }
375:
376: /*
377: * Certain node types never get a .meta file
378: */
379: #define SKIP_META_TYPE(_type) do { \
380: if ((gn->type & __CONCAT(OP_, _type))) { \
381: if (DEBUG(META)) { \
382: fprintf(debug_file, "Skipping meta for %s: .%s\n", \
383: gn->name, __STRING(_type)); \
384: } \
385: return (NULL); \
386: } \
387: } while (0)
388:
389: static FILE *
390: meta_create(BuildMon *pbm, GNode *gn)
391: {
392: meta_file_t mf;
393: char buf[MAXPATHLEN];
394: char objdir[MAXPATHLEN];
395: char **ptr;
396: const char *dname;
397: const char *tname;
398: char *fname;
399: const char *cp;
400: char *p[4]; /* >= possible uses */
401: int i;
402: struct stat fs;
403:
404:
405: /* This may be a phony node which we don't want meta data for... */
406: /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */
407: /* Or it may be explicitly flagged as .NOMETA */
408: SKIP_META_TYPE(NOMETA);
409: /* Unless it is explicitly flagged as .META */
410: if (!(gn->type & OP_META)) {
411: SKIP_META_TYPE(PHONY);
412: SKIP_META_TYPE(SPECIAL);
413: SKIP_META_TYPE(MAKE);
414: }
415:
416: mf.fp = NULL;
417:
418: i = 0;
419:
420: dname = Var_Value(".OBJDIR", gn, &p[i++]);
421: tname = Var_Value(TARGET, gn, &p[i++]);
422:
423: /* The object directory may not exist. Check it.. */
424: if (stat(dname, &fs) != 0) {
425: if (DEBUG(META))
426: fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n",
427: gn->name);
428: goto out;
429: }
430: /* Check if there are no commands to execute. */
431: if (Lst_IsEmpty(gn->commands)) {
432: if (DEBUG(META))
433: fprintf(debug_file, "Skipping meta for %s: no commands\n",
434: gn->name);
435: goto out;
436: }
437:
438: /* make sure these are canonical */
439: if (realpath(dname, objdir))
440: dname = objdir;
441:
442: /* If we aren't in the object directory, don't create a meta file. */
1.12 sjg 443: if (!metaCurdirOk && strcmp(curdir, dname) == 0) {
1.1 sjg 444: if (DEBUG(META))
445: fprintf(debug_file, "Skipping meta for %s: .OBJDIR == .CURDIR\n",
446: gn->name);
447: goto out;
448: }
449: if (!(gn->type & OP_META)) {
450: /* We do not generate .meta files for sub-makes */
451: if (Lst_ForEach(gn->commands, is_submake, gn)) {
452: if (DEBUG(META))
453: fprintf(debug_file, "Skipping meta for %s: .MAKE\n",
454: gn->name);
455: goto out;
456: }
457: }
458:
459: if (metaVerbose) {
460: char *mp;
461:
462: /* Describe the target we are building */
1.47 sjg 463: mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, VARF_WANTRES);
1.1 sjg 464: if (*mp)
465: fprintf(stdout, "%s\n", mp);
466: free(mp);
467: }
468: /* Get the basename of the target */
469: if ((cp = strrchr(tname, '/')) == NULL) {
470: cp = tname;
471: } else {
472: cp++;
473: }
474:
475: fflush(stdout);
476:
477: if (!writeMeta)
478: /* Don't create meta data. */
479: goto out;
480:
481: fname = meta_name(gn, pbm->meta_fname, sizeof(pbm->meta_fname),
482: dname, tname);
483:
1.8 sjg 484: #ifdef DEBUG_META_MODE
485: if (DEBUG(META))
486: fprintf(debug_file, "meta_create: %s\n", fname);
487: #endif
488:
1.1 sjg 489: if ((mf.fp = fopen(fname, "w")) == NULL)
490: err(1, "Could not open meta file '%s'", fname);
491:
492: fprintf(mf.fp, "# Meta data file %s\n", fname);
493:
494: mf.gn = gn;
495:
496: Lst_ForEach(gn->commands, printCMD, &mf);
497:
498: fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof(buf)));
499: fprintf(mf.fp, "TARGET %s\n", tname);
500:
501: if (metaEnv) {
502: for (ptr = environ; *ptr != NULL; ptr++)
503: fprintf(mf.fp, "ENV %s\n", *ptr);
504: }
505:
506: fprintf(mf.fp, "-- command output --\n");
507: fflush(mf.fp);
508:
509: Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
510: Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL);
1.22 sjg 511:
512: gn->type |= OP_META; /* in case anyone wants to know */
513: if (metaSilent) {
514: gn->type |= OP_SILENT;
515: }
1.1 sjg 516: out:
517: for (i--; i >= 0; i--) {
1.44 christos 518: free(p[i]);
1.1 sjg 519: }
520:
521: return (mf.fp);
522: }
523:
1.12 sjg 524: static Boolean
525: boolValue(char *s)
526: {
527: switch(*s) {
528: case '0':
529: case 'N':
530: case 'n':
531: case 'F':
532: case 'f':
533: return FALSE;
534: }
535: return TRUE;
536: }
1.1 sjg 537:
1.27 sjg 538: /*
539: * Initialization we need before reading makefiles.
540: */
541: void
1.30 sjg 542: meta_init(void)
1.27 sjg 543: {
544: #ifdef USE_FILEMON
545: /* this allows makefiles to test if we have filemon support */
546: Var_Set(".MAKE.PATH_FILEMON", _PATH_FILEMON, VAR_GLOBAL, 0);
547: #endif
548: }
549:
550:
551: /*
552: * Initialization we need after reading makefiles.
553: */
1.1 sjg 554: void
1.27 sjg 555: meta_mode_init(const char *make_mode)
1.1 sjg 556: {
557: static int once = 0;
1.12 sjg 558: char *cp;
1.1 sjg 559:
560: useMeta = TRUE;
561: useFilemon = TRUE;
562: writeMeta = TRUE;
563:
564: if (make_mode) {
565: if (strstr(make_mode, "env"))
566: metaEnv = TRUE;
567: if (strstr(make_mode, "verb"))
568: metaVerbose = TRUE;
569: if (strstr(make_mode, "read"))
570: writeMeta = FALSE;
571: if (strstr(make_mode, "nofilemon"))
572: useFilemon = FALSE;
1.13 sjg 573: if ((cp = strstr(make_mode, "curdirok="))) {
574: metaCurdirOk = boolValue(&cp[9]);
1.12 sjg 575: }
1.22 sjg 576: if ((cp = strstr(make_mode, "silent="))) {
577: metaSilent = boolValue(&cp[7]);
578: }
1.1 sjg 579: if (strstr(make_mode, "ignore-cmd"))
580: metaIgnoreCMDs = TRUE;
581: /* for backwards compatability */
582: Var_Set(".MAKE.META_CREATED", "${.MAKE.META.CREATED}", VAR_GLOBAL, 0);
583: Var_Set(".MAKE.META_FILES", "${.MAKE.META.FILES}", VAR_GLOBAL, 0);
584: }
585: if (metaVerbose && !Var_Exists(MAKE_META_PREFIX, VAR_GLOBAL)) {
586: /*
587: * The default value for MAKE_META_PREFIX
588: * prints the absolute path of the target.
589: * This works be cause :H will generate '.' if there is no /
590: * and :tA will resolve that to cwd.
591: */
592: Var_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}", VAR_GLOBAL, 0);
593: }
594: if (once)
595: return;
596: once = 1;
597: memset(&Mybm, 0, sizeof(Mybm));
1.17 sjg 598: /*
599: * We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
600: */
601: metaBailiwick = Lst_Init(FALSE);
1.53 christos 602: metaBailiwickStr = Var_Subst(NULL, "${.MAKE.META.BAILIWICK:O:u:tA}",
603: VAR_GLOBAL, VARF_WANTRES);
604: if (metaBailiwickStr) {
605: str2Lst_Append(metaBailiwick, metaBailiwickStr, NULL);
1.17 sjg 606: }
1.32 sjg 607: /*
608: * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
609: */
610: metaIgnorePaths = Lst_Init(FALSE);
611: Var_Append(MAKE_META_IGNORE_PATHS,
612: "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL);
1.53 christos 613: metaIgnorePathsStr = Var_Subst(NULL,
1.40 sjg 614: "${" MAKE_META_IGNORE_PATHS ":O:u:tA}", VAR_GLOBAL,
1.47 sjg 615: VARF_WANTRES);
1.53 christos 616: if (metaIgnorePathsStr) {
617: str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr, NULL);
1.32 sjg 618: }
1.1 sjg 619: }
620:
621: /*
622: * In each case below we allow for job==NULL
623: */
624: void
625: meta_job_start(Job *job, GNode *gn)
626: {
627: BuildMon *pbm;
628:
629: if (job != NULL) {
630: pbm = &job->bm;
631: } else {
632: pbm = &Mybm;
633: }
634: pbm->mfp = meta_create(pbm, gn);
635: #ifdef USE_FILEMON_ONCE
636: /* compat mode we open the filemon dev once per command */
637: if (job == NULL)
638: return;
639: #endif
640: #ifdef USE_FILEMON
641: if (pbm->mfp != NULL && useFilemon) {
642: filemon_open(pbm);
643: } else {
644: pbm->mon_fd = pbm->filemon_fd = -1;
645: }
646: #endif
647: }
648:
649: /*
650: * The child calls this before doing anything.
651: * It does not disturb our state.
652: */
653: void
654: meta_job_child(Job *job)
655: {
656: #ifdef USE_FILEMON
657: BuildMon *pbm;
658:
659: if (job != NULL) {
660: pbm = &job->bm;
661: } else {
662: pbm = &Mybm;
663: }
1.37 sjg 664: if (pbm->mfp != NULL) {
665: close(fileno(pbm->mfp));
666: if (useFilemon) {
667: pid_t pid;
668:
669: pid = getpid();
670: if (ioctl(pbm->filemon_fd, FILEMON_SET_PID, &pid) < 0) {
671: err(1, "Could not set filemon pid!");
672: }
1.1 sjg 673: }
674: }
675: #endif
676: }
677:
678: void
679: meta_job_error(Job *job, GNode *gn, int flags, int status)
680: {
681: char cwd[MAXPATHLEN];
682: BuildMon *pbm;
683:
684: if (job != NULL) {
685: pbm = &job->bm;
686: if (!gn)
687: gn = job->node;
1.52 christos 688: } else {
1.1 sjg 689: pbm = &Mybm;
690: }
691: if (pbm->mfp != NULL) {
692: fprintf(pbm->mfp, "*** Error code %d%s\n",
693: status,
694: (flags & JOB_IGNERR) ?
695: "(ignored)" : "");
696: }
697: if (gn) {
698: Var_Set(".ERROR_TARGET", gn->path ? gn->path : gn->name, VAR_GLOBAL, 0);
699: }
700: getcwd(cwd, sizeof(cwd));
701: Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL, 0);
1.49 christos 702: if (pbm->meta_fname[0]) {
1.1 sjg 703: Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL, 0);
704: }
1.16 sjg 705: meta_job_finish(job);
1.1 sjg 706: }
707:
708: void
709: meta_job_output(Job *job, char *cp, const char *nl)
710: {
711: BuildMon *pbm;
712:
713: if (job != NULL) {
714: pbm = &job->bm;
715: } else {
716: pbm = &Mybm;
717: }
718: if (pbm->mfp != NULL) {
719: if (metaVerbose) {
720: static char *meta_prefix = NULL;
721: static int meta_prefix_len;
722:
723: if (!meta_prefix) {
724: char *cp2;
725:
1.40 sjg 726: meta_prefix = Var_Subst(NULL, "${" MAKE_META_PREFIX "}",
1.47 sjg 727: VAR_GLOBAL, VARF_WANTRES);
1.1 sjg 728: if ((cp2 = strchr(meta_prefix, '$')))
729: meta_prefix_len = cp2 - meta_prefix;
730: else
731: meta_prefix_len = strlen(meta_prefix);
732: }
733: if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) {
734: cp = strchr(cp+1, '\n');
735: if (!cp++)
736: return;
737: }
738: }
739: fprintf(pbm->mfp, "%s%s", cp, nl);
740: }
741: }
742:
743: void
744: meta_cmd_finish(void *pbmp)
745: {
746: #ifdef USE_FILEMON
747: BuildMon *pbm = pbmp;
748:
749: if (!pbm)
750: pbm = &Mybm;
751:
752: if (pbm->filemon_fd >= 0) {
753: close(pbm->filemon_fd);
754: filemon_read(pbm->mfp, pbm->mon_fd);
755: pbm->filemon_fd = pbm->mon_fd = -1;
756: }
757: #endif
758: }
759:
760: void
761: meta_job_finish(Job *job)
762: {
763: BuildMon *pbm;
764:
765: if (job != NULL) {
766: pbm = &job->bm;
767: } else {
768: pbm = &Mybm;
769: }
770: if (pbm->mfp != NULL) {
771: meta_cmd_finish(pbm);
772: fclose(pbm->mfp);
773: pbm->mfp = NULL;
1.6 sjg 774: pbm->meta_fname[0] = '\0';
1.1 sjg 775: }
776: }
777:
1.53 christos 778: void
779: meta_finish(void)
780: {
781: Lst_Destroy(metaBailiwick, NULL);
782: free(metaBailiwickStr);
783: Lst_Destroy(metaIgnorePaths, NULL);
784: free(metaIgnorePathsStr);
785: }
786:
1.1 sjg 787: /*
788: * Fetch a full line from fp - growing bufp if needed
789: * Return length in bufp.
790: */
791: static int
792: fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
793: {
794: char *buf = *bufp;
795: size_t bufsz = *szp;
796: struct stat fs;
797: int x;
798:
799: if (fgets(&buf[o], bufsz - o, fp) != NULL) {
800: check_newline:
801: x = o + strlen(&buf[o]);
802: if (buf[x - 1] == '\n')
803: return x;
804: /*
805: * We need to grow the buffer.
806: * The meta file can give us a clue.
807: */
808: if (fstat(fileno(fp), &fs) == 0) {
809: size_t newsz;
810: char *p;
811:
812: newsz = ROUNDUP((fs.st_size / 2), BUFSIZ);
813: if (newsz <= bufsz)
814: newsz = ROUNDUP(fs.st_size, BUFSIZ);
815: if (DEBUG(META))
1.19 sjg 816: fprintf(debug_file, "growing buffer %zu -> %zu\n",
817: bufsz, newsz);
1.1 sjg 818: p = bmake_realloc(buf, newsz);
819: if (p) {
820: *bufp = buf = p;
821: *szp = bufsz = newsz;
822: /* fetch the rest */
823: if (!fgets(&buf[x], bufsz - x, fp))
824: return x; /* truncated! */
825: goto check_newline;
826: }
827: }
828: }
829: return 0;
830: }
831:
1.17 sjg 832: static int
833: prefix_match(void *p, void *q)
834: {
835: const char *prefix = p;
836: const char *path = q;
837: size_t n = strlen(prefix);
838:
839: return (0 == strncmp(path, prefix, n));
840: }
841:
842: static int
843: string_match(const void *p, const void *q)
844: {
845: const char *p1 = p;
846: const char *p2 = q;
847:
848: return strcmp(p1, p2);
849: }
850:
851:
1.1 sjg 852: /*
853: * When running with 'meta' functionality, a target can be out-of-date
1.34 snj 854: * if any of the references in its meta data file is more recent.
1.5 sjg 855: * We have to track the latestdir on a per-process basis.
1.1 sjg 856: */
1.38 sjg 857: #define LCWD_VNAME_FMT ".meta.%d.lcwd"
1.5 sjg 858: #define LDIR_VNAME_FMT ".meta.%d.ldir"
859:
1.20 sjg 860: /*
861: * It is possible that a .meta file is corrupted,
862: * if we detect this we want to reproduce it.
863: * Setting oodate TRUE will have that effect.
864: */
865: #define CHECK_VALID_META(p) if (!(p && *p)) { \
866: warnx("%s: %d: malformed", fname, lineno); \
867: oodate = TRUE; \
868: continue; \
869: }
870:
1.33 sjg 871: #define DEQUOTE(p) if (*p == '\'') { \
872: char *ep; \
873: p++; \
874: if ((ep = strchr(p, '\''))) \
875: *ep = '\0'; \
876: }
877:
1.1 sjg 878: Boolean
879: meta_oodate(GNode *gn, Boolean oodate)
880: {
1.5 sjg 881: static char *tmpdir = NULL;
1.11 sjg 882: static char cwd[MAXPATHLEN];
1.38 sjg 883: char lcwd_vname[64];
1.5 sjg 884: char ldir_vname[64];
1.38 sjg 885: char lcwd[MAXPATHLEN];
1.1 sjg 886: char latestdir[MAXPATHLEN];
887: char fname[MAXPATHLEN];
888: char fname1[MAXPATHLEN];
1.5 sjg 889: char fname2[MAXPATHLEN];
1.38 sjg 890: char fname3[MAXPATHLEN];
1.1 sjg 891: char *p;
892: char *cp;
1.33 sjg 893: char *link_src;
894: char *move_target;
1.11 sjg 895: static size_t cwdlen = 0;
1.7 sjg 896: static size_t tmplen = 0;
1.1 sjg 897: FILE *fp;
1.26 sjg 898: Boolean needOODATE = FALSE;
1.17 sjg 899: Lst missingFiles;
900:
1.4 sjg 901: if (oodate)
902: return oodate; /* we're done */
903:
1.17 sjg 904: missingFiles = Lst_Init(FALSE);
905:
1.1 sjg 906: /*
907: * We need to check if the target is out-of-date. This includes
908: * checking if the expanded command has changed. This in turn
909: * requires that all variables are set in the same way that they
910: * would be if the target needs to be re-built.
911: */
912: Make_DoAllVar(gn);
913:
914: meta_name(gn, fname, sizeof(fname), NULL, NULL);
915:
1.8 sjg 916: #ifdef DEBUG_META_MODE
917: if (DEBUG(META))
918: fprintf(debug_file, "meta_oodate: %s\n", fname);
919: #endif
920:
1.1 sjg 921: if ((fp = fopen(fname, "r")) != NULL) {
922: static char *buf = NULL;
923: static size_t bufsz;
924: int lineno = 0;
1.5 sjg 925: int lastpid = 0;
926: int pid;
1.1 sjg 927: int f = 0;
928: int x;
929: LstNode ln;
930: struct stat fs;
931:
932: if (!buf) {
933: bufsz = 8 * BUFSIZ;
934: buf = bmake_malloc(bufsz);
935: }
1.5 sjg 936:
1.11 sjg 937: if (!cwdlen) {
938: if (getcwd(cwd, sizeof(cwd)) == NULL)
939: err(1, "Could not get current working directory");
940: cwdlen = strlen(cwd);
941: }
1.38 sjg 942: strlcpy(lcwd, cwd, sizeof(lcwd));
943: strlcpy(latestdir, cwd, sizeof(latestdir));
1.11 sjg 944:
1.5 sjg 945: if (!tmpdir) {
946: tmpdir = getTmpdir();
947: tmplen = strlen(tmpdir);
948: }
949:
1.1 sjg 950: /* we want to track all the .meta we read */
951: Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
952:
953: ln = Lst_First(gn->commands);
954: while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) {
955: lineno++;
956: if (buf[x - 1] == '\n')
957: buf[x - 1] = '\0';
1.20 sjg 958: else {
1.1 sjg 959: warnx("%s: %d: line truncated at %u", fname, lineno, x);
1.20 sjg 960: oodate = TRUE;
961: break;
962: }
1.33 sjg 963: link_src = NULL;
964: move_target = NULL;
1.1 sjg 965: /* Find the start of the build monitor section. */
966: if (!f) {
967: if (strncmp(buf, "-- filemon", 10) == 0) {
968: f = 1;
969: continue;
970: }
971: if (strncmp(buf, "# buildmon", 10) == 0) {
972: f = 1;
973: continue;
974: }
975: }
976:
977: /* Delimit the record type. */
978: p = buf;
1.7 sjg 979: #ifdef DEBUG_META_MODE
980: if (DEBUG(META))
981: fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf);
982: #endif
1.1 sjg 983: strsep(&p, " ");
984: if (f) {
1.5 sjg 985: /*
986: * We are in the 'filemon' output section.
987: * Each record from filemon follows the general form:
988: *
989: * <key> <pid> <data>
990: *
991: * Where:
992: * <key> is a single letter, denoting the syscall.
993: * <pid> is the process that made the syscall.
994: * <data> is the arguments (of interest).
995: */
996: switch(buf[0]) {
997: case '#': /* comment */
998: case 'V': /* version */
999: break;
1000: default:
1001: /*
1002: * We need to track pathnames per-process.
1003: *
1004: * Each process run by make, starts off in the 'CWD'
1005: * recorded in the .meta file, if it chdirs ('C')
1006: * elsewhere we need to track that - but only for
1007: * that process. If it forks ('F'), we initialize
1008: * the child to have the same cwd as its parent.
1009: *
1010: * We also need to track the 'latestdir' of
1011: * interest. This is usually the same as cwd, but
1012: * not if a process is reading directories.
1013: *
1014: * Each time we spot a different process ('pid')
1015: * we save the current value of 'latestdir' in a
1016: * variable qualified by 'lastpid', and
1017: * re-initialize 'latestdir' to any pre-saved
1018: * value for the current 'pid' and 'CWD' if none.
1019: */
1.20 sjg 1020: CHECK_VALID_META(p);
1.5 sjg 1021: pid = atoi(p);
1022: if (pid > 0 && pid != lastpid) {
1023: char *ldir;
1024: char *tp;
1025:
1026: if (lastpid > 0) {
1.38 sjg 1027: /* We need to remember these. */
1028: Var_Set(lcwd_vname, lcwd, VAR_GLOBAL, 0);
1.5 sjg 1029: Var_Set(ldir_vname, latestdir, VAR_GLOBAL, 0);
1030: }
1.38 sjg 1031: snprintf(lcwd_vname, sizeof(lcwd_vname), LCWD_VNAME_FMT, pid);
1.5 sjg 1032: snprintf(ldir_vname, sizeof(ldir_vname), LDIR_VNAME_FMT, pid);
1033: lastpid = pid;
1034: ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp);
1035: if (ldir) {
1036: strlcpy(latestdir, ldir, sizeof(latestdir));
1.44 christos 1037: free(tp);
1.38 sjg 1038: }
1039: ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp);
1040: if (ldir) {
1041: strlcpy(lcwd, ldir, sizeof(lcwd));
1.44 christos 1042: free(tp);
1.38 sjg 1043: }
1.5 sjg 1044: }
1045: /* Skip past the pid. */
1046: if (strsep(&p, " ") == NULL)
1047: continue;
1.7 sjg 1048: #ifdef DEBUG_META_MODE
1049: if (DEBUG(META))
1.38 sjg 1050: fprintf(debug_file, "%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n",
1051: fname, lineno,
1052: pid, buf[0], cwd, lcwd, latestdir);
1.7 sjg 1053: #endif
1.5 sjg 1054: break;
1055: }
1056:
1.20 sjg 1057: CHECK_VALID_META(p);
1058:
1.1 sjg 1059: /* Process according to record type. */
1060: switch (buf[0]) {
1.5 sjg 1061: case 'X': /* eXit */
1.38 sjg 1062: Var_Delete(lcwd_vname, VAR_GLOBAL);
1.5 sjg 1063: Var_Delete(ldir_vname, VAR_GLOBAL);
1064: lastpid = 0; /* no need to save ldir_vname */
1065: break;
1066:
1067: case 'F': /* [v]Fork */
1068: {
1069: char cldir[64];
1070: int child;
1071:
1072: child = atoi(p);
1073: if (child > 0) {
1.38 sjg 1074: snprintf(cldir, sizeof(cldir), LCWD_VNAME_FMT, child);
1075: Var_Set(cldir, lcwd, VAR_GLOBAL, 0);
1.5 sjg 1076: snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child);
1077: Var_Set(cldir, latestdir, VAR_GLOBAL, 0);
1.38 sjg 1078: #ifdef DEBUG_META_MODE
1079: if (DEBUG(META))
1080: fprintf(debug_file, "%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n",
1081: fname, lineno,
1082: child, cwd, lcwd, latestdir);
1083: #endif
1.5 sjg 1084: }
1085: }
1086: break;
1.1 sjg 1087:
1.5 sjg 1088: case 'C': /* Chdir */
1.38 sjg 1089: /* Update lcwd and latest directory. */
1090: strlcpy(latestdir, p, sizeof(latestdir));
1091: strlcpy(lcwd, p, sizeof(lcwd));
1092: Var_Set(lcwd_vname, lcwd, VAR_GLOBAL, 0);
1093: Var_Set(ldir_vname, lcwd, VAR_GLOBAL, 0);
1094: #ifdef DEBUG_META_MODE
1095: if (DEBUG(META))
1096: fprintf(debug_file, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, lcwd);
1097: #endif
1.1 sjg 1098: break;
1099:
1.17 sjg 1100: case 'M': /* renaMe */
1.33 sjg 1101: /*
1102: * For 'M'oves we want to check
1103: * the src as for 'R'ead
1104: * and the target as for 'W'rite.
1105: */
1106: cp = p; /* save this for a second */
1107: /* now get target */
1108: if (strsep(&p, " ") == NULL)
1109: continue;
1110: CHECK_VALID_META(p);
1111: move_target = p;
1112: p = cp;
1.17 sjg 1113: /* 'L' and 'M' put single quotes around the args */
1.33 sjg 1114: DEQUOTE(p);
1115: DEQUOTE(move_target);
1.17 sjg 1116: /* FALLTHROUGH */
1117: case 'D': /* unlink */
1118: if (*p == '/' && !Lst_IsEmpty(missingFiles)) {
1119: /* remove p from the missingFiles list if present */
1120: if ((ln = Lst_Find(missingFiles, p, string_match)) != NULL) {
1121: char *tp = Lst_Datum(ln);
1122: Lst_Remove(missingFiles, ln);
1123: free(tp);
1.28 sjg 1124: ln = NULL; /* we're done with it */
1.17 sjg 1125: }
1126: }
1.33 sjg 1127: if (buf[0] == 'M') {
1128: /* the target of the mv is a file 'W'ritten */
1129: #ifdef DEBUG_META_MODE
1130: if (DEBUG(META))
1131: fprintf(debug_file, "meta_oodate: M %s -> %s\n",
1132: p, move_target);
1133: #endif
1134: p = move_target;
1135: goto check_write;
1136: }
1.17 sjg 1137: break;
1138: case 'L': /* Link */
1.33 sjg 1139: /*
1140: * For 'L'inks check
1141: * the src as for 'R'ead
1142: * and the target as for 'W'rite.
1143: */
1144: link_src = p;
1145: /* now get target */
1.17 sjg 1146: if (strsep(&p, " ") == NULL)
1147: continue;
1.20 sjg 1148: CHECK_VALID_META(p);
1.17 sjg 1149: /* 'L' and 'M' put single quotes around the args */
1.33 sjg 1150: DEQUOTE(p);
1151: DEQUOTE(link_src);
1152: #ifdef DEBUG_META_MODE
1153: if (DEBUG(META))
1154: fprintf(debug_file, "meta_oodate: L %s -> %s\n",
1155: link_src, p);
1156: #endif
1.17 sjg 1157: /* FALLTHROUGH */
1158: case 'W': /* Write */
1.33 sjg 1159: check_write:
1.17 sjg 1160: /*
1161: * If a file we generated within our bailiwick
1162: * but outside of .OBJDIR is missing,
1163: * we need to do it again.
1164: */
1165: /* ignore non-absolute paths */
1166: if (*p != '/')
1167: break;
1168:
1169: if (Lst_IsEmpty(metaBailiwick))
1170: break;
1171:
1172: /* ignore cwd - normal dependencies handle those */
1173: if (strncmp(p, cwd, cwdlen) == 0)
1174: break;
1175:
1176: if (!Lst_ForEach(metaBailiwick, prefix_match, p))
1177: break;
1178:
1179: /* tmpdir might be within */
1180: if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0)
1181: break;
1182:
1183: /* ignore anything containing the string "tmp" */
1184: if ((strstr("tmp", p)))
1185: break;
1186:
1.41 sjg 1187: if ((link_src != NULL && lstat(p, &fs) < 0) ||
1188: (link_src == NULL && stat(p, &fs) < 0)) {
1.54 ! sjg 1189: if (Lst_Find(missingFiles, p, string_match) == NULL)
! 1190: Lst_AtEnd(missingFiles, bmake_strdup(p));
1.17 sjg 1191: }
1192: break;
1.33 sjg 1193: check_link_src:
1194: p = link_src;
1195: link_src = NULL;
1196: #ifdef DEBUG_META_MODE
1197: if (DEBUG(META))
1198: fprintf(debug_file, "meta_oodate: L src %s\n", p);
1199: #endif
1200: /* FALLTHROUGH */
1.5 sjg 1201: case 'R': /* Read */
1202: case 'E': /* Exec */
1.1 sjg 1203: /*
1204: * Check for runtime files that can't
1205: * be part of the dependencies because
1206: * they are _expected_ to change.
1207: */
1.32 sjg 1208: if (*p == '/' &&
1209: Lst_ForEach(metaIgnorePaths, prefix_match, p)) {
1210: #ifdef DEBUG_META_MODE
1211: if (DEBUG(META))
1212: fprintf(debug_file, "meta_oodate: ignoring: %s\n",
1213: p);
1214: #endif
1.31 sjg 1215: break;
1.32 sjg 1216: }
1.31 sjg 1217:
1.1 sjg 1218: /*
1.5 sjg 1219: * The rest of the record is the file name.
1220: * Check if it's not an absolute path.
1.1 sjg 1221: */
1.5 sjg 1222: {
1223: char *sdirs[4];
1224: char **sdp;
1225: int sdx = 0;
1226: int found = 0;
1227:
1228: if (*p == '/') {
1229: sdirs[sdx++] = p; /* done */
1230: } else {
1231: if (strcmp(".", p) == 0)
1232: continue; /* no point */
1233:
1234: /* Check vs latestdir */
1235: snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p);
1236: sdirs[sdx++] = fname1;
1237:
1.38 sjg 1238: if (strcmp(latestdir, lcwd) != 0) {
1239: /* Check vs lcwd */
1240: snprintf(fname2, sizeof(fname2), "%s/%s", lcwd, p);
1241: sdirs[sdx++] = fname2;
1242: }
1243: if (strcmp(lcwd, cwd) != 0) {
1.5 sjg 1244: /* Check vs cwd */
1.38 sjg 1245: snprintf(fname3, sizeof(fname3), "%s/%s", cwd, p);
1246: sdirs[sdx++] = fname3;
1.5 sjg 1247: }
1248: }
1249: sdirs[sdx++] = NULL;
1.1 sjg 1250:
1.5 sjg 1251: for (sdp = sdirs; *sdp && !found; sdp++) {
1.7 sjg 1252: #ifdef DEBUG_META_MODE
1253: if (DEBUG(META))
1254: fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp);
1255: #endif
1.5 sjg 1256: if (stat(*sdp, &fs) == 0) {
1257: found = 1;
1258: p = *sdp;
1259: }
1260: }
1261: if (found) {
1.7 sjg 1262: #ifdef DEBUG_META_MODE
1263: if (DEBUG(META))
1264: fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p);
1265: #endif
1.5 sjg 1266: if (!S_ISDIR(fs.st_mode) &&
1267: fs.st_mtime > gn->mtime) {
1268: if (DEBUG(META))
1269: fprintf(debug_file, "%s: %d: file '%s' is newer than the target...\n", fname, lineno, p);
1270: oodate = TRUE;
1271: } else if (S_ISDIR(fs.st_mode)) {
1272: /* Update the latest directory. */
1273: realpath(p, latestdir);
1274: }
1275: } else if (errno == ENOENT && *p == '/' &&
1276: strncmp(p, cwd, cwdlen) != 0) {
1277: /*
1278: * A referenced file outside of CWD is missing.
1279: * We cannot catch every eventuality here...
1280: */
1.54 ! sjg 1281: if (Lst_Find(missingFiles, p, string_match) == NULL)
! 1282: Lst_AtEnd(missingFiles, bmake_strdup(p));
1.4 sjg 1283: }
1.1 sjg 1284: }
1.38 sjg 1285: if (buf[0] == 'E') {
1286: /* previous latestdir is no longer relevant */
1287: strlcpy(latestdir, lcwd, sizeof(latestdir));
1288: }
1.1 sjg 1289: break;
1290: default:
1291: break;
1292: }
1.33 sjg 1293: if (!oodate && buf[0] == 'L' && link_src != NULL)
1294: goto check_link_src;
1.5 sjg 1295: } else if (strcmp(buf, "CMD") == 0) {
1.1 sjg 1296: /*
1297: * Compare the current command with the one in the
1298: * meta data file.
1299: */
1300: if (ln == NULL) {
1301: if (DEBUG(META))
1302: fprintf(debug_file, "%s: %d: there were more build commands in the meta data file than there are now...\n", fname, lineno);
1303: oodate = TRUE;
1304: } else {
1305: char *cmd = (char *)Lst_Datum(ln);
1.29 sjg 1306: Boolean hasOODATE = FALSE;
1.1 sjg 1307:
1.29 sjg 1308: if (strstr(cmd, "$?"))
1309: hasOODATE = TRUE;
1310: else if ((cp = strstr(cmd, ".OODATE"))) {
1311: /* check for $[{(].OODATE[:)}] */
1312: if (cp > cmd + 2 && cp[-2] == '$')
1313: hasOODATE = TRUE;
1314: }
1315: if (hasOODATE) {
1316: needOODATE = TRUE;
1317: if (DEBUG(META))
1318: fprintf(debug_file, "%s: %d: cannot compare command using .OODATE\n", fname, lineno);
1.1 sjg 1319: }
1.47 sjg 1320: cmd = Var_Subst(NULL, cmd, gn, VARF_WANTRES|VARF_UNDEFERR);
1.1 sjg 1321:
1322: if ((cp = strchr(cmd, '\n'))) {
1323: int n;
1324:
1325: /*
1326: * This command contains newlines, we need to
1327: * fetch more from the .meta file before we
1328: * attempt a comparison.
1329: */
1330: /* first put the newline back at buf[x - 1] */
1331: buf[x - 1] = '\n';
1332: do {
1333: /* now fetch the next line */
1334: if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0)
1335: break;
1336: x = n;
1337: lineno++;
1338: if (buf[x - 1] != '\n') {
1339: warnx("%s: %d: line truncated at %u", fname, lineno, x);
1340: break;
1341: }
1342: cp = strchr(++cp, '\n');
1343: } while (cp);
1344: if (buf[x - 1] == '\n')
1345: buf[x - 1] = '\0';
1346: }
1.29 sjg 1347: if (!hasOODATE &&
1.1 sjg 1348: !(gn->type & OP_NOMETA_CMP) &&
1349: strcmp(p, cmd) != 0) {
1350: if (DEBUG(META))
1351: fprintf(debug_file, "%s: %d: a build command has changed\n%s\nvs\n%s\n", fname, lineno, p, cmd);
1352: if (!metaIgnoreCMDs)
1353: oodate = TRUE;
1354: }
1355: free(cmd);
1356: ln = Lst_Succ(ln);
1357: }
1358: } else if (strcmp(buf, "CWD") == 0) {
1.14 sjg 1359: /*
1360: * Check if there are extra commands now
1361: * that weren't in the meta data file.
1362: */
1363: if (!oodate && ln != NULL) {
1364: if (DEBUG(META))
1365: fprintf(debug_file, "%s: %d: there are extra build commands now that weren't in the meta data file\n", fname, lineno);
1366: oodate = TRUE;
1367: }
1.11 sjg 1368: if (strcmp(p, cwd) != 0) {
1.1 sjg 1369: if (DEBUG(META))
1370: fprintf(debug_file, "%s: %d: the current working directory has changed from '%s' to '%s'\n", fname, lineno, p, curdir);
1371: oodate = TRUE;
1372: }
1373: }
1374: }
1375:
1376: fclose(fp);
1.17 sjg 1377: if (!Lst_IsEmpty(missingFiles)) {
1378: if (DEBUG(META))
1379: fprintf(debug_file, "%s: missing files: %s...\n",
1380: fname, (char *)Lst_Datum(Lst_First(missingFiles)));
1381: oodate = TRUE;
1382: }
1.21 sjg 1383: } else {
1384: if ((gn->type & OP_META)) {
1385: if (DEBUG(META))
1386: fprintf(debug_file, "%s: required but missing\n", fname);
1387: oodate = TRUE;
1388: }
1.1 sjg 1389: }
1.50 christos 1390:
1391: Lst_Destroy(missingFiles, (FreeProc *)free);
1392:
1.26 sjg 1393: if (oodate && needOODATE) {
1.4 sjg 1394: /*
1.26 sjg 1395: * Target uses .OODATE which is empty; or we wouldn't be here.
1396: * We have decided it is oodate, so .OODATE needs to be set.
1397: * All we can sanely do is set it to .ALLSRC.
1.4 sjg 1398: */
1399: Var_Delete(OODATE, gn);
1.26 sjg 1400: Var_Set(OODATE, Var_Value(ALLSRC, gn, &cp), gn, 0);
1.44 christos 1401: free(cp);
1.4 sjg 1402: }
1.1 sjg 1403: return oodate;
1404: }
1405:
1406: /* support for compat mode */
1407:
1408: static int childPipe[2];
1409:
1410: void
1411: meta_compat_start(void)
1412: {
1413: #ifdef USE_FILEMON_ONCE
1414: /*
1415: * We need to re-open filemon for each cmd.
1416: */
1417: BuildMon *pbm = &Mybm;
1418:
1419: if (pbm->mfp != NULL && useFilemon) {
1420: filemon_open(pbm);
1421: } else {
1422: pbm->mon_fd = pbm->filemon_fd = -1;
1423: }
1424: #endif
1425: if (pipe(childPipe) < 0)
1426: Punt("Cannot create pipe: %s", strerror(errno));
1427: /* Set close-on-exec flag for both */
1.42 christos 1428: (void)fcntl(childPipe[0], F_SETFD, FD_CLOEXEC);
1429: (void)fcntl(childPipe[1], F_SETFD, FD_CLOEXEC);
1.1 sjg 1430: }
1431:
1432: void
1433: meta_compat_child(void)
1434: {
1435: meta_job_child(NULL);
1436: if (dup2(childPipe[1], 1) < 0 ||
1437: dup2(1, 2) < 0) {
1438: execError("dup2", "pipe");
1439: _exit(1);
1440: }
1441: }
1442:
1443: void
1444: meta_compat_parent(void)
1445: {
1446: FILE *fp;
1447: char buf[BUFSIZ];
1448:
1449: close(childPipe[1]); /* child side */
1450: fp = fdopen(childPipe[0], "r");
1451: while (fgets(buf, sizeof(buf), fp)) {
1452: meta_job_output(NULL, buf, "");
1453: printf("%s", buf);
1454: }
1455: fclose(fp);
1456: }
1.3 sjg 1457:
1458: #endif /* USE_META */
CVSweb <webmaster@jp.NetBSD.org>