.fi
.RE
..
-.TH SCONS 1 "August 2004"
+.TH SCONS 1 "October 2004"
.SH NAME
scons \- a software construction tool
.SH SYNOPSIS
This looks for a tool specification in tools/foo.py (as well as
using the ordinary default tools for the platform). foo.py should
-have two functions: generate(env) and exists(env). generate()
-modifies the passed in environment and exists() should return a true
-value if the tool is available. Tools in the toolpath are used before
+have two functions: generate(env, **kw) and exists(env).
+The
+.B generate()
+function
+modifies the passed in environment
+to set up variables so that the tool
+can be executed;
+it may use any keyword arguments
+that the user supplies (see below)
+to vary its initialization.
+The
+.B exists()
+function should return a true
+value if the tool is available.
+Tools in the toolpath are used before
any of the built-in ones. For example, adding gcc.py to the toolpath
would override the built-in gcc tool.
env = Environment(tools = [my_tool])
.EE
+The individual elements of the tools list
+may also themselves be two-element lists of the form
+.RI ( toolname ", " kw_dict ).
+SCons searches for the
+.I toolname
+specification file as described above, and
+passes
+.IR kw_dict ,
+which must be a dictionary, as keyword arguments to the tool's
+.B generate
+function.
+The
+.B generate
+function can use the arguments to modify the tool's behavior
+by setting up the environment in different ways
+or otherwise changing its initialization.
+
+.ES
+# in tools/my_tool.py:
+def generate(env, **kw):
+ # Sets MY_TOOL to the value of keyword argument 'arg1' or 1.
+ env['MY_TOOL'] = kw.get('arg1', '1')
+def exists(env):
+ return 1
+
+# in SConstruct:
+env = Environment(tools = ['default', ('my_tool', {'arg1': 'abc'})],
+ toolpath=['tools'])
+.EE
+
The tool definition (i.e. my_tool()) can use the PLATFORM variable from
the environment it receives to customize the tool for different platforms.
'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.TP
-.RI Tool( string, toolpath=[] )
+.RI Tool( string [, toolpath ", " **kw ])
Returns a callable object
that can be used to initialize
a construction environment using the
tools keyword of the Environment() method.
The object may be called with a construction
environment as an argument,
-in which case the object will be
+in which case the object will
add the necessary variables
to the construction environment
and the name of the tool will be added to the
.B $TOOLS
construction variable.
+Additional keyword arguments are passed to the tool's
+.B generate()
+method.
+
.ES
env = Environment(tools = [ Tool('msvc') ])
u(env) # adds 'opengl' to the TOOLS variable
.EE
.TP
-.RI env.Tool( string [, toolpath] )
+.RI env.Tool( string [, toolpath ", " **kw ])
Applies the callable object for the specified tool
.I string
to the environment through which the method was called.
+Additional keyword arguments are passed to the tool's
+.B generate()
+method.
+
.ES
env.Tool('gcc')
env.Tool('opengl', toolpath = ['build/tools'])
- Add a --debug=findlibs option to print what's happening when
the scanner is searching for libraries.
+ - Allow Tool specifications to be passed a dictionary of keyword
+ arguments.
+
From Chris Pawling:
- Have the linkloc tool use $MSVS_VERSION to select the Microsoft
return copy
def apply_tools(env, tools, toolpath):
- if tools:
- # Filter out null tools from the list.
- tools = filter(None, tools)
- for tool in tools:
- if SCons.Util.is_String(tool):
- env.Tool(tool, toolpath)
- else:
- tool(env)
+ if not tools:
+ return
+ # Filter out null tools from the list.
+ for tool in filter(None, tools):
+ if SCons.Util.is_List(tool) or type(tool)==type(()):
+ toolname = tool[0]
+ toolargs = tool[1] # should be a dict of kw args
+ tool = apply(env.Tool, (toolname, toolpath), toolargs)
+ else:
+ env.Tool(tool, toolpath)
# These names are controlled by SCons; users should never set or override
# them. This warning can optionally be turned off, but scons will still
self._dict[key] = self._dict[key] + val
self.scanner_map_delete(kw)
- def Copy(self, tools=None, toolpath=[], **kw):
+ def Copy(self, tools=[], toolpath=[], **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
del kw[k]
apply(self.Replace, (), kw)
- def Tool(self, tool, toolpath=[]):
- tool = self.subst(tool)
- return SCons.Tool.Tool(tool, map(self.subst, toolpath))(self)
+ def Tool(self, tool, toolpath=[], **kw):
+ if SCons.Util.is_String(tool):
+ tool = self.subst(tool)
+ toolpath = map(self.subst, toolpath)
+ tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
+ tool(self)
def WhereIs(self, prog, path=None, pathext=None, reject=[]):
"""Find prog in the path.
t4(env)
assert env['TOOL4'] == 444, env
+ test = TestCmd.TestCmd(workdir = '')
+ test.write('faketool.py', """\
+def generate(env, **kw):
+ for k, v in kw.items():
+ env[k] = v
+
+def exists(env):
+ return 1
+""")
+
+ env = Environment(tools = [('faketool', {'a':1, 'b':2, 'c':3})],
+ toolpath = [test.workpath('')])
+ assert env['a'] == 1, env['a']
+ assert env['b'] == 2, env['b']
+ assert env['c'] == 3, env['c']
+
def test_Default_TOOLS(self):
"""Test overriding the default TOOLS variable"""
def t5(env):
import SCons.Defaults
class ToolSpec:
- def __init__(self, name):
+ def __init__(self, name, **kw):
self.name = name
+ # remember these so we can merge them into the call
+ self.init_kw = kw
def __call__(self, env, *args, **kw):
+ if self.init_kw is not None:
+ # Merge call kws into init kws;
+ # but don't bash self.init_kw.
+ if kw is not None:
+ call_kw = kw
+ kw = self.init_kw.copy()
+ kw.update(call_kw)
+ else:
+ kw = self.init_kw
env.Append(TOOLS = [ self.name ])
apply(self.generate, ( env, ) + args, kw)
def __str__(self):
return self.name
-def Tool(name, toolpath=[]):
+def Tool(name, toolpath=[], **kw):
"Select a canned Tool specification, optionally searching in toolpath."
try:
file, path, desc = imp.find_module(name, toolpath)
try:
module = imp.load_module(name, file, path, desc)
- spec = ToolSpec(name)
+ spec = apply(ToolSpec, (name,), kw)
spec.generate = module.generate
spec.exists = module.exists
return spec
raise SCons.Errors.UserError, "No tool named '%s': %s" % (name, e)
if file:
file.close()
- spec = ToolSpec(name)
+ spec = apply(ToolSpec, (name,), kw)
spec.generate = sys.modules[full_name].generate
spec.exists = sys.modules[full_name].exists
return spec
--- /dev/null
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# 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__"
+
+"""
+Test the ability to pass a dictionary of keyword arguments to
+a Tool specification's generate() method.
+"""
+
+import os.path
+import sys
+import time
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+# Test passing kw args to Tool constructor
+env1 = Environment(tools=[Tool('FooTool', toolpath=['.'], kw1='kw1val')])
+print "env1['TOOL_FOO'] =", env1.get('TOOL_FOO')
+print "env1['kw1'] =", env1.get('kw1')
+
+# Test apply_tools taking a list of (name, kwargs_dict)
+env2 = Environment(tools=[('FooTool', {'kw2':'kw2val'})], toolpath=['.'])
+print "env2['TOOL_FOO'] =", env2.get('TOOL_FOO')
+print "env2['kw2'] =", env2.get('kw2')
+
+""")
+
+test.write('FooTool.py', r"""\
+def generate(env, **kw):
+ for k in kw.keys():
+ env[k] = kw[k]
+ env['TOOL_FOO'] = 1
+def exists(env):
+ return 1
+""")
+
+test.run(arguments = '.', stdout = """\
+scons: Reading SConscript files ...
+env1['TOOL_FOO'] = 1
+env1['kw1'] = kw1val
+env2['TOOL_FOO'] = 1
+env2['kw2'] = kw2val
+scons: done reading SConscript files.
+scons: Building targets ...
+scons: `.' is up to date.
+scons: done building targets.
+""")
+
+test.pass_test()