env.Program('bar.c')
.EE
+It is possible to override or add construction variables when calling a
+builder by passing additional keyword arguments. These overriden or added
+variables will only be in effect when building the target, so they will not
+effect other parts of the build. For example, if you want to add additional
+libraries for just one program:
+
+.ES
+env.Program('hello', 'hello.c', LIBS=['gl', 'glut'])
+.EE
+
+or generate a shared library with a nonstandard suffix:
+
+.ES
+env.SharedLibrary('word', 'word.cpp', SHLIBSUFFIX='.ocx')
+.EE
+
.B scons
provides the following builders:
(see the next section);
or a list of any of the above.
+An action function
+takes three arguments:
+.I source
+- a list of source nodes,
+.I target
+- a list of target nodes,
+.I env
+- the construction environment.
+
.IP multi
Specifies whether this builder is allowed to be called multiple times for
the same target file(s). The default is 0, which means the builder
can not be called multiple times for the same target file(s). Calling a
builder multiple times for the same target simply adds additional source
files to the target; it is not allowed to change the environment associated
-with the target, specify addition build arguments, or associate a different
+with the target, specify addition environment overrides, or associate a different
builder with the target.
.IP prefix
and the list of sources for this builder.
This allows the target and source lists to
be manipulated before the target(s) are actually built.
+
+The emitter function
+takes three arguments:
+.I source
+- a list of source nodes,
+.I target
+- a list of target nodes,
+.I env
+- the construction environment.
+
Example:
.ES
def show(self, string):
print string
- def subst_dict(self, **kw):
+ def subst_dict(self, target, source, env):
"""Create a dictionary for substitution of construction
variables.
construction variables
source - the source (object or array of objects),
- used to generate the SOURCES and SOURCE
+ used to generate the SOURCES and SOURCE
construction variables
-
- Any other keyword arguments are copied into the
- dictionary."""
+ """
dict = {}
- if kw.has_key('env'):
- dict.update(kw['env'])
- del kw['env']
- try:
- cwd = kw['dir']
- except KeyError:
- cwd = None
- else:
- del kw['dir']
+ for k,v in env.items(): dict[k] = v
- if kw.has_key('target'):
- t = kw['target']
- del kw['target']
- if not SCons.Util.is_List(t):
- t = [t]
- try:
- cwd = t[0].cwd
- except (IndexError, AttributeError):
- pass
- dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(str, t)))
- if dict['TARGETS']:
- dict['TARGET'] = dict['TARGETS'][0]
+ if not SCons.Util.is_List(target):
+ target = [target]
- if kw.has_key('source'):
- def rstr(x):
- try:
- return x.rstr()
- except AttributeError:
- return str(x)
- s = kw['source']
- del kw['source']
- if not SCons.Util.is_List(s):
- s = [s]
- dict['SOURCES'] = SCons.Util.PathList(map(os.path.normpath, map(rstr, s)))
- if dict['SOURCES']:
- dict['SOURCE'] = dict['SOURCES'][0]
+ dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(str, target)))
+ if dict['TARGETS']:
+ dict['TARGET'] = dict['TARGETS'][0]
- dict.update(kw)
+ def rstr(x):
+ try:
+ return x.rstr()
+ except AttributeError:
+ return str(x)
+ if not SCons.Util.is_List(source):
+ source = [source]
+ dict['SOURCES'] = SCons.Util.PathList(map(os.path.normpath, map(rstr, source)))
+ if dict['SOURCES']:
+ dict['SOURCE'] = dict['SOURCES'][0]
return dict
_rm = re.compile(r'\$[()]')
_remove = re.compile(r'\$\(([^\$]|\$[^\(])*?\$\)')
-class EnvDictProxy(UserDict.UserDict):
- """This is a dictionary-like class that contains the
- Environment dictionary we pass to FunctionActions
- and CommandGeneratorActions.
-
- In addition to providing
- normal dictionary-like access to the variables in the
- Environment, it also exposes the functions subst()
- and subst_list(), allowing users to easily do variable
- interpolation when writing their FunctionActions
- and CommandGeneratorActions."""
-
- def __init__(self, env):
- UserDict.UserDict.__init__(self, env)
-
- def subst(self, string, raw=0):
- if raw:
- regex_remove = None
- else:
- regex_remove = _rm
- return SCons.Util.scons_subst(string, self.data, {}, regex_remove)
-
- def subst_list(self, string, raw=0):
- if raw:
- regex_remove = None
- else:
- regex_remove = _rm
- return SCons.Util.scons_subst_list(string, self.data, {}, regex_remove)
-
class CommandAction(ActionBase):
"""Class for command-execution actions."""
def __init__(self, cmd):
import SCons.Util
-
+
self.cmd_list = map(SCons.Util.to_String, cmd)
- def execute(self, **kw):
- dict = apply(self.subst_dict, (), kw)
+ def execute(self, target, source, env):
+ dict = self.subst_dict(target, source, env)
import SCons.Util
cmd_list = SCons.Util.scons_subst_list(self.cmd_list, dict, {}, _rm)
for cmd_line in cmd_list:
self.show(_string_from_cmd_list(cmd_line))
if execute_actions:
try:
- ENV = kw['env']['ENV']
+ ENV = dict['ENV']
except KeyError:
global default_ENV
if not default_ENV:
return ret
return 0
- def _sig_dict(self, kw):
+ def _sig_dict(self, target, source, env):
"""Supply a dictionary for use in computing signatures.
For signature purposes, it doesn't matter what targets or
so the signature stays the same. We supply an array of two
of each to allow for distinction between TARGET and TARGETS.
"""
- kw['target'] = ['__t1__', '__t2__']
- kw['source'] = ['__s1__', '__s2__']
- return apply(self.subst_dict, (), kw)
+ return self.subst_dict(['__t1__', '__t2__'], ['__s1__', '__s2__'], env)
- def get_raw_contents(self, **kw):
+ def get_raw_contents(self, target, source, env):
"""Return the complete contents of this action's command line.
"""
return SCons.Util.scons_subst(string.join(self.cmd_list),
- self._sig_dict(kw), {})
+ self._sig_dict(target, source, env), {})
- def get_contents(self, **kw):
+ def get_contents(self, target, source, env):
"""Return the signature contents of this action's command line.
This strips $(-$) and everything in between the string,
since those parts don't affect signatures.
"""
return SCons.Util.scons_subst(string.join(self.cmd_list),
- self._sig_dict(kw), {}, _remove)
+ self._sig_dict(target, source, env), {}, _remove)
class CommandGeneratorAction(ActionBase):
"""Class for command-generator actions."""
def __init__(self, generator):
self.generator = generator
- def __generate(self, kw, for_signature):
+ def __generate(self, target, source, env, for_signature):
import SCons.Util
- # Wrap the environment dictionary in an EnvDictProxy
- # object to make variable interpolation easier for the
- # client.
- args = copy.copy(kw)
- args['for_signature'] = for_signature
- if args.has_key("env") and not isinstance(args["env"], EnvDictProxy):
- args["env"] = EnvDictProxy(args["env"])
-
# ensure that target is a list, to make it easier to write
# generator functions:
- if args.has_key("target") and not SCons.Util.is_List(args["target"]):
- args["target"] = [args["target"]]
+ if not SCons.Util.is_List(target):
+ target = [target]
- ret = apply(self.generator, (), args)
+ ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
gen_cmd = Action(ret)
if not gen_cmd:
raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
return gen_cmd
- def execute(self, **kw):
- return apply(self.__generate(kw, 0).execute, (), kw)
+ def execute(self, target, source, env):
+ return self.__generate(target, source, env, 0).execute(target, source, env)
- def get_contents(self, **kw):
+ def get_contents(self, target, source, env):
"""Return the signature contents of this action's command line.
This strips $(-$) and everything in between the string,
since those parts don't affect signatures.
"""
- return apply(self.__generate(kw, 1).get_contents, (), kw)
+ return self.__generate(target, source, env, 1).get_contents(target, source, env)
class LazyCmdGenerator:
"""This is a simple callable class that acts as a command generator.
def __init__(self, var):
self.var = SCons.Util.to_String(var)
- def __call__(self, env, **kw):
+ def __call__(self, target, source, env, for_signature):
if env.has_key(self.var):
return env[self.var]
else:
def __init__(self, function):
self.function = function
- def execute(self, **kw):
+ def execute(self, target, source, env):
# if print_actions:
# XXX: WHAT SHOULD WE PRINT HERE?
if execute_actions:
- if kw.has_key('target') and not \
- SCons.Util.is_List(kw['target']):
- kw['target'] = [ kw['target'] ]
- if kw.has_key('source'):
- def rfile(n):
- try:
- return n.rfile()
- except AttributeError:
- return n
- if not SCons.Util.is_List(kw['source']):
- kw['source'] = [ kw['source'] ]
- kw['source'] = map(rfile, kw['source'])
- if kw.has_key("env") and not isinstance(kw["env"], EnvDictProxy):
- kw["env"] = EnvDictProxy(kw["env"])
- return apply(self.function, (), kw)
-
- def get_contents(self, **kw):
+ if not SCons.Util.is_List(target):
+ target = [target]
+
+ def rfile(n):
+ try:
+ return n.rfile()
+ except AttributeError:
+ return n
+ if not SCons.Util.is_List(source):
+ source = [source]
+ source = map(rfile, source)
+
+ return self.function(target=target, source=source, env=env)
+
+ def get_contents(self, target, source, env):
"""Return the signature contents of this callable action.
By providing direct access to the code object of the
def __init__(self, list):
self.list = map(lambda x: Action(x), list)
- def execute(self, **kw):
+ def execute(self, target, source, env):
for l in self.list:
- r = apply(l.execute, (), kw)
+ r = l.execute(target, source, env)
if r:
return r
return 0
- def get_contents(self, **kw):
+ def get_contents(self, target, source, env):
"""Return the signature contents of this action list.
Simple concatenation of the signatures of the elements.
"""
- return reduce(lambda x, y, kw=kw: x + str(apply(y.get_contents, (), kw)), self.list, "")
+ ret = ""
+ for a in self.list:
+ ret = ret + a.get_contents(target, source, env)
+ return ret
+
import SCons.Action
import TestCmd
+import UserDict
+
+import SCons.Environment
+Environment = SCons.Environment.EnvProxy
+
class ActionTestCase(unittest.TestCase):
def runTest(self):
"""
a = SCons.Action.Action("x")
- d = a.subst_dict(env = {'a' : 'A', 'b' : 'B'})
+ d = a.subst_dict([],[],Environment({'a' : 'A', 'b' : 'B'}))
assert d['a'] == 'A', d
assert d['b'] == 'B', d
- d = a.subst_dict(target = 't', source = 's')
+ d = a.subst_dict(target = 't', source = 's',env=Environment({}))
assert str(d['TARGETS']) == 't', d['TARGETS']
assert str(d['TARGET']) == 't', d['TARGET']
assert str(d['SOURCES']) == 's', d['SOURCES']
assert str(d['SOURCE']) == 's', d['SOURCE']
- d = a.subst_dict(target = ['t1', 't2'], source = ['s1', 's2'])
+ d = a.subst_dict(target = ['t1', 't2'], source = ['s1', 's2'], env=Environment({}))
TARGETS = map(lambda x: str(x), d['TARGETS'])
TARGETS.sort()
assert TARGETS == ['t1', 't2'], d['TARGETS']
def rstr(self):
return 'rstr-' + self.name
- d = a.subst_dict(target = [N('t3'), 't4'], source = ['s3', N('s4')])
+ d = a.subst_dict(target = [N('t3'), 't4'], source = ['s3', N('s4')], env=Environment({}))
TARGETS = map(lambda x: str(x), d['TARGETS'])
TARGETS.sort()
assert TARGETS == ['t3', 't4'], d['TARGETS']
SCons.Action.SetCommandHandler(func)
assert SCons.Action.spawn is func
a = SCons.Action.CommandAction(["xyzzy"])
- a.execute()
+ a.execute([],[],Environment({}))
assert t.executed == 1
def test_get_raw_contents(self):
a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar",
"$)", "|"])
c = a.get_contents(target=[], source=[],
- foo = 'FFF', bar = 'BBB')
+ env=Environment({'foo':'FFF', 'bar':'BBB'}))
assert c == "| |", c
class CommandGeneratorActionTestCase(unittest.TestCase):
"""Test executing a command generator Action
"""
- def f(dummy, env, for_signature, self=self):
+ def f(target, source, env, for_signature, self=self):
+ dummy = env['dummy']
self.dummy = dummy
assert env.subst("$FOO $( bar $) baz") == 'foo baz\nbar ack bar baz', env.subst("$FOO $( bar $) baz")
assert env.subst("$FOO $( bar $) baz", raw=1) == 'foo baz\nbar ack $( bar $) baz', env.subst("$FOO $( bar $) baz", raw=1)
raw=1) == [ [ 'foo', 'baz' ],
[ 'bar', 'ack', '$(', 'bar', '$)', 'baz' ] ], env.subst_list("$FOO $( bar $) baz", raw=1)
return "$FOO"
- def func_action(env, dummy, self=self):
+ def func_action(target, source,env, self=self):
+ dummy=env['dummy']
assert env.subst('$foo $( bar $)') == 'bar bar', env.subst('$foo $( bar $)')
assert env.subst('$foo $( bar $)',
raw=1) == 'bar $( bar $)', env.subst('$foo $( bar $)', raw=1)
assert env.subst_list([ '$foo', '$(', 'bar', '$)' ],
raw=1) == [[ 'bar', '$(', 'bar', '$)' ]], env.subst_list([ '$foo', '$(', 'bar', '$)' ], raw=1)
self.dummy=dummy
- def f2(dummy, env, for_signature, f=func_action):
+ def f2(target, source, env, for_signature, f=func_action):
return f
def ch(cmd, args, env, self=self):
self.cmd.append(cmd)
self.args = []
try:
SCons.Action.SetCommandHandler(ch)
- a.execute(dummy=1, env={ 'FOO' : 'foo baz\nbar ack' })
+ a.execute([],[],env=Environment({ 'FOO' : 'foo baz\nbar ack' , 'dummy':1}))
finally:
SCons.Action.SetCommandHandler(old_hdl)
assert self.dummy == 1
b=SCons.Action.CommandGeneratorAction(f2)
self.dummy = 0
- b.execute(dummy=2, env={ 'foo' : 'bar' })
+ b.execute(target=[], source=[], env=Environment({ 'foo' : 'bar','dummy':2 }))
assert self.dummy==2, self.dummy
del self.dummy
def test_get_contents(self):
"""Test fetching the contents of a command generator Action
"""
- def f(target, source, foo, bar, for_signature):
+ def f(target, source, env, for_signature):
+ foo = env['foo']
+ bar = env['bar']
assert for_signature, for_signature
return [["guux", foo, "$(", "ignore", "$)", bar]]
a = SCons.Action.CommandGeneratorAction(f)
c = a.get_contents(target=[], source=[],
- foo = 'FFF', bar = 'BBB')
+ env=Environment({'foo':'FFF', 'bar' : 'BBB'}))
assert c == "guux FFF BBB", c
"""Test executing a function Action
"""
self.inc = 0
- def f(s, target, source, env):
+ def f(target, source, env):
+ s = env['s']
s.inc = s.inc + 1
s.target = target
s.source=source
env.subst_list("foo$BAR")
return 0
a = SCons.Action.FunctionAction(f)
- a.execute(s = self, target=1, source=2, env={'BAR':'foo bar'})
+ a.execute(target=1, source=2, env=Environment({'BAR':'foo bar','s':self}))
assert self.inc == 1, self.inc
assert self.source == [2], self.source
assert self.target == [1], self.target
"""Test fetching the contents of a function Action
"""
a = SCons.Action.FunctionAction(Func)
- c = a.get_contents(target=[], source=[])
+ c = a.get_contents(target=[], source=[], env=Environment({}))
assert c == "\177\036\000\177\037\000d\000\000S", repr(c)
class ListActionTestCase(unittest.TestCase):
"""Test executing a list of subsidiary Actions
"""
self.inc = 0
- def f(s):
+ def f(target,source,env):
+ s = env['s']
s.inc = s.inc + 1
a = SCons.Action.ListAction([f, f, f])
- a.execute(s = self)
+ a.execute([],[],Environment({'s':self}))
assert self.inc == 3, self.inc
def test_get_contents(self):
"""Test fetching the contents of a list of subsidiary Actions
"""
self.foo=0
- def gen(target, source, s, for_signature):
+ def gen(target, source, env, for_signature):
+ s = env['s']
s.foo=1
return "y"
a = SCons.Action.ListAction(["x",
SCons.Action.CommandGenerator(gen),
"z"])
- c = a.get_contents(target=[], source=[], s=self)
+ c = a.get_contents(target=[], source=[], env=Environment({'s':self}))
assert self.foo==1, self.foo
assert c == "xyz", c
def test_execute(self):
"""Test executing a lazy-evalueation Action
"""
- def f(s, env):
+ def f(target, source, env):
+ s = env['s']
s.test=1
return 0
a = SCons.Action.Action('$BAR')
- a.execute(s = self, env={'BAR':f})
+ a.execute([],[], env=Environment({'BAR':f,'s':self}))
assert self.test == 1, self.test
def test_get_contents(self):
"""
a = SCons.Action.Action("${FOO}")
c = a.get_contents(target=[], source=[],
- env={'FOO':[["This", "is", "$(", "a", "$)", "test"]]})
+ env=Environment({'FOO':[["This", "is", "$(", "a", "$)", "test"]]}))
assert c == "This is test", c
"""
self.action_dict[suffix] = action
- def __call__(self, source, target, env, **kw):
+ def __call__(self, target, source, env, for_signature):
ext = None
for src in map(str, source):
my_ext = SCons.Util.splitext(src)[1]
if not var:
raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % kw['emitter']
kw['emitter'] = EmitterProxy(var)
-
+
if kw.has_key('src_builder'):
ret = apply(MultiStepBuilder, (), kw)
else:
return ret
-def _init_nodes(builder, env, args, tlist, slist):
+def _init_nodes(builder, env, overrides, tlist, slist):
"""Initialize lists of target and source nodes with all of
the proper Builder information.
"""
if t.side_effect:
raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
if t.builder is not None:
- if t.env != env:
+ if t.env != env:
raise UserError, "Two different environments were specified for the same target: %s"%str(t)
- elif t.build_args != args:
- raise UserError, "Two different sets of build arguments were specified for the same target: %s"%str(t)
+ elif t.overrides != overrides:
+ raise UserError, "Two different sets of overrides were specified for the same target: %s"%str(t)
elif builder.scanner and builder.scanner != t.target_scanner:
raise UserError, "Two different scanners were specified for the same target: %s"%str(t)
elif t.sources != slist:
raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
- t.build_args = args
+ t.overrides = overrides
t.cwd = SCons.Node.FS.default_fs.getcwd()
t.builder_set(builder)
t.env_set(env)
t.add_source(slist)
if builder.scanner:
t.target_scanner = builder.scanner
-
+
def _adjust_suffix(suff):
if suff and not suff[0] in [ '.', '$' ]:
return '.' + suff
def __init__(self, var):
self.var = SCons.Util.to_String(var)
- def __call__(self, target, source, env, **kw):
+ def __call__(self, target, source, env):
emitter = self.var
# Recursively substitute the variable.
emitter = env[emitter]
if not callable(emitter):
return (target, source)
- args = { 'target':target,
- 'source':source,
- 'env':env }
- args.update(kw)
- return apply(emitter, (), args)
+
+ return emitter(target, source, env)
def __cmp__(self, other):
return cmp(self.var, other.var)
def __cmp__(self, other):
return cmp(self.__dict__, other.__dict__)
- def _create_nodes(self, env, args, target = None, source = None):
+ def _create_nodes(self, env, overrides, target = None, source = None):
"""Create and return lists of target and source nodes.
"""
def adjustixes(files, pre, suf):
ret.append(f)
return ret
+ env = env.Override(overrides)
+
pre = self.get_prefix(env)
suf = self.get_suffix(env)
src_suf = self.get_src_suffix(env)
if self.emitter:
# pass the targets and sources to the emitter as strings
- # rather than nodes since str(node) doesn't work
+ # rather than nodes since str(node) doesn't work
# properly from any directory other than the top directory,
# and emitters are called "in" the SConscript directory:
- emit_args = { 'target' : target,
- 'source' : source,
- 'env' : env }
- emit_args.update(args)
- target, source = apply(self.emitter, (), emit_args)
+ target, source = self.emitter(target=target, source=source, env=env)
slist = SCons.Node.arg2nodes(source, self.source_factory)
tlist = SCons.Node.arg2nodes(target, self.target_factory)
return tlist, slist
- def __call__(self, env, target = None, source = _null, **kw):
+ def __call__(self, env, target = None, source = _null, **overrides):
if source is _null:
source = target
target = None
- tlist, slist = self._create_nodes(env, kw, target, source)
+ tlist, slist = self._create_nodes(env, overrides, target, source)
if len(tlist) == 1:
- _init_nodes(self, env, kw, tlist, slist)
+ _init_nodes(self, env, overrides, tlist, slist)
tlist = tlist[0]
else:
- _init_nodes(ListBuilder(self, env, tlist), env, kw, tlist, slist)
+ _init_nodes(ListBuilder(self, env, tlist), env, overrides, tlist, slist)
return tlist
- def execute(self, **kw):
+ def execute(self, target, source, env):
"""Execute a builder's action to create an output object.
"""
- return apply(self.action.execute, (), kw)
+ return self.action.execute(target, source, env)
- def get_raw_contents(self, **kw):
+ def get_raw_contents(self, target, source, env):
"""Fetch the "contents" of the builder's action.
"""
- return apply(self.action.get_raw_contents, (), kw)
+ return self.action.get_raw_contents(target, source, env)
- def get_contents(self, **kw):
+ def get_contents(self, target, source, env):
"""Fetch the "contents" of the builder's action
(for signature calculation).
"""
- return apply(self.action.get_contents, (), kw)
+ return self.action.get_contents(target, source, env)
def src_suffixes(self, env):
return map(lambda x, e=env: e.subst(_adjust_suffix(x)),
self.multi = builder.multi
self.name = "ListBuilder(%s)"%builder.name
- def execute(self, **kw):
+ def execute(self, target, source, env):
if hasattr(self, 'status'):
return self.status
for t in self.tlist:
# unlink all targets and make all directories
# before building anything
t.prepare()
- kw['target'] = self.tlist
- self.status = apply(self.builder.execute, (), kw)
+ target = self.tlist
+ self.status = self.builder.execute(target, source, env)
for t in self.tlist:
- if not t is kw['target']:
+ if not t is target:
t.build()
return self.status
if not SCons.Util.is_List(src_builder):
src_builder = [ src_builder ]
self.src_builder = src_builder
- self.sdict = {}
+ self.sdict = {}
self.cached_src_suffixes = {} # source suffixes keyed on id(env)
def __call__(self, env, target = None, source = None, **kw):
continue
for suf in bld.src_suffixes(env):
sdict[suf] = bld
-
+
src_suffixes = self.src_suffixes(env)
+
for snode in slist:
path, ext = SCons.Util.splitext(snode.abspath)
if sdict.has_key(ext):
return self.d[item]
def has_key(self, item):
return self.d.has_key(item)
+ def keys(self):
+ return self.d.keys()
+ def get(self, key, value):
+ return self.d.get(key, value)
+ def Override(self, overrides):
+ env = apply(Environment, (), self.d)
+ env.d.update(overrides)
+ return env
+ def items(self):
+ return self.d.items()
env = Environment()
cmd1 = r'%s %s %s xyzzy' % (python, act_py, outfile)
builder = MyBuilder(action = cmd1, name = "cmd1")
- r = builder.execute()
+ r = builder.execute([],[],Environment())
assert r == 0
c = test.read(outfile, 'r')
assert c == "act.py: 'xyzzy'\n", c
cmd2 = r'%s %s %s $TARGET' % (python, act_py, outfile)
builder = MyBuilder(action = cmd2, name = "cmd2")
- r = builder.execute(target = 'foo')
+ r = builder.execute('foo', [], Environment())
assert r == 0
c = test.read(outfile, 'r')
assert c == "act.py: 'foo'\n", c
cmd3 = r'%s %s %s ${TARGETS}' % (python, act_py, outfile)
builder = MyBuilder(action = cmd3, name = "cmd3")
- r = builder.execute(target = ['aaa', 'bbb'])
+ r = builder.execute(['aaa', 'bbb'], [], Environment())
assert r == 0
c = test.read(outfile, 'r')
assert c == "act.py: 'aaa' 'bbb'\n", c
cmd4 = r'%s %s %s $SOURCES' % (python, act_py, outfile)
builder = MyBuilder(action = cmd4, name = "cmd4")
- r = builder.execute(source = ['one', 'two'])
+ r = builder.execute([], ['one', 'two'], Environment())
assert r == 0
c = test.read(outfile, 'r')
assert c == "act.py: 'one' 'two'\n", c
cmd4 = r'%s %s %s ${SOURCES[:2]}' % (python, act_py, outfile)
builder = MyBuilder(action = cmd4, name = "cmd4")
- r = builder.execute(source = ['three', 'four', 'five'])
+ r = builder.execute([], source = ['three', 'four', 'five'], env=Environment())
assert r == 0
c = test.read(outfile, 'r')
assert c == "act.py: 'three' 'four'\n", c
cmd5 = r'%s %s %s $TARGET XYZZY' % (python, act_py, outfile)
builder = MyBuilder(action = cmd5, name = "cmd5")
- r = builder.execute(target = 'out5', env = {'ENV' : {'XYZZY' : 'xyzzy'}})
+ r = builder.execute(target = 'out5', source = [], env = Environment(ENV={'XYZZY' : 'xyzzy'}))
assert r == 0
c = test.read(outfile, 'r')
assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy'\n", c
builder = MyBuilder(action = cmd6, name = "cmd6")
r = builder.execute(target = [Obj('111'), Obj('222')],
- source = [Obj('333'), Obj('444'), Obj('555')])
+ source = [Obj('333'), Obj('444'), Obj('555')],
+ env = Environment())
assert r == 0
c = test.read(outfile, 'r')
assert c == "act.py: '222' '111' '333' '444'\n", c
for action in builder.action.list:
action.show = my_show
- r = builder.execute()
+ r = builder.execute([],[],Environment())
assert r == 0
assert show_string == expect7, show_string
global count
count = 0
- def function1(**kw):
+ def function1(target, source, env):
global count
count = count + 1
- if not type(kw['target']) is type([]):
- kw['target'] = [ kw['target'] ]
- for t in kw['target']:
+ for t in target:
open(t, 'w').write("function1\n")
return 1
builder = MyBuilder(action = function1, name = "function1")
try:
- r = builder.execute(target = [outfile, outfile2])
+ r = builder.execute(target = [outfile, outfile2], source=[], env=Environment())
except SCons.Errors.BuildError:
pass
assert r == 1
assert c == "function1\n", c
class class1a:
- def __init__(self, **kw):
- open(kw['out'], 'w').write("class1a\n")
+ def __init__(self, target, source, env):
+ open(env['out'], 'w').write("class1a\n")
builder = MyBuilder(action = class1a, name = "class1a")
- r = builder.execute(out = outfile)
+ r = builder.execute([],[],Environment(out = outfile))
assert r.__class__ == class1a
c = test.read(outfile, 'r')
assert c == "class1a\n", c
class class1b:
- def __call__(self, **kw):
- open(kw['out'], 'w').write("class1b\n")
+ def __call__(self, target, source, env):
+ open(env['out'], 'w').write("class1b\n")
return 2
builder = MyBuilder(action = class1b(), name = "class1b")
- r = builder.execute(out = outfile)
+ r = builder.execute([],[],Environment(out = outfile))
assert r == 2
c = test.read(outfile, 'r')
assert c == "class1b\n", c
cmd2 = r'%s %s %s syzygy' % (python, act_py, outfile)
- def function2(**kw):
- open(kw['out'], 'a').write("function2\n")
+ def function2(target, source, env):
+ open(env['out'], 'a').write("function2\n")
return 0
class class2a:
- def __call__(self, **kw):
- open(kw['out'], 'a').write("class2a\n")
+ def __call__(self, target, source, env):
+ open(env['out'], 'a').write("class2a\n")
return 0
class class2b:
- def __init__(self, **kw):
- open(kw['out'], 'a').write("class2b\n")
+ def __init__(self, target, source, env):
+ open(env['out'], 'a').write("class2b\n")
builder = MyBuilder(action = SCons.Action.ListAction([cmd2, function2, class2a(), class2b]), name = "clist")
- r = builder.execute(out = outfile)
+ r = builder.execute([],[],Environment(out = outfile))
assert r.__class__ == class2b
c = test.read(outfile, 'r')
assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c
# Test that a nonexistent command returns 127
builder = MyBuilder(action = python + "_XyZzY_", name="badcmd")
- r = builder.execute(out = outfile)
+ r = builder.execute([],[],Environment(out = outfile))
assert r == expect_nonexistent, "r == %d" % r
# Test that trying to execute a directory returns 126
dir, tail = os.path.split(python)
builder = MyBuilder(action = dir, name = "dir")
- r = builder.execute(out = outfile)
+ r = builder.execute([],[],Environment(out = outfile))
assert r == expect_nonexecutable, "r == %d" % r
# Test that trying to execute a non-executable file returns 126
builder = MyBuilder(action = outfile, name = "badfile")
- r = builder.execute(out = outfile)
+ r = builder.execute([],[],Environment(out = outfile))
assert r == expect_nonexecutable, "r == %d" % r
def test_get_contents(self):
"""
b1 = SCons.Builder.Builder(name = "b1", action = "foo")
- contents = b1.get_contents()
+ contents = b1.get_contents([],[],Environment())
assert contents == "foo", contents
b2 = SCons.Builder.Builder(name = "b2", action = Func)
- contents = b2.get_contents()
+ contents = b2.get_contents([],[],Environment())
assert contents == "\177\036\000\177\037\000d\000\000S", repr(contents)
b3 = SCons.Builder.Builder(name = "b3", action = SCons.Action.ListAction(["foo", Func, "bar"]))
- contents = b3.get_contents()
+ contents = b3.get_contents([],[],Environment())
assert contents == "foo\177\036\000\177\037\000d\000\000Sbar", repr(contents)
def test_node_factory(self):
"""Testing ListBuilder class."""
global count
count = 0
- def function2(tlist = [outfile, outfile2], **kw):
+ def function2(target, source, env, tlist = [outfile, outfile2], **kw):
global count
count = count + 1
- for t in kw['target']:
+ for t in target:
open(str(t), 'w').write("function2\n")
for t in tlist:
- if not t in map(str, kw['target']):
+ if not t in map(str, target):
open(t, 'w').write("function2\n")
return 1
builder = SCons.Builder.Builder(action = function2, name = "function2")
tgts = builder(env, target = [outfile, outfile2], source = 'foo')
try:
- r = tgts[0].builder.execute(target = tgts)
+ r = tgts[0].builder.execute(tgts, 'foo', env)
except SCons.Errors.BuildError:
pass
c = test.read(outfile, 'r')
assert c == "function2\n", c
c = test.read(outfile2, 'r')
assert c == "function2\n", c
- r = tgts[1].builder.execute(target = tgts[1])
+ r = tgts[1].builder.execute(tgts[1], 'foo', env)
assert r == 1, r
assert count == 1, count
sub2_out = test.workpath('sub2', 'out')
count = 0
- def function3(tlist = [sub1_out, sub2_out], **kw):
+ def function3(target, source, env, tlist = [sub1_out, sub2_out]):
global count
count = count + 1
- for t in kw['target']:
+ for t in target:
open(str(t), 'w').write("function3\n")
for t in tlist:
- if not t in map(str, kw['target']):
+ if not t in map(str, target):
open(t, 'w').write("function3\n")
return 1
builder = SCons.Builder.Builder(action = function3, name = "function3")
tgts = builder(env, target = [sub1_out, sub2_out], source = 'foo')
try:
- r = tgts[0].builder.execute(target = tgts)
+ r = tgts[0].builder.execute(tgts, 'foo', env)
except:
pass
assert r == 1, r
def test_Builder_Args(self):
"""Testing passing extra agrs to a builder."""
- def buildFunc(target, source, env, foo, bar, s=self):
- s.foo=foo
- s.bar=bar
+ def buildFunc(target, source, env, s=self):
+ s.foo=env['foo']
+ s.bar=env['bar']
+ assert env['CC'] == 'mycc'
+
+ env=Environment(CC='cc')
builder = SCons.Builder.Builder(name="builder", action=buildFunc)
- tgt = builder(env, target='foo', source='bar', foo=1, bar=2)
+ tgt = builder(env, target='foo', source='bar', foo=1, bar=2, CC='mycc')
tgt.build()
assert self.foo == 1, self.foo
assert self.bar == 2, self.bar
def test_emitter(self):
"""Test emitter functions."""
- def emit(target, source, env, foo=0, bar=0):
+ def emit(target, source, env):
+ foo = env.get('foo', 0)
+ bar = env.get('bar', 0)
if foo:
target.append("bar%d"%foo)
if bar:
import SCons.Platform
import SCons.Tool
-def installFunc(env, target, source):
+def installFunc(target, source, env):
try:
map(lambda t: os.unlink(str(t)), target)
except OSError:
pass
-
+
try:
SCons.Node.FS.file_link(str(source[0]), str(target[0]))
print 'Install file: "%s" as "%s"' % \
action=installFunc)
def our_deepcopy(x):
- """deepcopy lists and dictionaries, and just copy the reference
- for everything else."""
+ """deepcopy lists and dictionaries, and just copy the reference
+ for everything else."""
if SCons.Util.is_Dict(x):
copy = {}
for key in x.keys():
UserDict.__delitem__(self, item)
self.env.Replace()
+_rm = re.compile(r'\$[()]')
+
+class EnvProxy(UserDict):
+ """This is a dictionary-like class that is returned
+ by Environment.Override().
+
+ In addition to providing
+ normal dictionary-like access to the variables in the
+ Environment, it also exposes the functions subst()
+ and subst_list(), allowing users to easily do variable
+ interpolation when writing their FunctionActions
+ and CommandGeneratorActions."""
+
+ def __init__(self, env):
+ UserDict.__init__(self, env)
+
+ def subst(self, string, raw=0):
+ if raw:
+ regex_remove = None
+ else:
+ regex_remove = _rm
+ return SCons.Util.scons_subst(string, self.data, {}, regex_remove)
+
+ def subst_list(self, string, raw=0):
+ if raw:
+ regex_remove = None
+ else:
+ regex_remove = _rm
+ return SCons.Util.scons_subst_list(string, self.data, {}, regex_remove)
+
+ def Override(self, overrides):
+ if overrides:
+ proxy = EnvProxy(self)
+ proxy.update(overrides)
+ return proxy
+ else:
+ return self
+
class Environment:
"""Base class for construction Environments. These are
the primary objects used to communicate dependency and
if path: return prog
return None
+ def Override(self, overrides):
+ """
+ Produce a modified psuedo-environment whose variables
+ are overriden by the overrides dictionaries.
+
+ overrides - a dictionaru that will override
+ the variables of this environment.
+ """
+
+ if overrides:
+ proxy = EnvProxy(self._dict)
+ proxy.update(overrides)
+ return proxy
+ else:
+ return self
+
+ def get(self, key, default):
+ "Emulates the get() method of dictionaries."""
+ return self._dict.get(key, default)
+
class VarInterpolator:
def __init__(self, dest, src, prefix, suffix):
self.dest = dest
class EnvironmentTestCase(unittest.TestCase):
+ def test_Override(self):
+ env = Environment(ONE=1, TWO=2)
+ assert env['ONE'] == 1
+ assert env['TWO'] == 2
+ env2 = env.Override({'TWO':'10'})
+ assert env2['ONE'] == 1
+ assert env2['TWO'] == '10'
+
def test_Builder_calls(self):
"""Test Builder calls through different environments
"""
def __init__(self, factory):
self.factory = factory
- def execute(self, **kw):
+ def execute(self, target, source, env):
global built_it
built_it = 1
return 0
return {}
def get_scanner(self, skey):
return self.scanner
+ def Override(self, overrides):
+ return self
class BuildDirTestCase(unittest.TestCase):
def runTest(self):
fs.BuildDir('../var2', 'src')
f1 = fs.File('../var1/test1')
f2 = fs.File('../var2/test1')
+ assert hasattr(f1, 'overrides')
assert f1.srcpath == os.path.normpath('src/test1'), f1.srcpath
assert f2.srcpath == os.path.normpath('src/test1'), f2.srcpath
cycle_detected = None
class Builder:
- def execute(self, **kw):
+ def execute(self, target, source, env):
global built_it, built_target, built_source, built_args
built_it = 1
- built_target = kw['target']
- built_source = kw['source']
- built_args = kw
+ built_target = target
+ built_source = source
+ built_args = env
return 0
- def get_contents(self, env, target, source):
+ def get_contents(self, target, source, env):
return 7
class NoneBuilder(Builder):
- def execute(self, **kw):
- apply(Builder.execute, (self,), kw)
+ def execute(self, target, source, env):
+ Builder.execute(self, target, source, env)
return None
class ListBuilder(Builder):
def __init__(self, *nodes):
self.nodes = nodes
- def execute(self, **kw):
+ def execute(self, target, source, env):
if hasattr(self, 'status'):
return self.status
for n in self.nodes:
n.prepare()
- kw['target'] = self.nodes[0]
- self.status = apply(Builder.execute, (self,), kw)
+ target = self.nodes[0]
+ self.status = Builder.execute(self, target, source, env)
class FailBuilder:
- def execute(self, **kw):
+ def execute(self, target, source, env):
return 1
class ExceptBuilder:
- def execute(self, **kw):
+ def execute(self, target, source, env):
raise SCons.Errors.BuildError
class ExceptBuilder2:
- def execute(self, **kw):
+ def execute(self, target, source, env):
raise "foo"
class Environment:
return {}
def autogenerate(self, **kw):
return {}
+ def Override(selv, overrides):
+ return overrides
node.env_set(Environment())
node.path = "qqq"
node.sources = ["rrr", "sss"]
- node.build_args = { "foo" : 1, "bar" : 2 }
+ node.overrides = { "foo" : 1, "bar" : 2 }
node.build()
assert built_it
assert type(built_target) == type(MyNode()), type(built_target)
self.precious = None
self.found_includes = {}
self.includes = None
- self.build_args = {}
+ self.overrides = {} # construction variable overrides for building this node
self.attributes = self.Attrs() # Generic place to stick information about the Node.
self.side_effect = 0 # true iff this node is a side effect
self.side_effects = [] # the side effects of building this target
- def generate_build_args(self):
- dict = copy.copy(self.env.Dictionary())
+ def generate_build_env(self):
if hasattr(self, 'cwd'):
auto = self.env.autogenerate(dir = self.cwd)
else:
auto = self.env.autogenerate()
- dict.update(auto)
- dictArgs = { 'env' : dict,
- 'target' : self,
- 'source' : self.sources }
- dictArgs.update(self.build_args)
- return dictArgs
+ dict = {}
+ dict.update(auto)
+ dict.update(self.overrides)
+ return self.env.Override(dict)
def build(self):
"""Actually build the node. Return the status from the build."""
stat = self.builder.status
except AttributeError:
try:
- stat = apply(self.builder.execute, (),
- self.generate_build_args())
+ stat = self.builder.execute(self, self.sources, self.generate_build_env())
except KeyboardInterrupt:
raise
except UserError:
def __init__(self, node):
self.node = node
def get_contents(self):
- return apply(self.node.builder.get_contents, (),
- self.node.generate_build_args())
+ return self.node.builder.get_contents(self.node, self.node.sources, self.node.generate_build_env())
def get_timestamp(self):
return None
return Adapter(self)
if implicit_deps_unchanged or calc.current(self, calc.bsig(self)):
return
else:
- # one of this node's sources has changed, so
+ # one of this node's sources has changed, so
# we need to recalculate the implicit deps,
# and the bsig:
self.implicit = []
return [ [cmd[0], '@' + tmp],
['del', tmp] ]
-def win32LinkGenerator(env, target, source, for_signature, **kw):
+def win32LinkGenerator(env, target, source, for_signature):
args = [ '$LINK', '$LINKFLAGS', '/OUT:%s' % target[0],
'$(', '$_LIBDIRFLAGS', '$)', '$_LIBFLAGS' ]
args.extend(map(SCons.Util.to_String, source))
return win32TempFileMunge(env, args, for_signature)
-def win32LibGenerator(target, source, env, for_signature, no_import_lib=0):
+def win32LibGenerator(target, source, env, for_signature):
listCmd = [ "$SHLINK", "$SHLINKFLAGS" ]
+ no_import_lib = env.get('no_import_lib', 0)
for tgt in target:
ext = os.path.splitext(str(tgt))[1]
listCmd.append(str(src))
return win32TempFileMunge(env, listCmd, for_signature)
-def win32LibEmitter(target, source, env, no_import_lib=0):
+def win32LibEmitter(target, source, env):
dll = None
+ no_import_lib = env.get('no_import_lib', 0)
+
for tgt in target:
ext = os.path.splitext(str(tgt))[1]
if ext == env.subst("$SHLIBSUFFIX"):
test.run(arguments='foo.out',
status=2,
stderr="""
-SCons error: Two different sets of build arguments were specified for the same target: foo.out
+SCons error: Two different sets of overrides were specified for the same target: foo.out
File "SConstruct", line 10, in ?
""")
--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import TestSCons
+import sys
+
+
+test = TestSCons.TestSCons()
+
+
+python = sys.executable
+
+test.write('SConstruct', """
+env = Environment(LIBS=['a'])
+def build(target, source, env):
+ assert env['CC'] == 'mycc'
+ assert env['LIBS'] == ['a','b']
+builder = Builder(action=build)
+env['BUILDERS']['Build'] = builder
+
+Default(env.Build('foo', 'bar', CC='mycc', LIBS = env['LIBS']+['b']))
+""")
+
+test.run()
+
+test.write('SConstruct', """
+env = Environment()
+env.Program('hello', 'hello.c',
+ CC=r'%s mycc.py',
+ LINK=r'%s mylink.py',
+ OBJSUFFIX='.not_obj',
+ PROGSUFFIX='.not_exe')
+"""%(python,python))
+
+test.write('hello.c',"this ain't no c file!\n")
+
+test.write('mycc.py',"""
+open('hello.not_obj', 'wt').write('this is no object file!')
+""")
+
+test.write('mylink.py',"""
+open('hello.not_exe', 'wt').write('this is not a program!')
+""")
+
+test.run(arguments='hello.not_exe')
+
+assert test.read('hello.not_obj') == 'this is no object file!'
+assert test.read('hello.not_exe') == 'this is not a program!'
+
+test.up_to_date(arguments='hello.not_exe')
+
+test.pass_test()
+
+