1 |
|
2 |
|
3 |
# Extensions to Scons |
4 |
|
5 |
import py_compile |
6 |
import sys |
7 |
import os |
8 |
import time |
9 |
import glob |
10 |
import fnmatch |
11 |
import types |
12 |
|
13 |
from SCons.Script.SConscript import SConsEnvironment |
14 |
|
15 |
############################################################################### |
16 |
def matchingFiles(env,directory,includes='*',excludes=None) : |
17 |
|
18 |
# Pre-process for more convenient arguments |
19 |
|
20 |
if isinstance(includes,str) : |
21 |
includes = env.Split(includes) |
22 |
|
23 |
if isinstance(excludes,str) : |
24 |
excludes = env.Split(excludes) |
25 |
|
26 |
def fn_filter(node): |
27 |
fn = os.path.basename(str(node)) |
28 |
match = False |
29 |
for include in includes: |
30 |
if fnmatch.fnmatchcase( fn, include ): |
31 |
match = True |
32 |
break |
33 |
|
34 |
if match and not excludes is None: |
35 |
for exclude in excludes: |
36 |
if fnmatch.fnmatchcase( fn, exclude ): |
37 |
match = False |
38 |
break |
39 |
|
40 |
return match |
41 |
|
42 |
def filter_nodes(where): |
43 |
contents = glob.glob( os.path.join(str(where),"*") ) |
44 |
children = [ x for x in contents if fn_filter(x) ] |
45 |
nodes = [] |
46 |
for f in children: |
47 |
nodes.append(gen_node(f)) |
48 |
return nodes |
49 |
|
50 |
def gen_node(n): |
51 |
"""Checks first to see if the node is a file or a dir, then |
52 |
creates the appropriate node. [code seems redundant, if the node |
53 |
is a node, then shouldn't it just be left as is? |
54 |
""" |
55 |
if type(n) in (type(''), type(u'')): |
56 |
path = n |
57 |
else: |
58 |
path = n.abspath |
59 |
|
60 |
if os.path.isdir(path): |
61 |
return env.Dir(n) |
62 |
|
63 |
return env.File(n) |
64 |
|
65 |
here = env.Dir(directory) |
66 |
nodes = filter_nodes(here) |
67 |
|
68 |
node_srcs = [n.srcnode() for n in nodes] |
69 |
|
70 |
src = here.srcnode() |
71 |
if src is not here: |
72 |
for s in filter_nodes(src): |
73 |
if s not in node_srcs: |
74 |
# Probably need to check if this node is a directory |
75 |
nodes.append( |
76 |
gen_node(os.path.join(str(directory), |
77 |
os.path.basename(str(s))))) |
78 |
|
79 |
return nodes |
80 |
#============================================================================== |
81 |
SConsEnvironment.matchingFiles = matchingFiles |
82 |
############################################################################### |
83 |
|
84 |
|
85 |
|
86 |
############################################################################### |
87 |
def InstallPyModule(env,target_dir,source,shlib=None,excludes=None) : |
88 |
|
89 |
# put the .so/.dll over in <target_dir> |
90 |
if shlib : |
91 |
shtarg = env.Install(target_dir, shlib) |
92 |
|
93 |
# Gather the python sources |
94 |
python_src = env.matchingFiles(source, "*.py",excludes=excludes) |
95 |
|
96 |
# Here is a hack to deal with the (possibly not yet generated) |
97 |
# __init__.py. The problem is that the python_src list is built before |
98 |
# __init__.py is updated from __init__.in. The AlwaysBuild call |
99 |
# ensures that this does not cause a problem, except on the first |
100 |
# build after a clean checkout, in which case there is no old |
101 |
# __init__.py in src, and hence it does not make it into python_src! |
102 |
|
103 |
init_input = env.matchingFiles(source, "__init__.in") |
104 |
if init_input : |
105 |
if python_src : |
106 |
names = [x.name for x in python_src] |
107 |
if "__init__.py" not in names : |
108 |
python_src.append(env.File(os.path.join(str(source), |
109 |
"__init__.py"))) |
110 |
else: |
111 |
python_src = [env.File(os.path.join(str(source),"__init__.py"))] |
112 |
|
113 |
# decide if we're doing py or pyc distribn and install. |
114 |
|
115 |
if env['distrib_py_src'] : |
116 |
pytarg = env.Install(target_dir, python_src) |
117 |
else: |
118 |
pyc = env.PyCompile(python_src) |
119 |
pytarg = env.Install(target_dir, pyc) |
120 |
|
121 |
if shlib : |
122 |
targ_ret = env.Flatten([pytarg] + [shtarg]) |
123 |
else: |
124 |
targ_ret = pytarg |
125 |
|
126 |
return targ_ret |
127 |
#============================================================================== |
128 |
SConsEnvironment.InstallPyModule = InstallPyModule |
129 |
############################################################################### |
130 |
|
131 |
############################################################################### |
132 |
def installDirectory(env,target,source,includes="*", excludes=None, |
133 |
recursive=False): |
134 |
|
135 |
|
136 |
if os.path.isfile(str(target)) : |
137 |
raise UserError("target must be a directory") |
138 |
|
139 |
if os.path.isfile(str(source)) : |
140 |
raise UserError("source must be a directory") |
141 |
|
142 |
source_files = env.matchingFiles(source,includes,excludes) |
143 |
|
144 |
ret_targ = [] |
145 |
|
146 |
for f in source_files : |
147 |
if f.isfile() : |
148 |
targ = env.Install(target,f) |
149 |
ret_targ.append(targ) |
150 |
|
151 |
if f.isdir() and recursive : |
152 |
x = os.path.basename(str(f)) |
153 |
t = env.Dir(os.path.join(str(target),x)) |
154 |
targ = env.installDirectory(t,f,includes,excludes,recursive) |
155 |
ret_targ += targ |
156 |
|
157 |
return ret_targ |
158 |
#============================================================================== |
159 |
SConsEnvironment.installDirectory = installDirectory |
160 |
############################################################################### |
161 |
|
162 |
############################################################################### |
163 |
# Code to build .pyc from .py |
164 |
def build_py(target, source, env): |
165 |
py_compile.compile(str(source[0]), str(target[0])) |
166 |
return 0 |
167 |
|
168 |
def doSubstitution(target,source,env) : |
169 |
import product_info as PI |
170 |
data = source[0].get_contents() |
171 |
data = data.replace('$ProductName$',PI.PRODUCT_NAME) |
172 |
data = data.replace('$LowerProductName$',PI.product_name) |
173 |
data = data.replace('$ProductVersion$',PI.product_version) |
174 |
data = data.replace('$VersionString$',PI.pkg_version_string) |
175 |
data = data.replace('$SVNRevision$',PI.svn_revision) |
176 |
open(str(target[0]),'w').write(data) |
177 |
return 0 |
178 |
|
179 |
# Code to run unit_test executables |
180 |
def runUnitTest(target, source, env): |
181 |
time_start = time.time() |
182 |
app = str(source[0].abspath) |
183 |
if env['useMPI']: app = env['mpi_run'] + ' ' + app |
184 |
print "Executing test: " + app |
185 |
if not env.Execute(app): |
186 |
open(str(target[0]),'w').write("PASSED\n") |
187 |
else: |
188 |
return 1 |
189 |
print "Test execution time: ", round(time.time() - time_start, 1), " seconds wall time for " + str(source[0].abspath) |
190 |
return None |
191 |
|
192 |
def runPyUnitTest(target, source, env): |
193 |
time_start = time.time() |
194 |
app = str(source[0].abspath) |
195 |
if env['useMPI']: |
196 |
app = env['mpi_run'] +' lib/pythonMPI ' + app |
197 |
else: |
198 |
app = sys.executable + " " + app |
199 |
print "Executing test: " + app |
200 |
if env.Execute(app) == 0: |
201 |
open(str(target[0]),'w').write("PASSED\n") |
202 |
else: |
203 |
return 1 |
204 |
print "Test execution time: ", round(time.time() - time_start, 1), " seconds wall time for " + str(source[0].abspath) |
205 |
return None |
206 |
|
207 |
def addBuilders(env) : |
208 |
py_builder = env.Builder(action = build_py, |
209 |
suffix = '.pyc', |
210 |
src_suffix = '.py', |
211 |
single_source=True) |
212 |
|
213 |
env.Append(BUILDERS = {'PyCompile' : py_builder}); |
214 |
|
215 |
substituter = env.Builder(action = doSubstitution, |
216 |
suffix = '', |
217 |
src_suffix = '.in', |
218 |
single_source=True ) |
219 |
|
220 |
env.Append(BUILDERS = {'VariableSubstitution' : substituter}); |
221 |
|
222 |
runUnitTest_builder = env.Builder(action = runUnitTest, |
223 |
suffix = '.passed', |
224 |
src_suffix=env.get('PROGSUFFIX',''), |
225 |
single_source=True) |
226 |
|
227 |
env.Append(BUILDERS = {'RunUnitTest' : runUnitTest_builder}); |
228 |
|
229 |
runPyUnitTest_builder = env.Builder(action = runPyUnitTest, |
230 |
suffix = '.passed', |
231 |
src_suffix='.py', |
232 |
single_source=True) |
233 |
|
234 |
env.Append(BUILDERS = {'RunPyUnitTest' : runPyUnitTest_builder}); |
235 |
return |
236 |
#============================================================================== |
237 |
SConsEnvironment.addBuilders = addBuilders |
238 |
############################################################################### |
239 |
|
240 |
############################################################################### |
241 |
def epydocAction(target, source, env): |
242 |
|
243 |
doc_dir = os.path.dirname(str(target[0].abspath)) |
244 |
|
245 |
cmd = [ |
246 |
[env['epydoc_path'], "-qqqq", "-o", doc_dir, str(source[0].tpath)] |
247 |
] |
248 |
print 'executing epydoc...' |
249 |
return env.Execute(cmd,"Build epydoc documentation") |
250 |
|
251 |
|
252 |
def epydocDepend(env, target, source, |
253 |
src_pattern="*.py", |
254 |
file_names=['index.html','epydoc.css'], |
255 |
subdirs=['private','public']) : |
256 |
""" |
257 |
\brief add a dependency between the directory containing the doco and that |
258 |
containing the source |
259 |
\param target - the directory containing index.html for the doco, |
260 |
and the private and public sub-directories. |
261 |
\param source - the python module source directory |
262 |
""" |
263 |
the_subdirs = [os.path.join(target.abspath,sub) for sub in subdirs] |
264 |
the_targets = [os.path.join(target.abspath,file) for file in file_names] |
265 |
|
266 |
|
267 |
ret_target = target |
268 |
|
269 |
# if absolutely anything goes wrong, turn this on. |
270 |
force_build = False |
271 |
|
272 |
dst_time = 0 |
273 |
src_time = 1 |
274 |
|
275 |
try: |
276 |
|
277 |
# have a shot at digging out all the source file times. |
278 |
src_time = max( |
279 |
[os.path.getmtime(str(x)) |
280 |
for x in env.matchingFiles(source,src_pattern) + |
281 |
[source.abspath]] |
282 |
) |
283 |
|
284 |
# now try to find the target files and their mod time. |
285 |
a = [os.path.getmtime(os.path.join(target.abspath,str(x))) |
286 |
for x in file_names ] |
287 |
|
288 |
for directory in the_subdirs : |
289 |
# include the mod time of the directory |
290 |
a += [os.path.getmtime(str(directory))] |
291 |
|
292 |
# now go for the mod times of all files below the subdirs. |
293 |
if os.path.isdir(str(directory)) : |
294 |
a += [os.path.getmtime(str(x)) |
295 |
for x in env.matchingFiles(directory,"*")] |
296 |
|
297 |
else: |
298 |
# if it is not a directory, and we expected it to be |
299 |
# do something nasty. |
300 |
# we're in a try anyway. |
301 |
force_build = True |
302 |
os.unlink(directory) |
303 |
|
304 |
dst_time = max(a) |
305 |
|
306 |
except: |
307 |
# Force an unlink and re-build. |
308 |
force_build = True |
309 |
|
310 |
if src_time > dst_time or force_build : |
311 |
for x in the_targets : |
312 |
try: |
313 |
os.unlink(x) |
314 |
except OSError: |
315 |
pass |
316 |
|
317 |
ret_target = env.Command(the_targets,source,epydocAction) |
318 |
|
319 |
|
320 |
env.Clean(target, the_subdirs + file_names) |
321 |
|
322 |
return ret_target |
323 |
#============================================================================== |
324 |
SConsEnvironment.epydocDepend = epydocDepend |
325 |
############################################################################### |
326 |
|
327 |
|
328 |
############################################################################### |
329 |
def genSConscriptCalls(env,dir_list): |
330 |
|
331 |
for d in dir_list : |
332 |
print 'calling SConscript in "./%s"' %(d) |
333 |
env.SConscript(dirs = [d], |
334 |
build_dir='build/$PLATFORM/' + str(d), |
335 |
duplicate=0) |
336 |
|
337 |
|
338 |
return |
339 |
#============================================================================== |
340 |
SConsEnvironment.genSConscriptCalls = genSConscriptCalls |
341 |
############################################################################### |
342 |
|
343 |
|
344 |
############################################################################### |
345 |
def print_all_nodes(env,dirnode, level=0): |
346 |
"""Print all the scons nodes that are children of this node, recursively.""" |
347 |
if type(dirnode)==type(''): |
348 |
dirnode=env.Dir(dirnode) |
349 |
dt = type(env.Dir('.')) |
350 |
for f in dirnode.all_children(): |
351 |
if type(f) == dt: |
352 |
print "%s%s: .............."%(level * ' ', str(f)) |
353 |
env.print_all_nodes(f, level+2) |
354 |
print "%s%s"%(level * ' ', str(f)) |
355 |
|
356 |
#============================================================================== |
357 |
SConsEnvironment.print_all_nodes = print_all_nodes |
358 |
############################################################################### |
359 |
|
360 |
|
361 |
|
362 |
|
363 |
|
364 |
|
365 |
|
366 |
|
367 |
|
368 |
|
369 |
|
370 |
|
371 |
|
372 |
|
373 |
|
374 |
|
375 |
|
376 |
|
377 |
|
378 |
|
379 |
|
380 |
############################################################################### |
381 |
def Glob(env,dir='.',includes="*",excludes=None, scan_dir=True): |
382 |
|
383 |
"""Similar to glob.glob, except globs SCons nodes, and thus sees |
384 |
generated files and files from build directories. Basically, it sees |
385 |
anything SCons knows about. A key subtlety is that since this function |
386 |
operates on generated nodes as well as source nodes on the filesystem, |
387 |
it needs to be called after builders that generate files you want to |
388 |
include. |
389 |
|
390 |
It will return both Dir entries and File entries |
391 |
""" |
392 |
|
393 |
# Pre-process for more convenient arguments |
394 |
|
395 |
if isinstance(includes,str) : |
396 |
includes = env.Split(includes) |
397 |
|
398 |
if isinstance(excludes,str) : |
399 |
excludes = env.Split(excludes) |
400 |
|
401 |
def fn_filter(node): |
402 |
fn = os.path.basename(str(node)) |
403 |
match = 0 |
404 |
for include in includes: |
405 |
if fnmatch.fnmatchcase( fn, include ): |
406 |
match = 1 |
407 |
break |
408 |
|
409 |
if match == 1 and not excludes is None: |
410 |
for exclude in excludes: |
411 |
if fnmatch.fnmatchcase( fn, exclude ): |
412 |
match = 0 |
413 |
break |
414 |
|
415 |
return match |
416 |
|
417 |
def filter_nodes(where): |
418 |
contents = where.all_children(scan=scan_dir) |
419 |
children = [ x for x in contents if fn_filter(x) ] |
420 |
nodes = [] |
421 |
for f in children: |
422 |
nodes.append(gen_node(f)) |
423 |
return nodes |
424 |
|
425 |
def gen_node(n): |
426 |
"""Checks first to see if the node is a file or a dir, then |
427 |
creates the appropriate node. [code seems redundant, if the node |
428 |
is a node, then shouldn't it just be left as is? |
429 |
""" |
430 |
if type(n) in (type(''), type(u'')): |
431 |
path = n |
432 |
else: |
433 |
path = n.abspath |
434 |
if os.path.isdir(path): |
435 |
return env.Dir(n) |
436 |
else: |
437 |
return env.File(n) |
438 |
|
439 |
here = env.Dir(dir) |
440 |
nodes = filter_nodes(here) |
441 |
|
442 |
node_srcs = [n.srcnode() for n in nodes] |
443 |
|
444 |
src = here.srcnode() |
445 |
if src is not here: |
446 |
for s in filter_nodes(src): |
447 |
if s not in node_srcs: |
448 |
# Probably need to check if this node is a directory |
449 |
nodes.append( |
450 |
gen_node(os.path.join(dir,os.path.basename(str(s))))) |
451 |
|
452 |
return nodes |