env.subst(self.prefix),
env.subst(self.suffix)),
self.node_factory)
+
slist = scons_str2nodes(adjustixes(source, None,
env.subst(self.src_suffix)),
self.node_factory)
+
for t in tlist:
t.builder_set(self)
t.env_set(env)
t.add_source(slist)
+ for s in slist:
+ s.env_set(env, 1)
+
if len(tlist) == 1:
tlist = tlist[0]
return tlist
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+# Define a null function for use as a builder action.
+# Where this is defined in the file seems to affect its
+# byte-code contents, so try to minimize changes by
+# defining it here, before we even import anything.
+def Func():
+ pass
+
import sys
import unittest
return self.name
def builder_set(self, builder):
self.builder = builder
- def env_set(self, env):
+ def env_set(self, env, safe=0):
self.env = env
def add_source(self, source):
self.sources.extend(source)
assert n1.env == env
assert n1.builder == builder
assert n1.sources == [n2]
+ assert n2.env == env
def test_action(self):
"""Test Builder creation
contents = b1.get_contents()
assert contents == "foo", contents
- def func():
- pass
-
- b2 = SCons.Builder.Builder(action = func)
+ b2 = SCons.Builder.Builder(action = Func)
contents = b2.get_contents()
- assert contents == "\177\340\0\177\341\0d\0\0S", contents
+ assert contents == "\177\036\000\177\037\000d\000\000S", repr(contents)
- b3 = SCons.Builder.Builder(action = ["foo", func, "bar"])
+ b3 = SCons.Builder.Builder(action = ["foo", Func, "bar"])
contents = b3.get_contents()
- assert contents == "foo\177\340\0\177\341\0d\0\0Sbar", contents
+ assert contents == "foo\177\036\000\177\037\000d\000\000Sbar", repr(contents)
def test_name(self):
"""Test Builder creation with a specified name
import os
import SCons.Builder
-
+import SCons.Scanner.C
Object = SCons.Builder.Builder(name = 'Object',
src_suffix = '$OBJSUFFIX',
src_builder = Object)
+CScan = SCons.Scanner.C.CScan()
if os.name == 'posix':
'ARFLAGS' : 'r',
'ARCOM' : '$AR $ARFLAGS $TARGET $SOURCES\nranlib $TARGET',
'BUILDERS' : [Object, Program, Library],
+ 'SCANNERS' : [CScan],
'OBJPREFIX' : '',
'OBJSUFFIX' : '.o',
'PROGPREFIX' : '',
'ARFLAGS' : '/nologo',
'ARCOM' : '$AR $ARFLAGS /out:$TARGET $SOURCES',
'BUILDERS' : [Object, Program, Library],
+ 'SCANNERS' : [CScan],
'OBJPREFIX' : '',
'OBJSUFFIX' : '.obj',
'PROGPREFIX' : '',
import types
import SCons.Util
import SCons.Builder
-
+from SCons.Errors import UserError
def Command():
pass # XXX
self._dict = copy.deepcopy(SCons.Defaults.ConstructionEnvironment)
if kw.has_key('BUILDERS') and type(kw['BUILDERS']) != type([]):
kw['BUILDERS'] = [kw['BUILDERS']]
+ if kw.has_key('SCANNERS') and type(kw['SCANNERS']) != type([]):
+ kw['SCANNERS'] = [kw['SCANNERS']]
self._dict.update(copy.deepcopy(kw))
class BuilderWrapper:
for b in self._dict['BUILDERS']:
setattr(self, b.name, BuilderWrapper(self, b))
-
+ for s in self._dict['SCANNERS']:
+ setattr(self, s.name, s)
def __cmp__(self, other):
return cmp(self._dict, other._dict)
trailing characters.
"""
return SCons.Util.scons_subst(string, self._dict, {})
+
+ def get_scanner(self, skey):
+ """Find the appropriate scanner given a key (usually a file suffix).
+ Does a linear search. Could be sped up by creating a dictionary if
+ this proves too slow.
+ """
+ if self._dict['SCANNERS']:
+ for scanner in self._dict['SCANNERS']:
+ if skey in scanner.skeys:
+ return scanner
+ return None
+scanned_it = {}
+
+class Scanner:
+ """A dummy Scanner class for testing purposes. "Scanning"
+ a target is simply setting a value in the dictionary.
+ """
+ def __init__(self, name, skeys=[]):
+ self.name = name
+ self.skeys = skeys
+
+ def scan(self, filename):
+ scanned_it[filename] = 1
+
+ def __cmp__(self, other):
+ return cmp(self.__dict__, other.__dict__)
+
+
+
class EnvironmentTestCase(unittest.TestCase):
def test_Builders(self):
assert built_it['out2']
assert built_it['out3']
+ def test_Scanners(self):
+ """Test Scanner execution through different environments
+
+ One environment is initialized with a single
+ Scanner object, one with a list of a single Scanner
+ object, and one with a list of two Scanner objects.
+ """
+ global scanned_it
+
+ s1 = Scanner(name = 'scanner1', skeys = [".c", ".cc"])
+ s2 = Scanner(name = 'scanner2', skeys = [".m4"])
+
+ scanned_it = {}
+ env1 = Environment(SCANNERS = s1)
+ env1.scanner1.scan(filename = 'out1')
+ assert scanned_it['out1']
+
+ scanned_it = {}
+ env2 = Environment(SCANNERS = [s1])
+ env1.scanner1.scan(filename = 'out1')
+ assert scanned_it['out1']
+
+ scanned_it = {}
+ env3 = Environment(SCANNERS = [s1, s2])
+ env3.scanner1.scan(filename = 'out1')
+ env3.scanner2.scan(filename = 'out2')
+ env3.scanner1.scan(filename = 'out3')
+ assert scanned_it['out1']
+ assert scanned_it['out2']
+ assert scanned_it['out3']
+
+ s = env3.get_scanner(".c")
+ assert s == s1, s
+ s = env3.get_scanner(skey=".m4")
+ assert s == s2, s
+ s = env3.get_scanner(".cxx")
+ assert s == None, s
+
def test_Command(self):
pass # XXX
def test_InstallAs(self):
pass # XXX
- def test_Scanners(self):
- pass # XXX
-
def test_Update(self):
"""Test updating an Environment with new construction variables
import os
import os.path
+import types
import SCons.Node
from UserDict import UserDict
import sys
.sconsign entry."""
return self.dir.sconsign().get(self.name)
+ def scan(self):
+ if not self.scanned and self.env:
+ if self.scanner:
+ scanner = self.scanner
+ else:
+ scanner = self.env.get_scanner(os.path.splitext(self.name)[1])
+ if scanner:
+ self.add_dependency(scanner.scan(self.path_, self.env))
+ self.scanned = 1
+
default_fs = FS()
import string
import sys
import unittest
-
import SCons.Node.FS
-
built_it = None
class Builder:
built_it = 1
return 0
+class Scanner:
+ def scan(self, filename, env):
+ return [SCons.Node.FS.default_fs.File(filename)]
+
class Environment:
+ def __init__(self):
+ self.scanner = Scanner()
def Dictionary(self, *args):
pass
-
+ def get_scanner(self, skey):
+ return self.scanner
class FSTestCase(unittest.TestCase):
e13 = fs.Entry("subdir/e13")
assert e13.path == "subdir/subdir/e13"
+ # Test scanning
+ f1.scanner = Scanner()
+ f1.scan()
+ assert f1.depends[0].path_ == "d1/f1"
+ f1.scanner = None
+ f1.depends = []
+ f1.scanned = 0
+ f1.scan()
+ assert f1.depends[0].path_ == "d1/f1"
+
#XXX test exists()
#XXX test current() for directories
self.depends = []
self.parents = []
self.builder = None
+ self.scanner = None
+ self.scanned = 0
self.env = None
self.state = None
self.bsig = None
return self.node.builder.get_contents(env = env)
return Adapter(self)
- def env_set(self, env):
+ def scanner_set(self, scanner):
+ self.scanner = scanner
+
+ def scan(self):
+ self.scanned = 1
+
+ def env_set(self, env, safe=0):
+ if safe and self.env:
+ return
self.env = env
def get_bsig(self):
if parent not in self.parents: self.parents.append(parent)
def children(self):
+ if not self.scanned:
+ self.scan()
return self.sources + self.depends
def get_parents(self):
def CScan():
"Return a Scanner instance for scanning C/C++ source files"
- return SCons.Scanner.Scanner(scan)
+ s = SCons.Scanner.Scanner(scan, SCons.Node.FS.default_fs.File,
+ [".c", ".C", ".cxx", ".cpp", ".c++"])
+ s.name = "CScan"
+ return s
def find_files(filenames, paths):
"""
return fullnames
-def scan(filename, env):
+def scan(filename, env, node_factory):
"""
scan(str, Environment) -> [str]
deps = (find_files(angle_includes, paths + [source_dir])
+ find_files(quote_includes, [source_dir] + paths))
+ deps = map(node_factory, deps)
return deps
-
-
-
-
-
-
__version__ = "__VERSION__"
+
+from SCons.Util import scons_str2nodes
+
+
class _Null:
pass
class Scanner:
- def __init__(self, function, argument=_null):
+ def __init__(self, function, argument=_null, skeys=[]):
"""
Construct a new scanner object given a scanner function.
'argument' - an optional argument that will be passed to the
scanner function if it is given.
+ 'skeys; - an optional list argument that can be used to determine
+ which scanner should be used for a given Node. In the case of File
+ nodes, for example, the 'skeys' would be file suffixes.
+
The scanner function's first argument will be the name of a file
that should be scanned for dependencies, the second argument will
be an Environment object, the third argument will be the value
passed into 'argument', and the returned list should contain the
- file names of all the direct dependencies of the file.
+ Nodes for all the direct dependencies of the file.
Examples:
self.function = function
self.argument = argument
+ self.name = "NONE"
+ self.skeys = skeys
def scan(self, filename, env):
"""
This method does the actually scanning. 'filename' is the filename
that will be passed to the scanner function, and 'env' is the
environment that will be passed to the scanner function. A list of
- dependencies will be returned.
+ dependencies will be returned (i.e. a list of 'Node's).
"""
if not self.argument is _null:
return self.function(filename, env, self.argument)
else:
return self.function(filename, env)
-
+ def __cmp__(self, other):
+ return cmp(self.__dict__, other.__dict__)
+ def __call__(self, sources=None):
+ slist = scons_str2nodes(source, self.node_factory)
+ for s in slist:
+ s.scanner_set(self)
+ if len(slist) == 1:
+ slist = slist[0]
+ return slist