Optimise the %.hi : %.o rule
authorSimon Marlow <marlowsd@gmail.com>
Tue, 23 Jun 2009 12:49:01 +0000 (12:49 +0000)
committerSimon Marlow <marlowsd@gmail.com>
Tue, 23 Jun 2009 12:49:01 +0000 (12:49 +0000)
Previously this rule had a sanity check for the existence of the .o
file.  However, the sanity check is expensive, especially on Windows,
because it requires spawning a shell.  So now we use an empty command
here.

This change reduced the time to do 'make' in an up-to-date tree on
Windows from 33s to 16s for me.  (the actual saving depends on how
much rebuilding you've been doing, and how many .hi files are older
than their .o files).

The comments in this file now describe various versions of the rule
that don't work.

rules/hi-rule.mk

index c30a093..35baffd 100644 (file)
 
 
 # Here's an interesting rule!
-# The .hi file depends on the .o file,
-# so if the .hi file is dated earlier than the .o file (commonly the case,
-# when interfaces are stable) this rule just makes sure that the .o file,
-# is up to date.  Then it does nothing to generate the .hi file from the
-# .o file, because the act of making sure the .o file is up to date also
-# updates the .hi file (if necessary).
+
+# The .hi file may or may not change when we compile the corresponding
+# .hs file.  If GHC figures out that the .hi file has not changed, it
+# doesn't touch it.  This is a useful optimisation, because it means
+# some modules may not get recompiled if the .hi files of the modules
+# they depend on have not changed.
+#
+# See:
+#   http://hackage.haskell.org/trac/ghc/wiki/Commentary/Compiler/RecompilationAvoidance
+#
+# So how do we express this dependency to make?  The exact form of
+# this rule is quite fragile.  Here are some versions that don't work
+# very well:
+#
+# %.hi : %.o
+#      @if [ ! -f $@ ] ; then \
+#          echo Panic! $< exists, but $@ does not.; \
+#          exit 1; \
+#      fi
+#
+# This version adds a useful sanity check; but it is also expensive on
+# Windows where spawning a shell takes a while (about 0.3s).  We'd
+# like to avoid the shell if necessary.  This also hides the message
+# "nothing to be done for 'all'", since make thinks it has actually done
+# something.
+#
+# %.hi : %.o
+#
+# This version doesn't work: GNU make knows it has't done anything to
+# update the .hi file, so even if the .o file has been updated, it
+# won't rebuild anything that depends on the .hi file.  So you might
+# think a more correct way is to change the .hs rule:
+#
+# %.hi %.o : %.hs
+#      $(HC) ...
+#
+# this says "compiling %.hs updates both %.hi and %.o", but that's not
+# true, since compiling the .hs file might not update the .hi file, if
+# the .hi file didn't change.  And if we use this version, then make
+# will keep trying to rebuild %.hi if it is out of date with respect
+# to %.hs.
+#
+# Using this form seems to be the best compromise:
+#
+# %.hi : %.o ;
+#
+# the ';' at the end signifies an "empty command" (see the GNU make
+# documentation).  An empty command is enough to get GNU make to think
+# it has updated %.hi, but without actually spawning a shell to do so.
 
 define hi-rule # $1 = way
-%.$$($1_hisuf) : %.$$($1_osuf)
-       @if [ ! -f $$@ ] ; then \
-           echo Panic! $$< exists, but $$@ does not.; \
-           exit 1; \
-       fi
-
-%.$$($1_way_)hi-boot : %.$$($1_way_)o-boot
-       @if [ ! -f $$@ ] ; then \
-           echo Panic! $$< exists, but $$@ does not.; \
-           exit 1; \
-       fi
+
+%.$$($1_hisuf) : %.$$($1_osuf) ;
+
+%.$$($1_way_)hi-boot : %.$$($1_way_)o-boot ;
+
 endef