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