]> git.ipfire.org Git - thirdparty/make.git/commitdiff
Initial revision
authorRoland McGrath <roland@redhat.com>
Tue, 8 Oct 1991 20:20:29 +0000 (20:20 +0000)
committerRoland McGrath <roland@redhat.com>
Tue, 8 Oct 1991 20:20:29 +0000 (20:20 +0000)
commands.c [new file with mode: 0644]

diff --git a/commands.c b/commands.c
new file mode 100644 (file)
index 0000000..2fbdc9a
--- /dev/null
@@ -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
+\f
+/* 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
+}
+\f
+/* 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;
+       }
+    }
+}
+\f
+/* 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);
+}
+\f
+#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;
+}
+\f
+/* 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;
+}
+\f
+/* 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;
+    }
+}