meta.c revision 1.180 1 /* $NetBSD: meta.c,v 1.180 2021/04/03 11:08:40 rillig Exp $ */
2
3 /*
4 * Implement 'meta' mode.
5 * Adapted from John Birrell's patches to FreeBSD make.
6 * --sjg
7 */
8 /*
9 * Copyright (c) 2009-2016, 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 */
33 #if defined(USE_META)
34
35 #ifdef HAVE_CONFIG_H
36 # include "config.h"
37 #endif
38 #include <sys/stat.h>
39 #include <libgen.h>
40 #include <errno.h>
41 #if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H)
42 #include <err.h>
43 #endif
44
45 #include "make.h"
46 #include "dir.h"
47 #include "job.h"
48
49 #ifdef USE_FILEMON
50 #include "filemon/filemon.h"
51 #endif
52
53 static BuildMon Mybm; /* for compat */
54 static StringList metaBailiwick = LST_INIT; /* our scope of control */
55 static char *metaBailiwickStr; /* string storage for the list */
56 static StringList metaIgnorePaths = LST_INIT; /* paths we deliberately ignore */
57 static char *metaIgnorePathsStr; /* string storage for the list */
58
59 #ifndef MAKE_META_IGNORE_PATHS
60 #define MAKE_META_IGNORE_PATHS ".MAKE.META.IGNORE_PATHS"
61 #endif
62 #ifndef MAKE_META_IGNORE_PATTERNS
63 #define MAKE_META_IGNORE_PATTERNS ".MAKE.META.IGNORE_PATTERNS"
64 #endif
65 #ifndef MAKE_META_IGNORE_FILTER
66 #define MAKE_META_IGNORE_FILTER ".MAKE.META.IGNORE_FILTER"
67 #endif
68
69 bool useMeta = false;
70 static bool useFilemon = false;
71 static bool writeMeta = false;
72 static bool metaMissing = false; /* oodate if missing */
73 static bool filemonMissing = false; /* oodate if missing */
74 static bool metaEnv = false; /* don't save env unless asked */
75 static bool metaVerbose = false;
76 static bool metaIgnoreCMDs = false; /* ignore CMDs in .meta files */
77 static bool metaIgnorePatterns = false; /* do we need to do pattern matches */
78 static bool metaIgnoreFilter = false; /* do we have more complex filtering? */
79 static bool metaCurdirOk = false; /* write .meta in .CURDIR Ok? */
80 static bool metaSilent = false; /* if we have a .meta be SILENT */
81
82 extern bool forceJobs;
83 extern char **environ;
84
85 #define MAKE_META_PREFIX ".MAKE.META.PREFIX"
86
87 #ifndef N2U
88 # define N2U(n, u) (((n) + ((u) - 1)) / (u))
89 #endif
90 #ifndef ROUNDUP
91 # define ROUNDUP(n, u) (N2U((n), (u)) * (u))
92 #endif
93
94 #if !defined(HAVE_STRSEP)
95 # define strsep(s, d) stresep((s), (d), '\0')
96 #endif
97
98 /*
99 * Filemon is a kernel module which snoops certain syscalls.
100 *
101 * C chdir
102 * E exec
103 * F [v]fork
104 * L [sym]link
105 * M rename
106 * R read
107 * W write
108 * S stat
109 *
110 * See meta_oodate below - we mainly care about 'E' and 'R'.
111 *
112 * We can still use meta mode without filemon, but
113 * the benefits are more limited.
114 */
115 #ifdef USE_FILEMON
116
117 /*
118 * Open the filemon device.
119 */
120 static void
121 meta_open_filemon(BuildMon *pbm)
122 {
123 int dupfd;
124
125 pbm->mon_fd = -1;
126 pbm->filemon = NULL;
127 if (!useFilemon || pbm->mfp == NULL)
128 return;
129
130 pbm->filemon = filemon_open();
131 if (pbm->filemon == NULL) {
132 useFilemon = false;
133 warn("Could not open filemon %s", filemon_path());
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 if (!opts.compatMake)
144 pbm->mon_fd = Job_TempFile("filemon.XXXXXX", NULL, 0);
145 else
146 pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL, 0);
147 if ((dupfd = dup(pbm->mon_fd)) == -1) {
148 err(1, "Could not dup filemon output!");
149 }
150 (void)fcntl(dupfd, F_SETFD, FD_CLOEXEC);
151 if (filemon_setfd(pbm->filemon, dupfd) == -1) {
152 err(1, "Could not set filemon file descriptor!");
153 }
154 /* we don't need these once we exec */
155 (void)fcntl(pbm->mon_fd, F_SETFD, FD_CLOEXEC);
156 }
157
158 /*
159 * Read the build monitor output file and write records to the target's
160 * metadata file.
161 */
162 static int
163 filemon_read(FILE *mfp, int fd)
164 {
165 char buf[BUFSIZ];
166 int error;
167
168 /* Check if we're not writing to a meta data file.*/
169 if (mfp == NULL) {
170 if (fd >= 0)
171 close(fd); /* not interested */
172 return 0;
173 }
174 /* rewind */
175 if (lseek(fd, (off_t)0, SEEK_SET) < 0) {
176 error = errno;
177 warn("Could not rewind filemon");
178 fprintf(mfp, "\n");
179 } else {
180 ssize_t n;
181
182 error = 0;
183 fprintf(mfp, "\n-- filemon acquired metadata --\n");
184
185 while ((n = read(fd, buf, sizeof buf)) > 0) {
186 if ((ssize_t)fwrite(buf, 1, (size_t)n, mfp) < n)
187 error = EIO;
188 }
189 }
190 fflush(mfp);
191 if (close(fd) < 0)
192 error = errno;
193 return error;
194 }
195 #endif
196
197 /*
198 * when realpath() fails,
199 * we use this, to clean up ./ and ../
200 */
201 static void
202 eat_dots(char *buf, size_t bufsz, int dots)
203 {
204 char *cp;
205 char *cp2;
206 const char *eat;
207 size_t eatlen;
208
209 switch (dots) {
210 case 1:
211 eat = "/./";
212 eatlen = 2;
213 break;
214 case 2:
215 eat = "/../";
216 eatlen = 3;
217 break;
218 default:
219 return;
220 }
221
222 do {
223 cp = strstr(buf, eat);
224 if (cp != NULL) {
225 cp2 = cp + eatlen;
226 if (dots == 2 && cp > buf) {
227 do {
228 cp--;
229 } while (cp > buf && *cp != '/');
230 }
231 if (*cp == '/') {
232 strlcpy(cp, cp2, bufsz - (size_t)(cp - buf));
233 } else {
234 return; /* can't happen? */
235 }
236 }
237 } while (cp != NULL);
238 }
239
240 static char *
241 meta_name(char *mname, size_t mnamelen,
242 const char *dname,
243 const char *tname,
244 const char *cwd)
245 {
246 char buf[MAXPATHLEN];
247 char *rp;
248 char *cp;
249 char *tp;
250 char *dtp;
251 size_t ldname;
252
253 /*
254 * Weed out relative paths from the target file name.
255 * We have to be careful though since if target is a
256 * symlink, the result will be unstable.
257 * So we use realpath() just to get the dirname, and leave the
258 * basename as given to us.
259 */
260 if ((cp = strrchr(tname, '/')) != NULL) {
261 if (cached_realpath(tname, buf) != NULL) {
262 if ((rp = strrchr(buf, '/')) != NULL) {
263 rp++;
264 cp++;
265 if (strcmp(cp, rp) != 0)
266 strlcpy(rp, cp, sizeof buf - (size_t)(rp - buf));
267 }
268 tname = buf;
269 } else {
270 /*
271 * We likely have a directory which is about to be made.
272 * We pretend realpath() succeeded, to have a chance
273 * of generating the same meta file name that we will
274 * next time through.
275 */
276 if (tname[0] == '/') {
277 strlcpy(buf, tname, sizeof buf);
278 } else {
279 snprintf(buf, sizeof buf, "%s/%s", cwd, tname);
280 }
281 eat_dots(buf, sizeof buf, 1); /* ./ */
282 eat_dots(buf, sizeof buf, 2); /* ../ */
283 tname = buf;
284 }
285 }
286 /* on some systems dirname may modify its arg */
287 tp = bmake_strdup(tname);
288 dtp = dirname(tp);
289 if (strcmp(dname, dtp) == 0)
290 snprintf(mname, mnamelen, "%s.meta", tname);
291 else {
292 ldname = strlen(dname);
293 if (strncmp(dname, dtp, ldname) == 0 && dtp[ldname] == '/')
294 snprintf(mname, mnamelen, "%s/%s.meta", dname, &tname[ldname+1]);
295 else
296 snprintf(mname, mnamelen, "%s/%s.meta", dname, tname);
297
298 /*
299 * Replace path separators in the file name after the
300 * current object directory path.
301 */
302 cp = mname + strlen(dname) + 1;
303
304 while (*cp != '\0') {
305 if (*cp == '/')
306 *cp = '_';
307 cp++;
308 }
309 }
310 free(tp);
311 return mname;
312 }
313
314 /*
315 * Return true if running ${.MAKE}
316 * Bypassed if target is flagged .MAKE
317 */
318 static bool
319 is_submake(const char *cmd, GNode *gn)
320 {
321 static const char *p_make = NULL;
322 static size_t p_len;
323 char *mp = NULL;
324 char *cp;
325 char *cp2;
326 bool rc = false;
327
328 if (p_make == NULL) {
329 p_make = Var_Value(gn, ".MAKE").str;
330 p_len = strlen(p_make);
331 }
332 cp = strchr(cmd, '$');
333 if (cp != NULL) {
334 (void)Var_Subst(cmd, gn, VARE_WANTRES, &mp);
335 /* TODO: handle errors */
336 cmd = mp;
337 }
338 cp2 = strstr(cmd, p_make);
339 if (cp2 != NULL) {
340 switch (cp2[p_len]) {
341 case '\0':
342 case ' ':
343 case '\t':
344 case '\n':
345 rc = true;
346 break;
347 }
348 if (cp2 > cmd && rc) {
349 switch (cp2[-1]) {
350 case ' ':
351 case '\t':
352 case '\n':
353 break;
354 default:
355 rc = false; /* no match */
356 break;
357 }
358 }
359 }
360 free(mp);
361 return rc;
362 }
363
364 static bool
365 any_is_submake(GNode *gn)
366 {
367 StringListNode *ln;
368
369 for (ln = gn->commands.first; ln != NULL; ln = ln->next)
370 if (is_submake(ln->datum, gn))
371 return true;
372 return false;
373 }
374
375 static void
376 printCMD(const char *ucmd, FILE *fp, GNode *gn)
377 {
378 FStr xcmd = FStr_InitRefer(ucmd);
379
380 if (strchr(ucmd, '$') != NULL) {
381 char *expanded;
382 (void)Var_Subst(ucmd, gn, VARE_WANTRES, &expanded);
383 /* TODO: handle errors */
384 xcmd = FStr_InitOwn(expanded);
385 }
386
387 fprintf(fp, "CMD %s\n", xcmd.str);
388 FStr_Done(&xcmd);
389 }
390
391 static void
392 printCMDs(GNode *gn, FILE *fp)
393 {
394 StringListNode *ln;
395
396 for (ln = gn->commands.first; ln != NULL; ln = ln->next)
397 printCMD(ln->datum, fp, gn);
398 }
399
400 /*
401 * Certain node types never get a .meta file
402 */
403 #define SKIP_META_TYPE(_type) do { \
404 if ((gn->type & __CONCAT(OP_, _type))) { \
405 if (verbose) { \
406 debug_printf("Skipping meta for %s: .%s\n", \
407 gn->name, __STRING(_type)); \
408 } \
409 return false; \
410 } \
411 } while (/*CONSTCOND*/false)
412
413
414 /*
415 * Do we need/want a .meta file ?
416 */
417 static bool
418 meta_needed(GNode *gn, const char *dname,
419 char *objdir_realpath, bool verbose)
420 {
421 struct cached_stat cst;
422
423 if (verbose)
424 verbose = DEBUG(META);
425
426 /* This may be a phony node which we don't want meta data for... */
427 /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */
428 /* Or it may be explicitly flagged as .NOMETA */
429 SKIP_META_TYPE(NOMETA);
430 /* Unless it is explicitly flagged as .META */
431 if (!(gn->type & OP_META)) {
432 SKIP_META_TYPE(PHONY);
433 SKIP_META_TYPE(SPECIAL);
434 SKIP_META_TYPE(MAKE);
435 }
436
437 /* Check if there are no commands to execute. */
438 if (Lst_IsEmpty(&gn->commands)) {
439 if (verbose)
440 debug_printf("Skipping meta for %s: no commands\n", gn->name);
441 return false;
442 }
443 if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) {
444 /* OP_SUBMAKE is a bit too aggressive */
445 if (any_is_submake(gn)) {
446 DEBUG1(META, "Skipping meta for %s: .SUBMAKE\n", gn->name);
447 return false;
448 }
449 }
450
451 /* The object directory may not exist. Check it.. */
452 if (cached_stat(dname, &cst) != 0) {
453 if (verbose)
454 debug_printf("Skipping meta for %s: no .OBJDIR\n", gn->name);
455 return false;
456 }
457
458 /* make sure these are canonical */
459 if (cached_realpath(dname, objdir_realpath) != NULL)
460 dname = objdir_realpath;
461
462 /* If we aren't in the object directory, don't create a meta file. */
463 if (!metaCurdirOk && strcmp(curdir, dname) == 0) {
464 if (verbose)
465 debug_printf("Skipping meta for %s: .OBJDIR == .CURDIR\n",
466 gn->name);
467 return false;
468 }
469 return true;
470 }
471
472
473 static FILE *
474 meta_create(BuildMon *pbm, GNode *gn)
475 {
476 FILE *fp;
477 char buf[MAXPATHLEN];
478 char objdir_realpath[MAXPATHLEN];
479 char **ptr;
480 FStr dname;
481 const char *tname;
482 char *fname;
483 const char *cp;
484
485 fp = NULL;
486
487 dname = Var_Value(gn, ".OBJDIR");
488 tname = GNode_VarTarget(gn);
489
490 /* if this succeeds objdir_realpath is realpath of dname */
491 if (!meta_needed(gn, dname.str, objdir_realpath, true))
492 goto out;
493 dname.str = objdir_realpath;
494
495 if (metaVerbose) {
496 char *mp;
497
498 /* Describe the target we are building */
499 (void)Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES, &mp);
500 /* TODO: handle errors */
501 if (mp[0] != '\0')
502 fprintf(stdout, "%s\n", mp);
503 free(mp);
504 }
505 /* Get the basename of the target */
506 cp = str_basename(tname);
507
508 fflush(stdout);
509
510 if (!writeMeta)
511 /* Don't create meta data. */
512 goto out;
513
514 fname = meta_name(pbm->meta_fname, sizeof pbm->meta_fname,
515 dname.str, tname, objdir_realpath);
516
517 #ifdef DEBUG_META_MODE
518 DEBUG1(META, "meta_create: %s\n", fname);
519 #endif
520
521 if ((fp = fopen(fname, "w")) == NULL)
522 err(1, "Could not open meta file '%s'", fname);
523
524 fprintf(fp, "# Meta data file %s\n", fname);
525
526 printCMDs(gn, fp);
527
528 fprintf(fp, "CWD %s\n", getcwd(buf, sizeof buf));
529 fprintf(fp, "TARGET %s\n", tname);
530 cp = GNode_VarOodate(gn);
531 if (cp != NULL && *cp != '\0') {
532 fprintf(fp, "OODATE %s\n", cp);
533 }
534 if (metaEnv) {
535 for (ptr = environ; *ptr != NULL; ptr++)
536 fprintf(fp, "ENV %s\n", *ptr);
537 }
538
539 fprintf(fp, "-- command output --\n");
540 fflush(fp);
541
542 Global_Append(".MAKE.META.FILES", fname);
543 Global_Append(".MAKE.META.CREATED", fname);
544
545 gn->type |= OP_META; /* in case anyone wants to know */
546 if (metaSilent) {
547 gn->type |= OP_SILENT;
548 }
549 out:
550 FStr_Done(&dname);
551
552 return fp;
553 }
554
555 static bool
556 boolValue(char *s)
557 {
558 switch(*s) {
559 case '0':
560 case 'N':
561 case 'n':
562 case 'F':
563 case 'f':
564 return false;
565 }
566 return true;
567 }
568
569 /*
570 * Initialization we need before reading makefiles.
571 */
572 void
573 meta_init(void)
574 {
575 #ifdef USE_FILEMON
576 /* this allows makefiles to test if we have filemon support */
577 Global_Set(".MAKE.PATH_FILEMON", filemon_path());
578 #endif
579 }
580
581
582 #define get_mode_bf(bf, token) \
583 if ((cp = strstr(make_mode, token)) != NULL) \
584 bf = boolValue(cp + sizeof (token) - 1)
585
586 /*
587 * Initialization we need after reading makefiles.
588 */
589 void
590 meta_mode_init(const char *make_mode)
591 {
592 static bool once = false;
593 char *cp;
594 FStr value;
595
596 useMeta = true;
597 useFilemon = true;
598 writeMeta = true;
599
600 if (make_mode != NULL) {
601 if (strstr(make_mode, "env") != NULL)
602 metaEnv = true;
603 if (strstr(make_mode, "verb") != NULL)
604 metaVerbose = true;
605 if (strstr(make_mode, "read") != NULL)
606 writeMeta = false;
607 if (strstr(make_mode, "nofilemon") != NULL)
608 useFilemon = false;
609 if (strstr(make_mode, "ignore-cmd") != NULL)
610 metaIgnoreCMDs = true;
611 if (useFilemon)
612 get_mode_bf(filemonMissing, "missing-filemon=");
613 get_mode_bf(metaCurdirOk, "curdirok=");
614 get_mode_bf(metaMissing, "missing-meta=");
615 get_mode_bf(metaSilent, "silent=");
616 }
617 if (metaVerbose && !Var_Exists(SCOPE_GLOBAL, MAKE_META_PREFIX)) {
618 /*
619 * The default value for MAKE_META_PREFIX
620 * prints the absolute path of the target.
621 * This works be cause :H will generate '.' if there is no /
622 * and :tA will resolve that to cwd.
623 */
624 Global_Set(MAKE_META_PREFIX,
625 "Building ${.TARGET:H:tA}/${.TARGET:T}");
626 }
627 if (once)
628 return;
629 once = true;
630 memset(&Mybm, 0, sizeof Mybm);
631 /*
632 * We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
633 */
634 (void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
635 SCOPE_GLOBAL, VARE_WANTRES, &metaBailiwickStr);
636 /* TODO: handle errors */
637 str2Lst_Append(&metaBailiwick, metaBailiwickStr);
638 /*
639 * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
640 */
641 Global_Append(MAKE_META_IGNORE_PATHS,
642 "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}");
643 (void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
644 SCOPE_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr);
645 /* TODO: handle errors */
646 str2Lst_Append(&metaIgnorePaths, metaIgnorePathsStr);
647
648 /*
649 * We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}
650 */
651 value = Var_Value(SCOPE_GLOBAL, MAKE_META_IGNORE_PATTERNS);
652 if (value.str != NULL) {
653 metaIgnorePatterns = true;
654 FStr_Done(&value);
655 }
656 value = Var_Value(SCOPE_GLOBAL, MAKE_META_IGNORE_FILTER);
657 if (value.str != NULL) {
658 metaIgnoreFilter = true;
659 FStr_Done(&value);
660 }
661 }
662
663 /*
664 * In each case below we allow for job==NULL
665 */
666 void
667 meta_job_start(Job *job, GNode *gn)
668 {
669 BuildMon *pbm;
670
671 if (job != NULL) {
672 pbm = &job->bm;
673 } else {
674 pbm = &Mybm;
675 }
676 pbm->mfp = meta_create(pbm, gn);
677 #ifdef USE_FILEMON_ONCE
678 /* compat mode we open the filemon dev once per command */
679 if (job == NULL)
680 return;
681 #endif
682 #ifdef USE_FILEMON
683 if (pbm->mfp != NULL && useFilemon) {
684 meta_open_filemon(pbm);
685 } else {
686 pbm->mon_fd = -1;
687 pbm->filemon = NULL;
688 }
689 #endif
690 }
691
692 /*
693 * The child calls this before doing anything.
694 * It does not disturb our state.
695 */
696 void
697 meta_job_child(Job *job)
698 {
699 #ifdef USE_FILEMON
700 BuildMon *pbm;
701
702 if (job != NULL) {
703 pbm = &job->bm;
704 } else {
705 pbm = &Mybm;
706 }
707 if (pbm->mfp != NULL) {
708 close(fileno(pbm->mfp));
709 if (useFilemon && pbm->filemon != NULL) {
710 pid_t pid;
711
712 pid = getpid();
713 if (filemon_setpid_child(pbm->filemon, pid) == -1) {
714 err(1, "Could not set filemon pid!");
715 }
716 }
717 }
718 #endif
719 }
720
721 void
722 meta_job_parent(Job *job, pid_t pid)
723 {
724 #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
725 BuildMon *pbm;
726
727 if (job != NULL) {
728 pbm = &job->bm;
729 } else {
730 pbm = &Mybm;
731 }
732 if (useFilemon && pbm->filemon != NULL) {
733 filemon_setpid_parent(pbm->filemon, pid);
734 }
735 #endif
736 }
737
738 int
739 meta_job_fd(Job *job)
740 {
741 #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
742 BuildMon *pbm;
743
744 if (job != NULL) {
745 pbm = &job->bm;
746 } else {
747 pbm = &Mybm;
748 }
749 if (useFilemon && pbm->filemon != NULL) {
750 return filemon_readfd(pbm->filemon);
751 }
752 #endif
753 return -1;
754 }
755
756 int
757 meta_job_event(Job *job)
758 {
759 #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
760 BuildMon *pbm;
761
762 if (job != NULL) {
763 pbm = &job->bm;
764 } else {
765 pbm = &Mybm;
766 }
767 if (useFilemon && pbm->filemon != NULL) {
768 return filemon_process(pbm->filemon);
769 }
770 #endif
771 return 0;
772 }
773
774 void
775 meta_job_error(Job *job, GNode *gn, bool ignerr, int status)
776 {
777 char cwd[MAXPATHLEN];
778 BuildMon *pbm;
779
780 if (job != NULL) {
781 pbm = &job->bm;
782 if (gn == NULL)
783 gn = job->node;
784 } else {
785 pbm = &Mybm;
786 }
787 if (pbm->mfp != NULL) {
788 fprintf(pbm->mfp, "\n*** Error code %d%s\n",
789 status, ignerr ? "(ignored)" : "");
790 }
791 if (gn != NULL)
792 Global_Set(".ERROR_TARGET", GNode_Path(gn));
793 getcwd(cwd, sizeof cwd);
794 Global_Set(".ERROR_CWD", cwd);
795 if (pbm->meta_fname[0] != '\0') {
796 Global_Set(".ERROR_META_FILE", pbm->meta_fname);
797 }
798 meta_job_finish(job);
799 }
800
801 void
802 meta_job_output(Job *job, char *cp, const char *nl)
803 {
804 BuildMon *pbm;
805
806 if (job != NULL) {
807 pbm = &job->bm;
808 } else {
809 pbm = &Mybm;
810 }
811 if (pbm->mfp != NULL) {
812 if (metaVerbose) {
813 static char *meta_prefix = NULL;
814 static size_t meta_prefix_len;
815
816 if (meta_prefix == NULL) {
817 char *cp2;
818
819 (void)Var_Subst("${" MAKE_META_PREFIX "}",
820 SCOPE_GLOBAL, VARE_WANTRES, &meta_prefix);
821 /* TODO: handle errors */
822 if ((cp2 = strchr(meta_prefix, '$')) != NULL)
823 meta_prefix_len = (size_t)(cp2 - meta_prefix);
824 else
825 meta_prefix_len = strlen(meta_prefix);
826 }
827 if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) {
828 cp = strchr(cp + 1, '\n');
829 if (cp == NULL)
830 return;
831 cp++;
832 }
833 }
834 fprintf(pbm->mfp, "%s%s", cp, nl);
835 }
836 }
837
838 int
839 meta_cmd_finish(void *pbmp)
840 {
841 int error = 0;
842 BuildMon *pbm = pbmp;
843 #ifdef USE_FILEMON
844 int x;
845 #endif
846
847 if (pbm == NULL)
848 pbm = &Mybm;
849
850 #ifdef USE_FILEMON
851 if (pbm->filemon != NULL) {
852 while (filemon_process(pbm->filemon) > 0)
853 continue;
854 if (filemon_close(pbm->filemon) == -1)
855 error = errno;
856 x = filemon_read(pbm->mfp, pbm->mon_fd);
857 if (error == 0 && x != 0)
858 error = x;
859 pbm->mon_fd = -1;
860 pbm->filemon = NULL;
861 return error;
862 }
863 #endif
864
865 fprintf(pbm->mfp, "\n"); /* ensure end with newline */
866 return error;
867 }
868
869 int
870 meta_job_finish(Job *job)
871 {
872 BuildMon *pbm;
873 int error = 0;
874 int x;
875
876 if (job != NULL) {
877 pbm = &job->bm;
878 } else {
879 pbm = &Mybm;
880 }
881 if (pbm->mfp != NULL) {
882 error = meta_cmd_finish(pbm);
883 x = fclose(pbm->mfp);
884 if (error == 0 && x != 0)
885 error = errno;
886 pbm->mfp = NULL;
887 pbm->meta_fname[0] = '\0';
888 }
889 return error;
890 }
891
892 void
893 meta_finish(void)
894 {
895 Lst_Done(&metaBailiwick);
896 free(metaBailiwickStr);
897 Lst_Done(&metaIgnorePaths);
898 free(metaIgnorePathsStr);
899 }
900
901 /*
902 * Fetch a full line from fp - growing bufp if needed
903 * Return length in bufp.
904 */
905 static int
906 fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
907 {
908 char *buf = *bufp;
909 size_t bufsz = *szp;
910 struct stat fs;
911 int x;
912
913 if (fgets(&buf[o], (int)bufsz - o, fp) != NULL) {
914 check_newline:
915 x = o + (int)strlen(&buf[o]);
916 if (buf[x - 1] == '\n')
917 return x;
918 /*
919 * We need to grow the buffer.
920 * The meta file can give us a clue.
921 */
922 if (fstat(fileno(fp), &fs) == 0) {
923 size_t newsz;
924 char *p;
925
926 newsz = ROUNDUP(((size_t)fs.st_size / 2), BUFSIZ);
927 if (newsz <= bufsz)
928 newsz = ROUNDUP((size_t)fs.st_size, BUFSIZ);
929 if (newsz <= bufsz)
930 return x; /* truncated */
931 DEBUG2(META, "growing buffer %u -> %u\n",
932 (unsigned)bufsz, (unsigned)newsz);
933 p = bmake_realloc(buf, newsz);
934 *bufp = buf = p;
935 *szp = bufsz = newsz;
936 /* fetch the rest */
937 if (fgets(&buf[x], (int)bufsz - x, fp) == NULL)
938 return x; /* truncated! */
939 goto check_newline;
940 }
941 }
942 return 0;
943 }
944
945 static bool
946 prefix_match(const char *prefix, const char *path)
947 {
948 size_t n = strlen(prefix);
949
950 return strncmp(path, prefix, n) == 0;
951 }
952
953 static bool
954 has_any_prefix(const char *path, StringList *prefixes)
955 {
956 StringListNode *ln;
957
958 for (ln = prefixes->first; ln != NULL; ln = ln->next)
959 if (prefix_match(ln->datum, path))
960 return true;
961 return false;
962 }
963
964 /* See if the path equals prefix or starts with "prefix/". */
965 static bool
966 path_starts_with(const char *path, const char *prefix)
967 {
968 size_t n = strlen(prefix);
969
970 if (strncmp(path, prefix, n) != 0)
971 return false;
972 return path[n] == '\0' || path[n] == '/';
973 }
974
975 static bool
976 meta_ignore(GNode *gn, const char *p)
977 {
978 char fname[MAXPATHLEN];
979
980 if (p == NULL)
981 return true;
982
983 if (*p == '/') {
984 cached_realpath(p, fname); /* clean it up */
985 if (has_any_prefix(fname, &metaIgnorePaths)) {
986 #ifdef DEBUG_META_MODE
987 DEBUG1(META, "meta_oodate: ignoring path: %s\n", p);
988 #endif
989 return true;
990 }
991 }
992
993 if (metaIgnorePatterns) {
994 const char *expr;
995 char *pm;
996
997 /*
998 * XXX: This variable is set on a target GNode but is not one of
999 * the usual local variables. It should be deleted afterwards.
1000 * Ideally it would not be created in the first place, just like
1001 * in a .for loop.
1002 */
1003 Var_Set(gn, ".p.", p);
1004 expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}";
1005 (void)Var_Subst(expr, gn, VARE_WANTRES, &pm);
1006 /* TODO: handle errors */
1007 if (pm[0] != '\0') {
1008 #ifdef DEBUG_META_MODE
1009 DEBUG1(META, "meta_oodate: ignoring pattern: %s\n", p);
1010 #endif
1011 free(pm);
1012 return true;
1013 }
1014 free(pm);
1015 }
1016
1017 if (metaIgnoreFilter) {
1018 char *fm;
1019
1020 /* skip if filter result is empty */
1021 snprintf(fname, sizeof fname,
1022 "${%s:L:${%s:ts:}}",
1023 p, MAKE_META_IGNORE_FILTER);
1024 (void)Var_Subst(fname, gn, VARE_WANTRES, &fm);
1025 /* TODO: handle errors */
1026 if (*fm == '\0') {
1027 #ifdef DEBUG_META_MODE
1028 DEBUG1(META, "meta_oodate: ignoring filtered: %s\n", p);
1029 #endif
1030 free(fm);
1031 return true;
1032 }
1033 free(fm);
1034 }
1035 return false;
1036 }
1037
1038 /*
1039 * When running with 'meta' functionality, a target can be out-of-date
1040 * if any of the references in its meta data file is more recent.
1041 * We have to track the latestdir on a per-process basis.
1042 */
1043 #define LCWD_VNAME_FMT ".meta.%d.lcwd"
1044 #define LDIR_VNAME_FMT ".meta.%d.ldir"
1045
1046 /*
1047 * It is possible that a .meta file is corrupted,
1048 * if we detect this we want to reproduce it.
1049 * Setting oodate true will have that effect.
1050 */
1051 #define CHECK_VALID_META(p) if (!(p != NULL && *p != '\0')) { \
1052 warnx("%s: %d: malformed", fname, lineno); \
1053 oodate = true; \
1054 continue; \
1055 }
1056
1057 #define DEQUOTE(p) if (*p == '\'') { \
1058 char *ep; \
1059 p++; \
1060 if ((ep = strchr(p, '\'')) != NULL) \
1061 *ep = '\0'; \
1062 }
1063
1064 static void
1065 append_if_new(StringList *list, const char *str)
1066 {
1067 StringListNode *ln;
1068
1069 for (ln = list->first; ln != NULL; ln = ln->next)
1070 if (strcmp(ln->datum, str) == 0)
1071 return;
1072 Lst_Append(list, bmake_strdup(str));
1073 }
1074
1075 bool
1076 meta_oodate(GNode *gn, bool oodate)
1077 {
1078 static char *tmpdir = NULL;
1079 static char cwd[MAXPATHLEN];
1080 char lcwd_vname[64];
1081 char ldir_vname[64];
1082 char lcwd[MAXPATHLEN];
1083 char latestdir[MAXPATHLEN];
1084 char fname[MAXPATHLEN];
1085 char fname1[MAXPATHLEN];
1086 char fname2[MAXPATHLEN];
1087 char fname3[MAXPATHLEN];
1088 FStr dname;
1089 const char *tname;
1090 char *p;
1091 char *link_src;
1092 char *move_target;
1093 static size_t cwdlen = 0;
1094 static size_t tmplen = 0;
1095 FILE *fp;
1096 bool needOODATE = false;
1097 StringList missingFiles;
1098 bool have_filemon = false;
1099
1100 if (oodate)
1101 return oodate; /* we're done */
1102
1103 dname = Var_Value(gn, ".OBJDIR");
1104 tname = GNode_VarTarget(gn);
1105
1106 /* if this succeeds fname3 is realpath of dname */
1107 if (!meta_needed(gn, dname.str, fname3, false))
1108 goto oodate_out;
1109 dname.str = fname3;
1110
1111 Lst_Init(&missingFiles);
1112
1113 /*
1114 * We need to check if the target is out-of-date. This includes
1115 * checking if the expanded command has changed. This in turn
1116 * requires that all variables are set in the same way that they
1117 * would be if the target needs to be re-built.
1118 */
1119 Make_DoAllVar(gn);
1120
1121 meta_name(fname, sizeof fname, dname.str, tname, dname.str);
1122
1123 #ifdef DEBUG_META_MODE
1124 DEBUG1(META, "meta_oodate: %s\n", fname);
1125 #endif
1126
1127 if ((fp = fopen(fname, "r")) != NULL) {
1128 static char *buf = NULL;
1129 static size_t bufsz;
1130 int lineno = 0;
1131 int lastpid = 0;
1132 int pid;
1133 int x;
1134 StringListNode *cmdNode;
1135 struct cached_stat cst;
1136
1137 if (buf == NULL) {
1138 bufsz = 8 * BUFSIZ;
1139 buf = bmake_malloc(bufsz);
1140 }
1141
1142 if (cwdlen == 0) {
1143 if (getcwd(cwd, sizeof cwd) == NULL)
1144 err(1, "Could not get current working directory");
1145 cwdlen = strlen(cwd);
1146 }
1147 strlcpy(lcwd, cwd, sizeof lcwd);
1148 strlcpy(latestdir, cwd, sizeof latestdir);
1149
1150 if (tmpdir == NULL) {
1151 tmpdir = getTmpdir();
1152 tmplen = strlen(tmpdir);
1153 }
1154
1155 /* we want to track all the .meta we read */
1156 Global_Append(".MAKE.META.FILES", fname);
1157
1158 cmdNode = gn->commands.first;
1159 while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) {
1160 lineno++;
1161 if (buf[x - 1] == '\n')
1162 buf[x - 1] = '\0';
1163 else {
1164 warnx("%s: %d: line truncated at %u", fname, lineno, x);
1165 oodate = true;
1166 break;
1167 }
1168 link_src = NULL;
1169 move_target = NULL;
1170 /* Find the start of the build monitor section. */
1171 if (!have_filemon) {
1172 if (strncmp(buf, "-- filemon", 10) == 0) {
1173 have_filemon = true;
1174 continue;
1175 }
1176 if (strncmp(buf, "# buildmon", 10) == 0) {
1177 have_filemon = true;
1178 continue;
1179 }
1180 }
1181
1182 /* Delimit the record type. */
1183 p = buf;
1184 #ifdef DEBUG_META_MODE
1185 DEBUG3(META, "%s: %d: %s\n", fname, lineno, buf);
1186 #endif
1187 strsep(&p, " ");
1188 if (have_filemon) {
1189 /*
1190 * We are in the 'filemon' output section.
1191 * Each record from filemon follows the general form:
1192 *
1193 * <key> <pid> <data>
1194 *
1195 * Where:
1196 * <key> is a single letter, denoting the syscall.
1197 * <pid> is the process that made the syscall.
1198 * <data> is the arguments (of interest).
1199 */
1200 switch(buf[0]) {
1201 case '#': /* comment */
1202 case 'V': /* version */
1203 break;
1204 default:
1205 /*
1206 * We need to track pathnames per-process.
1207 *
1208 * Each process run by make starts off in the 'CWD'
1209 * recorded in the .meta file, if it chdirs ('C')
1210 * elsewhere we need to track that - but only for
1211 * that process. If it forks ('F'), we initialize
1212 * the child to have the same cwd as its parent.
1213 *
1214 * We also need to track the 'latestdir' of
1215 * interest. This is usually the same as cwd, but
1216 * not if a process is reading directories.
1217 *
1218 * Each time we spot a different process ('pid')
1219 * we save the current value of 'latestdir' in a
1220 * variable qualified by 'lastpid', and
1221 * re-initialize 'latestdir' to any pre-saved
1222 * value for the current 'pid' and 'CWD' if none.
1223 */
1224 CHECK_VALID_META(p);
1225 pid = atoi(p);
1226 if (pid > 0 && pid != lastpid) {
1227 FStr ldir;
1228
1229 if (lastpid > 0) {
1230 /* We need to remember these. */
1231 Global_SetExpand(lcwd_vname, lcwd);
1232 Global_SetExpand(ldir_vname, latestdir);
1233 }
1234 snprintf(lcwd_vname, sizeof lcwd_vname, LCWD_VNAME_FMT, pid);
1235 snprintf(ldir_vname, sizeof ldir_vname, LDIR_VNAME_FMT, pid);
1236 lastpid = pid;
1237 ldir = Var_Value(SCOPE_GLOBAL, ldir_vname);
1238 if (ldir.str != NULL) {
1239 strlcpy(latestdir, ldir.str, sizeof latestdir);
1240 FStr_Done(&ldir);
1241 }
1242 ldir = Var_Value(SCOPE_GLOBAL, lcwd_vname);
1243 if (ldir.str != NULL) {
1244 strlcpy(lcwd, ldir.str, sizeof lcwd);
1245 FStr_Done(&ldir);
1246 }
1247 }
1248 /* Skip past the pid. */
1249 if (strsep(&p, " ") == NULL)
1250 continue;
1251 #ifdef DEBUG_META_MODE
1252 if (DEBUG(META))
1253 debug_printf("%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n",
1254 fname, lineno,
1255 pid, buf[0], cwd, lcwd, latestdir);
1256 #endif
1257 break;
1258 }
1259
1260 CHECK_VALID_META(p);
1261
1262 /* Process according to record type. */
1263 switch (buf[0]) {
1264 case 'X': /* eXit */
1265 Var_DeleteExpand(SCOPE_GLOBAL, lcwd_vname);
1266 Var_DeleteExpand(SCOPE_GLOBAL, ldir_vname);
1267 lastpid = 0; /* no need to save ldir_vname */
1268 break;
1269
1270 case 'F': /* [v]Fork */
1271 {
1272 char cldir[64];
1273 int child;
1274
1275 child = atoi(p);
1276 if (child > 0) {
1277 snprintf(cldir, sizeof cldir, LCWD_VNAME_FMT, child);
1278 Global_SetExpand(cldir, lcwd);
1279 snprintf(cldir, sizeof cldir, LDIR_VNAME_FMT, child);
1280 Global_SetExpand(cldir, latestdir);
1281 #ifdef DEBUG_META_MODE
1282 if (DEBUG(META))
1283 debug_printf(
1284 "%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n",
1285 fname, lineno,
1286 child, cwd, lcwd, latestdir);
1287 #endif
1288 }
1289 }
1290 break;
1291
1292 case 'C': /* Chdir */
1293 /* Update lcwd and latest directory. */
1294 strlcpy(latestdir, p, sizeof latestdir);
1295 strlcpy(lcwd, p, sizeof lcwd);
1296 Global_SetExpand(lcwd_vname, lcwd);
1297 Global_SetExpand(ldir_vname, lcwd);
1298 #ifdef DEBUG_META_MODE
1299 DEBUG4(META, "%s: %d: cwd=%s ldir=%s\n",
1300 fname, lineno, cwd, lcwd);
1301 #endif
1302 break;
1303
1304 case 'M': /* renaMe */
1305 /*
1306 * For 'M'oves we want to check
1307 * the src as for 'R'ead
1308 * and the target as for 'W'rite.
1309 */
1310 {
1311 char *cp = p; /* save this for a second */
1312 /* now get target */
1313 if (strsep(&p, " ") == NULL)
1314 continue;
1315 CHECK_VALID_META(p);
1316 move_target = p;
1317 p = cp;
1318 }
1319 /* 'L' and 'M' put single quotes around the args */
1320 DEQUOTE(p);
1321 DEQUOTE(move_target);
1322 /* FALLTHROUGH */
1323 case 'D': /* unlink */
1324 if (*p == '/') {
1325 /* remove any missingFiles entries that match p */
1326 StringListNode *ln = missingFiles.first;
1327 while (ln != NULL) {
1328 StringListNode *next = ln->next;
1329 if (path_starts_with(ln->datum, p)) {
1330 free(ln->datum);
1331 Lst_Remove(&missingFiles, ln);
1332 }
1333 ln = next;
1334 }
1335 }
1336 if (buf[0] == 'M') {
1337 /* the target of the mv is a file 'W'ritten */
1338 #ifdef DEBUG_META_MODE
1339 DEBUG2(META, "meta_oodate: M %s -> %s\n",
1340 p, move_target);
1341 #endif
1342 p = move_target;
1343 goto check_write;
1344 }
1345 break;
1346 case 'L': /* Link */
1347 /*
1348 * For 'L'inks check
1349 * the src as for 'R'ead
1350 * and the target as for 'W'rite.
1351 */
1352 link_src = p;
1353 /* now get target */
1354 if (strsep(&p, " ") == NULL)
1355 continue;
1356 CHECK_VALID_META(p);
1357 /* 'L' and 'M' put single quotes around the args */
1358 DEQUOTE(p);
1359 DEQUOTE(link_src);
1360 #ifdef DEBUG_META_MODE
1361 DEBUG2(META, "meta_oodate: L %s -> %s\n", link_src, p);
1362 #endif
1363 /* FALLTHROUGH */
1364 case 'W': /* Write */
1365 check_write:
1366 /*
1367 * If a file we generated within our bailiwick
1368 * but outside of .OBJDIR is missing,
1369 * we need to do it again.
1370 */
1371 /* ignore non-absolute paths */
1372 if (*p != '/')
1373 break;
1374
1375 if (Lst_IsEmpty(&metaBailiwick))
1376 break;
1377
1378 /* ignore cwd - normal dependencies handle those */
1379 if (strncmp(p, cwd, cwdlen) == 0)
1380 break;
1381
1382 if (!has_any_prefix(p, &metaBailiwick))
1383 break;
1384
1385 /* tmpdir might be within */
1386 if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0)
1387 break;
1388
1389 /* ignore anything containing the string "tmp" */
1390 /* XXX: The arguments to strstr must be swapped. */
1391 if (strstr("tmp", p) != NULL)
1392 break;
1393
1394 if ((link_src != NULL && cached_lstat(p, &cst) < 0) ||
1395 (link_src == NULL && cached_stat(p, &cst) < 0)) {
1396 if (!meta_ignore(gn, p))
1397 append_if_new(&missingFiles, p);
1398 }
1399 break;
1400 check_link_src:
1401 p = link_src;
1402 link_src = NULL;
1403 #ifdef DEBUG_META_MODE
1404 DEBUG1(META, "meta_oodate: L src %s\n", p);
1405 #endif
1406 /* FALLTHROUGH */
1407 case 'R': /* Read */
1408 case 'E': /* Exec */
1409 /*
1410 * Check for runtime files that can't
1411 * be part of the dependencies because
1412 * they are _expected_ to change.
1413 */
1414 if (meta_ignore(gn, p))
1415 break;
1416
1417 /*
1418 * The rest of the record is the file name.
1419 * Check if it's not an absolute path.
1420 */
1421 {
1422 char *sdirs[4];
1423 char **sdp;
1424 int sdx = 0;
1425 bool found = false;
1426
1427 if (*p == '/') {
1428 sdirs[sdx++] = p; /* done */
1429 } else {
1430 if (strcmp(".", p) == 0)
1431 continue; /* no point */
1432
1433 /* Check vs latestdir */
1434 snprintf(fname1, sizeof fname1, "%s/%s", latestdir, p);
1435 sdirs[sdx++] = fname1;
1436
1437 if (strcmp(latestdir, lcwd) != 0) {
1438 /* Check vs lcwd */
1439 snprintf(fname2, sizeof fname2, "%s/%s", lcwd, p);
1440 sdirs[sdx++] = fname2;
1441 }
1442 if (strcmp(lcwd, cwd) != 0) {
1443 /* Check vs cwd */
1444 snprintf(fname3, sizeof fname3, "%s/%s", cwd, p);
1445 sdirs[sdx++] = fname3;
1446 }
1447 }
1448 sdirs[sdx++] = NULL;
1449
1450 for (sdp = sdirs; *sdp != NULL && !found; sdp++) {
1451 #ifdef DEBUG_META_MODE
1452 DEBUG3(META, "%s: %d: looking for: %s\n",
1453 fname, lineno, *sdp);
1454 #endif
1455 if (cached_stat(*sdp, &cst) == 0) {
1456 found = true;
1457 p = *sdp;
1458 }
1459 }
1460 if (found) {
1461 #ifdef DEBUG_META_MODE
1462 DEBUG3(META, "%s: %d: found: %s\n",
1463 fname, lineno, p);
1464 #endif
1465 if (!S_ISDIR(cst.cst_mode) &&
1466 cst.cst_mtime > gn->mtime) {
1467 DEBUG3(META, "%s: %d: file '%s' is newer than the target...\n",
1468 fname, lineno, p);
1469 oodate = true;
1470 } else if (S_ISDIR(cst.cst_mode)) {
1471 /* Update the latest directory. */
1472 cached_realpath(p, latestdir);
1473 }
1474 } else if (errno == ENOENT && *p == '/' &&
1475 strncmp(p, cwd, cwdlen) != 0) {
1476 /*
1477 * A referenced file outside of CWD is missing.
1478 * We cannot catch every eventuality here...
1479 */
1480 append_if_new(&missingFiles, p);
1481 }
1482 }
1483 if (buf[0] == 'E') {
1484 /* previous latestdir is no longer relevant */
1485 strlcpy(latestdir, lcwd, sizeof latestdir);
1486 }
1487 break;
1488 default:
1489 break;
1490 }
1491 if (!oodate && buf[0] == 'L' && link_src != NULL)
1492 goto check_link_src;
1493 } else if (strcmp(buf, "CMD") == 0) {
1494 /*
1495 * Compare the current command with the one in the
1496 * meta data file.
1497 */
1498 if (cmdNode == NULL) {
1499 DEBUG2(META, "%s: %d: there were more build commands in the meta data file than there are now...\n",
1500 fname, lineno);
1501 oodate = true;
1502 } else {
1503 const char *cp;
1504 char *cmd = cmdNode->datum;
1505 bool hasOODATE = false;
1506
1507 if (strstr(cmd, "$?") != NULL)
1508 hasOODATE = true;
1509 else if ((cp = strstr(cmd, ".OODATE")) != NULL) {
1510 /* check for $[{(].OODATE[:)}] */
1511 if (cp > cmd + 2 && cp[-2] == '$')
1512 hasOODATE = true;
1513 }
1514 if (hasOODATE) {
1515 needOODATE = true;
1516 DEBUG2(META, "%s: %d: cannot compare command using .OODATE\n",
1517 fname, lineno);
1518 }
1519 (void)Var_Subst(cmd, gn, VARE_UNDEFERR, &cmd);
1520 /* TODO: handle errors */
1521
1522 if ((cp = strchr(cmd, '\n')) != NULL) {
1523 int n;
1524
1525 /*
1526 * This command contains newlines, we need to
1527 * fetch more from the .meta file before we
1528 * attempt a comparison.
1529 */
1530 /* first put the newline back at buf[x - 1] */
1531 buf[x - 1] = '\n';
1532 do {
1533 /* now fetch the next line */
1534 if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0)
1535 break;
1536 x = n;
1537 lineno++;
1538 if (buf[x - 1] != '\n') {
1539 warnx("%s: %d: line truncated at %u", fname, lineno, x);
1540 break;
1541 }
1542 cp = strchr(cp + 1, '\n');
1543 } while (cp != NULL);
1544 if (buf[x - 1] == '\n')
1545 buf[x - 1] = '\0';
1546 }
1547 if (p != NULL &&
1548 !hasOODATE &&
1549 !(gn->type & OP_NOMETA_CMP) &&
1550 (strcmp(p, cmd) != 0)) {
1551 DEBUG4(META, "%s: %d: a build command has changed\n%s\nvs\n%s\n",
1552 fname, lineno, p, cmd);
1553 if (!metaIgnoreCMDs)
1554 oodate = true;
1555 }
1556 free(cmd);
1557 cmdNode = cmdNode->next;
1558 }
1559 } else if (strcmp(buf, "CWD") == 0) {
1560 /*
1561 * Check if there are extra commands now
1562 * that weren't in the meta data file.
1563 */
1564 if (!oodate && cmdNode != NULL) {
1565 DEBUG2(META, "%s: %d: there are extra build commands now that weren't in the meta data file\n",
1566 fname, lineno);
1567 oodate = true;
1568 }
1569 CHECK_VALID_META(p);
1570 if (strcmp(p, cwd) != 0) {
1571 DEBUG4(META, "%s: %d: the current working directory has changed from '%s' to '%s'\n",
1572 fname, lineno, p, curdir);
1573 oodate = true;
1574 }
1575 }
1576 }
1577
1578 fclose(fp);
1579 if (!Lst_IsEmpty(&missingFiles)) {
1580 DEBUG2(META, "%s: missing files: %s...\n",
1581 fname, (char *)missingFiles.first->datum);
1582 oodate = true;
1583 }
1584 if (!oodate && !have_filemon && filemonMissing) {
1585 DEBUG1(META, "%s: missing filemon data\n", fname);
1586 oodate = true;
1587 }
1588 } else {
1589 if (writeMeta && (metaMissing || (gn->type & OP_META))) {
1590 const char *cp = NULL;
1591
1592 /* if target is in .CURDIR we do not need a meta file */
1593 if (gn->path != NULL && (cp = strrchr(gn->path, '/')) != NULL &&
1594 (cp > gn->path)) {
1595 if (strncmp(curdir, gn->path, (size_t)(cp - gn->path)) != 0) {
1596 cp = NULL; /* not in .CURDIR */
1597 }
1598 }
1599 if (cp == NULL) {
1600 DEBUG1(META, "%s: required but missing\n", fname);
1601 oodate = true;
1602 needOODATE = true; /* assume the worst */
1603 }
1604 }
1605 }
1606
1607 Lst_DoneCall(&missingFiles, free);
1608
1609 if (oodate && needOODATE) {
1610 /*
1611 * Target uses .OODATE which is empty; or we wouldn't be here.
1612 * We have decided it is oodate, so .OODATE needs to be set.
1613 * All we can sanely do is set it to .ALLSRC.
1614 */
1615 Var_Delete(gn, OODATE);
1616 Var_Set(gn, OODATE, GNode_VarAllsrc(gn));
1617 }
1618
1619 oodate_out:
1620 FStr_Done(&dname);
1621 return oodate;
1622 }
1623
1624 /* support for compat mode */
1625
1626 static int childPipe[2];
1627
1628 void
1629 meta_compat_start(void)
1630 {
1631 #ifdef USE_FILEMON_ONCE
1632 /*
1633 * We need to re-open filemon for each cmd.
1634 */
1635 BuildMon *pbm = &Mybm;
1636
1637 if (pbm->mfp != NULL && useFilemon) {
1638 meta_open_filemon(pbm);
1639 } else {
1640 pbm->mon_fd = -1;
1641 pbm->filemon = NULL;
1642 }
1643 #endif
1644 if (pipe(childPipe) < 0)
1645 Punt("Cannot create pipe: %s", strerror(errno));
1646 /* Set close-on-exec flag for both */
1647 (void)fcntl(childPipe[0], F_SETFD, FD_CLOEXEC);
1648 (void)fcntl(childPipe[1], F_SETFD, FD_CLOEXEC);
1649 }
1650
1651 void
1652 meta_compat_child(void)
1653 {
1654 meta_job_child(NULL);
1655 if (dup2(childPipe[1], 1) < 0 || dup2(1, 2) < 0)
1656 execDie("dup2", "pipe");
1657 }
1658
1659 void
1660 meta_compat_parent(pid_t child)
1661 {
1662 int outfd, metafd, maxfd, nfds;
1663 char buf[BUFSIZ+1];
1664 fd_set readfds;
1665
1666 meta_job_parent(NULL, child);
1667 close(childPipe[1]); /* child side */
1668 outfd = childPipe[0];
1669 #ifdef USE_FILEMON
1670 metafd = Mybm.filemon != NULL ? filemon_readfd(Mybm.filemon) : -1;
1671 #else
1672 metafd = -1;
1673 #endif
1674 maxfd = -1;
1675 if (outfd > maxfd)
1676 maxfd = outfd;
1677 if (metafd > maxfd)
1678 maxfd = metafd;
1679
1680 while (outfd != -1 || metafd != -1) {
1681 FD_ZERO(&readfds);
1682 if (outfd != -1) {
1683 FD_SET(outfd, &readfds);
1684 }
1685 if (metafd != -1) {
1686 FD_SET(metafd, &readfds);
1687 }
1688 nfds = select(maxfd + 1, &readfds, NULL, NULL, NULL);
1689 if (nfds == -1) {
1690 if (errno == EINTR)
1691 continue;
1692 err(1, "select");
1693 }
1694
1695 if (outfd != -1 && FD_ISSET(outfd, &readfds) != 0) do {
1696 /* XXX this is not line-buffered */
1697 ssize_t nread = read(outfd, buf, sizeof buf - 1);
1698 if (nread == -1)
1699 err(1, "read");
1700 if (nread == 0) {
1701 close(outfd);
1702 outfd = -1;
1703 break;
1704 }
1705 fwrite(buf, 1, (size_t)nread, stdout);
1706 fflush(stdout);
1707 buf[nread] = '\0';
1708 meta_job_output(NULL, buf, "");
1709 } while (/*CONSTCOND*/false);
1710 if (metafd != -1 && FD_ISSET(metafd, &readfds) != 0) {
1711 if (meta_job_event(NULL) <= 0)
1712 metafd = -1;
1713 }
1714 }
1715 }
1716
1717 #endif /* USE_META */
1718