summaryrefslogtreecommitdiff
path: root/commands.c
diff options
context:
space:
mode:
Diffstat (limited to 'commands.c')
-rw-r--r--commands.c478
1 files changed, 478 insertions, 0 deletions
diff --git a/commands.c b/commands.c
new file mode 100644
index 0000000..2fbdc9a
--- /dev/null
+++ b/commands.c
@@ -0,0 +1,478 @@
+/* Command processing for GNU Make.
+Copyright (C) 1988, 1989, 1991 Free Software Foundation, Inc.
+This file is part of GNU Make.
+
+GNU Make is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Make is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Make; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "make.h"
+#include "dep.h"
+#include "commands.h"
+#include "file.h"
+#include "variable.h"
+#include "job.h"
+
+extern int remote_kill ();
+
+#if !defined(POSIX) && !defined(__GNU_LIBRARY__)
+extern int getpid ();
+#endif
+
+/* Set FILE's automatic variables up. */
+
+static void
+set_file_variables (file)
+ register struct file *file;
+{
+ register char *p;
+ char *at, *percent, *star, *less;
+
+#define DEFINE_VARIABLE(name, len, value) \
+ (void) define_variable_for_file (name, len, value, o_automatic, 0, file)
+
+#ifndef NO_ARCHIVES
+ /* If the target is an archive member `lib(member)',
+ then $@ is `lib' and $% is `member'. */
+
+ if (ar_name (file->name))
+ {
+ p = index (file->name, '(');
+ at = savestring (file->name, p - file->name);
+ ++p;
+ percent = savestring (p, strlen (p) - 1);
+ }
+ else
+#endif /* NO_ARCHIVES. */
+ {
+ at = savestring (file->name, strlen (file->name));
+ percent = "";
+ }
+
+ DEFINE_VARIABLE ("@", 1, at);
+ DEFINE_VARIABLE ("%", 1, percent);
+
+#define LASTSLASH(s) rindex ((s), '/')
+#define FILEONLY(s) (p != 0 ? p + 1 : (s))
+#define DIRONLY(s) (p == 0 ? "./" : p == (s) ? "/" \
+ : savestring ((s), (p - (s)) + 1))
+
+ /* $* is the stem from an implicit or static pattern rule. */
+ if (file->stem == 0)
+ {
+ /* In Unix make, $* is set to the target name with
+ any suffix in the .SUFFIXES list stripped off for
+ explicit rules. We store this in the `stem' member. */
+ register struct dep *d;
+ for (d = enter_file (".SUFFIXES")->deps; d != 0; d = d->next)
+ {
+ unsigned int len = strlen (file->name);
+ unsigned int slen = strlen (dep_name (d));
+ if (len > slen && streq (dep_name (d), file->name + len - slen))
+ {
+ file->stem = savestring (file->name, len - slen);
+ break;
+ }
+ }
+ if (d == 0)
+ file->stem = "";
+ }
+ star = file->stem;
+
+ DEFINE_VARIABLE ("*", 1, star);
+
+ /* $< is the first dependency. */
+ less = file->deps != 0 ? dep_name (file->deps) : "";
+
+ if (file->cmds == default_file->cmds)
+ /* This file got its commands from .DEFAULT.
+ In this case $< is the same as $@. */
+ less = at;
+
+ DEFINE_VARIABLE ("<", 1, less);
+
+ /* Set up the D and F versions. */
+ p = LASTSLASH (at);
+ DEFINE_VARIABLE ("@D", 2, DIRONLY (at));
+ DEFINE_VARIABLE ("@F", 2, FILEONLY (at));
+ p = LASTSLASH (star);
+ DEFINE_VARIABLE ("*D", 2, DIRONLY (star));
+ DEFINE_VARIABLE ("*F", 2, FILEONLY (star));
+ p = LASTSLASH (less);
+ DEFINE_VARIABLE ("<D", 2, DIRONLY (less));
+ DEFINE_VARIABLE ("<F", 2, FILEONLY (less));
+ p = LASTSLASH (percent);
+ DEFINE_VARIABLE ("%D", 2, DIRONLY (percent));
+ DEFINE_VARIABLE ("%F", 2, FILEONLY (percent));
+
+ /* Compute the values for $^ and $? and their F and D versions. */
+
+ {
+ register unsigned int caret_len, qmark_len;
+ char *caret_value, *caretD_value, *caretF_value;
+ register char *cp, *cDp, *cFp;
+ char *qmark_value, *qmarkD_value, *qmarkF_value;
+ register char *qp, *qDp, *qFp;
+ register struct dep *d;
+ unsigned int len;
+
+ caret_len = qmark_len = 0;
+ for (d = file->deps; d != 0; d = d->next)
+ {
+ register unsigned int i = strlen (dep_name (d)) + 1;
+ caret_len += i;
+ if (d->changed)
+ qmark_len += i;
+ }
+
+ len = caret_len == 0 ? 1 : caret_len;
+ cp = caret_value = (char *) xmalloc (len);
+ cDp = caretD_value = (char *) xmalloc (len);
+ cFp = caretF_value = (char *) xmalloc (len);
+ len = qmark_len == 0 ? 1 : qmark_len;
+ qp = qmark_value = (char *) xmalloc (len);
+ qDp = qmarkD_value = (char *) xmalloc (len);
+ qFp = qmarkF_value = (char *) xmalloc (len);
+
+ for (d = file->deps; d != 0; d = d->next)
+ {
+ char *c, *cD, *cF;
+ unsigned int Dlen, Flen;
+
+ c = dep_name (d);
+ len = strlen (c);
+ bcopy (c, cp, len);
+ cp += len;
+ *cp++ = ' ';
+
+ p = LASTSLASH (c);
+ if (p == 0)
+ {
+ cF = c;
+ Flen = len;
+ cD = "./";
+ Dlen = 2;
+ }
+ else if (p == c)
+ {
+ cD = c;
+ Dlen = 1;
+ cF = c + 1;
+ Flen = len - 1;
+ }
+ else
+ {
+ cF = p + 1;
+ Flen = len - (p + 1 - c);
+ cD = c;
+ Dlen = p - c;
+ }
+ bcopy (cD, cDp, Dlen);
+ cDp += Dlen;
+ *cDp++ = ' ';
+ bcopy (cF, cFp, Flen);
+ cFp += Flen;
+ *cFp++ = ' ';
+
+ if (d->changed)
+ {
+ bcopy (c, qp, len);
+ qp += len;
+ *qp++ = ' ';
+ bcopy (cD, qDp, Dlen);
+ qDp += Dlen;
+ *qDp++ = ' ';
+ bcopy (cF, qFp, Flen);
+ qFp += Flen;
+ *qFp++ = ' ';
+ }
+ }
+
+ /* Kill the last spaces and define the variables. */
+
+ cp[cp > caret_value ? -1 : 0] = '\0';
+ DEFINE_VARIABLE ("^", 1, caret_value);
+ cDp[cDp > caretD_value ? -1 : 0] = '\0';
+ DEFINE_VARIABLE ("^D", 2, caretD_value);
+ cFp[cFp > caretF_value ? -1 : 0] = '\0';
+ DEFINE_VARIABLE ("^F", 2, caretF_value);
+
+ qp[qp > qmark_value ? -1 : 0] = '\0';
+ DEFINE_VARIABLE ("?", 1, qmark_value);
+ qDp[qDp > qmarkD_value ? -1 : 0] = '\0';
+ DEFINE_VARIABLE ("?D", 2, qmarkD_value);
+ qFp[qFp > qmarkF_value ? -1 : 0] = '\0';
+ DEFINE_VARIABLE ("?F", 2, qmarkF_value);
+ }
+
+#undef LASTSLASH
+#undef FILEONLY
+#undef DIRONLY
+
+#undef DEFINE_VARIABLE
+}
+
+/* Chop CMDS up into individual command lines if necessary. */
+
+void
+chop_commands (cmds)
+ register struct commands *cmds;
+{
+ if (cmds != 0 && cmds->command_lines == 0)
+ {
+ /* Chop CMDS->commands up into lines in CMDS->command_lines.
+ Also set the corresponding CMDS->lines_recurse elements,
+ and the CMDS->any_recurse flag. */
+ register char *p;
+ unsigned int nlines, idx;
+ char **lines;
+
+ nlines = 5;
+ lines = (char **) xmalloc (5 * sizeof (char *));
+ idx = 0;
+ p = cmds->commands;
+ while (*p != '\0')
+ {
+ char *end = p;
+ find_end:;
+ end = index (end, '\n');
+ if (end == 0)
+ end = p + strlen (p);
+ else if (end > p && end[-1] == '\\')
+ {
+ int backslash = 1;
+ register char *b;
+ for (b = end - 2; b >= p && *b == '\\'; --b)
+ backslash = !backslash;
+ if (backslash)
+ {
+ ++end;
+ goto find_end;
+ }
+ }
+
+ if (idx == nlines)
+ {
+ nlines += 2;
+ lines = (char **) xrealloc ((char *) lines,
+ nlines * sizeof (char *));
+ }
+ lines[idx++] = savestring (p, end - p);
+ p = end;
+ if (*p != '\0')
+ ++p;
+ }
+
+ if (idx != nlines)
+ {
+ nlines = idx;
+ lines = (char **) xrealloc ((char *) lines,
+ nlines * sizeof (char *));
+ }
+
+ cmds->ncommand_lines = nlines;
+ cmds->command_lines = lines;
+
+ cmds->any_recurse = 0;
+ cmds->lines_recurse = (char *) xmalloc (nlines);
+ for (idx = 0; idx < nlines; ++idx)
+ {
+ unsigned int len;
+ int recursive;
+ p = lines[idx];
+ len = strlen (p);
+ recursive = (sindex (p, len, "$(MAKE)", 7) != 0
+ || sindex (p, len, "${MAKE}", 7) != 0);
+ cmds->lines_recurse[idx] = recursive;
+ cmds->any_recurse |= recursive;
+ }
+ }
+}
+
+/* Execute the commands to remake FILE. If they are currently executing,
+ return or have already finished executing, just return. Otherwise,
+ fork off a child process to run the first command line in the sequence. */
+
+void
+execute_file_commands (file)
+ struct file *file;
+{
+ register char *p;
+
+ /* Don't go through all the preparations if
+ the commands are nothing but whitespace. */
+
+ for (p = file->cmds->commands; *p != '\0'; ++p)
+ if (!isspace (*p) && *p != '-' && *p != '@')
+ break;
+ if (*p == '\0')
+ {
+ file->update_status = 0;
+ notice_finished_file (file);
+ return;
+ }
+
+ /* First set the automatic variables according to this file. */
+
+ initialize_file_variables (file);
+
+ set_file_variables (file);
+
+ /* Start the commands running. */
+ new_job (file);
+}
+
+#define PROPAGATED_SIGNAL_MASK \
+ (sigmask (SIGTERM) | sigmask (SIGINT) | sigmask (SIGHUP) | sigmask (SIGQUIT))
+
+/* Handle fatal signals. */
+
+int
+fatal_error_signal (sig)
+ int sig;
+{
+ signal (sig, SIG_DFL);
+#ifndef USG
+ (void) sigsetmask (0);
+#endif
+
+ /* A termination signal won't be sent to the entire
+ process group, but it means we want to kill the children. */
+
+ if (sig == SIGTERM)
+ {
+ register struct child *c;
+ push_signals_blocked_p (1);
+ for (c = children; c != 0; c = c->next)
+ if (!c->remote)
+ (void) kill (c->pid, SIGTERM);
+ pop_signals_blocked_p ();
+ }
+
+ /* If we got a signal that means the user
+ wanted to kill make, remove pending targets. */
+
+ if (PROPAGATED_SIGNAL_MASK & sigmask (sig))
+ {
+ register struct child *c;
+ push_signals_blocked_p (1);
+
+ /* Remote children won't automatically get signals sent
+ to the process group, so we must send them. */
+ for (c = children; c != 0; c = c->next)
+ if (c->remote)
+ (void) remote_kill (c->pid, sig);
+
+ for (c = children; c != 0; c = c->next)
+ delete_child_targets (c);
+
+ pop_signals_blocked_p ();
+
+ /* Clean up the children. We don't just use the call below because
+ we don't want to print the "Waiting for children" message. */
+ wait_for_children (0, 0);
+ }
+ else
+ /* Wait for our children to die. */
+ wait_for_children (0, 1);
+
+ /* Delete any non-precious intermediate files that were made. */
+
+ remove_intermediates (1);
+
+ if (sig == SIGQUIT)
+ /* We don't want to send ourselves SIGQUIT, because it will
+ cause a core dump. Just exit instead. */
+ exit (1);
+
+ /* Signal the same code; this time it will really be fatal. */
+ if (kill (getpid (), sig) < 0)
+ /* It shouldn't return, but if it does, die anyway. */
+ pfatal_with_name ("kill");
+
+ return 0;
+}
+
+/* Delete all non-precious targets of CHILD unless they were already deleted.
+ Set the flag in CHILD to say they've been deleted. */
+
+void
+delete_child_targets (child)
+ struct child *child;
+{
+ struct stat st;
+ struct dep *d;
+
+ if (child->deleted)
+ return;
+
+ /* Delete the file unless it's precious. */
+ if (!child->file->precious
+ && stat (child->file->name, &st) == 0
+ && S_ISREG (st.st_mode)
+ && (time_t) st.st_mtime != child->file->last_mtime)
+ {
+ error ("*** Deleting file `%s'", child->file->name);
+ if (unlink (child->file->name) < 0)
+ perror_with_name ("unlink: ", child->file->name);
+ }
+
+ /* Also remove any non-precious targets listed
+ in the `also_make' member. */
+ for (d = child->file->also_make; d != 0; d = d->next)
+ if (!d->file->precious)
+ if (stat (d->file->name, &st) == 0
+ && S_ISREG (st.st_mode)
+ && (time_t) st.st_mtime != d->file->last_mtime)
+ {
+ error ("*** [%s] Deleting file `%s'", child->file->name,
+ d->file->name);
+ if (unlink (d->file->name) < 0)
+ perror_with_name ("unlink: ", d->file->name);
+ }
+
+ child->deleted = 1;
+}
+
+/* Print out the commands in CMDS. */
+
+void
+print_commands (cmds)
+ register struct commands *cmds;
+{
+ register char *s;
+
+ fputs ("# commands to execute", stdout);
+
+ if (cmds->filename == 0)
+ puts (" (built-in):");
+ else
+ printf (" (from `%s', line %u):\n", cmds->filename, cmds->lineno);
+
+ s = cmds->commands;
+ while (*s != '\0')
+ {
+ char *end;
+
+ while (isspace (*s))
+ ++s;
+
+ end = index (s, '\n');
+ if (end == 0)
+ end = s + strlen (s);
+
+ printf ("\t%.*s\n", end - s, s);
+
+ s = end;
+ }
+}