| /* Basic dependency engine for GNU Make. |
| Copyright (C) 1988,89,90,91,92,93,94,95,96,97,99 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, Inc., 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. */ |
| |
| #include "make.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 |
| |
| extern int try_implicit_rule PARAMS ((struct file *file, unsigned int depth)); |
| |
| |
| /* this enum encapsulates the return status of the recursive update |
| functions. Note: these values are important so that the state of |
| precursors can be or'ed together */ |
| typedef enum { |
| ts_done = 0, /* all precursors and target are complete */ |
| ts_incomplete = 1, /* some precursor or target is still running */ |
| ts_failed = 3, /* some precursor or target failed */ |
| ts_sentinal /* make ANSI happy with non trailing , */ |
| } target_state_t; |
| |
| |
| |
| |
| /* the test for circular dependencies is based just on the 'updating' |
| bit in 'struct file'. However double colon targets have seperate |
| 'struct file's. Therefore, we will lose if we don't notice that a |
| double colon targets are really the same file. Use the 'struct |
| file' at the base of the double colon chain. */ |
| |
| #define set_updating(file) (((file)->double_colon ? \ |
| (file)->double_colon : (file))->updating = 1) |
| #define clr_updating(file) (((file)->double_colon ? \ |
| (file)->double_colon : (file))->updating = 0) |
| #define tst_updating(file) (((file)->double_colon ? \ |
| (file)->double_colon : (file))->updating) |
| |
| |
| /* Incremented when a command is started (under -n, when one would be). */ |
| unsigned int commands_started = 0; |
| |
| static target_state_t update_file PARAMS ((struct file *file, unsigned int depth)); |
| static target_state_t update_file_1 PARAMS ((struct file *file, unsigned int depth)); |
| static target_state_t check_dep PARAMS ((struct file *file, unsigned int depth, FILE_TIMESTAMP this_mtime, int *must_make_ptr)); |
| static int touch_file PARAMS ((struct file *file)); |
| static void remake_file PARAMS ((struct file *file)); |
| static FILE_TIMESTAMP name_mtime PARAMS ((char *name)); |
| static int library_search PARAMS ((char **lib, FILE_TIMESTAMP *mtime_ptr)); |
| |
| |
| /* 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 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. */ |
| |
| |
| int |
| update_goal_chain (goals, makefiles) |
| register struct dep *goals; |
| int makefiles; |
| { |
| int t = touch_flag, q = question_flag, n = just_print_flag; |
| unsigned int j = job_slots; |
| int status = -1; |
| |
| |
| /* Duplicate the chain so we can remove things from it. */ |
| |
| goals = copy_dep_chain (goals); |
| |
| { |
| /* Clear the `changed' flag of each goal in the chain. |
| We will use the flag below to notice when any commands |
| have actually been run for a target. When no commands |
| have been run, we give an "up to date" diagnostic. */ |
| |
| struct dep *g; |
| for (g = goals; g != 0; g = g->next) |
| g->changed = 0; |
| } |
| |
| /* All files start with the considered bit 0, so the global value is 1. */ |
| considered = 1; |
| |
| /* 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; |
| target_state_t target_state; |
| |
| target_state = ts_done; |
| |
| file = g->file; |
| check_renamed (file); |
| if (makefiles) |
| { |
| if (file->cmd_target) |
| { |
| touch_flag = t; |
| question_flag = q; |
| just_print_flag = n; |
| } |
| else |
| touch_flag = question_flag = just_print_flag = 0; |
| } |
| |
| commands_started = 0; |
| |
| target_state = update_file (file, 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. */ |
| g->changed |= commands_started ? 1 : 0; |
| |
| |
| /* process the file according to its return */ |
| switch (target_state) |
| { |
| case ts_failed: |
| /* Bail early? */ |
| if (!keep_going_flag && !question_flag && !makefiles) |
| return 1; |
| |
| status = 1; /* mark status failed, keep going */ |
| /* -k, Fall Though !!! */ |
| |
| case ts_done: |
| { |
| FILE_TIMESTAMP mtime = |
| (makefiles ? file_mtime_no_search (file) : file_mtime (file)); |
| |
| check_renamed (file); |
| |
| if (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 (status < 0) |
| { |
| if (!makefiles || (!just_print_flag && !question_flag)) |
| status = 0; |
| } |
| } |
| } |
| if (!makefiles |
| /* If the update_status is zero, 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 && !g->changed |
| /* Never give a message under -s or -q. */ |
| && !silent_flag && !question_flag) |
| message (1, ((file->phony || file->cmds == 0) |
| ? _("Nothing to be done for `%s'.") |
| : _("`%s' is up to date.")), |
| file->name); |
| |
| case ts_incomplete: |
| break; /* do nothing */ |
| } |
| |
| |
| /* advance to next target in goal chain */ |
| if (target_state == ts_incomplete) |
| { |
| lastgoal = g; |
| g = g->next; |
| } |
| else |
| { |
| if (lastgoal == 0) |
| goals = g->next; |
| else |
| lastgoal->next = g->next; |
| |
| free (g); |
| |
| g = lastgoal == 0 ? goals : lastgoal->next; |
| } |
| } |
| |
| /* If we reached the end of the dependency graph toggle the considered |
| flag for the next pass. */ |
| if (g == 0) |
| considered = !considered; |
| } |
| |
| if (makefiles) |
| { |
| touch_flag = t; |
| question_flag = q; |
| just_print_flag = n; |
| job_slots = j; |
| } |
| return status; |
| } |
| |
| /* If FILE is not up to date, execute the commands for it. Return |
| ts_done if FILE has completed successfully, ts_incomplete if FILE |
| or any of its precursors are incomplete, or ts_failed if FILE or |
| any of its precursors failed. |
| |
| 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 target_state_t |
| update_file (file, depth) |
| struct file *file; |
| unsigned int depth; |
| { |
| register target_state_t status = ts_done; |
| register struct file *f; |
| |
| f = file->double_colon ? file->double_colon : file; |
| |
| /* This loop runs until we start commands for a double colon rule, or until |
| the chain is exhausted. */ |
| for (; f != 0; f = f->prev) |
| { |
| status |= update_file_1 (f, depth); |
| check_renamed (f); |
| |
| if (status == ts_failed) |
| { |
| if (!keep_going_flag || question_flag) |
| return ts_failed; |
| else |
| break; |
| } |
| |
| if (status == ts_incomplete) |
| break; |
| |
| if (f->command_state == cs_running |
| || f->command_state == cs_deps_running) |
| { |
| /* Don't run the other :: rules for this |
| file until this rule is finished. */ |
| status = ts_incomplete; |
| break; |
| } |
| } |
| |
| /* Process the remaining rules in the double colon chain so they're marked |
| considered. Start their prerequisites, too. */ |
| for (f = (f ? f->prev : 0); f != 0 ; f = f->prev) |
| { |
| struct dep *d; |
| |
| f->considered = considered; |
| |
| for (d = f->deps; d != 0; d = d->next) |
| if ((status |= update_file (d->file, depth + 1)) == ts_failed) |
| { |
| if (!keep_going_flag || question_flag) |
| return ts_failed; |
| } |
| } |
| |
| return status; |
| } |
| |
| /* Consider a single `struct file' and update it as appropriate. |
| |
| Return ts_done if FILE has completed successfully, ts_incomplete if |
| FILE or any of its precursors are incomplete, or ts_failed if FILE |
| or any of its precursors failed. |
| */ |
| |
| static target_state_t |
| update_file_1 (file, depth) |
| struct file *file; |
| unsigned int depth; |
| { |
| register FILE_TIMESTAMP this_mtime; |
| int noexist, must_make, deps_changed; |
| target_state_t dep_status = ts_done; |
| register struct dep *d, *lastd; |
| int running = 0; |
| |
| /* 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 (file->considered == considered) |
| { |
| DBF (DB_VERBOSE, _("Pruning file `%s'.\n")); |
| if (file->command_state == cs_finished) |
| { |
| return file->update_status ? ts_failed : ts_done; |
| } |
| return ts_incomplete; |
| } |
| file->considered = considered; |
| |
| |
| DBF (DB_VERBOSE, _("Considering target file `%s'.\n")); |
| |
| |
| 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 ts_incomplete; |
| |
| case cs_finished: |
| if (file->update_status != 0) |
| { |
| DBF (DB_VERBOSE, |
| _("Recently tried and failed to update file `%s'.\n")); |
| return ts_failed; |
| } |
| |
| DBF (DB_VERBOSE, _("File `%s' was considered already.\n")); |
| return ts_done; |
| |
| default: |
| abort (); |
| } |
| |
| ++depth; |
| |
| /* Notice recursive update of the same file. */ |
| set_updating(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. */ |
| |
| this_mtime = file_mtime (file); |
| check_renamed (file); |
| must_make = noexist = this_mtime == (FILE_TIMESTAMP) -1; |
| if (noexist) |
| DBF (DB_BASIC, _("File `%s' does not exist.\n")); |
| |
| |
| |
| /* If file was specified as a target with no commands, |
| come up with some default commands. */ |
| |
| if (file->cmds == 0) |
| { |
| if (!file->phony && !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")); |
| } |
| if (!file->is_target |
| && default_file != 0 && default_file->cmds != 0) |
| { |
| DBF (DB_IMPLICIT, _("Using default commands 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. */ |
| |
| lastd = 0; |
| d = file->deps; |
| while (d != 0) |
| { |
| FILE_TIMESTAMP mtime; |
| |
| check_renamed (d->file); |
| |
| mtime = file_mtime (d->file); |
| check_renamed (d->file); |
| |
| if (tst_updating(d->file)) |
| { |
| 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; |
| dep_status |= check_dep (d->file, depth, this_mtime, &must_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 == ts_failed && !keep_going_flag) |
| break; |
| |
| if (!running) |
| d->changed = file_mtime (d->file) != mtime; |
| |
| lastd = d; |
| d = d->next; |
| } |
| |
| /* Now we know whether this target needs updating. |
| If it does, update all the intermediate files we depend on. */ |
| |
| if (must_make) |
| { |
| for (d = file->deps; d != 0; d = d->next) |
| if (d->file->intermediate) |
| { |
| FILE_TIMESTAMP mtime = file_mtime (d->file); |
| check_renamed (d->file); |
| d->file->parent = file; |
| dep_status |= update_file (d->file, depth); |
| 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 == ts_failed && !keep_going_flag) |
| break; |
| |
| if (!running) |
| d->changed = ((file->phony && file->cmds != 0) |
| || file_mtime (d->file) != mtime); |
| } |
| } |
| |
| clr_updating(file); |
| |
| 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 ts_incomplete; |
| } |
| |
| /* If any dependency failed, give up now. */ |
| |
| if (dep_status == ts_failed) |
| { |
| file->update_status = 1; /* if any precursors failed, so did we */ |
| 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) |
| error (NILF, |
| _("Target `%s' not remade because of errors."), file->name); |
| |
| return ts_failed; |
| } |
| |
| 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 dependencies are more |
| recent than this file, so we can define $?. */ |
| |
| deps_changed = 0; |
| for (d = file->deps; d != 0; d = d->next) |
| { |
| FILE_TIMESTAMP d_mtime = file_mtime (d->file); |
| check_renamed (d->file); |
| |
| #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 == (FILE_TIMESTAMP) -1 && !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_mtime == (FILE_TIMESTAMP) -1) |
| { |
| 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); |
| } |
| } |
| } |
| |
| /* 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) |
| { |
| must_make = 0; |
| DBF (DB_VERBOSE, |
| _("No commands for `%s' and no prerequisites actually changed.\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 ts_done; |
| } |
| |
| 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, _("Commands of `%s' are being run.\n")); |
| return ts_incomplete; |
| } |
| |
| if (file->update_status) |
| { |
| DBF (DB_BASIC, (question_flag ? _("Target file `%s' needs remade under -q.\n") |
| : _("Failed to remake target file `%s'.\n"))); |
| return ts_failed; |
| } |
| else |
| { |
| DBF (DB_BASIC, _("Successfully remade target file `%s'.\n")); |
| return ts_done; |
| } |
| } |
| |
| /* re-check FILE's 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 -1 if it was. */ |
| |
| void |
| notice_finished_file (file) |
| register struct file *file; |
| { |
| struct dep *d; |
| int ran = file->command_state == cs_running; |
| |
| file->command_state = cs_finished; |
| |
| if (touch_flag |
| /* The update status will be: |
| -1 if this target was not remade; |
| 0 if 0 or more commands (+ or ${MAKE}) were run and won; |
| 1 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 == 0) |
| { |
| 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) |
| file->update_status = 0; |
| else |
| /* Should set file's modification date and do nothing else. */ |
| file->update_status = touch_file (file) ? 1 : 0; |
| } |
| } |
| |
| if (file->mtime_before_update == 0) |
| file->mtime_before_update = file->last_mtime; |
| |
| if (ran && !file->phony) |
| { |
| struct file *f; |
| int i = 0; |
| |
| /* If -n 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) |
| { |
| 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 ? 0 : NEW_MTIME; |
| |
| /* Propagate the change of modification time to all the double-colon |
| entries for this file. */ |
| for (f = file->double_colon; f != 0; f = f->next) |
| f->last_mtime = file->last_mtime; |
| } |
| |
| if (ran) |
| /* 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->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. */ |
| (void) f_mtime (d->file, 0); |
| } |
| } |
| |
| /* 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 ts_done if FILE has completed successfully, ts_incomplete if |
| FILE or any of its precursors are incomplete, or ts_failed if FILE |
| or any of its precursors failed. |
| */ |
| |
| static target_state_t |
| check_dep (file, depth, this_mtime, must_make_ptr) |
| struct file *file; |
| unsigned int depth; |
| FILE_TIMESTAMP this_mtime; |
| int *must_make_ptr; |
| { |
| register struct dep *d; |
| target_state_t dep_status = ts_done; |
| |
| ++depth; |
| set_updating(file); |
| |
| if (!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 == (FILE_TIMESTAMP) -1 || mtime > this_mtime) |
| *must_make_ptr = 1; |
| } |
| 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")); |
| } |
| 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; |
| } |
| |
| /* If the intermediate file actually exists |
| and is newer, then we should remake from it. */ |
| check_renamed (file); |
| mtime = file_mtime (file); |
| check_renamed (file); |
| if (mtime != (FILE_TIMESTAMP) -1 && mtime > this_mtime) |
| *must_make_ptr = 1; |
| /* 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. */ |
| else |
| { |
| register struct dep *lastd; |
| |
| lastd = 0; |
| d = file->deps; |
| while (d != 0) |
| { |
| if (tst_updating(d->file)) |
| { |
| error (NILF, _("Circular %s <- %s dependency dropped."), |
| file->name, d->file->name); |
| if (lastd == 0) |
| { |
| file->deps = d->next; |
| free ((char *) d); |
| d = file->deps; |
| } |
| else |
| { |
| lastd->next = d->next; |
| free ((char *) d); |
| d = lastd->next; |
| } |
| continue; |
| } |
| |
| d->file->parent = file; |
| dep_status |= check_dep (d->file, depth, this_mtime, |
| must_make_ptr); |
| check_renamed (d->file); |
| if (dep_status == ts_failed && !keep_going_flag) |
| break; |
| |
| if (d->file->command_state == cs_running |
| || d->file->command_state == cs_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); |
| |
| lastd = d; |
| d = d->next; |
| } |
| } |
| } |
| |
| clr_updating(file); |
| return dep_status; |
| } |
| |
| /* Touch FILE. Return zero if successful, one if not. */ |
| |
| #define TOUCH_ERROR(call) return (perror_with_name (call, file->name), 1) |
| |
| static int |
| touch_file (file) |
| register struct file *file; |
| { |
| if (!silent_flag) |
| message (0, "touch %s", file->name); |
| |
| #ifndef NO_ARCHIVES |
| if (ar_name (file->name)) |
| return ar_touch (file->name); |
| else |
| #endif |
| { |
| int fd = open (file->name, O_RDWR | O_CREAT, 0666); |
| |
| if (fd < 0) |
| TOUCH_ERROR ("touch: open: "); |
| else |
| { |
| struct stat statbuf; |
| char buf; |
| int status; |
| |
| do |
| status = fstat (fd, &statbuf); |
| while (status < 0 && EINTR_SET); |
| |
| if (status < 0) |
| TOUCH_ERROR ("touch: fstat: "); |
| /* Rewrite character 0 same as it already is. */ |
| if (read (fd, &buf, 1) < 0) |
| TOUCH_ERROR ("touch: read: "); |
| if (lseek (fd, 0L, 0) < 0L) |
| TOUCH_ERROR ("touch: lseek: "); |
| if (write (fd, &buf, 1) < 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); |
| fd = open (file->name, O_RDWR | O_TRUNC, 0666); |
| if (fd < 0) |
| TOUCH_ERROR ("touch: open: "); |
| } |
| (void) close (fd); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* 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 (file) |
| struct file *file; |
| { |
| if (file->cmds == 0) |
| { |
| if (file->phony) |
| /* Phony target. Pretend it succeeded. */ |
| file->update_status = 0; |
| else if (file->is_target) |
| /* This is a nonexistent target file we cannot make. |
| Pretend it was successfully remade. */ |
| file->update_status = 0; |
| else |
| { |
| const char *msg_noparent |
| = _("%sNo rule to make target `%s'%s"); |
| const char *msg_parent |
| = _("%sNo rule to make target `%s', needed by `%s'%s"); |
| |
| /* This is a dependency file we cannot remake. Fail. */ |
| if (!keep_going_flag && !file->dontcare) |
| { |
| if (file->parent == 0) |
| fatal (NILF, msg_noparent, "", file->name, ""); |
| |
| fatal (NILF, msg_parent, "", file->name, file->parent->name, ""); |
| } |
| |
| if (!file->dontcare) |
| { |
| if (file->parent == 0) |
| error (NILF, msg_noparent, "*** ", file->name, "."); |
| else |
| error (NILF, msg_parent, "*** ", |
| file->name, file->parent->name, "."); |
| } |
| file->update_status = 1; |
| } |
| } |
| 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 = 0; |
| } |
| |
| /* 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 (file, search) |
| register struct file *file; |
| int search; |
| { |
| FILE_TIMESTAMP mtime; |
| |
| /* 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; |
| int arname_used = 0; |
| |
| /* 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 (arname); |
| arname_used = 1; |
| } |
| 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; |
| |
| if (!arname_used) |
| { |
| free (arname); |
| arname_used = 1; |
| } |
| |
| arname = arfile->hname; |
| arlen = strlen (arname); |
| memlen = strlen (memname); |
| |
| /* free (file->name); */ |
| |
| name = (char *) xmalloc (arlen + 1 + memlen + 2); |
| bcopy (arname, name, arlen); |
| name[arlen] = '('; |
| bcopy (memname, name + arlen + 1, 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, name); |
| else |
| rehash_file (file, name); |
| check_renamed (file); |
| } |
| |
| if (!arname_used) |
| free (arname); |
| free (memname); |
| |
| if (mtime == (FILE_TIMESTAMP) -1) |
| /* The archive doesn't exist, so it's members don't exist either. */ |
| return (FILE_TIMESTAMP) -1; |
| |
| mtime = ar_member_date (file->hname); |
| } |
| else |
| #endif |
| { |
| mtime = name_mtime (file->name); |
| |
| if (mtime == (FILE_TIMESTAMP) -1 && search && !file->ignore_vpath) |
| { |
| /* If name_mtime failed, search VPATH. */ |
| char *name = file->name; |
| if (vpath_search (&name, &mtime) |
| /* Last resort, is it a library (-lxxx)? */ |
| || (name[0] == '-' && name[1] == 'l' |
| && library_search (&name, &mtime))) |
| { |
| if (mtime != 0) |
| /* vpath_search and library_search store zero in 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. */ |
| if (gpath_search (name, strlen(name) - strlen(file->name) - 1)) |
| { |
| rename_file (file, name); |
| check_renamed (file); |
| return file_mtime (file); |
| } |
| |
| rehash_file (file, name); |
| check_renamed (file); |
| 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. */ |
| |
| static FILE_TIMESTAMP now = 0; |
| if (!clock_skew_detected |
| && mtime != (FILE_TIMESTAMP)-1 && mtime > now |
| && !(file->command_state == cs_finished)) |
| { |
| /* This file's time appears to be in the future. |
| Update our concept of the present, and compare again. */ |
| |
| now = file_timestamp_now (); |
| |
| #ifdef WINDOWS32 |
| /* |
| * FAT filesystems round time to nearest even second(!). Just |
| * allow for any file (NTFS or FAT) to perhaps suffer from this |
| * braindamage. |
| */ |
| if (mtime > now && (((mtime % 2) == 0) && ((mtime-1) > now))) |
| #else |
| #ifdef __MSDOS__ |
| /* Scrupulous testing indicates that some Windows |
| filesystems can set file times up to 3 sec into the future! */ |
| if (mtime > now + 3) |
| #else |
| if (mtime > now) |
| #endif |
| #endif |
| { |
| char mtimebuf[FILE_TIMESTAMP_PRINT_LEN_BOUND + 1]; |
| char nowbuf[FILE_TIMESTAMP_PRINT_LEN_BOUND + 1]; |
| |
| file_timestamp_sprintf (mtimebuf, mtime); |
| file_timestamp_sprintf (nowbuf, now); |
| error (NILF, _("*** Warning: File `%s' has modification time in the future (%s > %s)"), |
| file->name, mtimebuf, nowbuf); |
| clock_skew_detected = 1; |
| } |
| } |
| } |
| |
| /* Store the mtime into all the entries for this file. */ |
| if (file->double_colon) |
| file = file->double_colon; |
| |
| 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 != (FILE_TIMESTAMP)-1 && file->command_state == cs_not_started |
| && !file->tried_implicit && file->intermediate) |
| file->intermediate = 0; |
| |
| file->last_mtime = mtime; |
| file = file->prev; |
| } |
| while (file != 0); |
| |
| return mtime; |
| } |
| |
| |
| /* Return the mtime of the file or archive-member reference NAME. */ |
| |
| static FILE_TIMESTAMP |
| name_mtime (name) |
| register char *name; |
| { |
| struct stat st; |
| |
| if (stat (name, &st) < 0) |
| return (FILE_TIMESTAMP) -1; |
| |
| return FILE_TIMESTAMP_STAT_MODTIME (st); |
| } |
| |
| |
| /* Search for a library file specified as -lLIBNAME, searching for a |
| suitable library file in the system library directories and the VPATH |
| directories. */ |
| |
| static int |
| library_search (lib, mtime_ptr) |
| char **lib; |
| FILE_TIMESTAMP *mtime_ptr; |
| { |
| static char *dirs[] = |
| { |
| #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 |
| LIBDIR, /* Defined by configuration. */ |
| 0 |
| }; |
| |
| static char *libpatterns = NULL; |
| |
| char *libname = &(*lib)[2]; /* Name without the `-l'. */ |
| FILE_TIMESTAMP mtime; |
| |
| /* Loop variables for the libpatterns value. */ |
| char *p, *p2; |
| unsigned int len; |
| |
| char *file, **dp; |
| |
| /* If we don't have libpatterns, get it. */ |
| if (!libpatterns) |
| { |
| int save = warn_undefined_variables_flag; |
| warn_undefined_variables_flag = 0; |
| |
| libpatterns = xstrdup (variable_expand ("$(strip $(.LIBPATTERNS))")); |
| |
| warn_undefined_variables_flag = save; |
| } |
| |
| /* Loop through all the patterns in .LIBPATTERNS, and search on each one. */ |
| p2 = libpatterns; |
| while ((p = find_next_token (&p2, &len)) != 0) |
| { |
| static char *buf = NULL; |
| static int buflen = 0; |
| static int libdir_maxlen = -1; |
| char *libbuf = variable_expand (""); |
| |
| /* Expand the pattern using LIBNAME 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, then remove the |
| pattern so it's ignored next time. */ |
| error (NILF, _(".LIBPATTERNS element `%s' is not a pattern"), p); |
| for (; len; --len, ++p) |
| *p = ' '; |
| *p = c; |
| continue; |
| } |
| p4 = variable_buffer_output (libbuf, p, p3-p); |
| p4 = variable_buffer_output (p4, libname, strlen (libname)); |
| p4 = variable_buffer_output (p4, p3+1, len - (p3-p)); |
| p[len] = c; |
| } |
| |
| /* Look first for `libNAME.a' in the current directory. */ |
| mtime = name_mtime (libbuf); |
| if (mtime != (FILE_TIMESTAMP) -1) |
| { |
| *lib = xstrdup (libbuf); |
| if (mtime_ptr != 0) |
| *mtime_ptr = mtime; |
| return 1; |
| } |
| |
| /* Now try VPATH search on that. */ |
| |
| file = libbuf; |
| if (vpath_search (&file, mtime_ptr)) |
| { |
| *lib = file; |
| return 1; |
| } |
| |
| /* 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; |
| } |
| buflen = strlen (libbuf); |
| buf = xmalloc(libdir_maxlen + buflen + 2); |
| } |
| else if (buflen < strlen (libbuf)) |
| { |
| buflen = strlen (libbuf); |
| buf = xrealloc (buf, libdir_maxlen + buflen + 2); |
| } |
| |
| for (dp = dirs; *dp != 0; ++dp) |
| { |
| sprintf (buf, "%s/%s", *dp, libbuf); |
| mtime = name_mtime (buf); |
| if (mtime != (FILE_TIMESTAMP) -1) |
| { |
| *lib = xstrdup (buf); |
| if (mtime_ptr != 0) |
| *mtime_ptr = mtime; |
| return 1; |
| } |
| } |
| } |
| |
| return 0; |
| } |