Merge lp:~wence/fluidity/make-fixes into lp:fluidity

Proposed by Lawrence Mitchell
Status: Merged
Merged at revision: 3509
Proposed branch: lp:~wence/fluidity/make-fixes
Merge into: lp:fluidity
Diff against target: 286 lines (+102/-35)
5 files modified
Makefile.in (+7/-6)
libwm/Makefile.in (+2/-1)
preprocessor/Makefile.in (+6/-6)
scripts/make_check_options (+44/-11)
scripts/make_register_diagnostics (+43/-11)
To merge this branch: bzr merge lp:~wence/fluidity/make-fixes
Reviewer Review Type Date Requested Status
Tim Bond (community) Approve
Review via email: mp+65996@code.launchpad.net

This proposal supersedes a proposal from 2011-05-13.

Commit message

Reduce spurious recompilations in clean or mostly-clean trees

Avoid touching lib/libfluidity.a unless actually necessary. This comes in a few parts:

o Use bzr revision-info to set up Fluidity's version number (and only update confdefs.h if the version changed)
o Calculate hashes of the check_options and register_diagnostics files and only update them if the hash changes
o Avoid depending on .PHONY targets that just point directly at actual targets

Description of the change

I think I've addressed all of the comments (mostly mine) below, and copyright assignments are in, so I think this is good to go. Here's the original description:

Various small fixes to decrease build times in mostly built trees.

The main effect of the change is to avoid touching lib/libfluidity.a as much as possible. Timing example, in a just-built tree:

before:
$ time make all
real 0m40.936s
user 0m19.053s
sys 0m21.117s

after:
$ time make all
real 0m11.736s
user 0m7.471s
sys 0m4.223s

As a bonus, the version-number fluidity reports is now meaningful again.

To post a comment you must log in.
Revision history for this message
Patrick Farrell (pefarrell) wrote : Posted in a previous version of this proposal

Hi Lawrence,

Looks like great work. I've always found this business of the build being slow quite annoying.

I tried it out, and it changed the time from 9s to 7s on my workstation. Quick question: there is a noticeable delay at the start of the build now, as it calls bzr version-info; it appears like it isn't doing anything for a second or two. Is there any way to speed up the bzr version-info, or is that just how long it takes, and there's nothing we can do? With your change, but without the bzr version-info, it takes just 3s to compile on my workstation.

Revision history for this message
Lawrence Mitchell (wence) wrote : Posted in a previous version of this proposal

It seems like the custom version-info format increases the time spent significantly:

fluidity-trunk $ time bzr version-info >/dev/null

real 0m0.293s
user 0m0.138s
sys 0m0.150s

fluidity-trunk $ time bzr version-info --custom --template="{revno} ({revision_id})" >/dev/null

real 0m0.865s
user 0m0.522s
sys 0m0.338s

If I try this:
$ cat > gen-version<<\EOF
#!/bin/bash

