summaryrefslogtreecommitdiff
path: root/rules/hi-rule.mk
blob: be4a460abe7196af0fbe40cd1393f527c2c144e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# -----------------------------------------------------------------------------
#
# (c) 2009 The University of Glasgow
#
# This file is part of the GHC build system.
#
# To understand how the build system works and how to modify it, see
#      https://gitlab.haskell.org/ghc/ghc/wikis/building/architecture
#      https://gitlab.haskell.org/ghc/ghc/wikis/building/modifying
#
# -----------------------------------------------------------------------------


# Here's an interesting rule!

# 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:
#   https://gitlab.haskell.org/ghc/ghc/wikis/commentary/compiler/recompilation-avoidance
#
# 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, and is a good solution,
# except that it means spawning a shell. This can be expensive,
# especially on Windows where spawning a shell takes 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. Therefore we only use this version
# if ExtraMakefileSanityChecks is enabled.
#
# %.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.
#
# However, given that rule, make thinks that it can make .hi files
# for any object file, even if the object file was created from e.g.
# a C source file. We therefore also add a dependency on the .hs/.lhs
# source file, which means we end up with rules like:
#
# a/%.hi : a/%.o b/%.hs ;
#
# But! If a file is not explicitly mentioned in a makefile, then if
# make needs to build it using such a %-rule then it treats it as an
# 'intermediate file', and deletes it when it is finished. Most .hi
# files are mentioned in .depend* files, as some other module depends on
# them, but there are some library modules that aren't imported by
# anything in the tree.
#
# We could stop make from deleting the .hi files by declaring
# ".SECONDARY:", but if we do that then make takes a pathologically long
# time with our build system. So we now generate (by calling hi-rule
# from .depend* files) rules that look like
#
# a/B.hi a/B.dyn_hi : %hi : %o x/B.hs
#
# Now all the .hi files are explicitly mentioned in the makefiles, so
# make doesn't think they are merely intermediate files, and doesn't
# delete them.

ifeq "$(ExtraMakefileSanityChecks)" "NO"

define hi-rule # $1 = rule header
$1 ;
endef

else

define hi-rule # $1 = rule header
$1
	@if [ ! -f $$@ ] ; then \
	    echo "Panic! $$< exists, but $$@ does not."; \
	    exit 1; \
	fi

endef

endif