/[escript]/trunk/scons/scons_extensions.py
ViewVC logotype

Annotation of /trunk/scons/scons_extensions.py

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.26