write an explicit rule with the makefile as the target, and an empty
recipe (@pxref{Empty Recipes, ,Using Empty Recipes}).
-If the makefiles specify a double-colon rule to remake a file with
-a recipe but no prerequisites, that file will always be remade
-(@pxref{Double-Colon}). In the case of makefiles, a makefile that has a
-double-colon rule with a recipe but no prerequisites will be remade every
-time @code{make} is run, and then again after @code{make} starts over
-and reads the makefiles in again. This would cause an infinite loop:
-@code{make} would constantly remake the makefile, and never do anything
-else. So, to avoid this, @code{make} will @strong{not} attempt to
-remake makefiles which are specified as targets of a double-colon rule
-with a recipe but no prerequisites.@refill
+If the makefiles specify a double-colon rule to remake a file with a recipe
+but no prerequisites, that file will always be remade (@pxref{Double-Colon}).
+In the case of makefiles, a makefile that has a double-colon rule with a
+recipe but no prerequisites will be remade every time @code{make} is run, and
+then again after @code{make} starts over and reads the makefiles in again.
+This would cause an infinite loop: @code{make} would constantly remake the
+makefile and restart, and never do anything else. So, to avoid this,
+@code{make} will @strong{not} attempt to remake makefiles which are specified
+as targets of a double-colon rule with a recipe but no prerequisites.
+
+Phony targets (@pxref{Phony Targets}) have the same issue: they are never
+considered up-to-date and so an included file marked as phony would cause
+@code{make} to restart continuously. To avoid this @code{make} will not
+attempt to remake makefiles which are marked phony.
If you do not specify any makefiles to be read with @samp{-f} or
@samp{--file} options, @code{make} will try the default makefile names;
@end group
@end example
-There are problems with this method, however. First, any error
-detected in a sub-make is ignored by this rule, so it will continue
-to build the rest of the directories even when one fails. This can be
-overcome by adding shell commands to note the error and exit, but then
-it will do so even if @code{make} is invoked with the @code{-k}
-option, which is unfortunate. Second, and perhaps more importantly,
-you cannot take advantage of @code{make}'s ability to build targets in
-parallel (@pxref{Parallel, ,Parallel Execution}), since there is only
-one rule.
+There are problems with this method, however. First, any error detected in a
+sub-make is ignored by this rule, so it will continue to build the rest of the
+directories even when one fails. This can be overcome by adding shell
+commands to note the error and exit, but then it will do so even if
+@code{make} is invoked with the @code{-k} option, which is unfortunate.
+Second, and perhaps more importantly, you cannot take full advantage of
+@code{make}'s ability to build targets in parallel (@pxref{Parallel, ,Parallel
+Execution}), since there is only one rule. Each individual makefile's targets
+will be built in parallel, but only one sub-directory will be built at a time.
By declaring the sub-directories as @code{.PHONY} targets (you must do
this as the sub-directory obviously always exists; otherwise it won't
@code{.PHONY} is good for performance, even if you are not worried
about the actual file existing.
-A phony target should not be a prerequisite of a real target file; if it
-is, its recipe will be run every time @code{make} goes to update that
-file. As long as a phony target is never a prerequisite of a real
-target, the phony target recipe will be executed only when the phony
-target is a specified goal (@pxref{Goals, ,Arguments to Specify the
-Goals}).
+A phony target should not be a prerequisite of a real target file; if it is,
+its recipe will be run every time @code{make} considers that file. As long as
+a phony target is never a prerequisite of a real target, the phony target
+recipe will be executed only when the phony target is a specified goal
+(@pxref{Goals, ,Arguments to Specify the Goals}).
+
+You should not declare an included makefile as phony. Phony targets are not
+intended to represent real files, and because the target is always considered
+out of date make will always rebuild it then re-execute itself
+(@pxref{Remaking Makefiles, ,How Makefiles Are Remade}). To avoid this,
+@code{make} will not re-execute itself if an included file marked as phony is
+re-built.
Phony targets can have prerequisites. When one directory contains multiple
programs, it is most convenient to describe all of the programs in one
@printindex fn
@bye
+
+@c Local Variables:
+@c eval: (setq fill-column 78)
+@c End:
/* Update any makefiles if necessary. */
FILE_TIMESTAMP *makefile_mtimes;
+ struct goaldep *skipped_makefiles = NULL;
char **aargv = NULL;
const char **nargv;
+ int any_failed = 0;
int nargc;
enum update_status status;
while (d != 0)
{
- struct file *f;
-
- for (f = d->file->double_colon; f != NULL; f = f->prev)
- if (f->deps == 0 && f->cmds != 0)
- break;
+ int skip = 0;
+ struct file *f = d->file;
+
+ /* Check for makefiles that are either phony or a :: target with
+ commands, but no dependencies. These will always be remade,
+ which will cause an infinite restart loop, so don't try to
+ remake it (this will only happen if your makefiles are written
+ exceptionally stupidly; but if you work for Athena, that's how
+ you write your makefiles.) */
+
+ if (f->phony)
+ skip = 1;
+ else
+ for (f = f->double_colon; f != NULL; f = f->prev)
+ if (f->deps == NULL && f->cmds != NULL)
+ {
+ skip = 1;
+ break;
+ }
- if (f)
+ if (!skip)
+ {
+ makefile_mtimes[mm_idx++] = file_mtime_no_search (d->file);
+ last = d;
+ d = d->next;
+ }
+ else
{
- /* This makefile is a :: target with commands, but no
- dependencies. So, it will always be remade. This might
- well cause an infinite loop, so don't try to remake it.
- (This will only happen if your makefiles are written
- exceptionally stupidly; but if you work for Athena, that's
- how you write your makefiles.) */
-
DB (DB_VERBOSE,
(_("Makefile '%s' might loop; not remaking it.\n"),
f->name));
else
read_files = d->next;
- /* Free the storage. */
- free_goaldep (d);
+ if (d->error && ! (d->flags & RM_DONTCARE))
+ {
+ /* This file won't be rebuilt, was not found, and we care,
+ so remember it to report later. */
+ d->next = skipped_makefiles;
+ skipped_makefiles = d;
+ any_failed = 1;
+ }
+ else
+ free_goaldep (d);
d = last ? last->next : read_files;
}
- else
- {
- makefile_mtimes[mm_idx++] = file_mtime_no_search (d->file);
- last = d;
- d = d->next;
- }
}
}
db_level = orig_db_level;
}
+ /* Report errors for makefiles that needed to be remade but were not. */
+ while (skipped_makefiles != NULL)
+ {
+ struct goaldep *d = skipped_makefiles;
+ const char *err = strerror (d->error);
+
+ OSS (error, &d->floc, _("%s: %s"), dep_name (d), err);
+
+ skipped_makefiles = skipped_makefiles->next;
+ free_goaldep (d);
+ }
+
+ /* If we couldn't build something we need but otherwise we succeeded,
+ reset the status. */
+ if (any_failed && status == us_success)
+ status = us_none;
+
switch (status)
{
case us_question:
/* No makefiles needed to be updated. If we couldn't read some
included file that we care about, fail. */
{
- int any_failed = 0;
struct goaldep *d;
for (d = read_files; d != 0; d = d->next)
if (d->error && ! (d->flags & RM_DONTCARE))
{
/* This makefile couldn't be loaded, and we care. */
- OSS (error, &d->floc,
- _("%s: %s"), dep_name (d), strerror (d->error));
+ const char *err = strerror (d->error);
+ OSS (error, &d->floc, _("%s: %s"), dep_name (d), err);
any_failed = 1;
}
-
- if (any_failed)
- die (MAKE_FAILURE);
break;
}
{
/* Nonzero if any makefile was successfully remade. */
int any_remade = 0;
- /* Nonzero if any makefile we care about failed
- in updating or could not be found at all. */
- int any_failed = 0;
unsigned int i;
struct goaldep *d;
{
/* This makefile was updated. */
if (d->file->update_status == us_success)
- {
- /* It was successfully updated. */
- any_remade |= (file_mtime_no_search (d->file)
- != makefile_mtimes[i]);
- }
+ /* It was successfully updated. */
+ any_remade |= (file_mtime_no_search (d->file)
+ != makefile_mtimes[i]);
else if (! (d->flags & RM_DONTCARE))
{
FILE_TIMESTAMP mtime;
makefile_status = MAKE_FAILURE;
}
}
- else
- /* This makefile was not found at all. */
- if (! (d->flags & RM_DONTCARE))
- {
- const char *dnm = dep_name (d);
- size_t l = strlen (dnm);
-
- /* This is a makefile we care about. See how much. */
- if (d->flags & RM_INCLUDED)
- /* An included makefile. We don't need to die, but we
- do want to complain. */
- error (&d->floc, l,
- _("Included makefile '%s' was not found."), dnm);
- else
- {
- /* A normal makefile. We must die later. */
- error (NILF, l,
- _("Makefile '%s' was not found"), dnm);
- any_failed = 1;
- }
- }
+
+ /* This makefile was not found at all. */
+ else if (! (d->flags & RM_DONTCARE))
+ {
+ const char *dnm = dep_name (d);
+
+ /* This is a makefile we care about. See how much. */
+ if (d->flags & RM_INCLUDED)
+ /* An included makefile. We don't need to die, but we
+ do want to complain. */
+ OS (error, &d->floc,
+ _("Included makefile '%s' was not found."), dnm);
+ else
+ {
+ /* A normal makefile. We must die later. */
+ OS (error, NILF, _("Makefile '%s' was not found"), dnm);
+ any_failed = 1;
+ }
+ }
}
if (any_remade)
goto re_exec;
- if (any_failed)
- die (MAKE_FAILURE);
+
break;
}
free (aargv);
break;
}
+
+ if (any_failed)
+ die (MAKE_FAILURE);
}
/* Set up 'MAKEFLAGS' again for the normal targets. */
| (set_default ? 0 : RM_NO_DEFAULT_GOAL));
struct goaldep *d = eval_makefile (files->name, flags);
-
- if (errno)
- d->floc = *fstart;
+ d->floc = *fstart;
free_ns (files);
files = next;
'', "#MAKEFILE#:3: inc1: $ERR_no_such_file\n#MAKE#: *** No rule to make target 'foo', needed by 'inc1'. Stop.\n", 512);
rmfiles('inc1');
+
+ # Check that included double-colon targets with no prerequisites aren't
+ # built. This should fail as hello.mk doesn't exist
+
+ run_make_test(q!
+.PHONY: default
+default:;@echo 'FOO=$(FOO)'
+include hello.mk
+hello.mk:: ; echo 'FOO=bar' > $@
+!,
+ '', "#MAKEFILE#:4: hello.mk: $ERR_no_such_file", 512);
+
+ # Check that included phony targets aren't built.
+ # This should fail as hello.mk doesn't exist
+
+ run_make_test(q!
+.PHONY: default
+default:;@echo 'FOO=$(FOO)'
+include hello.mk
+hello.mk: ; echo 'FOO=bar' > $@
+.PHONY: hello.mk
+!,
+ '', "#MAKEFILE#:4: hello.mk: $ERR_no_such_file", 512);
}
if (defined $ERR_unreadable_file) {
# https://savannah.gnu.org/bugs/?57676
run_make_test(q!
+default:; @echo $(hello)
-include hello.mk
$(shell echo hello=world >hello.mk)
include hello.mk
-default:; @echo $(hello)
!,
'', "world\n");
unlink('hello.mk');
+# Check that included double-colon targets with no prerequisites aren't built.
+# This should succeed since hello.mk already exists
+
+touch('hello.mk');
+
+run_make_test(q!
+.PHONY: default
+default:;@echo 'FOO=$(FOO)'
+include hello.mk
+hello.mk:: ; echo 'FOO=bar' > $@
+!,
+ '', 'FOO=');
+
+unlink('hello.mk');
+
+# Check that included double-colon targets with no prerequisites aren't built.
+# This should succeed due to -include
+
+run_make_test(q!
+.PHONY: default
+default:;@echo 'FOO=$(FOO)'
+-include hello.mk
+hello.mk:: ; echo 'FOO=bar' > $@
+!,
+ '', 'FOO=');
+
+# Check that phony targets aren't built.
+# This should succeed since hello.mk already exists
+
+touch('hello.mk');
+
+run_make_test(q!
+.PHONY: default
+default:;@echo 'FOO=$(FOO)'
+include hello.mk
+hello.mk: ; echo 'FOO=bar' > $@
+.PHONY: hello.mk
+!,
+ '', 'FOO=');
+
+unlink('hello.mk');
+
+# Check that included double-colon targets with no prerequisites aren't built.
+# This should succeed due to -include
+
+run_make_test(q!
+.PHONY: default
+default:;@echo 'FOO=$(FOO)'
+-include hello.mk
+hello.mk: ; echo 'FOO=bar' > $@
+.PHONY: hello.mk
+!,
+ '', 'FOO=');
+
# Check the default makefiles... this requires us to invoke make with no
# arguments. Also check MAKEFILES