scons: Add a env.CodeGenerate method to simplify code generation via python scripts.
authorJosé Fonseca <jrfonseca@tungstengraphics.com>
Thu, 3 Jul 2008 03:42:23 +0000 (12:42 +0900)
committerJosé Fonseca <jrfonseca@tungstengraphics.com>
Thu, 3 Jul 2008 06:06:24 +0000 (15:06 +0900)
env.CodeGenerate(
target = 'my_source.c',
script = 'my_generator.py',
source = ['input.txt', 'another.txt'],
command = 'python $SCRIPT $SOURCE > $TARGET'
)

It will take care generating all appropriate dependencies, including any
module imported by the generator script, and the respective .pyc file
side effects.

scons/gallium.py

index 59ba34844b78ed9972e4b38543096fec4ce590ae..bfdd2de8db3b31b80099b72dece96502f600e6b2 100644 (file)
@@ -30,10 +30,13 @@ Frontend-tool for Gallium3D architecture.
 # 
 
 
+import os
 import os.path
+import re
 
 import SCons.Action
 import SCons.Builder
+import SCons.Scanner
 
 
 def quietCommandLines(env):
@@ -70,11 +73,73 @@ def createConvenienceLibBuilder(env):
                                   src_suffix = '$SHOBJSUFFIX',
                                   src_builder = 'SharedObject')
         env['BUILDERS']['ConvenienceLibrary'] = convenience_lib
-        env['BUILDERS']['Library'] = convenience_lib
 
     return convenience_lib
 
 
+# TODO: handle import statements with multiple modules
+# TODO: handle from import statements
+import_re = re.compile(r'^import\s+(\S+)$', re.M)
+
+def python_scan(node, env, path):
+       # http://www.scons.org/doc/0.98.5/HTML/scons-user/c2781.html#AEN2789
+       contents = node.get_contents()
+       source_dir = node.get_dir()
+       imports = import_re.findall(contents)
+       results = []
+       for imp in imports:
+               for dir in path:
+                       file = os.path.join(str(dir), imp.replace('.', os.sep) + '.py')
+                       if os.path.exists(file):  
+                               results.append(env.File(file))
+                               break
+                       file = os.path.join(str(dir), imp.replace('.', os.sep), '__init__.py')
+                       if os.path.exists(file):  
+                               results.append(env.File(file))
+                               break
+       return results
+
+python_scanner = SCons.Scanner.Scanner(function = python_scan, skeys = ['.py'])
+
+
+def code_generate(env, script, target, source, command):
+       """Method to simplify code generation via python scripts.
+       
+       http://www.scons.org/wiki/UsingCodeGenerators
+       http://www.scons.org/doc/0.98.5/HTML/scons-user/c2768.html
+       """
+       
+       # We're generating code using Python scripts, so we have to be 
+       # careful with our scons elements.  This entry represents
+       # the generator file *in the source directory*.
+       script_src = env.File(script).srcnode()
+       
+       # This command creates generated code *in the build directory*.
+       command = command.replace('$SCRIPT', script_src.path)
+       code = env.Command(target, source, command)
+
+       # Explicitly mark that the generated code depends on the generator,
+       # and on implicitly imported python modules
+       path = (script_src.get_dir(),)
+       deps = [script_src]
+       deps += script_src.get_implicit_deps(env, python_scanner, path)
+       env.Depends(code, deps)
+       
+       # Running the Python script causes .pyc files to be generated in the
+       # source directory.  When we clean up, they should go too. So add side 
+       # effects for .pyc files
+       for dep in deps:
+               pyc = env.File(str(dep) + 'c')
+               env.SideEffect(pyc, code)
+               
+       return code
+
+
+def createCodeGenerateMethod(env):
+       env.Append(SCANNERS = python_scanner)
+       env.AddMethod(code_generate, 'CodeGenerate')
+
+
 def generate(env):
        """Common environment generation code"""
        
@@ -336,9 +401,10 @@ def generate(env):
                        ]
        env.Append(LINKFLAGS = linkflags)
 
-       # Convenience Library Builder
+       # Custom builders and methods
        createConvenienceLibBuilder(env)
-       
+       createCodeGenerateMethod(env)
+
        # for debugging
        #print env.Dump()