diff options
author | Ron Yorston <rmy@pobox.com> | 2022-11-03 15:49:11 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2022-11-03 15:52:48 +0000 |
commit | 5f64589b84a7349316a04cdbe607a5824459b731 (patch) | |
tree | dae9145ccae5e67012ab172789f9bcb099db40dd | |
parent | 0aceca8673c1d9a8bc34faa460389768efa08eb7 (diff) | |
download | busybox-w32-5f64589b84a7349316a04cdbe607a5824459b731.tar.gz busybox-w32-5f64589b84a7349316a04cdbe607a5824459b731.tar.bz2 busybox-w32-5f64589b84a7349316a04cdbe607a5824459b731.zip |
make: fixes to inference rules
Austin Group defect report 875 clarifies some aspects of inference
rules. The crux of the issue is related to chained inference rules
so it doesn't affect POSIX mode. The test makefile looks like this:
.SUFFIXES: .a .b .c
.a.c:
@echo .a.c
.b.c:
@echo .b.c
test.c: test.b
test.a:
test.b:
The correct output is deemed to be '.a.c'. Additional complications
are:
- whether or not the prerequisite files are present;
- the use of the suffixes '.a' and '.c' may result in the builtin
inference rule '.c.a' being considered.
In favourable circumstances pdpmake managed to give the correct
result, in unfavourable it reported circular dependencies or
segfaulted.
Changes to fix these issues are:
- When prerequisites are being recursively built the standard says:
'Upon recursion, each prerequisite shall become a target itself.'
Follow this requirement.
- At the end of make() the target being built should have its time
(as represented by n_tim in struct name) updated when any action
has been taken.
- When dyndep() is looking for prerequisites it should:
* skip candidates that are in the process of being built;
* consider whether an explicit candidate is a target, not whether
it has any commands associated with it.
pdpmake now behaves similarly to GNU make when presented with
makefiles like the above. bmake gives the incorrect output '.b.c'.
-rw-r--r-- | miscutils/make.c | 39 | ||||
-rwxr-xr-x | testsuite/make.tests | 39 |
2 files changed, 60 insertions, 18 deletions
diff --git a/miscutils/make.c b/miscutils/make.c index daad75f5c..86d06e970 100644 --- a/miscutils/make.c +++ b/miscutils/make.c | |||
@@ -717,21 +717,23 @@ dyndep(struct name *np, struct rule *imprule) | |||
717 | sp = findname(auto_concat(newsuff, suff)); | 717 | sp = findname(auto_concat(newsuff, suff)); |
718 | if (sp && sp->n_rule) { | 718 | if (sp && sp->n_rule) { |
719 | // Generate a name for an implicit prerequisite | 719 | // Generate a name for an implicit prerequisite |
720 | pp = newname(auto_concat(base, newsuff)); | 720 | struct name *ip = newname(auto_concat(base, newsuff)); |
721 | if (!pp->n_tim.tv_sec) | 721 | if ((ip->n_flag & N_DOING)) |
722 | modtime(pp); | 722 | continue; |
723 | if ((!chain && (pp->n_tim.tv_sec || getcmd(pp))) || | 723 | if (!ip->n_tim.tv_sec) |
724 | (chain && dyndep(pp, NULL))) { | 724 | modtime(ip); |
725 | if (chain ? ip->n_tim.tv_sec || (ip->n_flag & N_TARGET) : | ||
726 | dyndep(ip, NULL) != NULL) { | ||
725 | // Prerequisite exists or we know how to make it | 727 | // Prerequisite exists or we know how to make it |
726 | if (imprule) { | 728 | if (imprule) { |
727 | dp = NULL; | 729 | dp = NULL; |
728 | newdep(&dp, pp); | 730 | newdep(&dp, ip); |
729 | imprule->r_dep = dp; | 731 | imprule->r_dep = dp; |
730 | imprule->r_cmd = sp->n_rule->r_cmd; | 732 | imprule->r_cmd = sp->n_rule->r_cmd; |
731 | } | 733 | } |
734 | pp = ip; | ||
732 | goto finish; | 735 | goto finish; |
733 | } | 736 | } |
734 | pp = NULL; | ||
735 | } | 737 | } |
736 | } | 738 | } |
737 | } | 739 | } |
@@ -2121,6 +2123,7 @@ make(struct name *np, int level) | |||
2121 | } | 2123 | } |
2122 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | 2124 | for (dp = rp->r_dep; dp; dp = dp->d_next) { |
2123 | // Make prerequisite | 2125 | // Make prerequisite |
2126 | dp->d_name->n_flag |= N_TARGET; | ||
2124 | estat |= make(dp->d_name, level + 1); | 2127 | estat |= make(dp->d_name, level + 1); |
2125 | 2128 | ||
2126 | // Make strings of out-of-date prerequisites (for $?), | 2129 | // Make strings of out-of-date prerequisites (for $?), |
@@ -2166,28 +2169,28 @@ make(struct name *np, int level) | |||
2166 | 2169 | ||
2167 | if (quest) { | 2170 | if (quest) { |
2168 | if (timespec_le(&np->n_tim, &dtim)) { | 2171 | if (timespec_le(&np->n_tim, &dtim)) { |
2169 | clock_gettime(CLOCK_REALTIME, &np->n_tim); | 2172 | didsomething = 1; |
2170 | return 1; // 1 means rebuild is needed | 2173 | estat = 1; // 1 means rebuild is needed |
2171 | } | 2174 | } |
2172 | } else if (!(np->n_flag & N_DOUBLE) && | 2175 | } else if (!(np->n_flag & N_DOUBLE) && |
2173 | ((np->n_flag & N_PHONY) || (timespec_le(&np->n_tim, &dtim)))) { | 2176 | ((np->n_flag & N_PHONY) || (timespec_le(&np->n_tim, &dtim)))) { |
2174 | if (estat == 0) { | 2177 | if (estat == 0) { |
2175 | if (!sc_cmd) { | 2178 | if (sc_cmd) |
2176 | if (!doinclude) | ||
2177 | bb_error_msg("nothing to be done for %s", np->n_name); | ||
2178 | } else { | ||
2179 | estat = make1(np, sc_cmd, oodate, allsrc, dedup, impdep); | 2179 | estat = make1(np, sc_cmd, oodate, allsrc, dedup, impdep); |
2180 | clock_gettime(CLOCK_REALTIME, &np->n_tim); | 2180 | else if (!doinclude) |
2181 | } | 2181 | bb_error_msg("nothing to be done for %s", np->n_name); |
2182 | didsomething = 1; | ||
2182 | } else if (!doinclude) { | 2183 | } else if (!doinclude) { |
2183 | bb_error_msg("'%s' not built due to errors", np->n_name); | 2184 | bb_error_msg("'%s' not built due to errors", np->n_name); |
2184 | } | 2185 | } |
2185 | free(oodate); | 2186 | free(oodate); |
2186 | } else if (didsomething) { | 2187 | } |
2188 | |||
2189 | if (didsomething) | ||
2187 | clock_gettime(CLOCK_REALTIME, &np->n_tim); | 2190 | clock_gettime(CLOCK_REALTIME, &np->n_tim); |
2188 | } else if (level == 0) { | 2191 | else if (!quest && level == 0) |
2189 | printf("%s: '%s' is up to date\n", applet_name, np->n_name); | 2192 | printf("%s: '%s' is up to date\n", applet_name, np->n_name); |
2190 | } | 2193 | |
2191 | free(allsrc); | 2194 | free(allsrc); |
2192 | free(dedup); | 2195 | free(dedup); |
2193 | return estat; | 2196 | return estat; |
diff --git a/testsuite/make.tests b/testsuite/make.tests index f52770438..7bb736ade 100755 --- a/testsuite/make.tests +++ b/testsuite/make.tests | |||
@@ -397,6 +397,45 @@ target: | |||
397 | : hash # hash | 397 | : hash # hash |
398 | ' | 398 | ' |
399 | 399 | ||
400 | # Austin Group defect report 875 clarifies certain aspects of the | ||
401 | # behaviour of inference rules. Study of this resulted in a number | ||
402 | # of changes to pdpmake. Since the issue at hand involves the use | ||
403 | # of chained inference rules it doesn't affect POSIX mode. | ||
404 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
405 | touch test.j test.k | ||
406 | testing "make proper handling of inference rules 1" \ | ||
407 | "make -f -" \ | ||
408 | ".j.l\n" "" ' | ||
409 | .SUFFIXES: .j .k .l | ||
410 | .j.l: | ||
411 | @echo .j.l | ||
412 | .k.l: | ||
413 | @echo .k.l | ||
414 | test.l: test.k | ||
415 | test.j: | ||
416 | test.k: | ||
417 | ' | ||
418 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
419 | |||
420 | # The actual test in the above-mentioned defect report was actually | ||
421 | # more demanding. It used the suffixes '.a .b .c'. This results in | ||
422 | # the built-in '.c.a' inference rule being introduced into the mix. | ||
423 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
424 | touch test.a test.b | ||
425 | testing "make proper handling of inference rules 2" \ | ||
426 | "make -f -" \ | ||
427 | ".a.c\n" "" ' | ||
428 | .SUFFIXES: .a .b .c | ||
429 | .a.c: | ||
430 | @echo .a.c | ||
431 | .b.c: | ||
432 | @echo .b.c | ||
433 | test.c: test.b | ||
434 | test.a: | ||
435 | test.b: | ||
436 | ' | ||
437 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
438 | |||
400 | # Skip duplicate entries in $? and $^ | 439 | # Skip duplicate entries in $? and $^ |
401 | mkdir make.tempdir && cd make.tempdir || exit 1 | 440 | mkdir make.tempdir && cd make.tempdir || exit 1 |
402 | touch -t 202206171200 file1 file3 | 441 | touch -t 202206171200 file1 file3 |