eMake differs from other Make variants in the way it searches for files needed by pattern rules (also called suffix or implicit rules) in a build.
At the beginning of each Make instance, eMake searches for matching files for all pattern rules before it runs any commands. After eMake has rules for every target that needs updating, it schedules the rules (creating jobs) and then runs those jobs in parallel for maximum concurrency.
Microsoft NMAKE and GNU Make match pattern rules as they run commands , interleaving execution and pattern search.
Because of the difference in the way eMake and NMAKE match pattern rules, NMAKE and eMake can produce different makefile output with hidden targets . A hidden target (also known as a “hidden dependency”) is a file that is:
created as an undeclared side-effect of updating another target
required by a pattern to build a rule
Consider the following makefile example:
all: bar.lib foo.obj
bar.lib: touch bar.lib foo.c
.c.obj: touch $@
foo.c is created as a side effect of updating the
bar.lib target. Until
bar.lib is updated, no rule is available to update
foo.obj because nothing matches the
.c.obj suffix rule.
NMAKE accepts this construct because it checks for
foo.c existence before it attempts to update
foo.obj. NMAKE produces the following result for this makefile:
touch bar.lib foo.c touch foo.obj
eMake, however, performs the search for files that match the suffix rule once so it can schedule all jobs immediately and maximize concurrency. eMake will not notice the existence of
foo.c by the time it attempts to update
foo.obj, even if
foo.c was created. eMake fails with:
NMAKE : fatal error U1073: don't know how to make 'foo.obj'Stop.
The fix is simply to identify
foo.c as a product for updating the
bar.lib target, so it is no longer a hidden target. For the example above, adding a line such as
foo.c: bar.lib is sufficient for eMake to understand that
.c.obj suffix rule matches the
foo.obj target if
bar.lib is built first. Adding this line is more accurate and has no effect on NMAKE.
GNU Make is similarly incompatible with eMake, but the incompatibility is sometimes masked by the GNU Make directory cache. GNU Make attempts to cache the directory contents on first access to improve performance. Unfortunately, because the time of first directory access can vary widely depending on which targets reference the directory and when they execute, GNU Make can appear to fail or succeed randomly in the presence of hidden targets.
For example, in this makefile, the file
$(DIR)/foo.yy is a hidden target created as a side-effect of updating
aa and needed by the pattern rule for
all: aa bb aa: touch $(DIR)/foo.yybb: foo.xx
Depending on the value of
DIR, this build might or might not work with GNU Make:
% mkdir sub; gmake DIR=sub touch sub/foo.yy foo.xx
% gmake DIR=. touch ./foo.yy gmake: *** No rule to make target ’foo.xx’, needed by ’bb’. Stop.
eMake does not attempt to emulate this behavior. Instead, it consistently refuses to schedule
foo.xx because it depends on a hidden target (just as it did in the NMAKE emulation mode in the earlier example). In this case, adding a single line declaring the target:
$(DIR)/foo.yy: aa is sufficient to ensure it always matches the
%.xx pattern rule.
If a build completes successfully with Microsoft NMAKE or GNU Make, but fails with “don’t know how to make |
There are many other reasons why hidden targets are problematic for all Make-based systems and why eliminating them is good practice in general. For more information, see:
“Paul’s Rules of Makefiles” by Paul Smith at https://make.mad-scientist.net/papers/rules-of-makefiles/. Among other useful guidelines for writing makefiles, the primary author of GNU Make writes, “Every non-
.PHONYrule must update a file with the exact name of its target. […] That way you and GNU Make always agree.”
“The Trouble with Hidden Targets” by John Graham-Cumming at https://www.cmcrossroads.com/article/trouble-hidden-targets.
|In a limited number of cases, eMake might conclude that a matching pattern rule for an output target does not exist. This occurs because eMake’s strict string equality matching for prerequisites determines that the prerequisites are different (even though the paths refer to the same file) and that there is no rule to build it.|