while read f; do
    if [[ $f == revision-id:\ * ]]; then
        revid=${f#revision-id: }
    elif [[ $f == revno:\ * ]]; then
        revno=${f#revno: }
    fi
done < <(bzr version-info) && echo "$revno (revid $revid)"
EOF

fluidity-trunk $ time ./gen-version >/dev/null

real 0m0.297s
user 0m0.123s
sys 0m0.170s

But is not portable over changes in the output format.

The clean solution is to use the format-spec, the fast solution appears to be to munge the textual output by hand.

The really fast solution is to do:

VERSION=$(shell cat .bzr/branch/last-revision)

but that is /really/ ugly, so I don't want to be the one doing it.

Revision history for this message
Lawrence Mitchell (wence) wrote : Posted in a previous version of this proposal

I'm not quite happy with this entire series as is. Specifically, it breaks the sequence:

make
make clean-light
make

Since libwm is not cleaned by clean-light, the object files are left lying around. The default rule in libwm/Makefile with this patch series is:

default: $(LIB)

$(LIB): $(OBJS)
     $(AR) $(ARFLAGS) $(LIB) $(OBJS)
 cp *.h ../include/

But the make sequence is:

make debug
make libwm

debug touches $(LIB) and so the modtime for $(LIB) is newer than $(OBJS), so they're not linked in to the library. This causes an undefined symbol link error when we try to build bin/fluidity.

I see two ways of fixing this:

revert to the previous (.PHONY) rule:

default: $(OBJS)
        ...

So that the object files are always stuffed in $(LIB)

Or add libwm to the list of clean-light targets.

I prefer the latter, thoughts?

Revision history for this message
Tim Bond (timothy-bond) wrote :

Looks good, approved.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile.in'
2--- Makefile.in 2011-06-02 11:01:17 +0000
3+++ Makefile.in 2011-06-27 14:15:53 +0000
4@@ -63,7 +63,7 @@
5
6 EVAL = scripts/silenteval
7
8-VERSION = $(shell svnversion .)
9+VERSION = $(shell bzr revision-info)
10
11 ifeq (@MBA2D@,yes)
12 LIBMBA2D = lib/libmba2d.a
13@@ -143,7 +143,7 @@
14 @cd @FLFEMDEM_PATH@;$(MAKE)
15 endif
16
17-bin/@FLUIDITY@: main.o fluidity_library
18+bin/@FLUIDITY@: main.o lib/lib@FLUIDITY@.a
19 @echo "BUILD fluidity"
20 @echo " MKDIR bin"
21 @mkdir -p bin
22@@ -262,8 +262,7 @@
23 fluidity_library: lib/lib@FLUIDITY@.a
24 lib/lib@FLUIDITY@.a: $(OBJS) python_build sub_system
25 @echo "DEFINE __FLUIDITY_VERSION__"
26- @echo "#undef __FLUIDITY_VERSION__" >> include/confdefs.h
27- @echo "#define __FLUIDITY_VERSION__ \"$(VERSION)\"" >> include/confdefs.h
28+ @grep "$(VERSION)" include/confdefs.h >/dev/null 2>&1 || (echo "#undef __FLUIDITY_VERSION__" >> include/confdefs.h && echo "#define __FLUIDITY_VERSION__ \"$(VERSION)\"" >> include/confdefs.h)
29 @echo "BUILD libfluidity"
30 @echo " MKDIR lib"
31 @mkdir -p lib
32@@ -309,10 +308,10 @@
33 @cd main; $(MAKE)
34 @echo " MAKE options_check"
35 @./scripts/make_check_options
36- @cd preprocessor; $(MAKE) check_options
37+ @cd preprocessor; $(MAKE) check_options.o
38 @echo " MAKE register_diagnostics"
39 @./scripts/make_register_diagnostics
40- @cd preprocessor; $(MAKE) register_diagnostics
41+ @cd preprocessor; $(MAKE) register_diagnostics.o
42
43 fldecomp: fluidity_library
44 @echo "BUILD fldecomp"
45@@ -334,6 +333,8 @@
46 clean-light:
47 @echo " CLEAN debug"
48 @cd debug; $(MAKE) clean
49+ @echo " CLEAN libwm"
50+ @cd libwm; $(MAKE) clean
51 @echo " CLEAN femtools"
52 @cd femtools; $(MAKE) clean
53 @echo " CLEAN femtools/tests"
54
55=== modified file 'libwm/Makefile.in'
56--- libwm/Makefile.in 2011-05-27 13:44:17 +0000
57+++ libwm/Makefile.in 2011-06-27 14:15:53 +0000
58@@ -63,9 +63,10 @@
59 @echo " CXX $<"
60 $(CXX) $(CXXFLAGS) -I. -I../include -c $<
61
62-default: $(OBJS)
63+$(LIB): $(OBJS)
64 $(AR) $(ARFLAGS) $(LIB) $(OBJS)
65 cp *.h ../include/
66+default: $(LIB)
67
68 clean:
69 rm -f *.o *.mod
70
71=== modified file 'preprocessor/Makefile.in'
72--- preprocessor/Makefile.in 2011-05-19 14:29:56 +0000
73+++ preprocessor/Makefile.in 2011-06-27 14:15:53 +0000
74@@ -74,17 +74,17 @@
75 @$(AR) $(ARFLAGS) $(LIB) $(OBJS)
76 default: $(LIB)
77
78-.PHONY:check_options
79-
80 # This gets run last by the fluidity build process.
81-check_options: check_options.o
82+check_options.o: check_options.F90
83+ @echo " FC $<"
84+ $(FC) $(FCFLAGS) -c $<
85 mkdir -p ../lib
86 $(AR) $(ARFLAGS) $(LIB) check_options.o &> check.log
87
88-.PHONY:register_diagnostics
89-
90 # This gets run last by the fluidity build process.
91-register_diagnostics: register_diagnostics.o
92+register_diagnostics.o: register_diagnostics.F90
93+ @echo " FC $<"
94+ $(FC) $(FCFLAGS) -c $<
95 mkdir -p ../lib
96 $(AR) $(ARFLAGS) $(LIB) register_diagnostics.o &> register_diagnostics.log
97
98
99=== modified file 'scripts/make_check_options'
100--- scripts/make_check_options 2010-11-16 23:08:06 +0000
101+++ scripts/make_check_options 2011-06-27 14:15:53 +0000
102@@ -1,6 +1,9 @@
103 #!/usr/bin/env python
104 import re
105 import glob
106+import hashlib
107+import StringIO
108+
109
110 # File header
111 header="""
112@@ -11,9 +14,23 @@
113 end subroutine check_options
114 """
115
116-outfile=file("preprocessor/check_options.F90", "w")
117-
118-outfile.write(header)
119+outfile='preprocessor/check_options.F90'
120+
121+# get sha1 digest of existing generated file. Can't use 'rw' here
122+# because it updates the modtime of the file, which we're trying to
123+# avoid doing.
124+orig=hashlib.sha1()
125+try:
126+ f=open(outfile, 'r')
127+ orig.update(f.read())
128+except IOError:
129+ pass
130+else:
131+ f.close()
132+
133+# Now read module files to generate potential new data
134+output=StringIO.StringIO()
135+output.write(header)
136
137 # List of fortran source files.
138 fortran_files=glob.glob("*/*.F")+glob.glob("*/*.F90")
139@@ -23,7 +40,7 @@
140 module_list=[]
141
142 for filename in fortran_files:
143-
144+
145 fortran=file(filename,"r").read()
146
147 modules=module_re.findall(fortran)
148@@ -31,21 +48,37 @@
149 for module in modules:
150
151 if re.search(r"^\s*subroutine\s+"+module+"_check_options\s*$",\
152- fortran,\
153- re.IGNORECASE|re.MULTILINE):
154+ fortran,\
155+ re.IGNORECASE|re.MULTILINE):
156 module_list.append(module)
157
158 for module in module_list:
159
160- outfile.write(" use "+module+", only: "+module+"_check_options\n")
161+ output.write(" use "+module+", only: "+module+"_check_options\n")
162
163 # Ensure that the subroutine is legal in the trivial case.
164-outfile.write("""
165+output.write("""
166 continue
167 """)
168
169 for module in module_list:
170
171- outfile.write(" call "+module+"_check_options\n")
172-
173-outfile.write(footer)
174+ output.write(" call "+module+"_check_options\n")
175+
176+output.write(footer)
177+
178+new=hashlib.sha1()
179+new.update(output.getvalue())
180+
181+# Only write file if sha1sums differ
182+if new.digest() != orig.digest():
183+ try:
184+ f=open(outfile, 'w')
185+ f.write(output.getvalue())
186+ except IOError:
187+ # Fixme, this should fail better
188+ pass
189+ else:
190+ f.close()
191+
192+output.close()
193
194=== modified file 'scripts/make_register_diagnostics'
195--- scripts/make_register_diagnostics 2011-02-05 16:05:31 +0000
196+++ scripts/make_register_diagnostics 2011-06-27 14:15:53 +0000
197@@ -1,6 +1,9 @@
198 #!/usr/bin/env python
199 import re
200 import glob
201+import hashlib
202+import StringIO
203+
204
205 # File header
206 header="""
207@@ -11,9 +14,23 @@
208 end subroutine register_diagnostics
209 """
210
211-outfile=file("preprocessor/register_diagnostics.F90", "w")
212-
213-outfile.write(header)
214+outfile = 'preprocessor/register_diagnostics.F90'
215+
216+# get sha1 digest of existing generated file. Can't use 'rw' here
217+# because it updates the modtime of the file, which we're trying to
218+# avoid doing.
219+orig=hashlib.sha1()
220+try:
221+ f=open(outfile, 'r')
222+ orig.update(f.read())
223+except IOError:
224+ pass
225+else:
226+ f.close()
227+
228+# Now read module files to generate potential new data
229+output=StringIO.StringIO()
230+output.write(header)
231
232 # List of fortran source files.
233 fortran_files=glob.glob("*/*.F")+glob.glob("*/*.F90")
234@@ -23,7 +40,7 @@
235 module_list=[]
236
237 for filename in fortran_files:
238-
239+
240 fortran=file(filename,"r").read()
241
242 modules=module_re.findall(fortran)
243@@ -31,21 +48,36 @@
244 for module in modules:
245
246 if re.search(r"^\s*subroutine\s+"+module+"_register_diagnostic\s*$",\
247- fortran,\
248- re.IGNORECASE|re.MULTILINE):
249+ fortran,\
250+ re.IGNORECASE|re.MULTILINE):
251 module_list.append(module)
252
253 for module in module_list:
254
255- outfile.write(" use "+module+", only: "+module+"_register_diagnostic\n")
256+ output.write(" use "+module+", only: "+module+"_register_diagnostic\n")
257
258 # Ensure that the subroutine is legal in the trivial case.
259-outfile.write("""
260+output.write("""
261 continue
262 """)
263
264 for module in module_list:
265
266- outfile.write(" call "+module+"_register_diagnostic\n")
267-
268-outfile.write(footer)
269+ output.write(" call "+module+"_register_diagnostic\n")
270+
271+output.write(footer)
272+
273+new=hashlib.sha1()
274+new.update(output.getvalue())
275+
276+# Only write file if sha1sums differ
277+if new.digest() != orig.digest():
278+ try:
279+ f=open(outfile, 'w')
280+ f.write(output.getvalue())
281+ except IOError:
282+ # Fixme, this should fail better
283+ pass
284+ else:
285+ f.close()
286+output.close()