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