| /* Implicit rule searching for GNU Make. |
| Copyright (C) 1988, 89, 90, 91, 92, 93, 94 Free Software Foundation, Inc. |
| This file is part of GNU Make. |
| |
| GNU Make is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Make is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Make; see the file COPYING. If not, write to |
| the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ |
| |
| #include "make.h" |
| #include "rule.h" |
| #include "dep.h" |
| #include "file.h" |
| |
| static int pattern_search (); |
| |
| /* For a FILE which has no commands specified, try to figure out some |
| from the implicit pattern rules. |
| Returns 1 if a suitable implicit rule was found, |
| after modifying FILE to contain the appropriate commands and deps, |
| or returns 0 if no implicit rule was found. */ |
| |
| int |
| try_implicit_rule (file, depth) |
| struct file *file; |
| unsigned int depth; |
| { |
| DEBUGPR ("Looking for an implicit rule for `%s'.\n"); |
| |
| /* The order of these searches was previously reversed. My logic now is |
| that since the non-archive search uses more information in the target |
| (the archive search omits the archive name), it is more specific and |
| should come first. */ |
| |
| if (pattern_search (file, 0, depth, 0)) |
| return 1; |
| |
| #ifndef NO_ARCHIVES |
| /* If this is an archive member reference, use just the |
| archive member name to search for implicit rules. */ |
| if (ar_name (file->name)) |
| { |
| DEBUGPR ("Looking for archive-member implicit rule for `%s'.\n"); |
| if (pattern_search (file, 1, depth, 0)) |
| return 1; |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| #define DEBUGP2(msg, a1, a2) \ |
| do { \ |
| if (debug_flag) \ |
| { print_spaces (depth); printf (msg, a1, a2); fflush (stdout); } \ |
| } while (0) |
| |
| /* Search the pattern rules for a rule with an existing dependency to make |
| FILE. If a rule is found, the appropriate commands and deps are put in FILE |
| and 1 is returned. If not, 0 is returned. |
| |
| If ARCHIVE is nonzero, FILE->name is of the form "LIB(MEMBER)". A rule for |
| "(MEMBER)" will be searched for, and "(MEMBER)" will not be chopped up into |
| directory and filename parts. |
| |
| If an intermediate file is found by pattern search, the intermediate file |
| is set up as a target by the recursive call and is also made a dependency |
| of FILE. |
| |
| DEPTH is used for debugging messages. */ |
| |
| static int |
| pattern_search (file, archive, depth, recursions) |
| struct file *file; |
| int archive; |
| unsigned int depth; |
| unsigned int recursions; |
| { |
| /* Filename we are searching for a rule for. */ |
| char *filename = archive ? index (file->name, '(') : file->name; |
| |
| /* Length of FILENAME. */ |
| unsigned int namelen = strlen (filename); |
| |
| /* The last slash in FILENAME (or nil if there is none). */ |
| char *lastslash; |
| |
| /* This is a file-object used as an argument in |
| recursive calls. It never contains any data |
| except during a recursive call. */ |
| struct file *intermediate_file = 0; |
| |
| /* List of dependencies found recursively. */ |
| struct file **intermediate_files |
| = (struct file **) alloca (max_pattern_deps * sizeof (struct file *)); |
| |
| /* List of the patterns used to find intermediate files. */ |
| char **intermediate_patterns |
| = (char **) alloca (max_pattern_deps * sizeof (char *)); |
| |
| /* This buffer records all the dependencies actually found for a rule. */ |
| char **found_files = (char **) alloca (max_pattern_deps * sizeof (char *)); |
| /* Number of dep names now in FOUND_FILES. */ |
| unsigned int deps_found; |
| |
| /* Names of possible dependencies are constructed in this buffer. */ |
| register char *depname = (char *) alloca (namelen + max_pattern_dep_length); |
| |
| /* The start and length of the stem of FILENAME for the current rule. */ |
| register char *stem; |
| register unsigned int stemlen; |
| |
| /* Buffer in which we store all the rules that are possibly applicable. */ |
| struct rule **tryrules |
| = (struct rule **) alloca (num_pattern_rules * max_pattern_targets |
| * sizeof (struct rule *)); |
| |
| /* Number of valid elements in TRYRULES. */ |
| unsigned int nrules; |
| |
| /* The numbers of the rule targets of each rule |
| in TRYRULES that matched the target file. */ |
| unsigned int *matches |
| = (unsigned int *) alloca (num_pattern_rules * sizeof (unsigned int)); |
| |
| /* Each element is nonzero if LASTSLASH was used in |
| matching the corresponding element of TRYRULES. */ |
| char *checked_lastslash |
| = (char *) alloca (num_pattern_rules * sizeof (char)); |
| |
| /* The index in TRYRULES of the rule we found. */ |
| unsigned int foundrule; |
| |
| /* Nonzero if should consider intermediate files as dependencies. */ |
| int intermed_ok; |
| |
| /* Nonzero if we have matched a pattern-rule target |
| that is not just `%'. */ |
| int specific_rule_matched = 0; |
| |
| register unsigned int i; |
| register struct rule *rule; |
| register struct dep *dep; |
| |
| char *p; |
| |
| #ifndef NO_ARCHIVES |
| if (archive || ar_name (filename)) |
| lastslash = 0; |
| else |
| #endif |
| { |
| /* Set LASTSLASH to point at the last slash in FILENAME |
| but not counting any slash at the end. (foo/bar/ counts as |
| bar/ in directory foo/, not empty in directory foo/bar/.) */ |
| lastslash = rindex (filename, '/'); |
| if (lastslash != 0 && lastslash[1] == '\0') |
| lastslash = 0; |
| } |
| |
| /* First see which pattern rules match this target |
| and may be considered. Put them in TRYRULES. */ |
| |
| nrules = 0; |
| for (rule = pattern_rules; rule != 0; rule = rule->next) |
| { |
| /* If the pattern rule has deps but no commands, ignore it. |
| Users cancel built-in rules by redefining them without commands. */ |
| if (rule->deps != 0 && rule->cmds == 0) |
| continue; |
| |
| /* If this rule is in use by a parent pattern_search, |
| don't use it here. */ |
| if (rule->in_use) |
| { |
| DEBUGP2 ("Avoiding implicit rule recursion.%s%s\n", "", ""); |
| continue; |
| } |
| |
| for (i = 0; rule->targets[i] != 0; ++i) |
| { |
| char *target = rule->targets[i]; |
| char *suffix = rule->suffixes[i]; |
| int check_lastslash; |
| |
| /* Rules that can match any filename and are not terminal |
| are ignored if we're recursing, so that they cannot be |
| intermediate files. */ |
| if (recursions > 0 && target[1] == '\0' && !rule->terminal) |
| continue; |
| |
| if (rule->lens[i] > namelen) |
| /* It can't possibly match. */ |
| continue; |
| |
| /* From the lengths of the filename and the pattern parts, |
| find the stem: the part of the filename that matches the %. */ |
| stem = filename + (suffix - target - 1); |
| stemlen = namelen - rule->lens[i] + 1; |
| |
| /* Set CHECK_LASTSLASH if FILENAME contains a directory |
| prefix and the target pattern does not contain a slash. */ |
| |
| check_lastslash = lastslash != 0 && index (target, '/') == 0; |
| if (check_lastslash) |
| { |
| /* In that case, don't include the |
| directory prefix in STEM here. */ |
| unsigned int difference = lastslash - filename + 1; |
| if (difference > stemlen) |
| continue; |
| stemlen -= difference; |
| stem += difference; |
| } |
| |
| /* Check that the rule pattern matches the text before the stem. */ |
| if (check_lastslash) |
| { |
| if (stem > (lastslash + 1) |
| && strncmp (target, lastslash + 1, stem - lastslash - 1)) |
| continue; |
| } |
| else if (stem > filename |
| && strncmp (target, filename, stem - filename)) |
| continue; |
| |
| /* Check that the rule pattern matches the text after the stem. |
| We could test simply use streq, but this way we compare the |
| first two characters immediately. This saves time in the very |
| common case where the first character matches because it is a |
| period. */ |
| if (*suffix != stem[stemlen] |
| || (*suffix != '\0' && !streq (&suffix[1], &stem[stemlen + 1]))) |
| continue; |
| |
| /* Record if we match a rule that not all filenames will match. */ |
| if (target[1] != '\0') |
| specific_rule_matched = 1; |
| |
| /* A rule with no dependencies and no commands exists solely to set |
| specific_rule_matched when it matches. Don't try to use it. */ |
| if (rule->deps == 0 && rule->cmds == 0) |
| continue; |
| |
| /* Record this rule in TRYRULES and the index of the matching |
| target in MATCHES. If several targets of the same rule match, |
| that rule will be in TRYRULES more than once. */ |
| tryrules[nrules] = rule; |
| matches[nrules] = i; |
| checked_lastslash[nrules] = check_lastslash; |
| ++nrules; |
| } |
| } |
| |
| /* If we have found a matching rule that won't match all filenames, |
| retroactively reject any "terminal" rules that do always match. */ |
| if (specific_rule_matched) |
| for (i = 0; i < nrules; ++i) |
| if (!tryrules[i]->terminal) |
| { |
| register unsigned int j; |
| for (j = 0; tryrules[i]->targets[j] != 0; ++j) |
| if (tryrules[i]->targets[j][1] == '\0') |
| break; |
| if (tryrules[i]->targets[j] != 0) |
| tryrules[i] = 0; |
| } |
| |
| /* Try each rule once without intermediate files, then once with them. */ |
| for (intermed_ok = 0; intermed_ok == !!intermed_ok; ++intermed_ok) |
| { |
| /* Try each pattern rule till we find one that applies. |
| If it does, copy the names of its dependencies (as substituted) |
| and store them in FOUND_FILES. DEPS_FOUND is the number of them. */ |
| |
| for (i = 0; i < nrules; i++) |
| { |
| int check_lastslash; |
| |
| rule = tryrules[i]; |
| |
| /* RULE is nil when we discover that a rule, |
| already placed in TRYRULES, should not be applied. */ |
| if (rule == 0) |
| continue; |
| |
| /* Reject any terminal rules if we're |
| looking to make intermediate files. */ |
| if (intermed_ok && rule->terminal) |
| continue; |
| |
| /* Mark this rule as in use so a recursive |
| pattern_search won't try to use it. */ |
| rule->in_use = 1; |
| |
| /* From the lengths of the filename and the matching pattern parts, |
| find the stem: the part of the filename that matches the %. */ |
| stem = filename |
| + (rule->suffixes[matches[i]] - rule->targets[matches[i]]) - 1; |
| stemlen = namelen - rule->lens[matches[i]] + 1; |
| check_lastslash = checked_lastslash[i]; |
| if (check_lastslash) |
| { |
| stem += lastslash - filename + 1; |
| stemlen -= (lastslash - filename) + 1; |
| } |
| |
| DEBUGP2 ("Trying pattern rule with stem `%.*s'.\n", |
| (int) stemlen, stem); |
| |
| /* Try each dependency; see if it "exists". */ |
| |
| deps_found = 0; |
| for (dep = rule->deps; dep != 0; dep = dep->next) |
| { |
| /* If the dependency name has a %, substitute the stem. */ |
| p = index (dep_name (dep), '%'); |
| if (p != 0) |
| { |
| register unsigned int i; |
| if (check_lastslash) |
| { |
| /* Copy directory name from the original FILENAME. */ |
| i = lastslash - filename + 1; |
| bcopy (filename, depname, i); |
| } |
| else |
| i = 0; |
| bcopy (dep_name (dep), depname + i, p - dep_name (dep)); |
| i += p - dep_name (dep); |
| bcopy (stem, depname + i, stemlen); |
| i += stemlen; |
| strcpy (depname + i, p + 1); |
| p = depname; |
| } |
| else |
| p = dep_name (dep); |
| |
| /* P is now the actual dependency name as substituted. */ |
| |
| if (file_impossible_p (p)) |
| { |
| /* If this dependency has already been ruled |
| "impossible", then the rule fails and don't |
| bother trying it on the second pass either |
| since we know that will fail too. */ |
| DEBUGP2 ("Rejecting impossible %s dependency `%s'.\n", |
| p == depname ? "implicit" : "rule", p); |
| tryrules[i] = 0; |
| break; |
| } |
| |
| intermediate_files[deps_found] = 0; |
| |
| DEBUGP2 ("Trying %s dependency `%s'.\n", |
| p == depname ? "implicit" : "rule", p); |
| |
| /* The DEP->changed flag says that this dependency resides in a |
| nonexistent directory. So we normally can skip looking for |
| the file. However, if CHECK_LASTSLASH is set, then the |
| dependency file we are actually looking for is in a different |
| directory (the one gotten by prepending FILENAME's directory), |
| so it might actually exist. */ |
| |
| if ((!dep->changed || check_lastslash) |
| && (lookup_file (p) != 0 || file_exists_p (p))) |
| { |
| found_files[deps_found++] = savestring (p, strlen (p)); |
| continue; |
| } |
| /* This code, given FILENAME = "lib/foo.o", dependency name |
| "lib/foo.c", and VPATH=src, searches for "src/lib/foo.c". */ |
| if (vpath_search (&p, (time_t *) 0)) |
| { |
| DEBUGP2 ("Found dependency as `%s'.%s\n", p, ""); |
| found_files[deps_found++] = p; |
| continue; |
| } |
| |
| /* We could not find the file in any place we should look. |
| Try to make this dependency as an intermediate file, |
| but only on the second pass. */ |
| |
| if (intermed_ok) |
| { |
| if (intermediate_file == 0) |
| intermediate_file |
| = (struct file *) alloca (sizeof (struct file)); |
| |
| DEBUGP2 ("Looking for a rule with %s file `%s'.\n", |
| "intermediate", p); |
| |
| bzero ((char *) intermediate_file, sizeof (struct file)); |
| intermediate_file->name = p; |
| if (pattern_search (intermediate_file, 0, depth + 1, |
| recursions + 1)) |
| { |
| p = savestring (p, strlen (p)); |
| intermediate_patterns[deps_found] |
| = intermediate_file->name; |
| intermediate_file->name = p; |
| intermediate_files[deps_found] = intermediate_file; |
| intermediate_file = 0; |
| /* Allocate an extra copy to go in FOUND_FILES, |
| because every elt of FOUND_FILES is consumed |
| or freed later. */ |
| found_files[deps_found] = savestring (p, strlen (p)); |
| ++deps_found; |
| continue; |
| } |
| |
| /* If we have tried to find P as an intermediate |
| file and failed, mark that name as impossible |
| so we won't go through the search again later. */ |
| file_impossible (p); |
| } |
| |
| /* A dependency of this rule does not exist. |
| Therefore, this rule fails. */ |
| break; |
| } |
| |
| /* This rule is no longer `in use' for recursive searches. */ |
| rule->in_use = 0; |
| |
| if (dep != 0) |
| { |
| /* This pattern rule does not apply. |
| If some of its dependencies succeeded, |
| free the data structure describing them. */ |
| while (deps_found-- > 0) |
| { |
| register struct file *f = intermediate_files[deps_found]; |
| free (found_files[deps_found]); |
| if (f != 0 |
| && (f->stem < f->name |
| || f->stem > f->name + strlen (f->name))) |
| free (f->stem); |
| } |
| } |
| else |
| /* This pattern rule does apply. Stop looking for one. */ |
| break; |
| } |
| |
| /* If we found an applicable rule without |
| intermediate files, don't try with them. */ |
| if (i < nrules) |
| break; |
| |
| rule = 0; |
| } |
| |
| /* RULE is nil if the loop went all the way |
| through the list and everything failed. */ |
| if (rule == 0) |
| return 0; |
| |
| foundrule = i; |
| |
| /* If we are recursing, store the pattern that matched |
| FILENAME in FILE->name for use in upper levels. */ |
| |
| if (recursions > 0) |
| /* Kludge-o-matic */ |
| file->name = rule->targets[matches[foundrule]]; |
| |
| /* FOUND_FILES lists the dependencies for the rule we found. |
| This includes the intermediate files, if any. |
| Convert them into entries on the deps-chain of FILE. */ |
| |
| while (deps_found-- > 0) |
| { |
| register char *s; |
| |
| if (intermediate_files[deps_found] != 0) |
| { |
| /* If we need to use an intermediate file, |
| make sure it is entered as a target, with the info that was |
| found for it in the recursive pattern_search call. |
| We know that the intermediate file did not already exist as |
| a target; therefore we can assume that the deps and cmds |
| of F below are null before we change them. */ |
| |
| struct file *imf = intermediate_files[deps_found]; |
| register struct file *f = enter_file (imf->name); |
| f->deps = imf->deps; |
| f->cmds = imf->cmds; |
| f->stem = imf->stem; |
| imf = lookup_file (intermediate_patterns[deps_found]); |
| if (imf != 0 && imf->precious) |
| f->precious = 1; |
| f->intermediate = 1; |
| f->tried_implicit = 1; |
| for (dep = f->deps; dep != 0; dep = dep->next) |
| { |
| dep->file = enter_file (dep->name); |
| dep->name = 0; |
| dep->file->tried_implicit |= dep->changed; |
| } |
| num_intermediates++; |
| } |
| |
| dep = (struct dep *) xmalloc (sizeof (struct dep)); |
| s = found_files[deps_found]; |
| if (recursions == 0) |
| { |
| dep->name = 0; |
| dep->file = lookup_file (s); |
| if (dep->file == 0) |
| /* enter_file consumes S's storage. */ |
| dep->file = enter_file (s); |
| else |
| /* A copy of S is already allocated in DEP->file->name. |
| So we can free S. */ |
| free (s); |
| } |
| else |
| { |
| dep->name = s; |
| dep->file = 0; |
| dep->changed = 0; |
| } |
| if (intermediate_files[deps_found] == 0 && tryrules[foundrule]->terminal) |
| { |
| /* If the file actually existed (was not an intermediate file), |
| and the rule that found it was a terminal one, then we want |
| to mark the found file so that it will not have implicit rule |
| search done for it. If we are not entering a `struct file' for |
| it now, we indicate this with the `changed' flag. */ |
| if (dep->file == 0) |
| dep->changed = 1; |
| else |
| dep->file->tried_implicit = 1; |
| } |
| dep->next = file->deps; |
| file->deps = dep; |
| } |
| |
| if (!checked_lastslash[foundrule]) |
| /* Always allocate new storage, since STEM might be |
| on the stack for an intermediate file. */ |
| file->stem = savestring (stem, stemlen); |
| else |
| { |
| /* We want to prepend the directory from |
| the original FILENAME onto the stem. */ |
| file->stem = (char *) xmalloc (((lastslash + 1) - filename) |
| + stemlen + 1); |
| bcopy (filename, file->stem, (lastslash + 1) - filename); |
| bcopy (stem, file->stem + ((lastslash + 1) - filename), stemlen); |
| file->stem[((lastslash + 1) - filename) + stemlen] = '\0'; |
| } |
| |
| file->cmds = rule->cmds; |
| |
| /* Put the targets other than the one that |
| matched into FILE's `also_make' member. */ |
| |
| /* If there was only one target, there is nothing to do. */ |
| if (rule->targets[1] != 0) |
| for (i = 0; rule->targets[i] != 0; ++i) |
| if (i != matches[foundrule]) |
| { |
| struct dep *new = (struct dep *) xmalloc (sizeof (struct dep)); |
| new->name = p = (char *) xmalloc (rule->lens[i] + stemlen + 1); |
| bcopy (rule->targets[i], p, |
| rule->suffixes[i] - rule->targets[i] - 1); |
| p += rule->suffixes[i] - rule->targets[i] - 1; |
| bcopy (stem, p, stemlen); |
| p += stemlen; |
| bcopy (rule->suffixes[i], p, |
| rule->lens[i] - (rule->suffixes[i] - rule->targets[i]) + 1); |
| new->file = enter_file (new->name); |
| new->next = file->also_make; |
| file->also_make = new; |
| } |
| |
| |
| return 1; |
| } |