diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:21:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:21:29 +0000 |
commit | 29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc (patch) | |
tree | 63ef546b10a81d461e5cf5ed9e98a68cd7dee1aa /src/kmk/remake.c | |
parent | Initial commit. (diff) | |
download | kbuild-29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc.tar.xz kbuild-29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc.zip |
Adding upstream version 1:0.1.9998svn3589+dfsg.upstream/1%0.1.9998svn3589+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/kmk/remake.c')
-rw-r--r-- | src/kmk/remake.c | 2124 |
1 files changed, 2124 insertions, 0 deletions
diff --git a/src/kmk/remake.c b/src/kmk/remake.c new file mode 100644 index 0000000..2551816 --- /dev/null +++ b/src/kmk/remake.c @@ -0,0 +1,2124 @@ +/* Basic dependency engine for GNU Make. +Copyright (C) 1988-2016 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 3 of the License, 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 +this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "makeint.h" +#include "filedef.h" +#include "job.h" +#include "commands.h" +#include "dep.h" +#include "variable.h" +#include "debug.h" + +#include <assert.h> + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#else +#include <sys/file.h> +#endif + +#ifdef VMS +#include <starlet.h> +#endif +#ifdef WINDOWS32 +#include <io.h> +#endif + + +/* The test for circular dependencies is based on the 'updating' bit in + 'struct file'. However, double colon targets have separate 'struct + file's; make sure we always use the base of the double colon chain. */ + +#define start_updating(_f) (((_f)->double_colon ? (_f)->double_colon : (_f))\ + ->updating = 1) +#define finish_updating(_f) (((_f)->double_colon ? (_f)->double_colon : (_f))\ + ->updating = 0) +#define is_updating(_f) (((_f)->double_colon ? (_f)->double_colon : (_f))\ + ->updating) + + +/* Incremented when a command is started (under -n, when one would be). */ +unsigned int commands_started = 0; + +/* Set to the goal dependency. Mostly needed for remaking makefiles. */ +static struct goaldep *goal_list; +static struct dep *goal_dep; + +/* Current value for pruning the scan of the goal chain. + All files start with considered == 0. */ +static unsigned int considered = 0; + +static enum update_status update_file (struct file *file, unsigned int depth); +static enum update_status update_file_1 (struct file *file, unsigned int depth); +static enum update_status check_dep (struct file *file, unsigned int depth, + FILE_TIMESTAMP this_mtime, int *must_make); +static enum update_status touch_file (struct file *file); +static void remake_file (struct file *file); +static FILE_TIMESTAMP name_mtime (const char *name); +static const char *library_search (const char *lib, FILE_TIMESTAMP *mtime_ptr); + +#ifdef CONFIG_WITH_DOT_MUST_MAKE +static int call_must_make_target_var (struct file *file, unsigned int depth); +#endif +#ifdef CONFIG_WITH_DOT_IS_CHANGED +static int call_is_changed_target_var (struct file *file); +#endif + + +/* Remake all the goals in the 'struct dep' chain GOALS. Return -1 if nothing + was done, 0 if all goals were updated successfully, or 1 if a goal failed. + + If rebuilding_makefiles is nonzero, these goals are makefiles, so -t, -q, + and -n should be disabled for them unless they were also command-line + targets, and we should only make one goal at a time and return as soon as + one goal whose 'changed' member is nonzero is successfully made. */ + +enum update_status +update_goal_chain (struct goaldep *goaldeps) +{ + int t = touch_flag, q = question_flag, n = just_print_flag; + enum update_status status = us_none; + + /* Duplicate the chain so we can remove things from it. */ + + struct dep *goals = copy_dep_chain ((struct dep *)goaldeps); + + goal_list = rebuilding_makefiles ? goaldeps : NULL; + +#define MTIME(file) (rebuilding_makefiles ? file_mtime_no_search (file) \ + : file_mtime (file)) + + /* Start a fresh batch of consideration. */ + ++considered; + + /* Update all the goals until they are all finished. */ + + while (goals != 0) + { + register struct dep *g, *lastgoal; + + /* Start jobs that are waiting for the load to go down. */ + + start_waiting_jobs (); + + /* Wait for a child to die. */ + + reap_children (1, 0); + + lastgoal = 0; + g = goals; + while (g != 0) + { + /* Iterate over all double-colon entries for this file. */ + struct file *file; + int stop = 0, any_not_updated = 0; + + goal_dep = g; + + for (file = g->file->double_colon ? g->file->double_colon : g->file; + file != NULL; + file = file->prev) + { + unsigned int ocommands_started; + enum update_status fail; + + file->dontcare = ANY_SET (g->flags, RM_DONTCARE); + + check_renamed (file); + if (rebuilding_makefiles) + { + if (file->cmd_target) + { + touch_flag = t; + question_flag = q; + just_print_flag = n; + } + else + touch_flag = question_flag = just_print_flag = 0; + } + + /* Save the old value of 'commands_started' so we can compare + later. It will be incremented when any commands are + actually run. */ + ocommands_started = commands_started; + + fail = update_file (file, rebuilding_makefiles ? 1 : 0); + check_renamed (file); + + /* Set the goal's 'changed' flag if any commands were started + by calling update_file above. We check this flag below to + decide when to give an "up to date" diagnostic. */ + if (commands_started > ocommands_started) + g->changed = 1; + + stop = 0; + if ((fail || file->updated) && status < us_question) + { + /* We updated this goal. Update STATUS and decide whether + to stop. */ + if (file->update_status) + { + /* Updating failed, or -q triggered. The STATUS value + tells our caller which. */ + status = file->update_status; + /* If -q just triggered, stop immediately. It doesn't + matter how much more we run, since we already know + the answer to return. */ + stop = (question_flag && !keep_going_flag + && !rebuilding_makefiles); + } + else + { + FILE_TIMESTAMP mtime = MTIME (file); + check_renamed (file); + + if (file->updated && g->changed && + mtime != file->mtime_before_update) + { + /* Updating was done. If this is a makefile and + just_print_flag or question_flag is set (meaning + -n or -q was given and this file was specified + as a command-line target), don't change STATUS. + If STATUS is changed, we will get re-exec'd, and + enter an infinite loop. */ + if (!rebuilding_makefiles + || (!just_print_flag && !question_flag)) + status = us_success; + if (rebuilding_makefiles && file->dontcare) + /* This is a default makefile; stop remaking. */ + stop = 1; + } + } + } + + /* Keep track if any double-colon entry is not finished. + When they are all finished, the goal is finished. */ + any_not_updated |= !file->updated; + + file->dontcare = 0; + + if (stop) + break; + } + + /* Reset FILE since it is null at the end of the loop. */ + file = g->file; + + if (stop || !any_not_updated) + { + /* If we have found nothing whatever to do for the goal, + print a message saying nothing needs doing. */ + + if (!rebuilding_makefiles + /* If the update_status is success, we updated successfully + or not at all. G->changed will have been set above if + any commands were actually started for this goal. */ + && file->update_status == us_success && !g->changed + /* Never give a message under -s or -q. */ + && !silent_flag && !question_flag) + OS (message, 1, ((file->phony || file->cmds == 0) + ? _("Nothing to be done for '%s'.") + : _("'%s' is up to date.")), + file->name); + + /* This goal is finished. Remove it from the chain. */ + if (lastgoal == 0) + goals = g->next; + else + lastgoal->next = g->next; + + /* Free the storage. */ +#ifndef CONFIG_WITH_ALLOC_CACHES + free (g); +#else + free_dep (g); +#endif + + g = lastgoal == 0 ? goals : lastgoal->next; + + if (stop) + break; + } + else + { + lastgoal = g; + g = g->next; + } + } + + /* If we reached the end of the dependency graph update CONSIDERED + for the next pass. */ + if (g == 0) + ++considered; + } + + if (rebuilding_makefiles) + { + touch_flag = t; + question_flag = q; + just_print_flag = n; + } + + return status; +} + +/* If we're rebuilding an included makefile that failed, and we care + about errors, show an error message the first time. */ + +void +show_goal_error (void) +{ + struct goaldep *goal; + + if ((goal_dep->flags & (RM_INCLUDED|RM_DONTCARE)) != RM_INCLUDED) + return; + + for (goal = goal_list; goal; goal = goal->next) + if (goal_dep->file == goal->file) + { + if (goal->error) + { + OSS (error, &goal->floc, "%s: %s", + goal->file->name, strerror ((int)goal->error)); + goal->error = 0; + } + return; + } +} + +/* If FILE is not up to date, execute the commands for it. + Return 0 if successful, non-0 if unsuccessful; + but with some flag settings, just call 'exit' if unsuccessful. + + DEPTH is the depth in recursions of this function. + We increment it during the consideration of our dependencies, + then decrement it again after finding out whether this file + is out of date. + + If there are multiple double-colon entries for FILE, + each is considered in turn. */ + +static enum update_status +update_file (struct file *file, unsigned int depth) +{ + enum update_status status = us_success; + struct file *f; + + f = file->double_colon ? file->double_colon : file; + + /* Prune the dependency graph: if we've already been here on _this_ + pass through the dependency graph, we don't have to go any further. + We won't reap_children until we start the next pass, so no state + change is possible below here until then. */ + if (f->considered == considered) + { + /* Check for the case where a target has been tried and failed but + the diagnostics haven't been issued. If we need the diagnostics + then we will have to continue. */ + if (!(f->updated && f->update_status > us_none + && !f->dontcare && f->no_diag)) + { + DBF (DB_VERBOSE, _("Pruning file '%s'.\n")); + return f->command_state == cs_finished ? f->update_status : us_success; + } + } + + /* This loop runs until we start commands for a double colon rule, or until + the chain is exhausted. */ + for (; f != 0; f = f->prev) + { + enum update_status new; + + f->considered = considered; + + new = update_file_1 (f, depth); + check_renamed (f); + + /* Clean up any alloca() used during the update. */ + alloca (0); + + /* If we got an error, don't bother with double_colon etc. */ + if (new && !keep_going_flag) + return new; + + if (f->command_state == cs_running + || f->command_state == cs_deps_running) + /* Don't run other :: rules for this target until + this rule is finished. */ + return us_success; + + if (new > status) + status = new; + } + + /* Process the remaining rules in the double colon chain so they're marked + considered. Start their prerequisites, too. */ + if (file->double_colon) + for (; f != 0 ; f = f->prev) + { + struct dep *d; + + f->considered = considered; + + for (d = f->deps; d != 0; d = d->next) + { + enum update_status new = update_file (d->file, depth + 1); + if (new > status) + status = new; + } + } + + return status; +} + +/* Show a message stating the target failed to build. */ + +static void +complain (struct file *file) +{ + /* If this file has no_diag set then it means we tried to update it + before in the dontcare mode and failed. The target that actually + failed is not necessarily this file but could be one of its direct + or indirect dependencies. So traverse this file's dependencies and + find the one that actually caused the failure. */ + + struct dep *d; + + for (d = file->deps; d != 0; d = d->next) + { + if (d->file->updated && d->file->update_status > us_none && file->no_diag) + { + complain (d->file); + break; + } + } + + if (d == 0) + { + show_goal_error (); + + /* Didn't find any dependencies to complain about. */ + +#ifdef KMK + /* jokes */ + if (!keep_going_flag && file->parent == 0) + { + const char *msg_joke = 0; + extern struct dep *goals; + + /* classics */ + if (!strcmp (file->name, "fire") + || !strcmp (file->name, "Fire")) + msg_joke = "No matches.\n"; + else if (!strcmp (file->name, "love") + || !strcmp (file->name, "Love") + || !strcmp (file->name, "peace") + || !strcmp (file->name, "Peace")) + msg_joke = "Not war.\n"; + else if (!strcmp (file->name, "war")) + msg_joke = "Don't know how to make war.\n"; + + /* http://xkcd.com/149/ - GNU Make bug #23273. */ + else if (( !strcmp (file->name, "me") + && goals != 0 + && !strcmp (dep_name(goals), "me") + && goals->next != 0 + && !strcmp (dep_name(goals->next), "a") + && goals->next->next != 0) + || !strncmp (file->name, "me a ", 5)) + msg_joke = +# ifdef HAVE_UNISTD_H + getuid () == 0 ? "Okay.\n" : +# endif + "What? Make it yourself!\n"; + if (msg_joke) + { + fputs (msg_joke, stderr); + die (2); + } + } +#endif /* KMK */ + + if (file->parent) + { + size_t l = strlen (file->name) + strlen (file->parent->name) + 4; + const char *m = _("%sNo rule to make target '%s', needed by '%s'%s"); + + if (!keep_going_flag) + fatal (NILF, l, m, "", file->name, file->parent->name, ""); + + error (NILF, l, m, "*** ", file->name, file->parent->name, "."); + } + else + { + size_t l = strlen (file->name) + 4; + const char *m = _("%sNo rule to make target '%s'%s"); + + if (!keep_going_flag) + fatal (NILF, l, m, "", file->name, ""); + + error (NILF, l, m, "*** ", file->name, "."); + } + + file->no_diag = 0; + } +} + +/* Consider a single 'struct file' and update it as appropriate. + Return 0 on success, or non-0 on failure. */ + +static enum update_status +update_file_1 (struct file *file, unsigned int depth) +{ + enum update_status dep_status = us_success; + FILE_TIMESTAMP this_mtime; + int noexist, must_make, deps_changed; + struct file *ofile; + struct dep *d, *ad; + struct dep amake; + int running = 0; +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + struct file *org_file; + struct file *req_file; + struct file *f2, *f3; + + /* Secondary target expansion leaves renamed files around, skip any rename + files to simplify multi target handling. */ + + check_renamed(file); + + /* Remember the request file and find the primary multi target file. Always + work on the primary file in a multi target recipe. */ + + req_file = file; + org_file = file; + if (file->multi_head != NULL) + { + if (file->multi_head == file) + DBS (DB_VERBOSE, (_("Considering target file '%s' (multi head).\n"), file->name)); + else + { + org_file = file = file->multi_head; + DBS (DB_VERBOSE, (_("Considering target file '%s' -> multi head '%s'.\n"), + req_file->name, file->name)); + assert (file->multi_head == file); + } + } + else +#endif /* CONFIG_WITH_EXPLICIT_MULTITARGET */ + DBF (DB_VERBOSE, _("Considering target file '%s'.\n")); + + if (file->updated) + { + if (file->update_status > us_none) + { + DBF (DB_VERBOSE, + _("Recently tried and failed to update file '%s'.\n")); + + /* If the file we tried to make is marked no_diag then no message + was printed about it when it failed during the makefile rebuild. + If we're trying to build it again in the normal rebuild, print a + message now. */ + if (file->no_diag && !file->dontcare) + complain (file); + + return file->update_status; + } + + DBF (DB_VERBOSE, _("File '%s' was considered already.\n")); + return 0; + } + + switch (file->command_state) + { + case cs_not_started: + case cs_deps_running: + break; + case cs_running: + DBF (DB_VERBOSE, _("Still updating file '%s'.\n")); + return 0; + case cs_finished: + DBF (DB_VERBOSE, _("Finished updating file '%s'.\n")); + return file->update_status; + default: + abort (); + } + + /* Determine whether the diagnostics will be issued should this update + fail. */ + file->no_diag = file->dontcare; + + ++depth; + + /* Notice recursive update of the same file. */ + start_updating (file); + + /* We might change file if we find a different one via vpath; + remember this one to turn off updating. */ + ofile = file; + + /* Looking at the file's modtime beforehand allows the possibility + that its name may be changed by a VPATH search, and thus it may + not need an implicit rule. If this were not done, the file + might get implicit commands that apply to its initial name, only + to have that name replaced with another found by VPATH search. + + For multi target files check the other files and use the time + of the oldest / non-existing file. */ + +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + this_mtime = file_mtime (file); + check_renamed (file); + f3 = file; + for (f2 = file->multi_next; + f2 != NULL && this_mtime != NONEXISTENT_MTIME; + f2 = f2->multi_next) + if (!f2->multi_maybe) + { + FILE_TIMESTAMP second_mtime = file_mtime (f2); + if (second_mtime < this_mtime) + { + this_mtime = second_mtime; + f3 = f2; + } + } + /** @todo this isn't sufficient, need to introduce a truly optional type and + * make |+ ignore mtime. let's hope that doesn't break too much... */ + /* If the requested file doesn't exist, always do a remake in the + hope that it is recreated even if it's "maybe" target. */ + else if (f2 == req_file && file_mtime (f2) == NONEXISTENT_MTIME) + { + this_mtime = NONEXISTENT_MTIME; + f3 = f2; + break; + } + check_renamed (f3); + noexist = this_mtime == NONEXISTENT_MTIME; + if (noexist) + DBS (DB_BASIC, (_("File '%s' does not exist.\n"), f3->name)); +#else /* !CONFIG_WITH_EXPLICIT_MULTITARGET */ + this_mtime = file_mtime (file); + check_renamed (file); + noexist = this_mtime == NONEXISTENT_MTIME; + if (noexist) + DBF (DB_BASIC, _("File '%s' does not exist.\n")); +#endif /* !CONFIG_WITH_EXPLICIT_MULTITARGET */ + else if (ORDINARY_MTIME_MIN <= this_mtime && this_mtime <= ORDINARY_MTIME_MAX + && file->low_resolution_time) + { + /* Avoid spurious rebuilds due to low resolution time stamps. */ + int ns = FILE_TIMESTAMP_NS (this_mtime); + if (ns != 0) + OS (error, NILF, + _("*** Warning: .LOW_RESOLUTION_TIME file '%s' has a high resolution time stamp"), + file->name); + this_mtime += FILE_TIMESTAMPS_PER_S - 1 - ns; + } + + must_make = noexist; + + /* If file was specified as a target with no commands, + come up with some default commands. */ + + if (!file->phony && file->cmds == 0 && !file->tried_implicit) + { + if (try_implicit_rule (file, depth)) + DBF (DB_IMPLICIT, _("Found an implicit rule for '%s'.\n")); + else + DBF (DB_IMPLICIT, _("No implicit rule found for '%s'.\n")); + file->tried_implicit = 1; + } + if (file->cmds == 0 && !file->is_target + && default_file != 0 && default_file->cmds != 0) + { + DBF (DB_IMPLICIT, _("Using default recipe for '%s'.\n")); + file->cmds = default_file->cmds; + } + + /* Update all non-intermediate files we depend on, if necessary, and see + whether any of them is more recent than this file. We need to walk our + deps, AND the deps of any also_make targets to ensure everything happens + in the correct order. + + bird: For explicit multitarget rules we must iterate all the output + files to get the correct picture. The special .MUST_MAKE + target variable call is also done from this context. */ + +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + assert (file == org_file); + for (f2 = file; f2; file = f2 = f2->multi_next) + { +#endif + amake.file = file; + amake.next = file->also_make; + ad = &amake; + while (ad) + { + struct dep *lastd = 0; + + /* Find the deps we're scanning */ + d = ad->file->deps; + ad = ad->next; + + while (d) + { + enum update_status new; + FILE_TIMESTAMP mtime; + int maybe_make; + int dontcare = 0; + + check_renamed (d->file); + + mtime = file_mtime (d->file); + check_renamed (d->file); + + if (is_updating (d->file)) + { +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + /* silently ignore the order-only dep hack. */ + if (file->multi_maybe && d->file == org_file) + { + lastd = d; + d = d->next; + continue; + } +#endif + + OSS (error, NILF, _("Circular %s <- %s dependency dropped."), + file->name, d->file->name); + /* We cannot free D here because our the caller will still have + a reference to it when we were called recursively via + check_dep below. */ + if (lastd == 0) + file->deps = d->next; + else + lastd->next = d->next; + d = d->next; + continue; + } + + d->file->parent = file; + maybe_make = must_make; + + /* Inherit dontcare flag from our parent. */ + if (rebuilding_makefiles) + { + dontcare = d->file->dontcare; + d->file->dontcare = file->dontcare; + } + + new = check_dep (d->file, depth, this_mtime, &maybe_make); + if (new > dep_status) + dep_status = new; + + /* Restore original dontcare flag. */ + if (rebuilding_makefiles) + d->file->dontcare = dontcare; + + if (! d->ignore_mtime) + must_make = maybe_make; + + check_renamed (d->file); + + { + register struct file *f = d->file; + if (f->double_colon) + f = f->double_colon; + do + { + running |= (f->command_state == cs_running + || f->command_state == cs_deps_running); + f = f->prev; + } + while (f != 0); + } + + if (dep_status && !keep_going_flag) + break; + + if (!running) + /* The prereq is considered changed if the timestamp has changed + while it was built, OR it doesn't exist. */ + d->changed = ((file_mtime (d->file) != mtime) + || (mtime == NONEXISTENT_MTIME)); + + lastd = d; + d = d->next; + } + } + +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + if (dep_status != 0 && !keep_going_flag) + break; + } + file = org_file; +#endif + +#ifdef CONFIG_WITH_DOT_MUST_MAKE + /* Check with the .MUST_MAKE target variable if it's + not already decided to make the file. */ + +# ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + if (!must_make) + for (f2 = org_file; f2 && !must_make; f2 = f2->multi_next) + must_make = call_must_make_target_var (f2, depth); +# else + if (!must_make) + must_make = call_must_make_target_var (file, depth); +# endif +#endif /* CONFIG_WITH_DOT_MUST_MAKE */ + + /* Now we know whether this target needs updating. + If it does, update all the intermediate files we depend on. */ + + if (must_make || always_make_flag) + { +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + for (file = f2 = org_file; f2; file = f2 = f2->multi_next) +#endif + for (d = file->deps; d != 0; d = d->next) + if (d->file->intermediate) + { + enum update_status new; + int dontcare = 0; + + FILE_TIMESTAMP mtime = file_mtime (d->file); + check_renamed (d->file); + d->file->parent = file; + + /* Inherit dontcare flag from our parent. */ + if (rebuilding_makefiles) + { + dontcare = d->file->dontcare; + d->file->dontcare = file->dontcare; + } + + /* We may have already considered this file, when we didn't know + we'd need to update it. Force update_file() to consider it and + not prune it. */ + d->file->considered = 0; + + new = update_file (d->file, depth); + if (new > dep_status) + dep_status = new; + + /* Restore original dontcare flag. */ + if (rebuilding_makefiles) + d->file->dontcare = dontcare; + + check_renamed (d->file); + + { + register struct file *f = d->file; + if (f->double_colon) + f = f->double_colon; + do + { + running |= (f->command_state == cs_running + || f->command_state == cs_deps_running); + f = f->prev; + } + while (f != 0); + } + + if (dep_status && !keep_going_flag) + break; + + if (!running) + d->changed = ((file->phony && file->cmds != 0) + || file_mtime (d->file) != mtime); + } +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + file = org_file; +#endif + } + + finish_updating (file); + finish_updating (ofile); + + DBF (DB_VERBOSE, _("Finished prerequisites of target file '%s'.\n")); + + if (running) + { + set_command_state (file, cs_deps_running); + --depth; + DBF (DB_VERBOSE, _("The prerequisites of '%s' are being made.\n")); + return 0; + } + + /* If any dependency failed, give up now. */ + + if (dep_status) + { + /* I'm not sure if we can't just assign dep_status... */ + file->update_status = dep_status == us_none ? us_failed : dep_status; + notice_finished_file (file); + + --depth; + + DBF (DB_VERBOSE, _("Giving up on target file '%s'.\n")); + + if (depth == 0 && keep_going_flag + && !just_print_flag && !question_flag) + OS (error, NILF, + _("Target '%s' not remade because of errors."), file->name); + + return dep_status; + } + + if (file->command_state == cs_deps_running) + /* The commands for some deps were running on the last iteration, but + they have finished now. Reset the command_state to not_started to + simplify later bookkeeping. It is important that we do this only + when the prior state was cs_deps_running, because that prior state + was definitely propagated to FILE's also_make's by set_command_state + (called above), but in another state an also_make may have + independently changed to finished state, and we would confuse that + file's bookkeeping (updated, but not_started is bogus state). */ + set_command_state (file, cs_not_started); + + /* Now record which prerequisites are more + recent than this file, so we can define $?. */ + + deps_changed = 0; +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + for (file = f2 = org_file; f2; file = f2 = f2->multi_next) +#endif + for (d = file->deps; d != 0; d = d->next) + { + FILE_TIMESTAMP d_mtime = file_mtime (d->file); +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + if (d->file == file && file->multi_maybe) + continue; +#endif + check_renamed (d->file); + + if (! d->ignore_mtime) + { +#if 1 + /* %%% In version 4, remove this code completely to + implement not remaking deps if their deps are newer + than their parents. */ + if (d_mtime == NONEXISTENT_MTIME && !d->file->intermediate) + /* We must remake if this dep does not + exist and is not intermediate. */ + must_make = 1; +#endif + + /* Set DEPS_CHANGED if this dep actually changed. */ + deps_changed |= d->changed; + } + + /* Set D->changed if either this dep actually changed, + or its dependent, FILE, is older or does not exist. */ + d->changed |= noexist || d_mtime > this_mtime; + + if (!noexist && ISDB (DB_BASIC|DB_VERBOSE)) + { + const char *fmt = 0; + + if (d->ignore_mtime) + { + if (ISDB (DB_VERBOSE)) + fmt = _("Prerequisite '%s' is order-only for target '%s'.\n"); + } + else if (d_mtime == NONEXISTENT_MTIME) + { + if (ISDB (DB_BASIC)) + fmt = _("Prerequisite '%s' of target '%s' does not exist.\n"); + } + else if (d->changed) + { + if (ISDB (DB_BASIC)) + fmt = _("Prerequisite '%s' is newer than target '%s'.\n"); + } + else if (ISDB (DB_VERBOSE)) + fmt = _("Prerequisite '%s' is older than target '%s'.\n"); + + if (fmt) + { + print_spaces (depth); + printf (fmt, dep_name (d), file->name); + fflush (stdout); + } + } + } +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + file = org_file; +#endif + + /* Here depth returns to the value it had when we were called. */ + depth--; + + if (file->double_colon && file->deps == 0) + { + must_make = 1; + DBF (DB_BASIC, + _("Target '%s' is double-colon and has no prerequisites.\n")); + } + else if (!noexist && file->is_target && !deps_changed && file->cmds == 0 + && !always_make_flag) + { + must_make = 0; + DBF (DB_VERBOSE, + _("No recipe for '%s' and no prerequisites actually changed.\n")); + } + else if (!must_make && file->cmds != 0 && always_make_flag) + { + must_make = 1; + DBF (DB_VERBOSE, _("Making '%s' due to always-make flag.\n")); + } + + if (!must_make) + { + if (ISDB (DB_VERBOSE)) + { + print_spaces (depth); + printf (_("No need to remake target '%s'"), file->name); + if (!streq (file->name, file->hname)) + printf (_("; using VPATH name '%s'"), file->hname); + puts ("."); + fflush (stdout); + } + + notice_finished_file (file); + + /* Since we don't need to remake the file, convert it to use the + VPATH filename if we found one. hfile will be either the + local name if no VPATH or the VPATH name if one was found. */ + + while (file) + { + file->name = file->hname; + file = file->prev; + } + + return 0; + } + + DBF (DB_BASIC, _("Must remake target '%s'.\n")); + + /* It needs to be remade. If it's VPATH and not reset via GPATH, toss the + VPATH. */ + if (!streq (file->name, file->hname)) + { + DB (DB_BASIC, (_(" Ignoring VPATH name '%s'.\n"), file->hname)); + file->ignore_vpath = 1; + } + + /* Now, take appropriate actions to remake the file. */ + remake_file (file); + + if (file->command_state != cs_finished) + { + DBF (DB_VERBOSE, _("Recipe of '%s' is being run.\n")); + return 0; + } + + switch (file->update_status) + { + case us_failed: + DBF (DB_BASIC, _("Failed to remake target file '%s'.\n")); + break; + case us_success: + DBF (DB_BASIC, _("Successfully remade target file '%s'.\n")); + break; + case us_question: + DBF (DB_BASIC, _("Target file '%s' needs to be remade under -q.\n")); + break; + case us_none: + break; + } + + file->updated = 1; + return file->update_status; +} + +#ifdef CONFIG_WITH_DOT_MUST_MAKE +/* Consider the .MUST_MAKE target variable if present. + + Returns 1 if must remake, 0 if not. + + The deal is that .MUST_MAKE returns non-zero if it thinks the target needs + updating. We have to initialize file variables (for the sake of pattern + vars) and set the most important file variables before calling (expanding) + the .MUST_MAKE variable. + + The file variables keeping the dependency lists, $+, $^, $? and $| are not + available at this point because $? depends on things happening after we've + decided to make the file. So, to keep things simple all 4 of them are + undefined in this call. */ +static int +call_must_make_target_var (struct file *file, unsigned int depth) +{ + struct variable *var; + unsigned char ch; + const char *str; + + if (file->variables) + { + var = lookup_variable_in_set (".MUST_MAKE", sizeof (".MUST_MAKE") - 1, + file->variables->set); + if (var) + { + initialize_file_variables (file, 0); + set_file_variables (file, 1 /* called early, no dep lists please */); + + str = variable_expand_for_file_2 (NULL, + var->value, var->value_length, + file, NULL); + + /* Stripped string should be non-zero. */ + + ch = *str; + while (ISSPACE (ch)) + ch = *++str; + + if (ch != '\0') + { + if (ISDB (DB_BASIC)) + { + print_spaces (depth); + printf (_(".MUST_MAKE returned `%s' for target `%s'.\n"), + str, file->name); + } + return 1; + } + } + } + return 0; +} +#endif /* CONFIG_WITH_DOT_MUST_MAKE */ + +#ifdef CONFIG_WITH_DOT_IS_CHANGED +/* Consider the .IS_CHANGED target variable if present. + + Returns 1 if the file should be considered modified, 0 if not. + + The calling context and restrictions are the same as for .MUST_MAKE. + Make uses the information from this 'function' for determining whether to + make a file which lists this as a prerequisite. So, this is the feature you + use to check MD5 sums, file sizes, time stamps and the like with data from + a previous run. + + FIXME: Would be nice to know which file is currently being considered. + FIXME: Is currently not invoked for intermediate files. */ +static int +call_is_changed_target_var (struct file *file) +{ + struct variable *var; + unsigned char ch; + const char *str; + + if (file->variables) + { + var = lookup_variable_in_set (".IS_CHANGED", sizeof (".IS_CHANGED") - 1, + file->variables->set); + if (var) + { + initialize_file_variables (file, 0); + set_file_variables (file, 1 /* called early, no dep lists please */); + + str = variable_expand_for_file_2 (NULL, + var->value, var->value_length, + file, NULL); + + /* stripped string should be non-zero. */ + do + ch = *str++; + while (ISSPACE (ch)); + + return (ch != '\0'); + } + } + return 0; +} +#endif /* CONFIG_WITH_DOT_IS_CHANGED */ + +/* Set FILE's 'updated' flag and re-check its mtime and the mtime's of all + files listed in its 'also_make' member. Under -t, this function also + touches FILE. + + On return, FILE->update_status will no longer be us_none if it was. */ + +void +notice_finished_file (struct file *file) +{ +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + struct file *f2; +#endif + struct dep *d; + int ran = file->command_state == cs_running; + int touched = 0; + DB (DB_JOBS, (_("notice_finished_file - entering: file=%p `%s' update_status=%d command_state=%d\n"), /* bird */ + (void *) file, file->name, file->update_status, file->command_state)); + + file->command_state = cs_finished; + file->updated = 1; +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + if (file->multi_head) + { + assert (file == file->multi_head); + for (f2 = file->multi_next; f2 != 0; f2 = f2->multi_next) + { + f2->command_state = cs_finished; + f2->updated = 1; + } + } +#endif + +#ifdef CONFIG_WITH_EXTENDED_NOTPARALLEL + /* update not_parallel if the file was flagged for that. */ + if ( ran + && (file->command_flags & (COMMANDS_NOTPARALLEL | COMMANDS_NO_COMMANDS)) + == COMMANDS_NOTPARALLEL) + { + DB (DB_KMK, (_("not_parallel %d -> %d (file=%p `%s') [notice_finished_file]\n"), not_parallel, + not_parallel - 1, (void *) file, file->name)); + assert(not_parallel >= 1); + --not_parallel; + } +#endif + + if (touch_flag + /* The update status will be: + us_success if 0 or more commands (+ or ${MAKE}) were run and won; + us_none if this target was not remade; + >us_none if some commands were run and lost. + We touch the target if it has commands which either were not run + or won when they ran (i.e. status is 0). */ + && file->update_status == us_success) + { + if (file->cmds != 0 && file->cmds->any_recurse) + { + /* If all the command lines were recursive, + we don't want to do the touching. */ + unsigned int i; + for (i = 0; i < file->cmds->ncommand_lines; ++i) + if (!(file->cmds->lines_flags[i] & COMMANDS_RECURSE)) + goto have_nonrecursing; + } + else + { + have_nonrecursing: + if (file->phony) +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + { + file->update_status = us_success; + if (file->multi_head) + for (f2 = file->multi_next; f2 != 0; f2 = f2->multi_next) + f2->update_status = us_success; + } +#else + file->update_status = us_success; +#endif + /* According to POSIX, -t doesn't affect targets with no cmds. */ + else if (file->cmds != 0) + { + /* Should set file's modification date and do nothing else. */ + file->update_status = touch_file (file); +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + if (file->multi_head) + for (f2 = file->multi_next; f2 != 0; f2 = f2->multi_next) + { + /* figure out this, touch if it exist ignore otherwise? */ + } +#endif + + /* Pretend we ran a real touch command, to suppress the + "'foo' is up to date" message. */ + commands_started++; + + /* Request for the timestamp to be updated (and distributed + to the double-colon entries). Simply setting ran=1 would + almost have done the trick, but messes up with the also_make + updating logic below. */ + touched = 1; + } + } + } + + if (file->mtime_before_update == UNKNOWN_MTIME) + file->mtime_before_update = file->last_mtime; +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + if (file->multi_head) + for (f2 = file->multi_next; f2 != 0; f2 = f2->multi_next) + if (f2->mtime_before_update == UNKNOWN_MTIME) + f2->mtime_before_update = f2->last_mtime; +#endif + + if ((ran && !file->phony) || touched) + { + int i = 0; + + /* If -n, -t, or -q and all the commands are recursive, we ran them so + really check the target's mtime again. Otherwise, assume the target + would have been updated. */ + + if ((question_flag || just_print_flag || touch_flag) && file->cmds) + { + for (i = file->cmds->ncommand_lines; i > 0; --i) + if (! (file->cmds->lines_flags[i-1] & COMMANDS_RECURSE)) + break; + } + + /* If there were no commands at all, it's always new. */ + + else if (file->is_target && file->cmds == 0) + i = 1; + + file->last_mtime = i == 0 ? UNKNOWN_MTIME : NEW_MTIME; +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + if (file->multi_head) + for (f2 = file->multi_next; f2 != 0; f2 = f2->multi_next) + file->last_mtime = i == 0 ? UNKNOWN_MTIME : NEW_MTIME; /*??*/ +#endif + } + + if (file->double_colon) + { + /* If this is a double colon rule and it is the last one to be + updated, propagate the change of modification time to all the + double-colon entries for this file. + + We do it on the last update because it is important to handle + individual entries as separate rules with separate timestamps + while they are treated as targets and then as one rule with the + unified timestamp when they are considered as a prerequisite + of some target. */ + + struct file *f; + FILE_TIMESTAMP max_mtime = file->last_mtime; + + /* Check that all rules were updated and at the same time find + the max timestamp. We assume UNKNOWN_MTIME is newer then + any other value. */ + for (f = file->double_colon; f != 0 && f->updated; f = f->prev) + if (max_mtime != UNKNOWN_MTIME + && (f->last_mtime == UNKNOWN_MTIME || f->last_mtime > max_mtime)) + max_mtime = f->last_mtime; + + if (f == 0) + for (f = file->double_colon; f != 0; f = f->prev) + f->last_mtime = max_mtime; + } + + if (ran && file->update_status != us_none) +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + { +#endif + /* We actually tried to update FILE, which has + updated its also_make's as well (if it worked). + If it didn't work, it wouldn't work again for them. + So mark them as updated with the same status. */ + for (d = file->also_make; d != 0; d = d->next) + { + d->file->command_state = cs_finished; + d->file->updated = 1; + d->file->update_status = file->update_status; + + if (ran && !d->file->phony) + /* Fetch the new modification time. + We do this instead of just invalidating the cached time + so that a vpath_search can happen. Otherwise, it would + never be done because the target is already updated. */ + f_mtime (d->file, 0); + } +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + /* Same as above but for explicit multi target rules. */ + if (file->multi_head) + for (f2 = file->multi_next; f2 != 0; f2 = f2->multi_next) + { + f2->update_status = file->update_status; + if (!f2->phony) + f_mtime (f2, 0); + } + } +#endif + else if (file->update_status == us_none) +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + { + /* Nothing was done for FILE, but it needed nothing done. + So mark it now as "succeeded". */ + file->update_status = 0; + if (file->multi_head) + for (f2 = file->multi_next; f2 != 0; f2 = f2->multi_next) + f2->update_status = 0; + } +#else + /* Nothing was done for FILE, but it needed nothing done. + So mark it now as "succeeded". */ + file->update_status = us_success; +#endif + +#ifdef CONFIG_WITH_MEMORY_OPTIMIZATIONS + /* We're done with this command, so free the memory held by the chopped + command lines. Saves heap for the compilers & linkers. */ + if (file->cmds && file->cmds->command_lines) + free_chopped_commands (file->cmds); +#endif +} + +/* Check whether another file (whose mtime is THIS_MTIME) needs updating on + account of a dependency which is file FILE. If it does, store 1 in + *MUST_MAKE_PTR. In the process, update any non-intermediate files that + FILE depends on (including FILE itself). Return nonzero if any updating + failed. */ + +static enum update_status +check_dep (struct file *file, unsigned int depth, + FILE_TIMESTAMP this_mtime, int *must_make_ptr) +{ + struct file *ofile; + struct dep *d; + enum update_status dep_status = us_success; + + ++depth; + start_updating (file); + + /* We might change file if we find a different one via vpath; + remember this one to turn off updating. */ + ofile = file; + + if (file->phony || !file->intermediate) + { + /* If this is a non-intermediate file, update it and record whether it + is newer than THIS_MTIME. */ + FILE_TIMESTAMP mtime; + dep_status = update_file (file, depth); + check_renamed (file); + mtime = file_mtime (file); + check_renamed (file); + if (mtime == NONEXISTENT_MTIME || mtime > this_mtime) + *must_make_ptr = 1; +#ifdef CONFIG_WITH_DOT_IS_CHANGED + else if ( *must_make_ptr == 0 + && call_is_changed_target_var (file)) + *must_make_ptr = 1; +#endif /* CONFIG_WITH_DOT_IS_CHANGED */ + } + else + { + /* FILE is an intermediate file. */ + FILE_TIMESTAMP mtime; + + if (!file->phony && file->cmds == 0 && !file->tried_implicit) + { + if (try_implicit_rule (file, depth)) + DBF (DB_IMPLICIT, _("Found an implicit rule for '%s'.\n")); + else + DBF (DB_IMPLICIT, _("No implicit rule found for '%s'.\n")); + file->tried_implicit = 1; + } + if (file->cmds == 0 && !file->is_target + && default_file != 0 && default_file->cmds != 0) + { + DBF (DB_IMPLICIT, _("Using default commands for '%s'.\n")); + file->cmds = default_file->cmds; + } + + check_renamed (file); + mtime = file_mtime (file); + check_renamed (file); + if (mtime != NONEXISTENT_MTIME && mtime > this_mtime) + /* If the intermediate file actually exists and is newer, then we + should remake from it. */ + *must_make_ptr = 1; + else + { + /* Otherwise, update all non-intermediate files we depend on, if + necessary, and see whether any of them is more recent than the + file on whose behalf we are checking. */ + struct dep *ld; + int deps_running = 0; + + /* If this target is not running, set it's state so that we check it + fresh. It could be it was checked as part of an order-only + prerequisite and so wasn't rebuilt then, but should be now. */ + if (file->command_state != cs_running) + { + /* If the target was waiting for a dependency it has to be + reconsidered, as that dependency might have finished. */ + if (file->command_state == cs_deps_running) + file->considered = 0; + + set_command_state (file, cs_not_started); + } + + ld = 0; + d = file->deps; + while (d != 0) + { + enum update_status new; + int maybe_make; + + if (is_updating (d->file)) + { + OSS (error, NILF, _("Circular %s <- %s dependency dropped."), + file->name, d->file->name); + if (ld == 0) + { + file->deps = d->next; + free_dep (d); + d = file->deps; + } + else + { + ld->next = d->next; + free_dep (d); + d = ld->next; + } + continue; + } + + d->file->parent = file; + maybe_make = *must_make_ptr; + new = check_dep (d->file, depth, this_mtime, &maybe_make); + if (new > dep_status) + dep_status = new; + + if (! d->ignore_mtime) + *must_make_ptr = maybe_make; + check_renamed (d->file); + if (dep_status && !keep_going_flag) + break; + + if (d->file->command_state == cs_running + || d->file->command_state == cs_deps_running) + deps_running = 1; + + ld = d; + d = d->next; + } + + if (deps_running) + /* Record that some of FILE's deps are still being made. + This tells the upper levels to wait on processing it until the + commands are finished. */ + set_command_state (file, cs_deps_running); + } + } + + finish_updating (file); + finish_updating (ofile); + + return dep_status; +} + +/* Touch FILE. Return us_success if successful, us_failed if not. */ + +#define TOUCH_ERROR(call) do{ perror_with_name ((call), file->name); \ + return us_failed; }while(0) + +static enum update_status +touch_file (struct file *file) +{ + if (!silent_flag) + OS (message, 0, "touch %s", file->name); + + /* Print-only (-n) takes precedence over touch (-t). */ + if (just_print_flag) + return us_success; + +#ifndef NO_ARCHIVES + if (ar_name (file->name)) + return ar_touch (file->name) ? us_failed : us_success; + else +#endif + { + int fd; + + EINTRLOOP (fd, open (file->name, O_RDWR | O_CREAT, 0666)); + if (fd < 0) + TOUCH_ERROR ("touch: open: "); + else + { + struct stat statbuf; + char buf = 'x'; + int e; + + EINTRLOOP (e, fstat (fd, &statbuf)); + if (e < 0) + TOUCH_ERROR ("touch: fstat: "); + /* Rewrite character 0 same as it already is. */ + EINTRLOOP (e, read (fd, &buf, 1)); + if (e < 0) + TOUCH_ERROR ("touch: read: "); + { + off_t o; + EINTRLOOP (o, lseek (fd, 0L, 0)); + if (o < 0L) + TOUCH_ERROR ("touch: lseek: "); + } + EINTRLOOP (e, write (fd, &buf, 1)); + if (e < 0) + TOUCH_ERROR ("touch: write: "); + + /* If file length was 0, we just changed it, so change it back. */ + if (statbuf.st_size == 0) + { + (void) close (fd); + EINTRLOOP (fd, open (file->name, O_RDWR | O_TRUNC, 0666)); + if (fd < 0) + TOUCH_ERROR ("touch: open: "); + } + (void) close (fd); + } + } + + return us_success; +} + +/* Having checked and updated the dependencies of FILE, + do whatever is appropriate to remake FILE itself. + Return the status from executing FILE's commands. */ + +static void +remake_file (struct file *file) +{ +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + assert(file->multi_head == NULL || file->multi_head == file); +#endif + + if (file->cmds == 0) + { + if (file->phony) + /* Phony target. Pretend it succeeded. */ + file->update_status = us_success; + else if (file->is_target) + /* This is a nonexistent target file we cannot make. + Pretend it was successfully remade. */ + file->update_status = us_success; + else + { + /* This is a dependency file we cannot remake. Fail. */ + if (!rebuilding_makefiles || !file->dontcare) + complain (file); + file->update_status = us_failed; + } + } + else + { + chop_commands (file->cmds); + + /* The normal case: start some commands. */ + if (!touch_flag || file->cmds->any_recurse) + { + execute_file_commands (file); + return; + } + + /* This tells notice_finished_file it is ok to touch the file. */ + file->update_status = us_success; + } + + /* This does the touching under -t. */ + notice_finished_file (file); +} + +/* Return the mtime of a file, given a 'struct file'. + Caches the time in the struct file to avoid excess stat calls. + + If the file is not found, and SEARCH is nonzero, VPATH searching and + replacement is done. If that fails, a library (-lLIBNAME) is tried and + the library's actual name (/lib/libLIBNAME.a, etc.) is substituted into + FILE. */ + +FILE_TIMESTAMP +f_mtime (struct file *file, int search) +{ + FILE_TIMESTAMP mtime; + int propagate_timestamp; + + /* File's mtime is not known; must get it from the system. */ + +#ifndef NO_ARCHIVES + if (ar_name (file->name)) + { + /* This file is an archive-member reference. */ + + char *arname, *memname; + struct file *arfile; + time_t member_date; + + /* Find the archive's name. */ + ar_parse_name (file->name, &arname, &memname); + + /* Find the modification time of the archive itself. + Also allow for its name to be changed via VPATH search. */ + arfile = lookup_file (arname); + if (arfile == 0) + arfile = enter_file (strcache_add (arname)); + mtime = f_mtime (arfile, search); + check_renamed (arfile); + if (search && strcmp (arfile->hname, arname)) + { + /* The archive's name has changed. + Change the archive-member reference accordingly. */ + + char *name; + unsigned int arlen, memlen; + + arlen = strlen (arfile->hname); + memlen = strlen (memname); + + name = alloca (arlen + 1 + memlen + 2); + memcpy (name, arfile->hname, arlen); + name[arlen] = '('; + memcpy (name + arlen + 1, memname, memlen); + name[arlen + 1 + memlen] = ')'; + name[arlen + 1 + memlen + 1] = '\0'; + + /* If the archive was found with GPATH, make the change permanent; + otherwise defer it until later. */ + if (arfile->name == arfile->hname) + rename_file (file, strcache_add (name)); + else + rehash_file (file, strcache_add (name)); + check_renamed (file); + } + + free (arname); + + file->low_resolution_time = 1; + + if (mtime == NONEXISTENT_MTIME) + /* The archive doesn't exist, so its members don't exist either. */ + return NONEXISTENT_MTIME; + + member_date = ar_member_date (file->hname); + mtime = (member_date == (time_t) -1 + ? NONEXISTENT_MTIME + : file_timestamp_cons (file->hname, member_date, 0)); + } + else +#endif + { + mtime = name_mtime (file->name); + + if (mtime == NONEXISTENT_MTIME && search && !file->ignore_vpath) + { + /* If name_mtime failed, search VPATH. */ + const char *name = vpath_search (file->name, &mtime, NULL, NULL); + if (name + /* Last resort, is it a library (-lxxx)? */ + || (file->name[0] == '-' && file->name[1] == 'l' + && (name = library_search (file->name, &mtime)) != 0)) + { + int name_len; + + if (mtime != UNKNOWN_MTIME) + /* vpath_search and library_search store UNKNOWN_MTIME + if they didn't need to do a stat call for their work. */ + file->last_mtime = mtime; + + /* If we found it in VPATH, see if it's in GPATH too; if so, + change the name right now; if not, defer until after the + dependencies are updated. */ +#ifndef VMS + name_len = strlen (name) - strlen (file->name) - 1; +#else + name_len = strlen (name) - strlen (file->name); + if (name[name_len - 1] == '/') + name_len--; +#endif + if (gpath_search (name, name_len)) + { + rename_file (file, name); + check_renamed (file); + return file_mtime (file); + } + + rehash_file (file, name); + check_renamed (file); + /* If the result of a vpath search is -o or -W, preserve it. + Otherwise, find the mtime of the resulting file. */ + if (mtime != OLD_MTIME && mtime != NEW_MTIME) + mtime = name_mtime (name); + } + } + } + + /* Files can have bogus timestamps that nothing newly made will be + "newer" than. Updating their dependents could just result in loops. + So notify the user of the anomaly with a warning. + + We only need to do this once, for now. */ + + if (!clock_skew_detected + && mtime != NONEXISTENT_MTIME && mtime != NEW_MTIME + && !file->updated) + { + static FILE_TIMESTAMP adjusted_now; + + FILE_TIMESTAMP adjusted_mtime = mtime; + +#if defined(WINDOWS32) || defined(__MSDOS__) + /* Experimentation has shown that FAT filesystems can set file times + up to 3 seconds into the future! Play it safe. */ + +#define FAT_ADJ_OFFSET (FILE_TIMESTAMP) 3 + + FILE_TIMESTAMP adjustment = FAT_ADJ_OFFSET << FILE_TIMESTAMP_LO_BITS; + if (ORDINARY_MTIME_MIN + adjustment <= adjusted_mtime) + adjusted_mtime -= adjustment; +#elif defined(__EMX__) + /* FAT filesystems round time to the nearest even second! + Allow for any file (NTFS or FAT) to perhaps suffer from this + brain damage. */ + FILE_TIMESTAMP adjustment = (((FILE_TIMESTAMP_S (adjusted_mtime) & 1) == 0 + && FILE_TIMESTAMP_NS (adjusted_mtime) == 0) + ? (FILE_TIMESTAMP) 1 << FILE_TIMESTAMP_LO_BITS + : 0); +#endif + + /* If the file's time appears to be in the future, update our + concept of the present and try once more. */ + if (adjusted_now < adjusted_mtime) + { + int resolution; + FILE_TIMESTAMP now = file_timestamp_now (&resolution); + adjusted_now = now + (resolution - 1); + if (adjusted_now < adjusted_mtime) + { +#ifdef NO_FLOAT + OS (error, NILF, + _("Warning: File '%s' has modification time in the future"), + file->name); +#else + double from_now = + (FILE_TIMESTAMP_S (mtime) - FILE_TIMESTAMP_S (now) + + ((FILE_TIMESTAMP_NS (mtime) - FILE_TIMESTAMP_NS (now)) + / 1e9)); + char from_now_string[100]; + + if (from_now >= 99 && from_now <= ULONG_MAX) + sprintf (from_now_string, "%lu", (unsigned long) from_now); + else + sprintf (from_now_string, "%.2g", from_now); + OSS (error, NILF, + _("Warning: File '%s' has modification time %s s in the future"), + file->name, from_now_string); +#endif + clock_skew_detected = 1; + } + } + } + + /* Store the mtime into all the entries for this file for which it is safe + to do so: avoid propagating timestamps to double-colon rules that haven't + been examined so they're run or not based on the pre-update timestamp. */ + if (file->double_colon) + file = file->double_colon; + + propagate_timestamp = file->updated; + do + { + /* If this file is not implicit but it is intermediate then it was + made so by the .INTERMEDIATE target. If this file has never + been built by us but was found now, it existed before make + started. So, turn off the intermediate bit so make doesn't + delete it, since it didn't create it. */ + if (mtime != NONEXISTENT_MTIME && file->command_state == cs_not_started + && !file->tried_implicit && file->intermediate) + file->intermediate = 0; + + if (file->updated == propagate_timestamp) + file->last_mtime = mtime; + file = file->prev; + } + while (file != 0); + + return mtime; +} + + +/* Return the mtime of the file or archive-member reference NAME. */ + +/* First, we check with stat(). If the file does not exist, then we return + NONEXISTENT_MTIME. If it does, and the symlink check flag is set, then + examine each indirection of the symlink and find the newest mtime. + This causes one duplicate stat() when -L is being used, but the code is + much cleaner. */ + +static FILE_TIMESTAMP +name_mtime (const char *name) +{ + FILE_TIMESTAMP mtime; + struct stat st; + int e; + +#if defined(KMK) && defined(KBUILD_OS_WINDOWS) + extern int stat_only_mtime(const char *pszPath, struct stat *pStat); + e = stat_only_mtime (name, &st); +#else + EINTRLOOP (e, stat (name, &st)); +#endif + if (e == 0) + mtime = FILE_TIMESTAMP_STAT_MODTIME (name, st); + else if (errno == ENOENT || errno == ENOTDIR) + mtime = NONEXISTENT_MTIME; + else + { + perror_with_name ("stat: ", name); + return NONEXISTENT_MTIME; + } + + /* If we get here we either found it, or it doesn't exist. + If it doesn't exist see if we can use a symlink mtime instead. */ + +#ifdef MAKE_SYMLINKS +#ifndef S_ISLNK +# define S_ISLNK(_m) (((_m)&S_IFMT)==S_IFLNK) +#endif + if (check_symlink_flag) + { + PATH_VAR (lpath); + + /* Check each symbolic link segment (if any). Find the latest mtime + amongst all of them (and the target file of course). + Note that we have already successfully dereferenced all the links + above. So, if we run into any error trying to lstat(), or + readlink(), or whatever, something bizarre-o happened. Just give up + and use whatever mtime we've already computed at that point. */ + strcpy (lpath, name); + while (1) + { + FILE_TIMESTAMP ltime; + PATH_VAR (lbuf); + long llen; + char *p; + + EINTRLOOP (e, lstat (lpath, &st)); + if (e) + { + /* Just take what we have so far. */ + if (errno != ENOENT && errno != ENOTDIR) + perror_with_name ("lstat: ", lpath); + break; + } + + /* If this is not a symlink, we're done (we started with the real + file's mtime so we don't need to test it again). */ + if (!S_ISLNK (st.st_mode)) + break; + + /* If this mtime is newer than what we had, keep the new one. */ + ltime = FILE_TIMESTAMP_STAT_MODTIME (lpath, st); + if (ltime > mtime) + mtime = ltime; + + /* Set up to check the file pointed to by this link. */ + EINTRLOOP (llen, readlink (lpath, lbuf, GET_PATH_MAX)); + if (llen < 0) + { + /* Eh? Just take what we have. */ + perror_with_name ("readlink: ", lpath); + break; + } + lbuf[llen] = '\0'; + + /* If the target is fully-qualified or the source is just a + filename, then the new path is the target. Otherwise it's the + source directory plus the target. */ + if (lbuf[0] == '/' || (p = strrchr (lpath, '/')) == NULL) + strcpy (lpath, lbuf); + else if ((p - lpath) + llen + 2 > GET_PATH_MAX) + /* Eh? Path too long! Again, just go with what we have. */ + break; + else + /* Create the next step in the symlink chain. */ + strcpy (p+1, lbuf); + } + } +#endif + + return mtime; +} + + +/* Search for a library file specified as -lLIBNAME, searching for a + suitable library file in the system library directories and the VPATH + directories. */ + +static const char * +library_search (const char *lib, FILE_TIMESTAMP *mtime_ptr) +{ + static const char *dirs[] = + { +#ifdef KMK + ".", +#else /* !KMK */ +#ifndef _AMIGA + "/lib", + "/usr/lib", +#endif +#if defined(WINDOWS32) && !defined(LIBDIR) +/* + * This is completely up to the user at product install time. Just define + * a placeholder. + */ +#define LIBDIR "." +#endif +# ifdef LIBDIR /* bird */ + LIBDIR, /* Defined by configuration. */ +# else /* bird */ + ".", /* bird */ +# endif /* bird */ +#endif /* !KMK */ + 0 + }; + + const char *file = 0; + char *libpatterns; + FILE_TIMESTAMP mtime; + + /* Loop variables for the libpatterns value. */ + char *p; + const char *p2; + unsigned int len; + unsigned int liblen; + + /* Information about the earliest (in the vpath sequence) match. */ + unsigned int best_vpath = 0, best_path = 0; + + const char **dp; + + libpatterns = xstrdup (variable_expand ("$(.LIBPATTERNS)")); + + /* Skip the '-l'. */ + lib += 2; + liblen = strlen (lib); + + /* Loop through all the patterns in .LIBPATTERNS, and search on each one. + To implement the linker-compatible behavior we have to search through + all entries in .LIBPATTERNS and choose the "earliest" one. */ + p2 = libpatterns; + while ((p = find_next_token (&p2, &len)) != 0) + { + static char *buf = NULL; + static unsigned int buflen = 0; + static int libdir_maxlen = -1; + static unsigned int std_dirs = 0; + char *libbuf = variable_expand (""); + const size_t libbuf_offset = libbuf - variable_buffer; /* bird */ + + /* Expand the pattern using LIB as a replacement. */ + { + char c = p[len]; + char *p3, *p4; + + p[len] = '\0'; + p3 = find_percent (p); + if (!p3) + { + /* Give a warning if there is no pattern. */ + OS (error, NILF, + _(".LIBPATTERNS element '%s' is not a pattern"), p); + p[len] = c; + continue; + } + p4 = variable_buffer_output (libbuf, p, p3-p); + p4 = variable_buffer_output (p4, lib, liblen); + p4 = variable_buffer_output (p4, p3+1, len - (p3-p)); + p[len] = c; + libbuf = variable_buffer + libbuf_offset; /* bird - variable_buffer may have been reallocated. UPSTREAM */ + } + + /* Look first for 'libNAME.a' in the current directory. */ + mtime = name_mtime (libbuf); + if (mtime != NONEXISTENT_MTIME) + { + if (mtime_ptr != 0) + *mtime_ptr = mtime; + file = strcache_add (libbuf); + /* This by definition will have the best index, so stop now. */ + break; + } + + /* Now try VPATH search on that. */ + + { + unsigned int vpath_index, path_index; + const char* f = vpath_search (libbuf, mtime_ptr ? &mtime : NULL, + &vpath_index, &path_index); + if (f) + { + /* If we have a better match, record it. */ + if (file == 0 || + vpath_index < best_vpath || + (vpath_index == best_vpath && path_index < best_path)) + { + file = f; + best_vpath = vpath_index; + best_path = path_index; + + if (mtime_ptr != 0) + *mtime_ptr = mtime; + } + } + } + + /* Now try the standard set of directories. */ + + if (!buflen) + { + for (dp = dirs; *dp != 0; ++dp) + { + int l = strlen (*dp); + if (l > libdir_maxlen) + libdir_maxlen = l; + std_dirs++; + } + buflen = strlen (libbuf); + buf = xmalloc (libdir_maxlen + buflen + 2); + } + else if (buflen < strlen (libbuf)) + { + buflen = strlen (libbuf); + buf = xrealloc (buf, libdir_maxlen + buflen + 2); + } + + { + /* Use the last std_dirs index for standard directories. This + was it will always be greater than the VPATH index. */ + unsigned int vpath_index = ~((unsigned int)0) - std_dirs; + + for (dp = dirs; *dp != 0; ++dp) + { + sprintf (buf, "%s/%s", *dp, libbuf); + mtime = name_mtime (buf); + if (mtime != NONEXISTENT_MTIME) + { + if (file == 0 || vpath_index < best_vpath) + { + file = strcache_add (buf); + best_vpath = vpath_index; + + if (mtime_ptr != 0) + *mtime_ptr = mtime; + } + } + + vpath_index++; + } + } + + } + + free (libpatterns); + return file; +} |