diff --git a/file.c b/file.c
index 82e7442c..f81c9b93 100644
--- a/file.c
+++ b/file.c
@@ -303,7 +303,8 @@ remove_intermediates (sig)
   doneany = 0;
   for (i = 0; i < FILE_BUCKETS; ++i)
     for (f = files[i]; f != 0; f = f->next)
-      if (f->intermediate && (f->dontcare || !f->precious))
+      if (f->intermediate && (f->dontcare || !f->precious)
+	  && !f->secondary)
 	{
 	  int status;
 	  if (f->update_status == -1)
@@ -349,7 +350,8 @@ remove_intermediates (sig)
 /* For each dependency of each file, make the `struct dep' point
    at the appropriate `struct file' (which may have to be created).
 
-   Also mark the files depended on by .PRECIOUS and .PHONY.  */
+   Also mark the files depended on by .PRECIOUS, .PHONY, .SILENT,
+   and various other special targets.  */
 
 void
 snap_deps ()
@@ -387,6 +389,41 @@ snap_deps ()
 	  f2->last_mtime = (time_t) -1;
 	}
 
+  for (f = lookup_file (".INTERMEDIATE"); f != 0; f = f->prev)
+    {
+      /* .INTERMEDIATE with deps listed
+	 marks those deps as intermediate files.  */
+      for (d = f->deps; d != 0; d = d->next)
+	for (f2 = d->file; f2 != 0; f2 = f2->prev)
+	  f2->intermediate = 1;
+      /* .INTERMEDIATE with no deps does nothing.
+	 Marking all files as intermediates is useless
+	 since the goal targets would be deleted after they are built.  */
+    }
+
+  for (f = lookup_file (".SECONDARY"); f != 0; f = f->prev)
+    {
+      /* .SECONDARY with deps listed
+	 marks those deps as intermediate files
+	 in that they don't get rebuilt if not actually needed;
+	 but unlike real intermediate files,
+	 these are not deleted after make finishes.  */
+      if (f->deps)
+	{
+	  for (d = f->deps; d != 0; d = d->next)
+	    for (f2 = d->file; f2 != 0; f2 = f2->prev)
+	      f2->intermediate = f2->secondary = 1;
+	}
+      /* .SECONDARY with no deps listed marks *all* files that way.  */
+      else
+	{
+	  int i;
+	  for (i = 0; i < FILE_BUCKETS; i++)
+	    for (f2 = files[i]; f2; f2= f2->next)
+	      f2->intermediate = f2->secondary = 1;
+	}
+    }
+
   f = lookup_file (".EXPORT_ALL_VARIABLES");
   if (f != 0 && f->is_target)
     export_all_variables = 1;
diff --git a/file.h b/file.h
index 5889474a..6070ecb1 100644
--- a/file.h
+++ b/file.h
@@ -72,6 +72,9 @@ struct file
     unsigned int phony:1;	/* Nonzero if this is a phony file
 				   i.e., a dependency of .PHONY.  */
     unsigned int intermediate:1;/* Nonzero if this is an intermediate file.  */
+    /* Nonzero, for an intermediate file,
+       means remove_intermediates should not delete it.  */
+    unsigned int secondary:1;
     unsigned int dontcare:1;	/* Nonzero if no complaint is to be made if
 				   this target cannot be remade.  */
   };
@@ -89,11 +92,17 @@ extern void rename_file (), file_hash_enter ();
 extern void set_command_state ();
 
 
+/* Return the mtime of file F (a struct file *), caching it.
+   The value is -1 if the file does not exist.  */
+#define file_mtime(f) file_mtime_1 ((f), 1)
+/* Return the mtime of file F (a struct file *), caching it.
+   Don't search using vpath for the file--if it doesn't actually exist,
+   we don't find it.
+   The value is -1 if the file does not exist.  */
+#define file_mtime_no_search(f) file_mtime_1 ((f), 0)
 extern time_t f_mtime ();
 #define file_mtime_1(f, v) \
   ((f)->last_mtime != (time_t) 0 ? (f)->last_mtime : f_mtime ((f), v))
-#define file_mtime(f) file_mtime_1 ((f), 1)
-#define file_mtime_no_search(f) file_mtime_1 ((f), 0)
 
 /* Modtime value to use for `infinitely new'.  We used to get the current time
    from the system and use that whenever we wanted `new'.  But that causes