diff options
author | Ron Yorston <rmy@pobox.com> | 2025-01-14 09:52:58 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2025-01-14 12:22:55 +0000 |
commit | 85bbce87d6428fbc6a8bf7126988c4458a033727 (patch) | |
tree | a82e077df6058209055843a7fc80ac4100f267be | |
parent | 0e8bc6527c9d4b9af17e9b0f759b1ad12ae1bc35 (diff) | |
download | busybox-w32-85bbce87d6428fbc6a8bf7126988c4458a033727.tar.gz busybox-w32-85bbce87d6428fbc6a8bf7126988c4458a033727.tar.bz2 busybox-w32-85bbce87d6428fbc6a8bf7126988c4458a033727.zip |
make: support GNU/BSD suffixes and inference rules
The specification of inference rules in POSIX implies that only
suffixes starting with a period and containing no other periods
are to be considered.
In contrast, both GNU and BSD make allow suffixes to contain an
arbitrary number of periods, or none at all. Allow this as an
extension.
Adds 640-816 bytes.
(pdpmake GitHub issue 70)
-rw-r--r-- | libbb/appletlib.c | 2 | ||||
-rw-r--r-- | miscutils/make.c | 263 | ||||
-rwxr-xr-x | testsuite/make.tests | 30 |
3 files changed, 224 insertions, 71 deletions
diff --git a/libbb/appletlib.c b/libbb/appletlib.c index d6e042775..b1064d10a 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c | |||
@@ -933,7 +933,7 @@ int busybox_main(int argc UNUSED_PARAM, char **argv) | |||
933 | full_write1_str(" multi-call binary.\n"); /* reuse */ | 933 | full_write1_str(" multi-call binary.\n"); /* reuse */ |
934 | #endif | 934 | #endif |
935 | full_write1_str( | 935 | full_write1_str( |
936 | "BusyBox is copyrighted by many authors between 1998-2024.\n" | 936 | "BusyBox is copyrighted by many authors between 1998-2025.\n" |
937 | "Licensed under GPLv2. See source distribution for detailed\n" | 937 | "Licensed under GPLv2. See source distribution for detailed\n" |
938 | "copyright notices.\n" | 938 | "copyright notices.\n" |
939 | "\n" | 939 | "\n" |
diff --git a/miscutils/make.c b/miscutils/make.c index 7316408bf..01fc18822 100644 --- a/miscutils/make.c +++ b/miscutils/make.c | |||
@@ -257,6 +257,11 @@ enum { | |||
257 | #define MAKE_FAILURE 0x01 | 257 | #define MAKE_FAILURE 0x01 |
258 | #define MAKE_DIDSOMETHING 0x02 | 258 | #define MAKE_DIDSOMETHING 0x02 |
259 | 259 | ||
260 | // Return TRUE if c is allowed in a POSIX 2017 macro or target name | ||
261 | #define ispname(c) (isalpha(c) || isdigit(c) || c == '.' || c == '_') | ||
262 | // Return TRUE if c is in the POSIX 'portable filename character set' | ||
263 | #define isfname(c) (ispname(c) || c == '-') | ||
264 | |||
260 | #define HTABSIZE 39 | 265 | #define HTABSIZE 39 |
261 | 266 | ||
262 | struct globals { | 267 | struct globals { |
@@ -320,11 +325,8 @@ struct globals { | |||
320 | #endif | 325 | #endif |
321 | 326 | ||
322 | static int make(struct name *np, int level); | 327 | static int make(struct name *np, int level); |
323 | 328 | static struct name *dyndep(struct name *np, struct rule *infrule, | |
324 | // Return TRUE if c is allowed in a POSIX 2017 macro or target name | 329 | const char **ptsuff); |
325 | #define ispname(c) (isalpha(c) || isdigit(c) || c == '.' || c == '_') | ||
326 | // Return TRUE if c is in the POSIX 'portable filename character set' | ||
327 | #define isfname(c) (ispname(c) || c == '-') | ||
328 | 330 | ||
329 | /* | 331 | /* |
330 | * Utility functions. | 332 | * Utility functions. |
@@ -993,38 +995,27 @@ suffix(const char *name) | |||
993 | } | 995 | } |
994 | 996 | ||
995 | /* | 997 | /* |
996 | * Dynamic dependency. This routine applies the suffix rules | 998 | * Search for an inference rule to convert some suffix ('psuff') |
997 | * to try and find a source and a set of rules for a missing | 999 | * to the target suffix 'tsuff'. The basename of the prerequisite |
998 | * target. NULL is returned on failure. On success the name of | 1000 | * is 'base'. |
999 | * the implicit prerequisite is returned and the details are | ||
1000 | * placed in the imprule structure provided by the caller. | ||
1001 | */ | 1001 | */ |
1002 | static struct name * | 1002 | static struct name * |
1003 | dyndep(struct name *np, struct rule *imprule) | 1003 | dyndep0(char *base, const char *tsuff, struct rule *infrule) |
1004 | { | 1004 | { |
1005 | char *suff, *newsuff; | 1005 | char *psuff; |
1006 | char *base, *name, *member; | ||
1007 | struct name *xp; // Suffixes | 1006 | struct name *xp; // Suffixes |
1008 | struct name *sp; // Suffix rule | 1007 | struct name *sp; // Suffix rule |
1009 | struct name *pp = NULL; // Implicit prerequisite | ||
1010 | struct rule *rp; | 1008 | struct rule *rp; |
1011 | struct depend *dp; | 1009 | struct depend *dp; |
1012 | bool chain = FALSE; | 1010 | bool chain = FALSE; |
1013 | 1011 | ||
1014 | member = NULL; | ||
1015 | name = splitlib(np->n_name, &member); | ||
1016 | |||
1017 | suff = xstrdup(suffix(name)); | ||
1018 | base = member ? member : name; | ||
1019 | *suffix(base) = '\0'; | ||
1020 | |||
1021 | xp = newname(".SUFFIXES"); | 1012 | xp = newname(".SUFFIXES"); |
1022 | retry: | 1013 | retry: |
1023 | for (rp = xp->n_rule; rp; rp = rp->r_next) { | 1014 | for (rp = xp->n_rule; rp; rp = rp->r_next) { |
1024 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | 1015 | for (dp = rp->r_dep; dp; dp = dp->d_next) { |
1025 | // Generate new suffix rule to try | 1016 | // Generate new suffix rule to try |
1026 | newsuff = dp->d_name->n_name; | 1017 | psuff = dp->d_name->n_name; |
1027 | sp = findname(auto_concat(newsuff, suff)); | 1018 | sp = findname(auto_concat(psuff, tsuff)); |
1028 | if (sp && sp->n_rule) { | 1019 | if (sp && sp->n_rule) { |
1029 | struct name *ip; | 1020 | struct name *ip; |
1030 | int got_ip; | 1021 | int got_ip; |
@@ -1032,9 +1023,8 @@ dyndep(struct name *np, struct rule *imprule) | |||
1032 | // Has rule already been used in this chain? | 1023 | // Has rule already been used in this chain? |
1033 | if ((sp->n_flag & N_MARK)) | 1024 | if ((sp->n_flag & N_MARK)) |
1034 | continue; | 1025 | continue; |
1035 | |||
1036 | // Generate a name for an implicit prerequisite | 1026 | // Generate a name for an implicit prerequisite |
1037 | ip = newname(auto_concat(base, newsuff)); | 1027 | ip = newname(auto_concat(base, psuff)); |
1038 | if ((ip->n_flag & N_DOING)) | 1028 | if ((ip->n_flag & N_DOING)) |
1039 | continue; | 1029 | continue; |
1040 | 1030 | ||
@@ -1045,20 +1035,19 @@ dyndep(struct name *np, struct rule *imprule) | |||
1045 | got_ip = ip->n_tim.tv_sec || (ip->n_flag & N_TARGET); | 1035 | got_ip = ip->n_tim.tv_sec || (ip->n_flag & N_TARGET); |
1046 | } else { | 1036 | } else { |
1047 | sp->n_flag |= N_MARK; | 1037 | sp->n_flag |= N_MARK; |
1048 | got_ip = dyndep(ip, NULL) != NULL; | 1038 | got_ip = dyndep(ip, NULL, NULL) != NULL; |
1049 | sp->n_flag &= ~N_MARK; | 1039 | sp->n_flag &= ~N_MARK; |
1050 | } | 1040 | } |
1051 | 1041 | ||
1052 | if (got_ip) { | 1042 | if (got_ip) { |
1053 | // Prerequisite exists or we know how to make it | 1043 | // Prerequisite exists or we know how to make it |
1054 | if (imprule) { | 1044 | if (infrule) { |
1055 | dp = NULL; | 1045 | dp = NULL; |
1056 | newdep(&dp, ip); | 1046 | newdep(&dp, ip); |
1057 | imprule->r_dep = dp; | 1047 | infrule->r_dep = dp; |
1058 | imprule->r_cmd = sp->n_rule->r_cmd; | 1048 | infrule->r_cmd = sp->n_rule->r_cmd; |
1059 | } | 1049 | } |
1060 | pp = ip; | 1050 | return ip; |
1061 | goto finish; | ||
1062 | } | 1051 | } |
1063 | } | 1052 | } |
1064 | } | 1053 | } |
@@ -1069,9 +1058,77 @@ dyndep(struct name *np, struct rule *imprule) | |||
1069 | chain = TRUE; | 1058 | chain = TRUE; |
1070 | goto retry; | 1059 | goto retry; |
1071 | } | 1060 | } |
1072 | finish: | 1061 | return NULL; |
1073 | free(suff); | 1062 | } |
1063 | |||
1064 | /* | ||
1065 | * If 'name' ends with 'suffix' return an allocated string containing | ||
1066 | * the name with the suffix removed, else return NULL. | ||
1067 | */ | ||
1068 | static char * | ||
1069 | has_suffix(const char *name, const char *suffix) | ||
1070 | { | ||
1071 | ssize_t delta = strlen(name) - strlen(suffix); | ||
1072 | char *base = NULL; | ||
1073 | |||
1074 | if (delta > 0 && strcmp(name + delta, suffix) == 0) { | ||
1075 | base = xstrdup(name); | ||
1076 | base[delta] = '\0'; | ||
1077 | } | ||
1078 | |||
1079 | return base; | ||
1080 | } | ||
1081 | |||
1082 | /* | ||
1083 | * Dynamic dependency. This routine applies the suffix rules | ||
1084 | * to try and find a source and a set of rules for a missing | ||
1085 | * target. NULL is returned on failure. On success the name of | ||
1086 | * the implicit prerequisite is returned and the rule used is | ||
1087 | * placed in the infrule structure provided by the caller. | ||
1088 | */ | ||
1089 | static struct name * | ||
1090 | dyndep(struct name *np, struct rule *infrule, const char **ptsuff) | ||
1091 | { | ||
1092 | const char *tsuff; | ||
1093 | char *base, *name, *member; | ||
1094 | struct name *pp = NULL; // Implicit prerequisite | ||
1095 | |||
1096 | member = NULL; | ||
1097 | name = splitlib(np->n_name, &member); | ||
1098 | |||
1099 | // POSIX only allows inference rules with one or two periods. | ||
1100 | // As an extension this restriction is lifted, but not for | ||
1101 | // targets of the form lib.a(member.o). | ||
1102 | if (!posix && member == NULL) { | ||
1103 | struct name *xp = newname(".SUFFIXES"); | ||
1104 | for (struct rule *rp = xp->n_rule; rp; rp = rp->r_next) { | ||
1105 | for (struct depend *dp = rp->r_dep; dp; dp = dp->d_next) { | ||
1106 | tsuff = dp->d_name->n_name; | ||
1107 | base = has_suffix(name, tsuff); | ||
1108 | if (base) { | ||
1109 | pp = dyndep0(base, tsuff, infrule); | ||
1110 | free(base); | ||
1111 | if (pp) { | ||
1112 | if (ptsuff) | ||
1113 | *ptsuff = tsuff; | ||
1114 | goto done; | ||
1115 | } | ||
1116 | } | ||
1117 | } | ||
1118 | } | ||
1119 | |||
1120 | } else | ||
1121 | { | ||
1122 | tsuff = xstrdup(suffix(name)); | ||
1123 | base = member ? member : name; | ||
1124 | *suffix(base) = '\0'; | ||
1125 | |||
1126 | pp = dyndep0(base, tsuff, infrule); | ||
1127 | free((void *)tsuff); | ||
1128 | } | ||
1129 | done: | ||
1074 | free(name); | 1130 | free(name); |
1131 | |||
1075 | return pp; | 1132 | return pp; |
1076 | } | 1133 | } |
1077 | 1134 | ||
@@ -1789,9 +1846,10 @@ readline(FILE *fd, int want_command) | |||
1789 | } | 1846 | } |
1790 | 1847 | ||
1791 | /* | 1848 | /* |
1792 | * Return TRUE if the argument is a known suffix. | 1849 | * Return a pointer to the suffix name if the argument is a known suffix |
1850 | * or NULL if it isn't. | ||
1793 | */ | 1851 | */ |
1794 | static int | 1852 | static const char * |
1795 | is_suffix(const char *s) | 1853 | is_suffix(const char *s) |
1796 | { | 1854 | { |
1797 | struct name *np; | 1855 | struct name *np; |
@@ -1802,7 +1860,39 @@ is_suffix(const char *s) | |||
1802 | for (rp = np->n_rule; rp; rp = rp->r_next) { | 1860 | for (rp = np->n_rule; rp; rp = rp->r_next) { |
1803 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | 1861 | for (dp = rp->r_dep; dp; dp = dp->d_next) { |
1804 | if (strcmp(s, dp->d_name->n_name) == 0) { | 1862 | if (strcmp(s, dp->d_name->n_name) == 0) { |
1805 | return TRUE; | 1863 | return dp->d_name->n_name; |
1864 | } | ||
1865 | } | ||
1866 | } | ||
1867 | return NULL; | ||
1868 | } | ||
1869 | |||
1870 | /* | ||
1871 | * Return TRUE if the argument is formed by concatenating two | ||
1872 | * known suffixes. | ||
1873 | */ | ||
1874 | static int | ||
1875 | is_inference_target(const char *s) | ||
1876 | { | ||
1877 | struct name *np; | ||
1878 | struct rule *rp1, *rp2; | ||
1879 | struct depend *dp1, *dp2; | ||
1880 | |||
1881 | np = newname(".SUFFIXES"); | ||
1882 | for (rp1 = np->n_rule; rp1; rp1 = rp1->r_next) { | ||
1883 | for (dp1 = rp1->r_dep; dp1; dp1 = dp1->d_next) { | ||
1884 | const char *suff1 = dp1->d_name->n_name; | ||
1885 | size_t len = strlen(suff1); | ||
1886 | |||
1887 | if (strncmp(s, suff1, len) == 0) { | ||
1888 | for (rp2 = np->n_rule; rp2; rp2 = rp2->r_next) { | ||
1889 | for (dp2 = rp2->r_dep; dp2; dp2 = dp2->d_next) { | ||
1890 | const char *suff2 = dp2->d_name->n_name; | ||
1891 | if (strcmp(s + len, suff2) == 0) { | ||
1892 | return TRUE; | ||
1893 | } | ||
1894 | } | ||
1895 | } | ||
1806 | } | 1896 | } |
1807 | } | 1897 | } |
1808 | } | 1898 | } |
@@ -1824,7 +1914,6 @@ enum { | |||
1824 | static int | 1914 | static int |
1825 | target_type(char *s) | 1915 | target_type(char *s) |
1826 | { | 1916 | { |
1827 | char *sfx; | ||
1828 | int ret; | 1917 | int ret; |
1829 | static const char *s_name = | 1918 | static const char *s_name = |
1830 | ".DEFAULT\0" | 1919 | ".DEFAULT\0" |
@@ -1854,9 +1943,6 @@ target_type(char *s) | |||
1854 | T_SPECIAL, | 1943 | T_SPECIAL, |
1855 | }; | 1944 | }; |
1856 | 1945 | ||
1857 | if (*s != '.') | ||
1858 | return T_NORMAL; | ||
1859 | |||
1860 | // Check for one of the known special targets | 1946 | // Check for one of the known special targets |
1861 | ret = index_in_strings(s_name, s); | 1947 | ret = index_in_strings(s_name, s); |
1862 | if (ret >= 0) | 1948 | if (ret >= 0) |
@@ -1864,16 +1950,24 @@ target_type(char *s) | |||
1864 | 1950 | ||
1865 | // Check for an inference rule | 1951 | // Check for an inference rule |
1866 | ret = T_NORMAL; | 1952 | ret = T_NORMAL; |
1867 | sfx = suffix(s); | 1953 | if (!posix) { |
1868 | if (is_suffix(sfx)) { | 1954 | if (is_suffix(s) || is_inference_target(s)) { |
1869 | if (s == sfx) { // Single suffix rule | ||
1870 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; | 1955 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; |
1871 | } else { | 1956 | } |
1872 | // Suffix is valid, check that prefix is too | 1957 | } else |
1873 | *sfx = '\0'; | 1958 | { |
1874 | if (is_suffix(s)) | 1959 | // In POSIX inference rule targets must contain one or two dots |
1960 | char *sfx = suffix(s); | ||
1961 | if (*s == '.' && is_suffix(sfx)) { | ||
1962 | if (s == sfx) { // Single suffix rule | ||
1875 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; | 1963 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; |
1876 | *sfx = '.'; | 1964 | } else { |
1965 | // Suffix is valid, check that prefix is too | ||
1966 | *sfx = '\0'; | ||
1967 | if (is_suffix(s)) | ||
1968 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; | ||
1969 | *sfx = '.'; | ||
1970 | } | ||
1877 | } | 1971 | } |
1878 | } | 1972 | } |
1879 | return ret; | 1973 | return ret; |
@@ -2587,7 +2681,7 @@ docmds(struct name *np, struct cmd *cp) | |||
2587 | 2681 | ||
2588 | static int | 2682 | static int |
2589 | make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc, | 2683 | make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc, |
2590 | char *dedup, struct name *implicit) | 2684 | char *dedup, struct name *implicit, const char *tsuff) |
2591 | { | 2685 | { |
2592 | char *name, *member = NULL, *base = NULL, *prereq = NULL; | 2686 | char *name, *member = NULL, *base = NULL, *prereq = NULL; |
2593 | 2687 | ||
@@ -2603,7 +2697,7 @@ make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc, | |||
2603 | char *s; | 2697 | char *s; |
2604 | 2698 | ||
2605 | // As an extension, if we're not dealing with an implicit | 2699 | // As an extension, if we're not dealing with an implicit |
2606 | // rule set $< to the first out-of-date prerequisite. | 2700 | // prerequisite set $< to the first out-of-date prerequisite. |
2607 | if (implicit == NULL) { | 2701 | if (implicit == NULL) { |
2608 | if (oodate) { | 2702 | if (oodate) { |
2609 | s = strchr(oodate, ' '); | 2703 | s = strchr(oodate, ' '); |
@@ -2614,16 +2708,43 @@ make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc, | |||
2614 | } else | 2708 | } else |
2615 | prereq = implicit->n_name; | 2709 | prereq = implicit->n_name; |
2616 | 2710 | ||
2617 | base = member ? member : name; | 2711 | if (!posix && member == NULL) { |
2618 | s = suffix(base); | 2712 | // As an extension remove the suffix from a target, either |
2619 | // As an extension, if we're not dealing with an implicit | 2713 | // that obtained by an inference rule or one of the known |
2620 | // rule and the target ends with a known suffix, remove it | 2714 | // suffixes. Not for targets of the form lib.a(member.o). |
2621 | // and set $* to the stem, else to an empty string. | 2715 | if (tsuff != NULL) { |
2622 | if (implicit == NULL && !is_suffix(s)) | 2716 | base = has_suffix(name, tsuff); |
2623 | base = NULL; | 2717 | if (base) { |
2624 | else | 2718 | free(name); |
2625 | *s = '\0'; | 2719 | name = base; |
2720 | } | ||
2721 | } else { | ||
2722 | struct name *xp = newname(".SUFFIXES"); | ||
2723 | for (struct rule *rp = xp->n_rule; rp; rp = rp->r_next) { | ||
2724 | for (struct depend *dp = rp->r_dep; dp; dp = dp->d_next) { | ||
2725 | base = has_suffix(name, dp->d_name->n_name); | ||
2726 | if (base) { | ||
2727 | free(name); | ||
2728 | name = base; | ||
2729 | goto done; | ||
2730 | } | ||
2731 | } | ||
2732 | } | ||
2733 | } | ||
2734 | } else | ||
2735 | { | ||
2736 | base = member ? member : name; | ||
2737 | s = suffix(base); | ||
2738 | // As an extension, if we're not dealing with an implicit | ||
2739 | // prerequisite and the target ends with a known suffix, | ||
2740 | // remove it and set $* to the stem, else to an empty string. | ||
2741 | if (implicit == NULL && !is_suffix(s)) | ||
2742 | base = NULL; | ||
2743 | else | ||
2744 | *s = '\0'; | ||
2745 | } | ||
2626 | } | 2746 | } |
2747 | done: | ||
2627 | setmacro("<", prereq, 0 | M_VALID); | 2748 | setmacro("<", prereq, 0 | M_VALID); |
2628 | setmacro("*", base, 0 | M_VALID); | 2749 | setmacro("*", base, 0 | M_VALID); |
2629 | free(name); | 2750 | free(name); |
@@ -2667,11 +2788,12 @@ make(struct name *np, int level) | |||
2667 | struct depend *dp; | 2788 | struct depend *dp; |
2668 | struct rule *rp; | 2789 | struct rule *rp; |
2669 | struct name *impdep = NULL; // implicit prerequisite | 2790 | struct name *impdep = NULL; // implicit prerequisite |
2670 | struct rule imprule; | 2791 | struct rule infrule; |
2671 | struct cmd *sc_cmd = NULL; // commands for single-colon rule | 2792 | struct cmd *sc_cmd = NULL; // commands for single-colon rule |
2672 | char *oodate = NULL; | 2793 | char *oodate = NULL; |
2673 | char *allsrc = NULL; | 2794 | char *allsrc = NULL; |
2674 | char *dedup = NULL; | 2795 | char *dedup = NULL; |
2796 | const char *tsuff = NULL; | ||
2675 | struct timespec dtim = {1, 0}; | 2797 | struct timespec dtim = {1, 0}; |
2676 | int estat = 0; | 2798 | int estat = 0; |
2677 | 2799 | ||
@@ -2690,10 +2812,10 @@ make(struct name *np, int level) | |||
2690 | // as an extension, not for phony targets) | 2812 | // as an extension, not for phony targets) |
2691 | sc_cmd = getcmd(np); | 2813 | sc_cmd = getcmd(np); |
2692 | if (!sc_cmd && (posix || !(np->n_flag & N_PHONY))) { | 2814 | if (!sc_cmd && (posix || !(np->n_flag & N_PHONY))) { |
2693 | impdep = dyndep(np, &imprule); | 2815 | impdep = dyndep(np, &infrule, &tsuff); |
2694 | if (impdep) { | 2816 | if (impdep) { |
2695 | sc_cmd = imprule.r_cmd; | 2817 | sc_cmd = infrule.r_cmd; |
2696 | addrule(np, imprule.r_dep, NULL, FALSE); | 2818 | addrule(np, infrule.r_dep, NULL, FALSE); |
2697 | } | 2819 | } |
2698 | } | 2820 | } |
2699 | 2821 | ||
@@ -2715,7 +2837,7 @@ make(struct name *np, int level) | |||
2715 | for (rp = np->n_rule; rp; rp = rp->r_next) { | 2837 | for (rp = np->n_rule; rp; rp = rp->r_next) { |
2716 | if (!rp->r_cmd) { | 2838 | if (!rp->r_cmd) { |
2717 | if (posix || !(np->n_flag & N_PHONY)) | 2839 | if (posix || !(np->n_flag & N_PHONY)) |
2718 | impdep = dyndep(np, &imprule); | 2840 | impdep = dyndep(np, &infrule, &tsuff); |
2719 | if (!impdep) { | 2841 | if (!impdep) { |
2720 | if (doinclude) | 2842 | if (doinclude) |
2721 | return 1; | 2843 | return 1; |
@@ -2743,9 +2865,9 @@ make(struct name *np, int level) | |||
2743 | // If the rule has no commands use the inference rule. | 2865 | // If the rule has no commands use the inference rule. |
2744 | if (!rp->r_cmd) { | 2866 | if (!rp->r_cmd) { |
2745 | locdep = impdep; | 2867 | locdep = impdep; |
2746 | imprule.r_dep->d_next = rp->r_dep; | 2868 | infrule.r_dep->d_next = rp->r_dep; |
2747 | rp->r_dep = imprule.r_dep; | 2869 | rp->r_dep = infrule.r_dep; |
2748 | rp->r_cmd = imprule.r_cmd; | 2870 | rp->r_cmd = infrule.r_cmd; |
2749 | } | 2871 | } |
2750 | // A rule with no prerequisities is executed unconditionally. | 2872 | // A rule with no prerequisities is executed unconditionally. |
2751 | if (!rp->r_dep) | 2873 | if (!rp->r_dep) |
@@ -2776,7 +2898,7 @@ make(struct name *np, int level) | |||
2776 | if (((np->n_flag & N_PHONY) || timespec_le(&np->n_tim, &dtim))) { | 2898 | if (((np->n_flag & N_PHONY) || timespec_le(&np->n_tim, &dtim))) { |
2777 | if (!(estat & MAKE_FAILURE)) { | 2899 | if (!(estat & MAKE_FAILURE)) { |
2778 | estat |= make1(np, rp->r_cmd, oodate, allsrc, | 2900 | estat |= make1(np, rp->r_cmd, oodate, allsrc, |
2779 | dedup, locdep); | 2901 | dedup, locdep, tsuff); |
2780 | dtim = (struct timespec){1, 0}; | 2902 | dtim = (struct timespec){1, 0}; |
2781 | } | 2903 | } |
2782 | free(oodate); | 2904 | free(oodate); |
@@ -2792,7 +2914,7 @@ make(struct name *np, int level) | |||
2792 | } | 2914 | } |
2793 | } | 2915 | } |
2794 | if ((np->n_flag & N_DOUBLE) && impdep) | 2916 | if ((np->n_flag & N_DOUBLE) && impdep) |
2795 | free(imprule.r_dep); | 2917 | free(infrule.r_dep); |
2796 | 2918 | ||
2797 | np->n_flag |= N_DONE; | 2919 | np->n_flag |= N_DONE; |
2798 | np->n_flag &= ~N_DOING; | 2920 | np->n_flag &= ~N_DOING; |
@@ -2801,7 +2923,8 @@ make(struct name *np, int level) | |||
2801 | ((np->n_flag & N_PHONY) || (timespec_le(&np->n_tim, &dtim)))) { | 2923 | ((np->n_flag & N_PHONY) || (timespec_le(&np->n_tim, &dtim)))) { |
2802 | if (!(estat & MAKE_FAILURE)) { | 2924 | if (!(estat & MAKE_FAILURE)) { |
2803 | if (sc_cmd) | 2925 | if (sc_cmd) |
2804 | estat |= make1(np, sc_cmd, oodate, allsrc, dedup, impdep); | 2926 | estat |= make1(np, sc_cmd, oodate, allsrc, dedup, |
2927 | impdep, tsuff); | ||
2805 | else if (!doinclude && level == 0 && !(estat & MAKE_DIDSOMETHING)) | 2928 | else if (!doinclude && level == 0 && !(estat & MAKE_DIDSOMETHING)) |
2806 | warning("nothing to be done for %s", np->n_name); | 2929 | warning("nothing to be done for %s", np->n_name); |
2807 | } else if (!doinclude && !quest) { | 2930 | } else if (!doinclude && !quest) { |
diff --git a/testsuite/make.tests b/testsuite/make.tests index 376bdcc15..e3103c514 100755 --- a/testsuite/make.tests +++ b/testsuite/make.tests | |||
@@ -541,6 +541,36 @@ testing "make chained inference rules" \ | |||
541 | ' | 541 | ' |
542 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | 542 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null |
543 | 543 | ||
544 | # Suffixes with multiple periods are supported | ||
545 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
546 | touch x.c.c | ||
547 | testing "make support multi-period suffixes" \ | ||
548 | "make -f -" "cat x.c.c >x.o.o\nx\n" "" ' | ||
549 | .SUFFIXES: .c.c .o.o | ||
550 | x.o.o: | ||
551 | .c.c.o.o: | ||
552 | cat $< >$@ | ||
553 | @echo $* | ||
554 | ' | ||
555 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
556 | |||
557 | # Suffixes with no periods are supported | ||
558 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
559 | touch filex | ||
560 | testing "make support suffixes without any periods" \ | ||
561 | "make -f -" "cp filex fileh\nfile\ncp filex filez\nfile\n" "" ' | ||
562 | .SUFFIXES: x h z | ||
563 | all: fileh filez | ||
564 | fileh: | ||
565 | filez: filex | ||
566 | cp filex filez | ||
567 | @echo $* | ||
568 | xh: | ||
569 | cp $< $@ | ||
570 | @echo $* | ||
571 | ' | ||
572 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
573 | |||
544 | # make supports *, ? and [] wildcards in targets and prerequisites | 574 | # make supports *, ? and [] wildcards in targets and prerequisites |
545 | mkdir make.tempdir && cd make.tempdir || exit 1 | 575 | mkdir make.tempdir && cd make.tempdir || exit 1 |
546 | touch -t 202206171201 t1a t2aa t3b | 576 | touch -t 202206171201 t1a t2aa t3b |