Builders named
Alias, CFile, CXXFile, DVI, Library, Object, PDF, PostScript, and Program
are available by default.
+If you initialize this variable when an
+Environment is created:
+.ES
+env = Environment(BUILDERS = {'NewBuilder' : foo})
+.EE
+the default Builders will no longer be available.
+To use a new Builder object in addition to the default Builders,
+add your new Builder object like this:
+.ES
+env = Environment()
+env.Append(BUILDERS = {'NewBuilder' : foo})
+.EE
+or this:
+.ES
+env = Environment()
+env['BUILDERS]['NewBuilder'] = foo
+.EE
.IP CC
The C compiler.
env.PDFBuilder(target = 'bar', source = 'bar')
.EE
+Note also that the above initialization
+overwrites the default Builder objects,
+so the Environment created above
+can not be used call Builders like env.Program(),
+env.Object(), env.StaticLibrary(), etc.
+
+.SS Adding Your Own Builder Object to an Environment
+
+.ES
+bld = Builder(action = 'pdftex < $SOURCES > $TARGET'
+ suffix = '.pdf',
+ src_suffix = '.tex')
+env = Environment()
+env.Append(BUILDERS = {'PDFBuilder' : bld})
+env.PDFBuilder(target = 'foo.pdf', source = 'foo.tex')
+env.Program(target = 'bar', source = 'bar.c')
+.EE
+
+You also can use other Pythonic techniques to add
+to the BUILDERS construction variable, such as:
+
+.ES
+env = Environment()
+env.['BUILDERS]['PDFBuilder'] = bld
+.EE
+
.SS Defining Your Own Scanner Object
.ES
- Doc changes: Eliminate description of deprecated "name" keyword
argument from Builder definition (reported by Gary Ruben).
+ - Support using env.Append() on BUILDERS (and other dictionaries).
+ (Bug reported by Bj=F6rn Bylander.)
+
+ - Setting the BUILDERS construction variable now properly clears
+ the previous Builder attributes from the construction Environment.
+ (Bug reported by Bj=F6rn Bylander.)
+
From Anthony Roach:
- Use a different static object suffix (.os) when using gcc so shared
env = Environment(SHOBJSUFFIX = '.o')
+ - Setting the BUILDERS construction variable now properly clears
+ the previous Builder attributes from the construction Environment.
+ Before, you could initialize BUILDERS like so:
+
+ env = Environment(BUILDERS = {'NewBuilder' : foo})
+
+ And still use the canned default Builders like env.Program(),
+ env.Object(), env.StaticLibrary(), etc. No more. Beginning with
+ SCons 0.10, an initialization like that above will create an
+ Environment with only the env.NewBuilder() Builder.
+
+ So now, if you want to use a new builder in addition to the default
+ builders, you should explicitly append your new builder to the
+ existing ones using techniques like the following:
+
+ env.Append(BUILDERS = {'NewBuilder' : foo})
+
+ env['BUILDERS']['newbuilder'] = foo
+
Please note the following important changes since release 0.09:
- The Scanner interface has been changed to make it easier to
copy = x
return copy
+class BuilderWrapper:
+ """Wrapper class that associates an environment with a Builder at
+ instantiation."""
+ def __init__(self, env, builder):
+ self.env = env
+ self.builder = builder
+
+ def __call__(self, *args, **kw):
+ return apply(self.builder, (self.env,) + args, kw)
+
+ # This allows a Builder to be executed directly
+ # through the Environment to which it's attached.
+ # In practice, we shouldn't need this, because
+ # builders actually get executed through a Node.
+ # But we do have a unit test for this, and can't
+ # yet rule out that it would be useful in the
+ # future, so leave it for now.
+ def execute(self, **kw):
+ kw['env'] = self.env
+ apply(self.builder.execute, (), kw)
+
class BuilderDict(UserDict):
- """This is a dictionary-like class used by Environment
- to hold Builders. We need to do this, because every time
- someone changes the Builders in the Environment's BUILDERS
- dictionary, we need to update the Environment's attributes."""
- def setEnvironment(self, env):
+ """This is a dictionary-like class used by an Environment to hold
+ the Builders. We need to do this because every time someone changes
+ the Builders in the Environment's BUILDERS dictionary, we must
+ update the Environment's attributes."""
+ def __init__(self, dict, env):
+ # Set self.env before calling the superclass initialization,
+ # because it will end up calling our other methods, which will
+ # need to point the values in this dictionary to self.env.
self.env = env
-
+ UserDict.__init__(self, dict)
+
def __setitem__(self, item, val):
UserDict.__setitem__(self, item, val)
try:
- self.env.Replace() # re-compute Builders
+ self.setenvattr(item, val)
except AttributeError:
- # Have to catch this because sometimes
- # __setitem__ gets called out of __init__, when
- # we don't have an env attribute yet, nor do
- # we want one!
+ # Have to catch this because sometimes __setitem__ gets
+ # called out of __init__, when we don't have an env
+ # attribute yet, nor do we want one!
pass
+ def setenvattr(self, item, val):
+ """Set the corresponding environment attribute for this Builder.
+
+ If the value is already a BuilderWrapper, we pull the builder
+ out of it and make another one, so that making a copy of an
+ existing BuilderDict is guaranteed separate wrappers for each
+ Builder + Environment pair."""
+ try:
+ builder = val.builder
+ except AttributeError:
+ builder = val
+ setattr(self.env, item, BuilderWrapper(self.env, builder))
+
def __delitem__(self, item):
UserDict.__delitem__(self, item)
- self.env.Replace()
+ delattr(self.env, item)
+
+ def update(self, dict):
+ for i, v in dict.items():
+ self.__setitem__(i, v)
_rm = re.compile(r'\$[()]')
self.fs = SCons.Node.FS.default_fs
self._dict = our_deepcopy(SCons.Defaults.ConstructionEnvironment)
+ self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
+
if SCons.Util.is_String(platform):
platform = SCons.Platform.Platform(platform)
platform(self)
pass # XXX
def Copy(self, **kw):
- """Return a copy of a construction Environment. The
- copy is like a Python "deep copy"--that is, independent
- copies are made recursively of each objects--except that
- a reference is copied when an object is not deep-copyable
- (like a function). There are no references to any mutable
- objects in the original Environment.
- """
+ """Return a copy of a construction Environment. The
+ copy is like a Python "deep copy"--that is, independent
+ copies are made recursively of each objects--except that
+ a reference is copied when an object is not deep-copyable
+ (like a function). There are no references to any mutable
+ objects in the original Environment.
+ """
clone = copy.copy(self)
clone._dict = our_deepcopy(self._dict)
+ try:
+ cbd = clone._dict['BUILDERS']
+ clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
+ except KeyError:
+ pass
apply(clone.Replace, (), kw)
- return clone
+ return clone
def Scanners(self):
pass # XXX
"""A deprecated synonym for Replace().
"""
apply(self.Replace, (), kw)
-
- def __updateBuildersAndScanners(self):
- """Update attributes for builders and scanners.
-
- Have to be careful in this function...we can't
- call functions like __setitem__() or Replace(), or
- we will have infinite recursion."""
-
- if self._dict.has_key('SCANNERS') and \
- not SCons.Util.is_List(self._dict['SCANNERS']):
- self._dict['SCANNERS'] = [self._dict['SCANNERS']]
-
- class BuilderWrapper:
- """Wrapper class that allows an environment to
- be associated with a Builder at instantiation.
- """
- def __init__(self, env, builder):
- self.env = env
- self.builder = builder
-
- def __call__(self, *args, **kw):
- return apply(self.builder, (self.env,) + args, kw)
-
- # This allows a Builder to be executed directly
- # through the Environment to which it's attached.
- # In practice, we shouldn't need this, because
- # builders actually get executed through a Node.
- # But we do have a unit test for this, and can't
- # yet rule out that it would be useful in the
- # future, so leave it for now.
- def execute(self, **kw):
- kw['env'] = self.env
- apply(self.builder.execute, (), kw)
-
- if self._dict.has_key('BUILDERS'):
- if SCons.Util.is_Dict(self._dict['BUILDERS']):
- bd = self._dict['BUILDERS']
- if not isinstance(bd, BuilderDict):
- # Convert it to a BuilderDict. This class
- # Updates our builder attributes every time
- # someone changes it.
- bd = BuilderDict(bd)
- bd.setEnvironment(self)
- self._dict['BUILDERS'] = bd
- for name, builder in bd.items():
- setattr(self, name, BuilderWrapper(self, builder))
- else:
- raise SCons.Errors.UserError, "The use of the BUILDERS Environment variable as a list or Builder instance is deprecated. BUILDERS should be a dictionary of name->Builder instead."
- for s in self._dict['SCANNERS']:
- setattr(self, s.name, s)
-
def Replace(self, **kw):
"""Replace existing construction variables in an Environment
with new construction variables and/or values.
"""
+ try:
+ kwbd = our_deepcopy(kw['BUILDERS'])
+ del kw['BUILDERS']
+ self.__setitem__('BUILDERS', kwbd)
+ except KeyError:
+ pass
self._dict.update(our_deepcopy(kw))
- self.__updateBuildersAndScanners()
def Append(self, **kw):
"""Append values to existing construction variables
elif SCons.Util.is_List(kw[key]) and not \
SCons.Util.is_List(self._dict[key]):
self._dict[key] = [ self._dict[key] ] + kw[key]
+ elif SCons.Util.is_Dict(self._dict[key]) and \
+ SCons.Util.is_Dict(kw[key]):
+ self._dict[key].update(kw[key])
else:
self._dict[key] = self._dict[key] + kw[key]
- self.__updateBuildersAndScanners()
def Prepend(self, **kw):
"""Prepend values to existing construction variables
elif SCons.Util.is_List(kw[key]) and not \
SCons.Util.is_List(self._dict[key]):
self._dict[key] = kw[key] + [ self._dict[key] ]
+ elif SCons.Util.is_Dict(self._dict[key]) and \
+ SCons.Util.is_Dict(kw[key]):
+ self._dict[key].update(kw[key])
else:
self._dict[key] = kw[key] + self._dict[key]
- self.__updateBuildersAndScanners()
def Depends(self, target, dependency):
"""Explicity specify that 'target's depend on 'dependency'."""
return dlist
def __setitem__(self, key, value):
- self._dict[key] = value
- self.__updateBuildersAndScanners()
+ if key == 'BUILDERS':
+ try:
+ bd = self._dict[key]
+ for k in bd.keys():
+ del bd[k]
+ except KeyError:
+ self._dict[key] = BuilderDict(kwbd, self)
+ self._dict[key].update(value)
+ else:
+ self._dict[key] = value
def __getitem__(self, key):
return self._dict[key]
assert not called_it.has_key('target')
assert not called_it.has_key('source')
-
def test_Builder_execs(self):
"""Test Builder execution through different environments
assert built_it['out3']
env4 = env3.Copy()
- assert env4.builder1.env is env4
- assert env4.builder2.env is env4
+ assert env4.builder1.env is env4, "builder1.env (%s) == env3 (%s)?" % (env4.builder1.env, env3)
+ assert env4.builder2.env is env4, "builder2.env (%s) == env3 (%s)?" % (env4.builder1.env, env3)
# Now test BUILDERS as a dictionary.
built_it = {}
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(filename = 'out1')
- assert scanned_it['out1']
-
- scanned_it = {}
- env2 = Environment(SCANNERS = [s1])
- env1.scanner1(filename = 'out1')
- assert scanned_it['out1']
-
- scanned_it = {}
- env3 = Environment()
- env3.Replace(SCANNERS = [s1, s2])
- env3.scanner1(filename = 'out1')
- env3.scanner2(filename = 'out2')
- env3.scanner1(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
+# global scanned_it
+#
+# s1 = Scanner(name = 'scanner1', skeys = [".c", ".cc"])
+# s2 = Scanner(name = 'scanner2', skeys = [".m4"])
+#
+# scanned_it = {}
+# env1 = Environment(SCANNERS = s1)
+# env1.scanner1(filename = 'out1')
+# assert scanned_it['out1']
+#
+# scanned_it = {}
+# env2 = Environment(SCANNERS = [s1])
+# env1.scanner1(filename = 'out1')
+# assert scanned_it['out1']
+#
+# scanned_it = {}
+# env3 = Environment()
+# env3.Replace(SCANNERS = [s1, s2])
+# env3.scanner1(filename = 'out1')
+# env3.scanner2(filename = 'out2')
+# env3.scanner1(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_Copy(self):
"""Test construction Environment copying
assert env2.Dictionary('ZZZ').has_key(5)
assert not env1.Dictionary('ZZZ').has_key(5)
+ #
+ env1 = Environment(BUILDERS = {'b1' : 1})
+ assert hasattr(env1, 'b1'), "env1.b1 was not set"
+ assert env1.b1.env == env1, "b1.env doesn't point to env1"
+ env2 = env1.Copy(BUILDERS = {'b2' : 2})
+ assert hasattr(env1, 'b1'), "b1 was mistakenly cleared from env1"
+ assert env1.b1.env == env1, "b1.env was changed"
+ assert not hasattr(env2, 'b1'), "b1 was not cleared from env2"
+ assert hasattr(env2, 'b2'), "env2.b2 was not set"
+ assert env2.b2.env == env2, "b2.env doesn't point to env2"
+
def test_Dictionary(self):
"""Test retrieval of known construction variables
"""
env1 = Environment(AAA = 'a', BBB = 'b')
env1.Replace(BBB = 'bbb', CCC = 'ccc')
+
env2 = Environment(AAA = 'a', BBB = 'bbb', CCC = 'ccc')
assert env1 == env2, diff_env(env1, env2)
+ env3 = Environment(BUILDERS = {'b1' : 1})
+ assert hasattr(env3, 'b1'), "b1 was not set"
+ env3.Replace(BUILDERS = {'b2' : 2})
+ assert not hasattr(env3, 'b1'), "b1 was not cleared"
+ assert hasattr(env3, 'b2'), "b2 was not set"
+
def test_Append(self):
"""Test appending to construction variables in an Environment
"""
KKK = UL(['k', 'K']), LLL = UL(['l', 'L']))
assert env1 == env2, diff_env(env1, env2)
- env3 = Environment(X = {'x' : 7})
- try:
- env3.Append(X = {'x' : 8})
- except TypeError:
- pass
- except:
- raise
+ env3 = Environment(X = {'x1' : 7})
+ env3.Append(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
+ assert env3['X'] == {'x1': 8, 'x2': 9}, env3['X']
+ assert env3['Y'] == {'y1': 10}, env3['Y']
+
+ env4 = Environment(BUILDERS = {'z1' : 11})
+ env4.Append(BUILDERS = {'z2' : 12})
+ assert env4['BUILDERS'] == {'z1' : 11, 'z2' : 12}, env4['BUILDERS']
+ assert hasattr(env4, 'z1')
+ assert hasattr(env4, 'z2')
def test_Prepend(self):
"""Test prepending to construction variables in an Environment
KKK = UL(['K', 'k']), LLL = UL(['L', 'l']))
assert env1 == env2, diff_env(env1, env2)
- env3 = Environment(X = {'x' : 7})
- try:
- env3.Prepend(X = {'x' : 8})
- except TypeError:
- pass
- except:
- raise
+ env3 = Environment(X = {'x1' : 7})
+ env3.Prepend(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
+ assert env3['X'] == {'x1': 8, 'x2' : 9}, env3['X']
+ assert env3['Y'] == {'y1': 10}, env3['Y']
+
+ env4 = Environment(BUILDERS = {'z1' : 11})
+ env4.Prepend(BUILDERS = {'z2' : 12})
+ assert env4['BUILDERS'] == {'z1' : 11, 'z2' : 12}, env4['BUILDERS']
+ assert hasattr(env4, 'z1')
+ assert hasattr(env4, 'z2')
def test_Depends(self):
"""Test the explicit Depends method."""
f.close()
XBuilder = Builder(action = cat, src_suffix = '.x', suffix = '.c')
-env = Environment(BUILDERS = { 'XBuilder': XBuilder })
+env = Environment()
+env.Append(BUILDERS = { 'XBuilder': XBuilder })
f = env.XBuilder(source = ['file.x'], target = ['file.c'])
env.Alias(target = ['cfiles'], source = f)
Default(['cfiles'])