if options:
arguments = options + " " + arguments
kw['arguments'] = arguments
- kw['stdout'] = self.wrap_stdout(read_str = read_str, build_str = s)
- kw['stdout'] = string.replace(kw['stdout'],'\n','\\n')
- kw['stdout'] = string.replace(kw['stdout'],'.','\\.')
+ stdout = self.wrap_stdout(read_str = read_str, build_str = s)
+ kw['stdout'] = re.escape(stdout)
kw['match'] = self.match_re_dotall
apply(self.run, [], kw)
This function is most useful in conjunction with the -n option.
"""
s = ""
- for arg in string.split(arguments):
- s = s + "(?!scons: `%s' is up to date.)" % arg
+ for arg in string.split(arguments):
+ s = s + "(?!scons: `%s' is up to date.)" % re.escape(arg)
if options:
arguments = options + " " + arguments
+ s = '('+s+'[^\n]*\n)*'
kw['arguments'] = arguments
- kw['stdout'] = self.wrap_stdout(build_str="("+s+"[^\n]*\n)*")
- kw['stdout'] = string.replace(kw['stdout'],'\n','\\n')
- kw['stdout'] = string.replace(kw['stdout'],'.','\\.')
+ stdout = re.escape(self.wrap_stdout(build_str='ARGUMENTSGOHERE'))
+ kw['stdout'] = string.replace(stdout, 'ARGUMENTSGOHERE', s)
kw['match'] = self.match_re_dotall
apply(self.run, [], kw)
def __init__(self, p):
self.pos = p
- def matchPart(log, logfile, lastEnd):
+ def matchPart(log, logfile, lastEnd, NoMatch=NoMatch):
m = re.match(log, logfile[lastEnd:])
if not m:
raise NoMatch, lastEnd
'interpreter' : python, # imported from TestSCons
}
- if os.path.exists(self.script_path('sconsign.py')):
- sconsign = 'sconsign.py'
- elif os.path.exists(self.script_path('sconsign')):
- sconsign = 'sconsign'
- else:
- print "Can find neither 'sconsign.py' nor 'sconsign' scripts."
- self.no_result()
+ if not kw.has_key('program'):
+ kw['program'] = os.environ.get('SCONS')
+ if not kw['program']:
+ if os.path.exists('scons'):
+ kw['program'] = 'scons'
+ else:
+ kw['program'] = 'scons.py'
+
+ sconsign = os.environ.get('SCONSIGN')
+ if not sconsign:
+ if os.path.exists(self.script_path('sconsign.py')):
+ sconsign = 'sconsign.py'
+ elif os.path.exists(self.script_path('sconsign')):
+ sconsign = 'sconsign'
+ else:
+ print "Can find neither 'sconsign.py' nor 'sconsign' scripts."
+ self.no_result()
self.set_sconsign(sconsign)
def script_path(self, script):
<scons_example name="ex2">
<file name="SConstruct" printme="1">
- import sys;
+ import sys
def BuildTestProg(env, testfile, resourcefile, testdir="tests"):
"""Build the test program;
prepends "test_" to src and target, and puts target into testdir."""
<scons_output_command>scons -Q</scons_output_command>
</scons_output>
+ <para>
+ Using AddMethod is better than just adding an instance method to an
+ Environment because it gets called as a proper method, and AddMethod
+ provides for copying the method to any copies of the Environment
+ instance.
+ </para>
cc -o tests/test_stuff test_stuff.o
</screen>
+ <para>
+ Using AddMethod is better than just adding an instance method to an
+ Environment because it gets called as a proper method, and AddMethod
+ provides for copying the method to any copies of the Environment
+ instance.
+ </para>
</section>
+ <section>
+ <title>Where To Put Your Custom Builders and Tools</title>
+
+ <para>
+
+ The <filename>site_scons</filename> directory gives you a place to
+ put Python modules you can import into your SConscripts
+ (site_scons), add-on tools that can integrate into &SCons;
+ (site_scons/site_tools), and a site_scons/site_init.py file that
+ gets read before any &SConstruct; or &SConscript;, allowing you to
+ change &SCons;'s default behavior.
+
+ </para>
+
+ <para>
+
+ If you get a tool from somewhere (the &SCons; wiki or a third party,
+ for instance) and you'd like to use it in your project, the
+ <filename>site_scons</filename> dir is the simplest place to put it.
+ Tools come in two flavors; either a Python function that operates on
+ an &Environment; or a Python file containing two functions, exists()
+ and generate().
+
+ </para>
+
+ <para>
+
+ A single-function Tool can just be included in your
+ <filename>site_scons/site_init.py</filename> file where it will be
+ parsed and made available for use. For instance, you could have a
+ <filename>site_scons/site_init.py</filename> file like this:
+
+ </para>
+
+ <scons_example name="site1">
+ <file name="site_scons/site_init.py" printme=1>
+ def TOOL_ADD_HEADER(env):
+ """A Tool to add a header from $HEADER to the source file"""
+ add_header = Builder(action=['echo "$HEADER" > $TARGET',
+ 'cat $SOURCE >> $TARGET'])
+ env.Append(BUILDERS = {'AddHeader' : add_header})
+ env['HEADER'] = '' # set default value
+ </file>
+ <file name="SConstruct">
+ env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
+ env.AddHeader('tgt', 'src')
+ </file>
+ <file name="src">
+ hi there
+ </file>
+ </scons_example>
+
+ <para>
+
+ and a &SConstruct; like this:
+
+ </para>
+
+ <sconstruct>
+ # Use TOOL_ADD_HEADER from site_scons/site_init.py
+ env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
+ env.AddHeader('tgt', 'src')
+ </sconstruct>
+
+ <para>
+
+ The <function>TOOL_ADD_HEADER</function> tool method will be
+ called to add the <function>AddHeader</function> tool to the
+ environment.
+
+ </para>
+
+ <!--
+ <scons_output example="site1" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+ -->
+
+ <para>
+ Similarly, a more full-fledged tool with
+ <function>exists()</function> and <function>generate()</function>
+ methods can be installed in
+ <filename>site_scons/site_tools/toolname.py</filename>. Since
+ <filename>site_scons/site_tools</filename> is automatically added
+ to the head of the tool search path, any tool found there will be
+ available to all environments. Furthermore, a tool found there
+ will override a built-in tool of the same name, so if you need to
+ change the behavior of a built-in tool, site_scons gives you the
+ hook you need.
+ </para>
+
+ <para>
+ Many people have a library of utility Python functions they'd like
+ to include in &SConscript;s; just put that module in
+ <filename>site_scons/my_utils.py</filename> or any valid Python module name of your
+ choice. For instance you can do something like this in
+ <filename>site_scons/my_utils.py</filename> to add a build_id method:
+ </para>
+
+ <scons_example name="site2">
+ <file name="site_scons/my_utils.py" printme=1>
+ def build_id():
+ """Return a build ID (stub version)"""
+ return "100"
+ </file>
+ <file name="SConscript">
+ import my_utils
+ print "build_id=" + my_utils.build_id()
+ </file>
+ </scons_example>
+
+ <para>
+
+ And then in your &SConscript; or any sub-&SConscript; anywhere in
+ your build, you can import <filename>my_utils</filename> and use it:
+
+ </para>
+
+ <sconstruct>
+ import my_utils
+ print "build_id=" + my_utils.build_id()
+ </sconstruct>
+
+ <para>
+
+ If you have a machine-wide site dir you'd like to use instead of
+ <filename>./site_scons</filename>, use the
+ <literal>--site-dir</literal> option to point to your dir.
+ <filename>site_init.py</filename> and
+ <filename>site_tools</filename> will be located under that dir.
+ To avoid using a <filename>site_scons</filename> dir at all, even
+ if it exists, use the <literal>--no-site-dir</literal> option.
+
+ </para>
+
+ </section>
+
+
<!--
<section>
</section>
+ <section>
+ <title>Where To Put Your Custom Builders and Tools</title>
+
+ <para>
+
+ The <filename>site_scons</filename> directory gives you a place to
+ put Python modules you can import into your SConscripts
+ (site_scons), add-on tools that can integrate into &SCons;
+ (site_scons/site_tools), and a site_scons/site_init.py file that
+ gets read before any &SConstruct; or &SConscript;, allowing you to
+ change &SCons;'s default behavior.
+
+ </para>
+
+ <para>
+
+ If you get a tool from somewhere (the &SCons; wiki or a third party,
+ for instance) and you'd like to use it in your project, the
+ <filename>site_scons</filename> dir is the simplest place to put it.
+ Tools come in two flavors; either a Python function that operates on
+ an &Environment; or a Python file containing two functions, exists()
+ and generate().
+
+ </para>
+
+ <para>
+
+ A single-function Tool can just be included in your
+ <filename>site_scons/site_init.py</filename> file where it will be
+ parsed and made available for use. For instance, you could have a
+ <filename>site_scons/site_init.py</filename> file like this:
+
+ </para>
+
+ <programlisting>
+ def TOOL_ADD_HEADER(env):
+ """A Tool to add a header from $HEADER to the source file"""
+ add_header = Builder(action=['echo "$HEADER" > $TARGET',
+ 'cat $SOURCE >> $TARGET'])
+ env.Append(BUILDERS = {'AddHeader' : add_header})
+ env['HEADER'] = '' # set default value
+ </programlisting>
+
+ <para>
+
+ and a &SConstruct; like this:
+
+ </para>
+
+ <programlisting>
+ # Use TOOL_ADD_HEADER from site_scons/site_init.py
+ env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
+ env.AddHeader('tgt', 'src')
+ </programlisting>
+
+ <para>
+
+ The <function>TOOL_ADD_HEADER</function> tool method will be
+ called to add the <function>AddHeader</function> tool to the
+ environment.
+
+ </para>
+
+ <!--
+ <scons_output example="site1" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+ -->
+
+ <para>
+ Similarly, a more full-fledged tool with
+ <function>exists()</function> and <function>generate()</function>
+ methods can be installed in
+ <filename>site_scons/site_tools/toolname.py</filename>. Since
+ <filename>site_scons/site_tools</filename> is automatically added
+ to the head of the tool search path, any tool found there will be
+ available to all environments. Furthermore, a tool found there
+ will override a built-in tool of the same name, so if you need to
+ change the behavior of a built-in tool, site_scons gives you the
+ hook you need.
+ </para>
+
+ <para>
+ Many people have a library of utility Python functions they'd like
+ to include in &SConscript;s; just put that module in
+ <filename>site_scons/my_utils.py</filename> or any valid Python module name of your
+ choice. For instance you can do something like this in
+ <filename>site_scons/my_utils.py</filename> to add a build_id method:
+ </para>
+
+ <programlisting>
+ def build_id():
+ """Return a build ID (stub version)"""
+ return "100"
+ </programlisting>
+
+ <para>
+
+ And then in your &SConscript; or any sub-&SConscript; anywhere in
+ your build, you can import <filename>my_utils</filename> and use it:
+
+ </para>
+
+ <programlisting>
+ import my_utils
+ print "build_id=" + my_utils.build_id()
+ </programlisting>
+
+ <para>
+
+ If you have a machine-wide site dir you'd like to use instead of
+ <filename>./site_scons</filename>, use the
+ <literal>--site-dir</literal> option to point to your dir.
+ <filename>site_init.py</filename> and
+ <filename>site_tools</filename> will be located under that dir.
+ To avoid using a <filename>site_scons</filename> dir at all, even
+ if it exists, use the <literal>--no-site-dir</literal> option.
+
+ </para>
+
+ </section>
+
+
<!--
<section>
XXX CheckTypeSize()
- XXX Glob()
-
XXX Progress()
XXX - - diskcheck=
- XXX site_scons
- XXX - - site-dir
- XXX - - no-site-dir
-
XXX - - warn=
XXX ARGLIST
XXX CheckTypeSize()
- XXX Glob()
-
XXX Progress()
XXX - - diskcheck=
- XXX site_scons
- XXX - - site-dir
- XXX - - no-site-dir
-
XXX - - warn=
XXX ARGLIST
+RELEASE 0.98.4 - XXX
+
+ From Benoit Belley:
+
+ - Fix calculation of signatures for Python function actions with
+ closures in Python versions before 2.5.
+
+ From David Cournapeau:
+
+ - Fix the initialization of $SHF77FLAGS so it includes $F77FLAGS.
+
+ From Jonas Olsson:
+
+ - Fix a syntax error in the Intel C compiler support on Windows.
+
+ From Steven Knight:
+
+ - Change how we represent Python Value Nodes when printing and when
+ stored in .sconsign files (to avoid blowing out memory by storing
+ huge strings in .sconsign files after multiple runs using Configure
+ contexts cause the Value strings to be re-escaped each time).
+
+ - Fix a regression in not executing configuration checks after failure
+ of any configuration check that used the same compiler or other tool.
+
+ - Handle multiple destinations in Visual Studio 8 settings for the
+ analogues to the INCLUDE, LIBRARY and PATH variables.
+
+
+
RELEASE 0.98.3 - Tue, 29 Apr 2008 22:40:12 -0700
From Greg Noel:
closure = []
#xxx = [_object_contents(x.cell_contents) for x in closure]
- xxx = map(lambda x: _object_contents(x.cell_contents), closure)
+ try:
+ xxx = map(lambda x: _object_contents(x.cell_contents), closure)
+ except AttributeError:
+ xxx = []
contents.append(',(' + string.join(xxx, ',') + ')')
return string.join(contents, '')
return c
def array(a):
def quote(s):
- return '"' + str(s) + '"'
+ try:
+ str_for_display = s.str_for_display
+ except AttributeError:
+ s = repr(s)
+ else:
+ s = str_for_display()
+ return s
return '[' + string.join(map(quote, a), ", ") + ']'
try:
strfunc = self.execfunction.strfunction
class DummyNode:
def __init__(self, name):
self.name = name
+ def str_for_display(self):
+ return '"' + self.name + '"'
def __str__(self):
return self.name
def rfile(self):
result = a("out", "in", env)
assert result.status == 7, result
s = sio.getvalue()
- assert s == 'execfunc(["out"], ["in"])\n', s
+ assert s == "execfunc(['out'], ['in'])\n", s
a.chdir = 'xyz'
- expect = 'os.chdir(%s)\nexecfunc(["out"], ["in"])\nos.chdir(%s)\n'
+ expect = "os.chdir(%s)\nexecfunc(['out'], ['in'])\nos.chdir(%s)\n"
sio = StringIO.StringIO()
sys.stdout = sio
result = b("out", "in", env)
assert result.status == 7, result.status
s = sio.getvalue()
- assert s == 'firstfunc(["out"], ["in"])\nexecfunc(["out"], ["in"])\n', s
+ assert s == "firstfunc(['out'], ['in'])\nexecfunc(['out'], ['in'])\n", s
SCons.Action.execute_actions = 0
result = a("out", "in", env)
assert result == 0, result
s = sio.getvalue()
- assert s == 'execfunc(["out"], ["in"])\n', s
+ assert s == "execfunc(['out'], ['in'])\n", s
sio = StringIO.StringIO()
sys.stdout = sio
result = b("out", "in", env)
assert result == 0, result
s = sio.getvalue()
- assert s == 'firstfunc(["out"], ["in"])\nexecfunc(["out"], ["in"])\nlastfunc(["out"], ["in"])\n', s
+ assert s == "firstfunc(['out'], ['in'])\nexecfunc(['out'], ['in'])\nlastfunc(['out'], ['in'])\n", s
SCons.Action.print_actions_presub = 1
SCons.Action.execute_actions = 1
result = a("out", "in", env)
assert result.status == 7, result.status
s = sio.getvalue()
- assert s == 'Building out with action:\n execfunc(target, source, env)\nexecfunc(["out"], ["in"])\n', s
+ assert s == "Building out with action:\n execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
sio = StringIO.StringIO()
sys.stdout = sio
result = a("out", "in", env, presub=0)
assert result.status == 7, result.status
s = sio.getvalue()
- assert s == 'execfunc(["out"], ["in"])\n', s
+ assert s == "execfunc(['out'], ['in'])\n", s
sio = StringIO.StringIO()
sys.stdout = sio
result = a("out", "in", env, presub=1)
assert result.status == 7, result.status
s = sio.getvalue()
- assert s == 'Building out with action:\n execfunc(target, source, env)\nexecfunc(["out"], ["in"])\n', s
+ assert s == "Building out with action:\n execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
sio = StringIO.StringIO()
sys.stdout = sio
result = b(["out"], "in", env, presub=1)
assert result.status == 7, result.status
s = sio.getvalue()
- assert s == 'Building out with action:\n firstfunc(target, source, env)\nfirstfunc(["out"], ["in"])\nBuilding out with action:\n execfunc(target, source, env)\nexecfunc(["out"], ["in"])\n', s
+ assert s == "Building out with action:\n firstfunc(target, source, env)\nfirstfunc(['out'], ['in'])\nBuilding out with action:\n execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
sio = StringIO.StringIO()
sys.stdout = sio
result = b(["out", "list"], "in", env, presub=1)
assert result.status == 7, result.status
s = sio.getvalue()
- assert s == 'Building out and list with action:\n firstfunc(target, source, env)\nfirstfunc(["out", "list"], ["in"])\nBuilding out and list with action:\n execfunc(target, source, env)\nexecfunc(["out", "list"], ["in"])\n', s
+ assert s == "Building out and list with action:\n firstfunc(target, source, env)\nfirstfunc(['out', 'list'], ['in'])\nBuilding out and list with action:\n execfunc(target, source, env)\nexecfunc(['out', 'list'], ['in'])\n", s
a2 = SCons.Action.Action(execfunc)
result = a2("out", "in", env)
assert result.status == 7, result.status
s = sio.getvalue()
- assert s == 'Building out with action:\n execfunc(target, source, env)\nexecfunc(["out"], ["in"])\n', s
+ assert s == "Building out with action:\n execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
sio = StringIO.StringIO()
sys.stdout = sio
result = a2("out", "in", env, presub=0)
assert result.status == 7, result.status
s = sio.getvalue()
- assert s == 'execfunc(["out"], ["in"])\n', s
+ assert s == "execfunc(['out'], ['in'])\n", s
SCons.Action.execute_actions = 0
result = a2("out", "in", env, presub=0)
assert result == 0, result
s = sio.getvalue()
- assert s == 'execfunc(["out"], ["in"])\n', s
+ assert s == "execfunc(['out'], ['in'])\n", s
sio = StringIO.StringIO()
sys.stdout = sio
result.append(s)
env['PRINT_CMD_LINE_FUNC'] = my_print_cmd_line
a("output", "input", env)
- assert result == ['execfunc(["output"], ["input"])'], result
+ assert result == ["execfunc(['output'], ['input'])"], result
finally:
SCons.Node.Node.__init__(self)
self.name = name
+ def str_for_display(self):
+ return '"' + self.__str__() + '"'
+
def __str__(self):
return self.name
self.cwd = None # will hold the SConscript directory for target nodes
self.duplicate = directory.duplicate
+ def str_for_display(self):
+ return '"' + self.__str__() + '"'
+
def must_be_same(self, klass):
"""
This node, which already existed, is being looked up as the
if not built_value is None:
self.built_value = built_value
- def __str__(self):
+ def str_for_display(self):
return repr(self.value)
+ def __str__(self):
+ return str(self.value)
+
def make_ready(self):
self.get_csig()
while self.candidates:
candidates = self.candidates
self.candidates = []
- self.will_not_build(candidates)
+ self.will_not_build(candidates, lambda n: n.state < NODE_UP_TO_DATE)
return None
def _find_next_ready_node(self):
# take a stab at evaluating them (or their children).
children_not_visited.reverse()
self.candidates.extend(self.order(children_not_visited))
+ #if T and children_not_visited:
+ # T.write('Taskmaster: adding to candidates: %s\n' % map(str, children_not_visited))
+ # T.write('Taskmaster: candidates now: %s\n' % map(str, self.candidates))
# Skip this node if any of its children have failed.
#
return task
- def will_not_build(self, nodes):
+ def will_not_build(self, nodes, mark_fail=lambda n: n.state != NODE_FAILED):
"""
Perform clean-up about nodes that will never be built.
"""
for node in nodes:
# Set failure state on all of the parents that were dependent
# on this failed build.
- if node.state != NODE_FAILED:
- node.state = NODE_FAILED
+ if mark_fail(node):
+ node.set_state(NODE_FAILED)
parents = node.waiting_parents
to_visit = to_visit | parents
pending_children = pending_children - parents
to_visit.remove(node)
else:
break
- if node.state != NODE_FAILED:
- node.state = NODE_FAILED
+ if mark_fail(node):
+ node.set_state(NODE_FAILED)
parents = node.waiting_parents
to_visit = to_visit | parents
pending_children = pending_children - parents
env['SMART_LINKCOM'] = smart_link[linkcom]
except KeyError:
def _smartLink(source, target, env, for_signature,
- defaultLinker=linkcom):
+ defaultLinker=linkcom, dc=dc):
if isD(source):
try:
libs = env['LIBS']
env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS')
env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS')
else:
- env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$SHFORTRANFLAGS -fPIC')
- env['SHF77FLAGS'] = SCons.Util.CLVar('$SHF77FLAGS -fPIC')
+ env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -fPIC')
+ env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -fPIC')
env['FORTRAN'] = fcomp
env['SHFORTRAN'] = '$FORTRAN'
if version is None:
version = ''
# Each path has a registry entry, use that or default to subdir
- for p in paths.keys():
+ for p in paths:
try:
path=get_intel_registry_value(p[1], version, abi)
# These paths may have $(ICInstallDir)
user_settings = doc.getElementsByTagName('UserSettings')[0]
tool_options = user_settings.getElementsByTagName('ToolsOptions')[0]
tool_options_categories = tool_options.getElementsByTagName('ToolsOptionsCategory')
+ environment_var_map = {
+ 'IncludeDirectories' : 'INCLUDE',
+ 'LibraryDirectories' : 'LIBRARY',
+ 'ExecutableDirectories' : 'PATH',
+ }
for category in tool_options_categories:
category_name = category.attributes.get('name')
if category_name is not None and category_name.value == 'Projects':
property_name = property.attributes.get('name')
if property_name is None:
continue
- elif property_name.value == 'IncludeDirectories':
- include_dirs = property.childNodes[0].data
- # ToDo: Support for other destinations than Win32
- include_dirs = include_dirs.replace('Win32|', '')
- dirs['INCLUDE'] = include_dirs
- elif property_name.value == 'LibraryDirectories':
- lib_dirs = property.childNodes[0].data.replace('Win32|', '')
- # ToDo: Support for other destinations than Win32
- lib_dirs = lib_dirs.replace('Win32|', '')
- dirs['LIBRARY'] = lib_dirs
- elif property_name.value == 'ExecutableDirectories':
- path_dirs = property.childNodes[0].data.replace('Win32|', '')
- # ToDo: Support for other destinations than Win32
- path_dirs = path_dirs.replace('Win32|', '')
- dirs['PATH'] = path_dirs
+ var_name = environment_var_map.get(property_name)
+ if var_name:
+ data = property.childNodes[0].data
+ value_list = string.split(data, '|')
+ if len(value_list) == 1:
+ dirs[var_name] = value_list[0]
+ else:
+ while value_list:
+ dest, value = value_list[:2]
+ del value_list[:2]
+ # ToDo: Support for destinations
+ # other than Win32
+ if dest == 'Win32':
+ dirs[var_name] = value
+ break
else:
# There are no default directories in the registry for VS8 Express :(
raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry."
l = []
for k in keys:
l.append('%s: %s' % (repr(k), repr(d.get(k))))
+ if '\n' in name:
+ name = repr(name)
return name + ': {' + string.join(l, ', ') + '}'
def nodeinfo_cooked(name, ninfo, prefix=""):
except AttributeError:
field_list = []
f = lambda x, ni=ninfo, v=Verbose: field(x, ni, v)
+ if '\n' in name:
+ name = repr(name)
outlist = [name+':'] + filter(None, map(f, field_list))
if Verbose:
sep = '\n ' + prefix
r = re.compile(optEnv['regexp'])
-toto = \
+withClosure = \
+r'''
+def toto(header='%(header)s', trailer='%(trailer)s'):
+ xxx = %(closure_cell_value)s
+ def writeDeps(target, source, env, b=%(b)s, r=r %(extraarg)s ,
+ header=header, trailer=trailer):
+ """+'"""%(docstring)s"""'+"""
+ def foo(b=b):
+ return %(nestedfuncexp)s
+ f = open(str(target[0]),'wb')
+ f.write(header)
+ for d in env['ENVDEPS']:
+ f.write(d+'%(separator)s')
+ f.write(trailer+'\\n')
+ f.write(str(foo())+'\\n')
+ f.write(r.match('aaaa').group(1)+'\\n')
+ %(extracode)s
+ try:
+ f.write(str(xarg)+'\\n')
+ except NameError:
+ pass
+ f.close()
+
+ return writeDeps
+'''
+
+NoClosure = \
r'''
def toto(header='%(header)s', trailer='%(trailer)s'):
xxx = %(closure_cell_value)s
return writeDeps
'''
-exec( toto % optEnv )
+try:
+ # Check that lexical closure are supported
+ def a():
+ x = 0
+ def b():
+ return x
+ return b
+ a().func_closure[0].cell_contents
+ exec( withClosure % optEnv )
+except (AttributeError, TypeError):
+ exec( NoClosure % optEnv )
genHeaderBld = SCons.Builder.Builder(
action = SCons.Action.Action(
scons: done building targets.
"""
-def runtest( arguments, expectedOutFile, expectedRebuild=True):
+import sys
+if sys.version[:3] == '2.1':
+ expectedStderr = """\
+%s:79: SyntaxWarning: local name 'x' in 'a' shadows use of 'x' as global in nested scope 'b'
+ def a():
+""" % test.workpath('SConstruct')
+else:
+ expectedStderr = ""
+
+def runtest(arguments, expectedOutFile, expectedRebuild=True, stderr=expectedStderr):
test.run(arguments=arguments,
- stdout=expectedRebuild and rebuildstr or nobuildstr)
+ stdout=expectedRebuild and rebuildstr or nobuildstr,
+ stderr=expectedStderr)
test.must_match('Out.gen.h', expectedOutFile)
# Should not be rebuild when ran a second time with the same
# arguments.
- test.run(arguments = arguments, stdout=nobuildstr)
+ test.run(arguments = arguments, stdout=nobuildstr, stderr=expectedStderr)
test.must_match('Out.gen.h', expectedOutFile)
--- /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__"
+
+"""
+Verify that Configure tests work even after an earlier test fails.
+
+This was broken in 0.98.3 because we'd mark the /usr/bin/g++ compiler
+as having failed (because it was on the candidates list as the implicit
+command dependency for both the object file and executable generated
+for the configuration test) and then avoid trying to rebuild anything
+else that used the "failed" Node.
+
+Thanks to Ben Webb for the test case.
+"""
+
+import os
+
+import TestSCons
+
+_obj = TestSCons._obj
+
+test = TestSCons.TestSCons(match = TestSCons.match_re_dotall)
+
+test.subdir('a', 'b')
+
+a_boost_hpp = os.path.join('..', 'a', 'boost.hpp')
+b_boost_hpp = os.path.join('..', 'b', 'boost.hpp')
+
+test.write('SConstruct', """\
+import os
+def _check(context):
+ for dir in ['a', 'b']:
+ inc = os.path.join('..', dir, 'boost.hpp')
+ result = context.TryRun('''
+ #include "%s"
+
+ int main() { return 0; }
+ ''' % inc, '.cpp')[0]
+ if result:
+ import sys
+ sys.stdout.write('%s: ' % inc)
+ break
+ context.Result(result)
+ return result
+env = Environment()
+conf = env.Configure(custom_tests={'CheckBoost':_check})
+conf.CheckBoost()
+conf.Finish()
+""")
+
+test.write(['b', 'boost.hpp'], """#define FILE "b/boost.hpp"\n""")
+
+expect = test.wrap_stdout(read_str = "%s: yes\n" % b_boost_hpp,
+ build_str = "scons: `.' is up to date.\n")
+
+test.run(arguments='--config=force', stdout=expect)
+
+expect = test.wrap_stdout(read_str = "%s: yes\n" % a_boost_hpp,
+ build_str = "scons: `.' is up to date.\n")
+
+test.write(['a', 'boost.hpp'], """#define FILE "a/boost.hpp"\n""")
+
+test.run(arguments='--config=force', stdout=expect)
+
+test.run()
+
+test.pass_test()
--- /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__"
+
+"""
+Verify that use of --implicit-cache with the Python Value Nodes
+used by the Configure subsystem generate the same .sconsign file
+and don't cause it to grow without limit.
+
+This was reported as issue 2033 in the tigris.org bug tracker, by the
+Ardour project. Prior to 0.98.4, the Value implementation would actually
+return the repr() of its value as the str(). This was done because
+it made saving a Value in a file and reading it back in kind of work,
+because a print a string Value into a file (for example) would in fact
+put quotes around it and be assignable in that file.
+
+The problem is that this would get stored in a .sconsign file as its
+repr(), with the specific problem being that Values with embedded newlines
+would get stored as strings containing backslash+n digraphs *and* the
+quotes at beginning and end of the string::
+
+ '\n#include <math.h>\n\n': {<.sconsign info>}
+
+Then, when we read that back in from the .sconsign file, we would store
+that repr() as a string Value itself, escaping the backslashes and
+including the quotes, so when we stored it the second time it would end
+up looking like:
+
+ "'\\n#include <math.h>\\n\\n'": {<.sconsign info>}
+
+Every time that we would read this value and store it again (because
+something else changed in the .sconf_temp directory), the string would
+get longer and longer until it blew out the users's memory.
+"""
+
+import TestSConsign
+
+test = TestSConsign.TestSConsign()
+
+test.write('SConstruct', """
+env = Environment(CPPPATH=['.'])
+conf = Configure(env)
+conf.CheckHeader( 'math.h' )
+if ARGUMENTS.get('USE_FOO'):
+ conf.CheckHeader( 'foo.h' )
+env = conf.Finish()
+""")
+
+test.write('foo.h', "#define FOO 1\n")
+
+# First run: Have the configure subsystem only look for math.h, and
+# squirrel away the .sconsign info for the conftest_0.c file that's
+# generated from the Python Value Node that we're using for our test.
+
+test.run(arguments = '.')
+
+test.run_sconsign('-d .sconf_temp -e conftest_0.c --raw .sconsign.dblite')
+old_sconsign_dblite = test.stdout()
+
+# Second run: Have the configure subsystem also look for foo.h, so
+# that there's a change in the .sconf_temp directory that will cause its
+# .sconsign information to get rewritten from disk. Squirrel away the
+# .sconsign info for the conftest_0.c file. The now-fixed bug would show
+# up because the entry would change with the additional string-escaping
+# described above. The now-correct behavior is that the re-stored value
+# for conftest_0.c doesn't change.
+
+test.run(arguments = '--implicit-cache USE_FOO=1 .')
+
+test.run_sconsign('-d .sconf_temp -e conftest_0.c --raw .sconsign.dblite')
+new_sconsign_dblite = test.stdout()
+
+if old_sconsign_dblite != new_sconsign_dblite:
+ print ".sconsign.dblite did not match:"
+ print "FIRST RUN =========="
+ print old_sconsign_dblite
+ print "SECOND RUN =========="
+ print new_sconsign_dblite
+ test.fail_test()
+
+test.pass_test()
Verify that we handle keyboard interrupts (CTRL-C) correctly.
"""
-import os
-
import TestSCons
test = TestSCons.TestSCons()
-if 'killpg' not in dir(os) or 'setpgrp' not in dir(os):
- test.skip_test("This Python version does not support killing process groups; skipping test.\n")
-
test.write('toto.c', r"""
void foo()
{}
test.write('SConstruct', r"""
import os
+import sys
import signal
-# Make sure that SCons is a process group leader.
-os.setpgrp()
+if 'killpg' not in dir(os) or 'setpgrp' not in dir(os) or sys.platform == 'cygwin':
+ # The platform does not support process group. Therefore, we
+ # directly invoked the SIGINT handler to simulate a
+ # KeyboardInterrupt. This hack is necessary because there is no
+ # easy way to get access to the current Job/Taskmaster object.
+ #
+ # Note that this way of performing the test is not as good as
+ # using killpg because the Taskmaster is stopped synchronously. In
+ # addition, the SCons subprocesses (or forked children before the
+ # exec() of the subprocess) are never killed. This therefore
+ # exercise less SCons functionality.
+ #
+ # FIXME: There seems to be a bug on Cygwin where the compiler
+ # process hangs after sending the SIGINT signal to the process
+ # group. It is probably a bug in cygwin1.dll, or maybe in the
+ # Python 'C' code or the Python subprocess module. We therefore do
+ # not use 'killpg' on Cygwin.
+ def explode(env, target, source):
+ handler = signal.getsignal(signal.SIGINT)
+ handler(signal.SIGINT, None)
+ return 0
+else:
+ # The platform does support process group so we use killpg to send
+ # a SIGINT to everyone.
+
+ # Make sure that SCons is a process group leader.
+ os.setpgrp()
+
+ def explode(env, target, source):
+ os.killpg(0, signal.SIGINT)
-all = []
-def explode(env, target, source):
- os.killpg(0, signal.SIGINT)
+all = []
for i in xrange(40):
- all += Object('toto%5d' % i, 'toto.c')
+ all.extend(Object('toto%5d' % i, 'toto.c'))
-all+= Command( 'broken', 'toto.c', explode)
+all.extend(Command( 'broken', 'toto.c', explode))
Default( Alias('all', all))
"""
test.run(arguments='-c')
test.run()
- out7 = """create_value(["'my value'"], ["f3.out"])"""
- out8 = """create_value_file(["f5.out"], ["'my value'"])"""
+ out7 = """create_value(['my value'], ["f3.out"])"""
+ out8 = """create_value_file(["f5.out"], ['my value'])"""
- out1 = """create(["f1.out"], ["'/usr/local'"])"""
- out2 = """create(["f2.out"], ["10"])"""
- out3 = """create\\(\\["f3.out"\\], \\["<.*.Custom instance at """
+ out1 = """create(["f1.out"], ['/usr/local'])"""
+ out2 = """create(["f2.out"], [10])"""
+ out3 = """create\\(\\["f3.out"\\], \\[<.*.Custom instance at """
#" <- unconfuses emacs syntax highlighting
test.fail_test(string.find(test.stdout(), out1) == -1)
test.up_to_date(arguments='.')
test.run(arguments='prefix=/usr')
- out4 = """create(["f1.out"], ["'/usr'"])"""
- out5 = """create(["f2.out"], ["4"])"""
- out6 = """create\\(\\["f3.out"\\], \\["<.*.Custom instance at """
+ out4 = """create(["f1.out"], ['/usr'])"""
+ out5 = """create(["f2.out"], [4])"""
+ out6 = """create\\(\\["f3.out"\\], \\[<.*.Custom instance at """
#" <- unconfuses emacs syntax highlighting
test.fail_test(string.find(test.stdout(), out4) == -1)
test.fail_test(string.find(test.stdout(), out5) == -1)
test.unlink('f3.out')
test.run(arguments='prefix=/var')
- out4 = """create(["f1.out"], ["'/var'"])"""
+ out4 = """create(["f1.out"], ['/var'])"""
test.fail_test(string.find(test.stdout(), out4) == -1)
test.fail_test(string.find(test.stdout(), out5) != -1)
test.subdir(['src'], ['src', 'subdir'])
-subdir_file8 = os.path.join('subdir', 'file8')
subdir_file7 = os.path.join('subdir', 'file7')
subdir_file7_in = os.path.join('subdir', 'file7.in')
+subdir_file8 = os.path.join('subdir', 'file8')
+subdir_file9 = os.path.join('subdir', 'file9')
cat_py = test.workpath('cat.py')
inc_aaa = test.workpath('inc', 'aaa')
AlwaysBuild(file6)
env.Cat('subdir/file7', 'subdir/file7.in')
env.OneCat('subdir/file8', ['subdir/file7.in', env.Value(%(test_value)s)] )
+env.OneCat('subdir/file9', ['subdir/file7.in', env.Value(7)] )
""" % valueDict )
test_value = '"first"'
%(_python_)s %(cat_py)s %(subdir_file7)s %(subdir_file7_in)s
scons: building `%(subdir_file8)s' because it doesn't exist
%(_python_)s %(cat_py)s %(subdir_file8)s %(subdir_file7_in)s
+scons: building `%(subdir_file9)s' because it doesn't exist
+%(_python_)s %(cat_py)s %(subdir_file9)s %(subdir_file7_in)s
""" % locals())
test.run(chdir='src', arguments=args, stdout=expect)
scons: rebuilding `file6' because AlwaysBuild() is specified
%(_python_)s %(cat_py)s file6 file6.in
scons: rebuilding `%(subdir_file8)s' because:
- `"'first'"' is no longer a dependency
- `'second'' is a new dependency
+ `first' is no longer a dependency
+ `second' is a new dependency
%(_python_)s %(cat_py)s %(subdir_file8)s %(subdir_file7_in)s
""" % locals())