summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Oberbrunner <garyo@oberbrunner.com>2013-11-10 14:55:24 -0500
committerGary Oberbrunner <garyo@oberbrunner.com>2013-11-10 14:55:24 -0500
commitac67fcbb6eb6284a461df1703b7f8f57bedc0f17 (patch)
treeb13805434b8db4150b113b93921066dc402a63aa
parentd612abd4a499483759fc66ca487c1542902da243 (diff)
parentba67ed921c3e6013e3270d2045718488f707b61b (diff)
downloadscons-ac67fcbb6eb6284a461df1703b7f8f57bedc0f17.tar.gz
Merged in dirkbaechle/scons (pull request #91)
Adding EPUB as output format for documentation
-rw-r--r--QMTest/TestSCons.py8
-rw-r--r--doc/generated/examples/troubleshoot_explain1_3.xml7
-rw-r--r--doc/man/scons.xml36
-rw-r--r--doc/user/troubleshoot.xml12
-rwxr-xr-x[-rw-r--r--]runtest.py38
-rw-r--r--src/CHANGES.txt20
-rw-r--r--src/engine/SCons/Action.py14
-rw-r--r--src/engine/SCons/Builder.py7
-rw-r--r--src/engine/SCons/CacheDir.py8
-rw-r--r--src/engine/SCons/Debug.py4
-rw-r--r--src/engine/SCons/Environment.py43
-rw-r--r--src/engine/SCons/EnvironmentTests.py31
-rw-r--r--src/engine/SCons/Executor.py5
-rw-r--r--src/engine/SCons/Node/FS.py11
-rw-r--r--src/engine/SCons/Node/NodeTests.py13
-rw-r--r--src/engine/SCons/Node/__init__.py19
-rw-r--r--src/engine/SCons/Platform/posix.py171
-rw-r--r--src/engine/SCons/Script/Main.py35
-rw-r--r--src/engine/SCons/Script/Main.xml21
-rw-r--r--src/engine/SCons/Script/SConsOptions.py47
-rw-r--r--src/engine/SCons/Tool/__init__.py8
-rw-r--r--src/engine/SCons/Tool/rpm.py4
-rw-r--r--src/engine/SCons/Warnings.py3
-rw-r--r--test/CacheDir/option--cr.py138
-rw-r--r--test/Docbook/basedir/htmlchunked/htmlchunked.py6
-rw-r--r--test/Docbook/basedir/htmlhelp/htmlhelp.py6
-rw-r--r--test/Docbook/basedir/slideshtml/slideshtml.py7
-rw-r--r--test/Docbook/basic/slideshtml/slideshtml.py7
-rw-r--r--test/Docbook/rootname/htmlchunked/htmlchunked.py6
-rw-r--r--test/Docbook/rootname/htmlhelp/htmlhelp.py6
-rw-r--r--test/Docbook/rootname/slideshtml/slideshtml.py7
-rw-r--r--test/Libs/SharedLibrary-update-deps.py17
-rw-r--r--test/Libs/bug2903/SConstruct13
-rw-r--r--test/Libs/bug2903/SConstruct-libs5
-rw-r--r--test/Libs/bug2903/lib.c4
-rw-r--r--test/Libs/bug2903/main.c6
-rw-r--r--test/Libs/bug2909/SConstruct3
-rw-r--r--test/Libs/bug2909/SConstruct-libs3
-rw-r--r--test/Libs/bug2909/lib.c1
-rw-r--r--test/Libs/bug2909/main.c3
-rw-r--r--test/Pseudo.py65
-rw-r--r--test/builderrors.py11
-rw-r--r--test/leaky-handles.py60
-rw-r--r--test/option/debug-multiple.py86
-rw-r--r--test/option/warn-dependency.py1
-rw-r--r--test/warning-TargetNotBuiltWarning.py55
46 files changed, 798 insertions, 283 deletions
diff --git a/QMTest/TestSCons.py b/QMTest/TestSCons.py
index 84cc60f0..ffba4c1a 100644
--- a/QMTest/TestSCons.py
+++ b/QMTest/TestSCons.py
@@ -151,7 +151,7 @@ def deprecated_python_version(version=sys.version_info):
if deprecated_python_version():
msg = r"""
-scons: warning: Support for pre-2.4 Python (%s) is deprecated.
+scons: warning: Support for pre-2.7.0 Python version (%s) is deprecated.
If this will cause hardship, contact dev@scons.tigris.org.
"""
@@ -1131,9 +1131,9 @@ SConscript( sconscript )
self.run(program = python, stdin = """\
import os, sys
try:
- py_ver = 'python%d.%d' % sys.version_info[:2]
+ py_ver = 'python%d.%d' % sys.version_info[:2]
except AttributeError:
- py_ver = 'python' + sys.version[:3]
+ py_ver = 'python' + sys.version[:3]
print os.path.join(sys.prefix, 'include', py_ver)
print os.path.join(sys.prefix, 'lib', py_ver, 'config')
print py_ver
@@ -1357,7 +1357,7 @@ class TimeSCons(TestSCons):
options = kw.get('options', '')
if additional is not None:
options += additional
- kw['options'] = options + ' --debug=memory --debug=time'
+ kw['options'] = options + ' --debug=memory,time'
def startup(self, *args, **kw):
"""
diff --git a/doc/generated/examples/troubleshoot_explain1_3.xml b/doc/generated/examples/troubleshoot_explain1_3.xml
new file mode 100644
index 00000000..b62be879
--- /dev/null
+++ b/doc/generated/examples/troubleshoot_explain1_3.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<screen xmlns="http://www.scons.org/dbxsd/v1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0/scons.xsd scons.xsd">% <userinput>scons -Q --warn=target-not-built</userinput>
+scons: building `file.out' because it doesn't exist
+cp file.in file.oout
+
+scons: warning: Cannot find target file.out after building
+</screen>
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index 82c782d2..55608474 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -64,12 +64,12 @@
<mediaobject role="cover"><imageobject><imagedata fileref="cover.jpg" format="JPG"/></imageobject></mediaobject>
</referenceinfo>
-
+
<title>SCons &buildversion;</title>
<subtitle>MAN page</subtitle>
-
-
-<refentry id='scons1'>
+
+
+<refentry id='scons1'>
<refmeta>
<refentrytitle>SCONS</refentrytitle>
<manvolnum>1</manvolnum>
@@ -82,7 +82,7 @@
<!-- body begins here -->
<refsynopsisdiv id='synopsis'>
<cmdsynopsis>
- <command>scons</command>
+ <command>scons</command>
<arg choice='opt' rep='repeat'><replaceable>options</replaceable></arg>
<arg choice='opt' rep='repeat'><replaceable>name=val</replaceable></arg>
<arg choice='opt' rep='repeat'><replaceable>targets</replaceable></arg>
@@ -384,6 +384,7 @@ will be retrieved from the cache instead of being rebuilt locally.
Caching behavior may be disabled and controlled in other ways by the
<option>--cache-force</option>,
<option>--cache-disable</option>,
+<option>--cache-readonly</option>,
and
<option>--cache-show</option>
command-line options. The
@@ -545,6 +546,15 @@ option.</para>
</listitem>
</varlistentry>
+<varlistentry>
+ <term>--cache-readonly</term>
+ <listitem>
+<para>Use the cache (if enabled) for reading, but do not not update the
+cache with changed files.
+</para>
+
+ </listitem>
+ </varlistentry>
<varlistentry>
<term>--cache-show</term>
<listitem>
@@ -664,8 +674,9 @@ directory.</para>
<term>--debug=<emphasis>type</emphasis></term>
<listitem>
<para>Debug the build process.
-<emphasis>type</emphasis>
-specifies what type of debugging:</para>
+<emphasis>type[,type...]</emphasis>
+specifies what type of debugging. Multiple types may be specified,
+separated by commas. The following types are valid:</para>
</listitem>
</varlistentry>
@@ -1097,6 +1108,7 @@ command:</para>
--cache-debug=FILE
--cache-disable, --no-cache
--cache-force, --cache-populate
+--cache-readonly
--cache-show
--debug=TYPE
-i, --ignore-errors
@@ -1943,6 +1955,14 @@ These warnings are enabled by default.</para>
</listitem>
</varlistentry>
<varlistentry>
+ <term>--warn=target_not_build, --warn=no-target_not_built</term>
+ <listitem>
+<para>Enables or disables warnings about a build rule not building the
+ expected targets. These warnings are not currently enabled by default.</para>
+
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term>-Y<emphasis> repository</emphasis>, --repository=<emphasis>repository</emphasis>, --srcdir=<emphasis>repository</emphasis></term>
<listitem>
<para>Search the specified repository for any input and target
@@ -6778,7 +6798,7 @@ specified in the
<emphasis role="bold">$MYPATH</emphasis>
construction variable. It lets SCons detect the file
<emphasis role="bold">incs/foo.inc</emphasis>
-, even if
+, even if
<emphasis role="bold">foo.x</emphasis>
contains the line
<emphasis role="bold">include foo.inc</emphasis>
diff --git a/doc/user/troubleshoot.xml b/doc/user/troubleshoot.xml
index 94af7275..65c91994 100644
--- a/doc/user/troubleshoot.xml
+++ b/doc/user/troubleshoot.xml
@@ -2,7 +2,7 @@
<!DOCTYPE sconsdoc [
<!ENTITY % scons SYSTEM "../scons.mod">
%scons;
-
+
<!ENTITY % builders-mod SYSTEM "../generated/builders.mod">
%builders-mod;
<!ENTITY % functions-mod SYSTEM "../generated/functions.mod">
@@ -160,6 +160,16 @@ file.in
</para>
<para>
+ Note that you can also use --warn=target-not-built which checks
+ whether or not expected targets exist after a build rule is
+ executed.
+ </para>
+
+ <scons_output example="troubleshoot_explain1" os="posix" suffix="3">
+ <scons_output_command>scons -Q --warn=target-not-built</scons_output_command>
+ </scons_output>
+
+ <para>
The &debug-explain; option also comes in handy
to help figure out what input file changed.
diff --git a/runtest.py b/runtest.py
index 6beb4ba7..2b7f7cee 100644..100755
--- a/runtest.py
+++ b/runtest.py
@@ -58,6 +58,8 @@
# command line it will execute before
# executing it. This suppresses that print.
#
+# --quit-on-failure Quit on any test failure
+#
# -s Short progress. Prints only the command line
# and a percentage value, based on the total and
# current number of tests.
@@ -123,6 +125,7 @@ print_progress = 1
suppress_stdout = False
suppress_stderr = False
allow_pipe_files = True
+quit_on_failure = False
helpstr = """\
Usage: runtest.py [OPTIONS] [TEST ...]
@@ -158,6 +161,7 @@ Options:
--passed Summarize which tests passed.
--qmtest Run using the QMTest harness (deprecated).
-q, --quiet Don't print the test being executed.
+ --quit-on-failure Quit on any test failure
-s, --short-progress Short progress, prints only the command line
and a percentage value, based on the total and
current number of tests.
@@ -214,7 +218,9 @@ opts, args = getopt.getopt(args, "3b:def:hj:klnP:p:qsv:Xx:t",
'jobs=',
'list', 'no-exec', 'nopipefiles',
'package=', 'passed', 'python=', 'qmtest',
- 'quiet', 'short-progress', 'time',
+ 'quiet',
+ 'quit-on-failure',
+ 'short-progress', 'time',
'version=', 'exec=',
'verbose='])
@@ -267,7 +273,9 @@ for o, a in opts:
elif o in ['-q', '--quiet']:
printcommand = 0
suppress_stdout = True
- suppress_stderr = True
+ suppress_stderr = True
+ elif o in ['--quit-on-failure']:
+ quit_on_failure = True
elif o in ['-s', '--short-progress']:
print_progress = 1
suppress_stdout = True
@@ -373,16 +381,16 @@ else:
# Else, we catch the output of both pipes...
if allow_pipe_files:
# The subprocess.Popen() suffers from a well-known
- # problem. Data for stdout/stderr is read into a
+ # problem. Data for stdout/stderr is read into a
# memory buffer of fixed size, 65K which is not very much.
# When it fills up, it simply stops letting the child process
# write to it. The child will then sit and patiently wait to
- # be able to write the rest of its output. Hang!
+ # be able to write the rest of its output. Hang!
# In order to work around this, we follow a suggestion
# by Anders Pearson in
# http://http://thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/
# and pass temp file objects to Popen() instead of the ubiquitous
- # subprocess.PIPE.
+ # subprocess.PIPE.
def spawn_it(command_args):
# Create temporary files
import tempfile
@@ -395,7 +403,7 @@ else:
shell=True)
# ... and wait for it to finish.
ret = p.wait()
-
+
try:
# Rewind to start of files
tmp_stdout.seek(0)
@@ -407,10 +415,10 @@ else:
# Remove temp files by closing them
tmp_stdout.close()
tmp_stderr.close()
-
+
# Return values
return (spawned_stderr, spawned_stdout, ret)
-
+
else:
# We get here only if the user gave the '--nopipefiles'
# option, meaning the "temp file" approach for
@@ -419,9 +427,9 @@ else:
# potential deadlock situation in the following code:
# If the subprocess writes a lot of data to its stderr,
# the pipe will fill up (nobody's reading it yet) and the
- # subprocess will wait for someone to read it.
+ # subprocess will wait for someone to read it.
# But the parent process is trying to read from stdin
- # (but the subprocess isn't writing anything there).
+ # (but the subprocess isn't writing anything there).
# Hence a deadlock.
# Be dragons here! Better don't use this!
def spawn_it(command_args):
@@ -469,7 +477,7 @@ class PopenExecutor(Base):
shell=True)
# ... and wait for it to finish.
self.status = p.wait()
-
+
try:
# Rewind to start of files
tmp_stdout.seek(0)
@@ -481,7 +489,7 @@ class PopenExecutor(Base):
# Remove temp files by closing them
tmp_stdout.close()
tmp_stderr.close()
- else:
+ else:
def execute(self):
p = subprocess.Popen(self.command_str,
stdout=subprocess.PIPE,
@@ -832,7 +840,7 @@ def run_test(t, io_lock, async=True):
if head:
os.environ['PYTHON_SCRIPT_DIR'] = head
else:
- os.environ['PYTHON_SCRIPT_DIR'] = ''
+ os.environ['PYTHON_SCRIPT_DIR'] = ''
test_start_time = time_func()
if execute_tests:
t.execute()
@@ -849,6 +857,10 @@ def run_test(t, io_lock, async=True):
print_time_func("Test execution time: %.1f seconds\n", t.test_time)
if io_lock:
io_lock.release()
+ if quit_on_failure and t.status == 1:
+ print "Exiting due to error"
+ print t.status
+ sys.exit(1)
class RunTest(threading.Thread):
def __init__(self, queue, io_lock):
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 50a27dcc..8b246969 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -6,6 +6,17 @@
RELEASE 2.3.1.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE
+ From Tom Tanner:
+ - Stop leaking file handles to subprocesses by switching to using subprocess
+ always.
+ - Allow multiple options to be specified with --debug=a,b,c
+ - Add support for a readonly cache (--cache-readonly)
+ - Always print stats if requested
+ - Generally try harder to print out a message on build errors
+ - Adds a switch to warn on missing targets
+ - Add Pseudo command to mark targets which should not exist after
+ they are built.
+
From Bogdan Tenea:
- Check for 8.3 filenames on cygwin as well as win32 to make variant_dir work properly.
@@ -30,6 +41,8 @@ RELEASE 2.3.1.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE
From Dirk Baechle:
- Update bootstrap.py so it can be used from any dir, to run
SCons from a source (non-installed) dir.
+ - Count statistics of instances are now collected only when
+ the --debug=count command-line option is used (#2922).
From Gary Oberbrunner:
- Test harness: fail_test() can now print a message to help debugging.
@@ -54,14 +67,14 @@ RELEASE 2.3.1.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE
approach, that also requires less external dependencies (programs and
Python packages). Added a customized Docbook XSD for strict validation of
all input XML files.
-
+
From Luca Falavigna:
- Fixed spelling errors in MAN pages (#2897).
-
+
From Michael McDougall:
- Fixed description of ignore_case for EnumVariable in the
MAN page (#2774).
-
+
RELEASE 2.3.0 - Mon, 02 Mar 2013 13:22:29 -0400
From Anatoly Techtonik:
@@ -5710,5 +5723,6 @@ A brief overview of important functionality available in release 0.01:
- Windows installer available.
+
__COPYRIGHT__
__FILE__ __REVISION__ __DATE__ __DEVELOPER__
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index c1eef756..a6dbb7c6 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -109,6 +109,7 @@ import re
import sys
import subprocess
+import SCons.Debug
from SCons.Debug import logInstanceCreation
import SCons.Errors
import SCons.Executor
@@ -439,7 +440,8 @@ class ActionBase(object):
vl = self.get_varlist(target, source, env)
if is_String(vl): vl = (vl,)
for v in vl:
- result.append(env.subst('${'+v+'}'))
+ # do the subst this way to ignore $(...$) parts:
+ result.append(env.subst_target_source('${'+v+'}', SCons.Subst.SUBST_SIG, target, source))
return ''.join(result)
def __add__(self, other):
@@ -698,7 +700,7 @@ class CommandAction(_ActionAction):
# factory above does). cmd will be passed to
# Environment.subst_list() for substituting environment
# variables.
- if __debug__: logInstanceCreation(self, 'Action.CommandAction')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.CommandAction')
_ActionAction.__init__(self, **kw)
if is_List(cmd):
@@ -855,7 +857,7 @@ class CommandAction(_ActionAction):
class CommandGeneratorAction(ActionBase):
"""Class for command-generator actions."""
def __init__(self, generator, kw):
- if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.CommandGeneratorAction')
self.generator = generator
self.gen_kw = kw
self.varlist = kw.get('varlist', ())
@@ -944,7 +946,7 @@ class CommandGeneratorAction(ActionBase):
class LazyAction(CommandGeneratorAction, CommandAction):
def __init__(self, var, kw):
- if __debug__: logInstanceCreation(self, 'Action.LazyAction')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.LazyAction')
#FUTURE CommandAction.__init__(self, '${'+var+'}', **kw)
CommandAction.__init__(self, '${'+var+'}', **kw)
self.var = SCons.Util.to_String(var)
@@ -986,7 +988,7 @@ class FunctionAction(_ActionAction):
"""Class for Python function actions."""
def __init__(self, execfunction, kw):
- if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.FunctionAction')
self.execfunction = execfunction
try:
@@ -1108,7 +1110,7 @@ class FunctionAction(_ActionAction):
class ListAction(ActionBase):
"""Class for lists of other actions."""
def __init__(self, actionlist):
- if __debug__: logInstanceCreation(self, 'Action.ListAction')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.ListAction')
def list_of_actions(x):
if isinstance(x, ActionBase):
return x
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 6dc9e17e..ed7650a8 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -102,6 +102,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import collections
import SCons.Action
+import SCons.Debug
from SCons.Debug import logInstanceCreation
from SCons.Errors import InternalError, UserError
import SCons.Executor
@@ -225,7 +226,7 @@ class OverrideWarner(collections.UserDict):
"""
def __init__(self, dict):
collections.UserDict.__init__(self, dict)
- if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.OverrideWarner')
self.already_warned = None
def warn(self):
if self.already_warned:
@@ -376,7 +377,7 @@ class BuilderBase(object):
src_builder = None,
ensure_suffix = False,
**overrides):
- if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.BuilderBase')
self._memo = {}
self.action = action
self.multi = multi
@@ -847,7 +848,7 @@ class CompositeBuilder(SCons.Util.Proxy):
"""
def __init__(self, builder, cmdgen):
- if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.CompositeBuilder')
SCons.Util.Proxy.__init__(self, builder)
# cmdgen should always be an instance of DictCmdGenerator.
diff --git a/src/engine/SCons/CacheDir.py b/src/engine/SCons/CacheDir.py
index 3516018b..9dd18e50 100644
--- a/src/engine/SCons/CacheDir.py
+++ b/src/engine/SCons/CacheDir.py
@@ -37,6 +37,7 @@ cache_enabled = True
cache_debug = False
cache_force = False
cache_show = False
+cache_readonly = False
def CacheRetrieveFunc(target, source, env):
t = target[0]
@@ -70,6 +71,8 @@ CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString)
CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None)
def CachePushFunc(target, source, env):
+ if cache_readonly: return
+
t = target[0]
if t.nocache:
return
@@ -150,6 +153,9 @@ class CacheDir(object):
def is_enabled(self):
return (cache_enabled and not self.path is None)
+ def is_readonly(self):
+ return cache_readonly
+
def cachepath(self, node):
"""
"""
@@ -201,7 +207,7 @@ class CacheDir(object):
return False
def push(self, node):
- if not self.is_enabled():
+ if self.is_readonly() or not self.is_enabled():
return
return CachePush(node, [], node.get_build_env())
diff --git a/src/engine/SCons/Debug.py b/src/engine/SCons/Debug.py
index 1c0c6385..99740398 100644
--- a/src/engine/SCons/Debug.py
+++ b/src/engine/SCons/Debug.py
@@ -35,6 +35,10 @@ import sys
import time
import weakref
+# Global variable that gets set to 'True' by the Main script,
+# when the creation of class instances should get tracked.
+track_instances = False
+# List of currently tracked classes
tracked_classes = {}
def logInstanceCreation(instance, name=None):
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 55a82065..3e499b6c 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -43,6 +43,7 @@ from collections import UserDict
import SCons.Action
import SCons.Builder
+import SCons.Debug
from SCons.Debug import logInstanceCreation
import SCons.Defaults
import SCons.Errors
@@ -370,7 +371,7 @@ class SubstitutionEnvironment(object):
def __init__(self, **kw):
"""Initialization of an underlying SubstitutionEnvironment class.
"""
- if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Environment.SubstitutionEnvironment')
self.fs = SCons.Node.FS.get_default_fs()
self.ans = SCons.Node.Alias.default_ans
self.lookup_list = SCons.Node.arg2nodes_lookups
@@ -704,7 +705,7 @@ class SubstitutionEnvironment(object):
# -symbolic (linker global binding)
# -R dir (deprecated linker rpath)
# IBM compilers may also accept -qframeworkdir=foo
-
+
params = shlex.split(arg)
append_next_arg_to = None # for multi-word args
for arg in params:
@@ -794,7 +795,7 @@ class SubstitutionEnvironment(object):
append_next_arg_to = arg
else:
dict['CCFLAGS'].append(arg)
-
+
for arg in flags:
do_parse(arg)
return dict
@@ -858,7 +859,7 @@ class SubstitutionEnvironment(object):
# def MergeShellPaths(self, args, prepend=1):
# """
-# Merge the dict in args into the shell environment in env['ENV'].
+# Merge the dict in args into the shell environment in env['ENV'].
# Shell path elements are appended or prepended according to prepend.
# Uses Pre/AppendENVPath, so it always appends or prepends uniquely.
@@ -931,7 +932,7 @@ class Base(SubstitutionEnvironment):
initialize things in a very specific order that doesn't work
with the much simpler base class initialization.
"""
- if __debug__: logInstanceCreation(self, 'Environment.Base')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Environment.Base')
self._memo = {}
self.fs = SCons.Node.FS.get_default_fs()
self.ans = SCons.Node.Alias.default_ans
@@ -961,14 +962,14 @@ class Base(SubstitutionEnvironment):
platform = SCons.Platform.Platform(platform)
self._dict['PLATFORM'] = str(platform)
platform(self)
-
+
self._dict['HOST_OS'] = self._dict.get('HOST_OS',None)
self._dict['HOST_ARCH'] = self._dict.get('HOST_ARCH',None)
-
+
# Now set defaults for TARGET_{OS|ARCH}
self._dict['TARGET_OS'] = self._dict.get('TARGET_OS',None)
self._dict['TARGET_ARCH'] = self._dict.get('TARGET_ARCH',None)
-
+
# Apply the passed-in and customizable variables to the
# environment before calling the tools, because they may use
@@ -1157,7 +1158,7 @@ class Base(SubstitutionEnvironment):
# "continue" statements whenever we finish processing an item,
# but Python 1.5.2 apparently doesn't let you use "continue"
# within try:-except: blocks, so we have to nest our code.
- try:
+ try:
if key == 'CPPDEFINES' and SCons.Util.is_String(self._dict[key]):
self._dict[key] = [self._dict[key]]
orig = self._dict[key]
@@ -1208,7 +1209,7 @@ class Base(SubstitutionEnvironment):
orig = orig.items()
orig += val
self._dict[key] = orig
- else:
+ else:
for v in val:
orig[v] = None
else:
@@ -1231,7 +1232,7 @@ class Base(SubstitutionEnvironment):
path = str(self.fs.Dir(path))
return path
- def AppendENVPath(self, name, newpath, envname = 'ENV',
+ def AppendENVPath(self, name, newpath, envname = 'ENV',
sep = os.pathsep, delete_existing=1):
"""Append path elements to the path 'name' in the 'ENV'
dictionary for this environment. Will only add any particular
@@ -1289,7 +1290,7 @@ class Base(SubstitutionEnvironment):
dk = dk.items()
elif SCons.Util.is_String(dk):
dk = [(dk,)]
- else:
+ else:
tmp = []
for i in dk:
if SCons.Util.is_List(i):
@@ -1334,7 +1335,7 @@ class Base(SubstitutionEnvironment):
dk = filter(lambda x, val=val: x not in val, dk)
self._dict[key] = dk + val
else:
- dk = [x for x in dk if x not in val]
+ dk = [x for x in dk if x not in val]
self._dict[key] = dk + val
else:
# By elimination, val is not a list. Since dk is a
@@ -1381,7 +1382,7 @@ class Base(SubstitutionEnvironment):
builders = self._dict['BUILDERS']
except KeyError:
pass
-
+
clone = copy.copy(self)
# BUILDERS is not safe to do a simple copy
clone._dict = semi_deepcopy_dict(self._dict, ['BUILDERS'])
@@ -1409,12 +1410,12 @@ class Base(SubstitutionEnvironment):
apply_tools(clone, tools, toolpath)
# apply them again in case the tools overwrote them
- clone.Replace(**new)
+ clone.Replace(**new)
# Finally, apply any flags to be merged in
if parse_flags: clone.MergeFlags(parse_flags)
- if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Environment.EnvironmentClone')
return clone
def Copy(self, *args, **kw):
@@ -2086,6 +2087,14 @@ class Base(SubstitutionEnvironment):
t.set_precious()
return tlist
+ def Pseudo(self, *targets):
+ tlist = []
+ for t in targets:
+ tlist.extend(self.arg2nodes(t, self.fs.Entry))
+ for t in tlist:
+ t.set_pseudo()
+ return tlist
+
def Repository(self, *dirs, **kw):
dirs = self.arg2nodes(list(dirs), self.fs.Dir)
self.fs.Repository(*dirs, **kw)
@@ -2270,7 +2279,7 @@ class OverrideEnvironment(Base):
"""
def __init__(self, subject, overrides={}):
- if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Environment.OverrideEnvironment')
self.__dict__['__subject'] = subject
self.__dict__['overrides'] = overrides
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index db788dc6..7fa8af4e 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -170,7 +170,7 @@ class TestEnvironmentFixture(object):
single_source = 1)
kw['BUILDERS'] = {'Object' : static_obj}
static_obj.add_action('.cpp', 'fake action')
-
+
env = Environment(*args, **kw)
return env
@@ -1722,7 +1722,7 @@ def exists(env):
assert env['DDD1'] == ['c', 'a', 'b'], env['DDD1'] # a & b move to end
env.AppendUnique(DDD1 = ['e','f', 'e'], delete_existing=1)
assert env['DDD1'] == ['c', 'a', 'b', 'f', 'e'], env['DDD1'] # add last
-
+
env['CLVar'] = CLVar([])
env.AppendUnique(CLVar = 'bar')
result = env['CLVar']
@@ -2030,7 +2030,7 @@ def generate(env):
try:
save_command = []
- env.backtick = my_backtick(save_command,
+ env.backtick = my_backtick(save_command,
"-I/usr/include/fum -I bar -X\n" + \
"-L/usr/fax -L foo -lxxx -l yyy " + \
"-Wa,-as -Wl,-link " + \
@@ -2375,7 +2375,7 @@ f5: \
env.PrependUnique(DDD1 = ['a','c'], delete_existing=1)
assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # a & c move to front
env.PrependUnique(DDD1 = ['d','e','d'], delete_existing=1)
- assert env['DDD1'] == ['d', 'e', 'a', 'c', 'b'], env['DDD1']
+ assert env['DDD1'] == ['d', 'e', 'a', 'c', 'b'], env['DDD1']
env['CLVar'] = CLVar([])
@@ -3125,6 +3125,29 @@ def generate(env):
assert t[4].path == 'p_ggg'
assert t[4].precious
+ def test_Pseudo(self):
+ """Test the Pseudo() method"""
+ env = self.TestEnvironment(FOO='ggg', BAR='hhh')
+ env.Dir('p_hhhb')
+ env.File('p_d')
+ t = env.Pseudo('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
+
+ assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
+ assert t[0].path == 'p_a'
+ assert t[0].pseudo
+ assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
+ assert t[1].path == 'p_hhhb'
+ assert t[1].pseudo
+ assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
+ assert t[2].path == 'p_c'
+ assert t[2].pseudo
+ assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
+ assert t[3].path == 'p_d'
+ assert t[3].pseudo
+ assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
+ assert t[4].path == 'p_ggg'
+ assert t[4].pseudo
+
def test_Repository(self):
"""Test the Repository() method."""
class MyFS(object):
diff --git a/src/engine/SCons/Executor.py b/src/engine/SCons/Executor.py
index 6f2489b3..7875537a 100644
--- a/src/engine/SCons/Executor.py
+++ b/src/engine/SCons/Executor.py
@@ -31,6 +31,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import collections
+import SCons.Debug
from SCons.Debug import logInstanceCreation
import SCons.Errors
import SCons.Memoize
@@ -123,7 +124,7 @@ class Executor(object):
def __init__(self, action, env=None, overridelist=[{}],
targets=[], sources=[], builder_kw={}):
- if __debug__: logInstanceCreation(self, 'Executor.Executor')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Executor.Executor')
self.set_action_list(action)
self.pre_actions = []
self.post_actions = []
@@ -575,7 +576,7 @@ class Null(object):
going to worry about unit tests for this--at least for now.
"""
def __init__(self, *args, **kw):
- if __debug__: logInstanceCreation(self, 'Executor.Null')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Executor.Null')
self.batches = [Batch(kw['targets'][:], [])]
def get_build_env(self):
return get_NullEnvironment()
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 43816974..18400e84 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -44,6 +44,7 @@ import time
import codecs
import SCons.Action
+import SCons.Debug
from SCons.Debug import logInstanceCreation
import SCons.Errors
import SCons.Memoize
@@ -581,7 +582,7 @@ class Base(SCons.Node.Node):
our relative and absolute paths, identify our parent
directory, and indicate that this node should use
signatures."""
- if __debug__: logInstanceCreation(self, 'Node.FS.Base')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS.Base')
SCons.Node.Node.__init__(self)
# Filenames and paths are probably reused and are intern'ed to
@@ -1111,7 +1112,7 @@ class FS(LocalFS):
The path argument must be a valid absolute path.
"""
- if __debug__: logInstanceCreation(self, 'Node.FS')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS')
self._memo = {}
@@ -1445,7 +1446,7 @@ class Dir(Base):
BuildInfo = DirBuildInfo
def __init__(self, name, directory, fs):
- if __debug__: logInstanceCreation(self, 'Node.FS.Dir')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS.Dir')
Base.__init__(self, name, directory, fs)
self._morph()
@@ -2113,7 +2114,7 @@ class RootDir(Dir):
this directory.
"""
def __init__(self, drive, fs):
- if __debug__: logInstanceCreation(self, 'Node.FS.RootDir')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS.RootDir')
# We're going to be our own parent directory (".." entry and .dir
# attribute) so we have to set up some values so Base.__init__()
# won't gag won't it calls some of our methods.
@@ -2361,7 +2362,7 @@ class File(Base):
"Directory %s found where file expected.")
def __init__(self, name, directory, fs):
- if __debug__: logInstanceCreation(self, 'Node.FS.File')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS.File')
Base.__init__(self, name, directory, fs)
self._morph()
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index a3016543..076ca65a 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -693,6 +693,15 @@ class NodeTestCase(unittest.TestCase):
node.set_precious(7)
assert node.precious == 7
+ def test_set_pseudo(self):
+ """Test setting a Node's pseudo value
+ """
+ node = SCons.Node.Node()
+ node.set_pseudo()
+ assert node.pseudo
+ node.set_pseudo(False)
+ assert not node.pseudo
+
def test_exists(self):
"""Test evaluating whether a Node exists.
"""
@@ -954,7 +963,7 @@ class NodeTestCase(unittest.TestCase):
self.source_scanner = scanner
builder = Builder2(ts1)
-
+
targets = builder([source])
s = targets[0].get_source_scanner(source)
assert s is ts1, s
@@ -967,7 +976,7 @@ class NodeTestCase(unittest.TestCase):
builder = Builder1(env=Environment(SCANNERS = [ts3]))
targets = builder([source])
-
+
s = targets[0].get_source_scanner(source)
assert s is ts3, s
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 992284dc..d353245d 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -47,6 +47,7 @@ import collections
import copy
from itertools import chain
+import SCons.Debug
from SCons.Debug import logInstanceCreation
import SCons.Executor
import SCons.Memoize
@@ -57,6 +58,10 @@ from SCons.Debug import Trace
def classname(obj):
return str(obj.__class__).split('.')[-1]
+# Set to false if we're doing a dry run. There's more than one of these
+# little treats
+do_store_info = True
+
# Node states
#
# These are in "priority" order, so that the maximum value for any
@@ -183,7 +188,7 @@ class Node(object):
pass
def __init__(self):
- if __debug__: logInstanceCreation(self, 'Node.Node')
+ if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.Node')
# Note that we no longer explicitly initialize a self.builder
# attribute to None here. That's because the self.builder
# attribute may be created on-the-fly later by a subclass (the
@@ -214,6 +219,7 @@ class Node(object):
self.env = None
self.state = no_state
self.precious = None
+ self.pseudo = False
self.noclean = 0
self.nocache = 0
self.cached = 0 # is this node pulled from cache?
@@ -385,6 +391,13 @@ class Node(object):
self.clear()
+ if self.pseudo:
+ if self.exists():
+ raise SCons.Errors.UserError("Pseudo target " + str(self) + " must not exist")
+ else:
+ if not self.exists() and do_store_info:
+ SCons.Warnings.warn(SCons.Warnings.TargetNotBuiltWarning,
+ "Cannot find target " + str(self) + " after building")
self.ninfo.update(self)
def visited(self):
@@ -788,6 +801,10 @@ class Node(object):
"""Set the Node's precious value."""
self.precious = precious
+ def set_pseudo(self, pseudo = True):
+ """Set the Node's precious value."""
+ self.pseudo = pseudo
+
def set_noclean(self, noclean = 1):
"""Set the Node's noclean value."""
# Make sure noclean is an integer so the --debug=stree
diff --git a/src/engine/SCons/Platform/posix.py b/src/engine/SCons/Platform/posix.py
index ece48d7d..2e21e5ab 100644
--- a/src/engine/SCons/Platform/posix.py
+++ b/src/engine/SCons/Platform/posix.py
@@ -58,175 +58,32 @@ def escape(arg):
return '"' + arg + '"'
-def exec_system(l, env):
- stat = os.system(' '.join(l))
- if stat & 0xff:
- return stat | 0x80
- return stat >> 8
+def exec_subprocess(l, env):
+ proc = subprocess.Popen(l, env = env, close_fds = True)
+ return proc.wait()
-def exec_spawnvpe(l, env):
- stat = os.spawnvpe(os.P_WAIT, l[0], l, env)
- # os.spawnvpe() returns the actual exit code, not the encoding
- # returned by os.waitpid() or os.system().
- return stat
-
-def exec_fork(l, env):
- pid = os.fork()
- if not pid:
- # Child process.
- exitval = 127
- try:
- os.execvpe(l[0], l, env)
- except OSError, e:
- exitval = exitvalmap.get(e[0], e[0])
- sys.stderr.write("scons: %s: %s\n" % (l[0], e[1]))
- os._exit(exitval)
- else:
- # Parent process.
- pid, stat = os.waitpid(pid, 0)
- if stat & 0xff:
- return stat | 0x80
- return stat >> 8
-
-def _get_env_command(sh, escape, cmd, args, env):
- s = ' '.join(args)
- if env:
- l = ['env', '-'] + \
- [escape(t[0])+'='+escape(t[1]) for t in env.items()] + \
- [sh, '-c', escape(s)]
- s = ' '.join(l)
- return s
-
-def env_spawn(sh, escape, cmd, args, env):
- return exec_system([_get_env_command( sh, escape, cmd, args, env)], env)
-
-def spawnvpe_spawn(sh, escape, cmd, args, env):
- return exec_spawnvpe([sh, '-c', ' '.join(args)], env)
-
-def fork_spawn(sh, escape, cmd, args, env):
- return exec_fork([sh, '-c', ' '.join(args)], env)
-
-def process_cmd_output(cmd_stdout, cmd_stderr, stdout, stderr):
- stdout_eof = stderr_eof = 0
- while not (stdout_eof and stderr_eof):
- try:
- (i,o,e) = select.select([cmd_stdout, cmd_stderr], [], [])
- if cmd_stdout in i:
- str = cmd_stdout.read()
- if len(str) == 0:
- stdout_eof = 1
- elif stdout is not None:
- stdout.write(str)
- if cmd_stderr in i:
- str = cmd_stderr.read()
- if len(str) == 0:
- #sys.__stderr__.write( "stderr_eof=1\n" )
- stderr_eof = 1
- else:
- #sys.__stderr__.write( "str(stderr) = %s\n" % str )
- stderr.write(str)
- except select.error, (_errno, _strerror):
- if _errno != errno.EINTR:
- raise
+def subprocess_spawn(sh, escape, cmd, args, env):
+ return exec_subprocess([sh, '-c', ' '.join(args)], env)
def exec_popen3(l, env, stdout, stderr):
- proc = subprocess.Popen(' '.join(l),
- stdout=stdout,
- stderr=stderr,
- shell=True)
- stat = proc.wait()
- if stat & 0xff:
- return stat | 0x80
- return stat >> 8
-
-def exec_piped_fork(l, env, stdout, stderr):
- # spawn using fork / exec and providing a pipe for the command's
- # stdout / stderr stream
- if stdout != stderr:
- (rFdOut, wFdOut) = os.pipe()
- (rFdErr, wFdErr) = os.pipe()
- else:
- (rFdOut, wFdOut) = os.pipe()
- rFdErr = rFdOut
- wFdErr = wFdOut
- # do the fork
- pid = os.fork()
- if not pid:
- # Child process
- os.close( rFdOut )
- if rFdOut != rFdErr:
- os.close( rFdErr )
- os.dup2( wFdOut, 1 ) # is there some symbolic way to do that ?
- os.dup2( wFdErr, 2 )
- os.close( wFdOut )
- if stdout != stderr:
- os.close( wFdErr )
- exitval = 127
- try:
- os.execvpe(l[0], l, env)
- except OSError, e:
- exitval = exitvalmap.get(e[0], e[0])
- stderr.write("scons: %s: %s\n" % (l[0], e[1]))
- os._exit(exitval)
- else:
- # Parent process
- pid, stat = os.waitpid(pid, 0)
- os.close( wFdOut )
- if stdout != stderr:
- os.close( wFdErr )
- childOut = os.fdopen( rFdOut )
- if stdout != stderr:
- childErr = os.fdopen( rFdErr )
- else:
- childErr = childOut
- process_cmd_output(childOut, childErr, stdout, stderr)
- os.close( rFdOut )
- if stdout != stderr:
- os.close( rFdErr )
- if stat & 0xff:
- return stat | 0x80
- return stat >> 8
+ proc = subprocess.Popen(l, env = env, close_fds = True,
+ stdout = stdout,
+ stderr = stderr)
+ return proc.wait()
def piped_env_spawn(sh, escape, cmd, args, env, stdout, stderr):
# spawn using Popen3 combined with the env command
# the command name and the command's stdout is written to stdout
# the command's stderr is written to stderr
- return exec_popen3([_get_env_command(sh, escape, cmd, args, env)],
+ return exec_popen3([sh, '-c', ' '.join(args)],
env, stdout, stderr)
-def piped_fork_spawn(sh, escape, cmd, args, env, stdout, stderr):
- # spawn using fork / exec and providing a pipe for the command's
- # stdout / stderr stream
- return exec_piped_fork([sh, '-c', ' '.join(args)],
- env, stdout, stderr)
-
-
def generate(env):
- # If os.spawnvpe() exists, we use it to spawn commands. Otherwise
- # if the env utility exists, we use os.system() to spawn commands,
- # finally we fall back on os.fork()/os.exec().
- #
- # os.spawnvpe() is prefered because it is the most efficient. But
- # for Python versions without it, os.system() is prefered because it
- # is claimed that it works better with threads (i.e. -j) and is more
- # efficient than forking Python.
- #
- # NB: Other people on the scons-users mailing list have claimed that
- # os.fork()/os.exec() works better than os.system(). There may just
- # not be a default that works best for all users.
-
- if 'spawnvpe' in os.__dict__:
- spawn = spawnvpe_spawn
- elif env.Detect('env'):
- spawn = env_spawn
- else:
- spawn = fork_spawn
-
- if env.Detect('env'):
- pspawn = piped_env_spawn
- else:
- pspawn = piped_fork_spawn
+ # Bearing in mind we have python 2.4 as a baseline, we can just do this:
+ spawn = subprocess_spawn
+ pspawn = piped_env_spawn
+ # Note that this means that 'escape' is no longer used
if 'ENV' not in env:
env['ENV'] = {}
diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py
index 837c1039..7c215191 100644
--- a/src/engine/SCons/Script/Main.py
+++ b/src/engine/SCons/Script/Main.py
@@ -79,7 +79,12 @@ def fetch_win32_parallel_msg():
import SCons.Platform.win32
return SCons.Platform.win32.parallel_msg
-#
+def revert_io():
+ # This call is added to revert stderr and stdout to the original
+ # ones just in case some build rule or something else in the system
+ # has redirected them elsewhere.
+ sys.stderr = sys.__stderr__
+ sys.stdout = sys.__stdout__
class SConsPrintHelpException(Exception):
pass
@@ -272,6 +277,9 @@ class BuildTask(SCons.Taskmaster.OutOfDateTask):
(EnvironmentError, SCons.Errors.StopError,
SCons.Errors.UserError))):
type, value, trace = buildError.exc_info
+ if tb and print_stacktrace:
+ sys.stderr.write("scons: internal stack trace:\n")
+ traceback.print_tb(tb, file=sys.stderr)
traceback.print_exception(type, value, trace)
elif tb and print_stacktrace:
sys.stderr.write("scons: internal stack trace:\n")
@@ -622,7 +630,7 @@ def _set_debug_values(options):
debug_values = options.debug
if "count" in debug_values:
- # All of the object counts are within "if __debug__:" blocks,
+ # All of the object counts are within "if track_instances:" blocks,
# which get stripped when running optimized (with python -O or
# from compiled *.pyo files). Provide a warning if __debug__ is
# stripped, so it doesn't just look like --debug=count is broken.
@@ -630,6 +638,7 @@ def _set_debug_values(options):
if __debug__: enable_count = True
if enable_count:
count_stats.enable(sys.stdout)
+ SCons.Debug.track_instances = True
else:
msg = "--debug=count is not supported when running SCons\n" + \
"\twith the python -O option or optimized (.pyo) modules."
@@ -644,6 +653,8 @@ def _set_debug_values(options):
if "memory" in debug_values:
memory_stats.enable(sys.stdout)
print_objects = ("objects" in debug_values)
+ if print_objects:
+ SCons.Debug.track_instances = True
if "presub" in debug_values:
SCons.Action.print_actions_presub = 1
if "stacktrace" in debug_values:
@@ -983,9 +994,9 @@ def _main(parser):
# reading SConscript files and haven't started building
# things yet, stop regardless of whether they used -i or -k
# or anything else.
+ revert_io()
sys.stderr.write("scons: *** %s Stop.\n" % e)
- exit_status = 2
- sys.exit(exit_status)
+ sys.exit(2)
global sconscript_time
sconscript_time = time.time() - start_time
@@ -1071,6 +1082,8 @@ def _main(parser):
# Build the targets
nodes = _build_targets(fs, options, targets, target_top)
if not nodes:
+ revert_io()
+ print 'Found nothing to build'
exit_status = 2
def _build_targets(fs, options, targets, target_top):
@@ -1083,12 +1096,14 @@ def _build_targets(fs, options, targets, target_top):
SCons.Action.print_actions = not options.silent
SCons.Action.execute_actions = not options.no_exec
SCons.Node.FS.do_store_info = not options.no_exec
+ SCons.Node.do_store_info = not options.no_exec
SCons.SConf.dryrun = options.no_exec
if options.diskcheck:
SCons.Node.FS.set_diskcheck(options.diskcheck)
SCons.CacheDir.cache_enabled = not options.cache_disable
+ SCons.CacheDir.cache_readonly = options.cache_readonly
SCons.CacheDir.cache_debug = options.cache_debug
SCons.CacheDir.cache_force = options.cache_force
SCons.CacheDir.cache_show = options.cache_show
@@ -1298,12 +1313,8 @@ def _exec_main(parser, values):
prof = Profile()
try:
prof.runcall(_main, parser)
- except SConsPrintHelpException, e:
+ finally:
prof.dump_stats(options.profile_file)
- raise e
- except SystemExit:
- pass
- prof.dump_stats(options.profile_file)
else:
_main(parser)
@@ -1341,7 +1352,10 @@ def main():
OptionsParser = parser
try:
- _exec_main(parser, values)
+ try:
+ _exec_main(parser, values)
+ finally:
+ revert_io()
except SystemExit, s:
if s:
exit_status = s
@@ -1358,6 +1372,7 @@ def main():
parser.print_help()
exit_status = 0
except SCons.Errors.BuildError, e:
+ print e
exit_status = e.exitstatus
except:
# An exception here is likely a builtin Python exception Python
diff --git a/src/engine/SCons/Script/Main.xml b/src/engine/SCons/Script/Main.xml
index b582e0d7..147e778a 100644
--- a/src/engine/SCons/Script/Main.xml
+++ b/src/engine/SCons/Script/Main.xml
@@ -685,6 +685,25 @@ Multiple targets can be passed in to a single call to
</summary>
</scons_function>
+<scons_function name="Pseudo">
+<arguments>
+(target, ...)
+</arguments>
+<summary>
+<para>
+This indicates that each given
+<varname>target</varname>
+should not be created by the build rule, and if the target is created,
+an error will be generated. This is similar to the gnu make .PHONY
+target. However, in the vast majority of cases, an
+&f-Alias;
+is more appropriate.
+
+Multiple targets can be passed in to a single call to
+&f-Pseudo;.
+</para>
+</summary>
+</scons_function>
<scons_function name="SetOption">
<arguments>
(name, value)
@@ -788,4 +807,4 @@ SetOption('max_drift', 1)
</summary>
</scons_function>
-</sconsdoc> \ No newline at end of file
+</sconsdoc>
diff --git a/src/engine/SCons/Script/SConsOptions.py b/src/engine/SCons/Script/SConsOptions.py
index 645ab11b..62033ba1 100644
--- a/src/engine/SCons/Script/SConsOptions.py
+++ b/src/engine/SCons/Script/SConsOptions.py
@@ -248,7 +248,7 @@ class SConsOption(optparse.Option):
class SConsOptionGroup(optparse.OptionGroup):
"""
A subclass for SCons-specific option groups.
-
+
The only difference between this and the base class is that we print
the group's help text flush left, underneath their own title but
lined up with the normal "SCons Options".
@@ -340,7 +340,7 @@ class SConsOptionParser(optparse.OptionParser):
def add_local_option(self, *args, **kw):
"""
Adds a local option to the parser.
-
+
This is initiated by a SetOption() call to add a user-defined
command-line option. We add the option to a separate option
group for the local options, creating the group if necessary.
@@ -394,11 +394,11 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter):
out liking:
-- add our own regular expression that doesn't break on hyphens
- (so things like --no-print-directory don't get broken);
+ (so things like --no-print-directory don't get broken);
-- wrap the list of options themselves when it's too long
(the wrapper.fill(opts) call below);
-
+
-- set the subsequent_indent when wrapping the help_text.
"""
# The help for each option consists of two parts:
@@ -564,6 +564,11 @@ def Parser(version):
action="store_true",
help="Copy already-built targets into the CacheDir.")
+ op.add_option('--cache-readonly',
+ dest='cache_readonly', default=False,
+ action="store_true",
+ help="Do not update CacheDir with built targets.")
+
op.add_option('--cache-show',
dest='cache_show', default=False,
action="store_true",
@@ -579,8 +584,10 @@ def Parser(version):
if not value in c_options:
raise OptionValueError(opt_invalid('config', value, c_options))
setattr(parser.values, option.dest, value)
+
opt_config_help = "Controls Configure subsystem: %s." \
% ", ".join(config_options)
+
op.add_option('--config',
nargs=1, type="string",
dest="config", default="auto",
@@ -606,23 +613,25 @@ def Parser(version):
"pdb", "prepare", "presub", "stacktrace",
"time"]
- def opt_debug(option, opt, value, parser,
+ def opt_debug(option, opt, value__, parser,
debug_options=debug_options,
deprecated_debug_options=deprecated_debug_options):
- if value in debug_options:
- parser.values.debug.append(value)
- elif value in deprecated_debug_options.keys():
- parser.values.debug.append(value)
- try:
- parser.values.delayed_warnings
- except AttributeError:
- parser.values.delayed_warnings = []
- msg = deprecated_debug_options[value]
- w = "The --debug=%s option is deprecated%s." % (value, msg)
- t = (SCons.Warnings.DeprecatedDebugOptionsWarning, w)
- parser.values.delayed_warnings.append(t)
- else:
- raise OptionValueError(opt_invalid('debug', value, debug_options))
+ for value in value__.split(','):
+ if value in debug_options:
+ parser.values.debug.append(value)
+ elif value in deprecated_debug_options.keys():
+ parser.values.debug.append(value)
+ try:
+ parser.values.delayed_warnings
+ except AttributeError:
+ parser.values.delayed_warnings = []
+ msg = deprecated_debug_options[value]
+ w = "The --debug=%s option is deprecated%s." % (value, msg)
+ t = (SCons.Warnings.DeprecatedDebugOptionsWarning, w)
+ parser.values.delayed_warnings.append(t)
+ else:
+ raise OptionValueError(opt_invalid('debug', value, debug_options))
+
opt_debug_help = "Print various types of debugging information: %s." \
% ", ".join(debug_options)
op.add_option('--debug',
diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py
index 7477f752..c09f8e48 100644
--- a/src/engine/SCons/Tool/__init__.py
+++ b/src/engine/SCons/Tool/__init__.py
@@ -364,8 +364,12 @@ symlinks for the platform we are on"""
print "VerShLib: made sym link of %s -> %s" % (linkname, lib_ver)
return result
-# Fix http://scons.tigris.org/issues/show_bug.cgi?id=2903 :
-# varlist=['$SHLINKCOM']: ensure we still depend on SCons.Defaults.ShLinkAction command line which is $SHLINKCOM
+# Fix http://scons.tigris.org/issues/show_bug.cgi?id=2903 :
+# Ensure we still depend on SCons.Defaults.ShLinkAction command line which is $SHLINKCOM.
+# This was tricky because we don't want changing LIBPATH to cause a rebuild, but
+# changing other link args should. LIBPATH has $( ... $) around it but until this
+# fix, when the varlist was added to the build sig those ignored parts weren't getting
+# ignored.
ShLibAction = SCons.Action.Action(VersionedSharedLibrary, None, varlist=['SHLINKCOM'])
def createSharedLibBuilder(env):
diff --git a/src/engine/SCons/Tool/rpm.py b/src/engine/SCons/Tool/rpm.py
index 26d21099..1f6eafe0 100644
--- a/src/engine/SCons/Tool/rpm.py
+++ b/src/engine/SCons/Tool/rpm.py
@@ -79,7 +79,7 @@ def build_rpm(target, source, env):
errstr=output,
filename=str(target[0]) )
else:
- # XXX: assume that LC_ALL=c is set while running rpmbuild
+ # XXX: assume that LC_ALL=C is set while running rpmbuild
output_files = re.compile( 'Wrote: (.*)' ).findall( output )
for output, input in zip( output_files, target ):
@@ -117,7 +117,7 @@ def generate(env):
bld = RpmBuilder
env['BUILDERS']['Rpm'] = bld
- env.SetDefault(RPM = 'LC_ALL=c rpmbuild')
+ env.SetDefault(RPM = 'LC_ALL=C rpmbuild')
env.SetDefault(RPMFLAGS = SCons.Util.CLVar('-ta'))
env.SetDefault(RPMCOM = rpmAction)
env.SetDefault(RPMSUFFIX = '.rpm')
diff --git a/src/engine/SCons/Warnings.py b/src/engine/SCons/Warnings.py
index a260c4ac..ca6acee9 100644
--- a/src/engine/SCons/Warnings.py
+++ b/src/engine/SCons/Warnings.py
@@ -42,6 +42,9 @@ class WarningOnByDefault(Warning):
# NOTE: If you add a new warning class, add it to the man page, too!
+class TargetNotBuiltWarning(Warning): # Should go to OnByDefault
+ pass
+
class CacheWriteErrorWarning(Warning):
pass
diff --git a/test/CacheDir/option--cr.py b/test/CacheDir/option--cr.py
new file mode 100644
index 00000000..de6bbc85
--- /dev/null
+++ b/test/CacheDir/option--cr.py
@@ -0,0 +1,138 @@
+#!/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 --cache-readonly option when retrieving derived files from a
+CacheDir. It should retrieve as normal but not update files.
+"""
+
+import os.path
+import shutil
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('cache', 'src')
+
+test.write(['src', 'SConstruct'], """
+def cat(env, source, target):
+ target = str(target[0])
+ open('cat.out', 'ab').write(target + "\\n")
+ f = open(target, "wb")
+ for src in source:
+ f.write(open(str(src), "rb").read())
+ f.close()
+env = Environment(BUILDERS={'Cat':Builder(action=cat)})
+env.Cat('aaa.out', 'aaa.in')
+env.Cat('bbb.out', 'bbb.in')
+env.Cat('ccc.out', 'ccc.in')
+env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out'])
+CacheDir(r'%s')
+""" % test.workpath('cache'))
+
+test.write(['src', 'aaa.in'], "aaa.in\n")
+test.write(['src', 'bbb.in'], "bbb.in\n")
+test.write(['src', 'ccc.in'], "ccc.in\n")
+
+# Verify that a normal build works correctly, and clean up.
+# This should populate the cache with our derived files.
+test.run(chdir = 'src', arguments = '.')
+
+test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n")
+test.must_match(['src', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n")
+
+test.up_to_date(chdir = 'src', arguments = '.')
+
+test.run(chdir = 'src', arguments = '-c .')
+test.unlink(['src', 'cat.out'])
+
+# Verify that we now retrieve the derived files from cache,
+# not rebuild them. Then clean up.
+test.run(chdir = 'src', arguments = '--cache-readonly .',
+ stdout = test.wrap_stdout("""\
+Retrieved `aaa.out' from cache
+Retrieved `bbb.out' from cache
+Retrieved `ccc.out' from cache
+Retrieved `all' from cache
+"""))
+
+test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n")
+test.must_not_exist(test.workpath('src', 'cat.out'))
+
+test.up_to_date(chdir = 'src', arguments = '.')
+
+test.run(chdir = 'src', arguments = '-c .')
+
+# What we do now is to change one of the files and rebuild
+test.write(['src', 'aaa.in'], "aaa.rebuild\n")
+
+# This should just rebuild aaa.out (and all)
+test.run(chdir = 'src',
+ arguments = '--cache-readonly .',
+ stdout = test.wrap_stdout("""\
+cat(["aaa.out"], ["aaa.in"])
+Retrieved `bbb.out' from cache
+Retrieved `ccc.out' from cache
+cat(["all"], ["aaa.out", "bbb.out", "ccc.out"])
+"""))
+
+test.must_match(['src', 'all'], "aaa.rebuild\nbbb.in\nccc.in\n")
+# cat.out contains only the things we built (not got from cache)
+test.must_match(['src', 'cat.out'], "aaa.out\nall\n")
+
+test.up_to_date(chdir = 'src', arguments = '.')
+
+test.run(chdir = 'src', arguments = '-c .')
+test.unlink(['src', 'cat.out'])
+
+# Verify that aaa.out contents weren't updated with the last build
+# Then clean up.
+test.run(chdir = 'src',
+ arguments = '--cache-readonly .',
+ stdout = test.wrap_stdout("""\
+cat(["aaa.out"], ["aaa.in"])
+Retrieved `bbb.out' from cache
+Retrieved `ccc.out' from cache
+cat(["all"], ["aaa.out", "bbb.out", "ccc.out"])
+"""))
+
+test.must_match(['src', 'all'], "aaa.rebuild\nbbb.in\nccc.in\n")
+test.must_match(['src', 'cat.out'], "aaa.out\nall\n")
+
+test.up_to_date(chdir = 'src', arguments = '.')
+
+test.run(chdir = 'src', arguments = '-c .')
+test.unlink(['src', 'cat.out'])
+
+# All done.
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/Docbook/basedir/htmlchunked/htmlchunked.py b/test/Docbook/basedir/htmlchunked/htmlchunked.py
index c5817017..cf5f3d17 100644
--- a/test/Docbook/basedir/htmlchunked/htmlchunked.py
+++ b/test/Docbook/basedir/htmlchunked/htmlchunked.py
@@ -26,10 +26,16 @@
Test the base_dir argument for the chunked HTML builder.
"""
+import os
+import sys
import TestSCons
test = TestSCons.TestSCons()
+if not (sys.platform.startswith('linux') and
+ os.path.isdir('/usr/share/xml/docbook/stylesheet/docbook-xsl')):
+ test.skip_test('Wrong OS or no stylesheets installed, skipping test.\n')
+
try:
import libxml2
except:
diff --git a/test/Docbook/basedir/htmlhelp/htmlhelp.py b/test/Docbook/basedir/htmlhelp/htmlhelp.py
index 736f7322..22bbd72a 100644
--- a/test/Docbook/basedir/htmlhelp/htmlhelp.py
+++ b/test/Docbook/basedir/htmlhelp/htmlhelp.py
@@ -26,10 +26,16 @@
Test the base_dir argument for the HTMLHELP builder.
"""
+import os
+import sys
import TestSCons
test = TestSCons.TestSCons()
+if not (sys.platform.startswith('linux') and
+ os.path.isdir('/usr/share/xml/docbook/stylesheet/docbook-xsl')):
+ test.skip_test('Wrong OS or no stylesheets installed, skipping test.\n')
+
try:
import libxml2
except:
diff --git a/test/Docbook/basedir/slideshtml/slideshtml.py b/test/Docbook/basedir/slideshtml/slideshtml.py
index 505d36de..a2375e4a 100644
--- a/test/Docbook/basedir/slideshtml/slideshtml.py
+++ b/test/Docbook/basedir/slideshtml/slideshtml.py
@@ -26,10 +26,17 @@
Test the base_dir argument for the Slides HTML builder.
"""
+import os
+import sys
import TestSCons
test = TestSCons.TestSCons()
+if not (sys.platform.startswith('linux') and
+ os.path.isdir('/usr/share/xml/docbook/stylesheet/docbook-xsl/slides') and
+ os.path.isdir('/usr/share/xml/docbook/custom/slides/3.3.1')):
+ test.skip_test('Wrong OS or no "slides" stylesheets installed, skipping test.\n')
+
try:
import libxml2
except:
diff --git a/test/Docbook/basic/slideshtml/slideshtml.py b/test/Docbook/basic/slideshtml/slideshtml.py
index d4636d4b..37c2be0c 100644
--- a/test/Docbook/basic/slideshtml/slideshtml.py
+++ b/test/Docbook/basic/slideshtml/slideshtml.py
@@ -26,10 +26,17 @@
Test the Slides HTML builder.
"""
+import os
+import sys
import TestSCons
test = TestSCons.TestSCons()
+if not (sys.platform.startswith('linux') and
+ os.path.isdir('/usr/share/xml/docbook/stylesheet/docbook-xsl/slides') and
+ os.path.isdir('/usr/share/xml/docbook/custom/slides/3.3.1')):
+ test.skip_test('Wrong OS or no "slides" stylesheets installed, skipping test.\n')
+
try:
import libxml2
except:
diff --git a/test/Docbook/rootname/htmlchunked/htmlchunked.py b/test/Docbook/rootname/htmlchunked/htmlchunked.py
index 24890194..8ab91d2d 100644
--- a/test/Docbook/rootname/htmlchunked/htmlchunked.py
+++ b/test/Docbook/rootname/htmlchunked/htmlchunked.py
@@ -26,10 +26,16 @@
Test the root.name argument for the chunked HTML builder.
"""
+import os
+import sys
import TestSCons
test = TestSCons.TestSCons()
+if not (sys.platform.startswith('linux') and
+ os.path.isdir('/usr/share/xml/docbook/stylesheet/docbook-xsl')):
+ test.skip_test('Wrong OS or no stylesheets installed, skipping test.\n')
+
try:
import libxml2
except:
diff --git a/test/Docbook/rootname/htmlhelp/htmlhelp.py b/test/Docbook/rootname/htmlhelp/htmlhelp.py
index 84be5d95..ee37e1a9 100644
--- a/test/Docbook/rootname/htmlhelp/htmlhelp.py
+++ b/test/Docbook/rootname/htmlhelp/htmlhelp.py
@@ -26,10 +26,16 @@
Test the root.name argument for the HTMLHELP builder.
"""
+import os
+import sys
import TestSCons
test = TestSCons.TestSCons()
+if not (sys.platform.startswith('linux') and
+ os.path.isdir('/usr/share/xml/docbook/stylesheet/docbook-xsl')):
+ test.skip_test('Wrong OS or no stylesheets installed, skipping test.\n')
+
try:
import libxml2
except:
diff --git a/test/Docbook/rootname/slideshtml/slideshtml.py b/test/Docbook/rootname/slideshtml/slideshtml.py
index c316adae..4d550358 100644
--- a/test/Docbook/rootname/slideshtml/slideshtml.py
+++ b/test/Docbook/rootname/slideshtml/slideshtml.py
@@ -26,10 +26,17 @@
Test the root.name argument for the Slides HTML builder.
"""
+import os
+import sys
import TestSCons
test = TestSCons.TestSCons()
+if not (sys.platform.startswith('linux') and
+ os.path.isdir('/usr/share/xml/docbook/stylesheet/docbook-xsl/slides') and
+ os.path.isdir('/usr/share/xml/docbook/custom/slides/3.3.1')):
+ test.skip_test('Wrong OS or no "slides" stylesheets installed, skipping test.\n')
+
try:
import libxml2
except:
diff --git a/test/Libs/SharedLibrary-update-deps.py b/test/Libs/SharedLibrary-update-deps.py
index 24c5262d..5394bc7a 100644
--- a/test/Libs/SharedLibrary-update-deps.py
+++ b/test/Libs/SharedLibrary-update-deps.py
@@ -26,18 +26,20 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Test that SharedLibrary() updates when a different lib is linked, even if it has the same md5.
-This is Tigris bug #2909.
+This is Tigris bug #2903.
"""
+import sys
import os.path
import TestSCons
test = TestSCons.TestSCons()
-test.dir_fixture( "bug2909" )
+test.dir_fixture( "bug2903" )
# Build the sub-libs (don't care about details of this)
test.run(arguments='-f SConstruct-libs')
+
# This should build the main lib, using libfoo.so
test.run(arguments='libname=foo')
# This should rebuild the main lib, using libbar.so;
@@ -48,6 +50,17 @@ test.must_not_contain_any_line(test.stdout(), ["is up to date"])
test.run(arguments='libname=foo')
test.must_not_contain_any_line(test.stdout(), ["is up to date"])
+# Now try changing the link command line (in an innocuous way); should rebuild.
+if sys.platform == 'win32':
+ extraflags='shlinkflags=/DEBUG'
+else:
+ extraflags='shlinkflags=-g'
+
+test.run(arguments=['libname=foo', extraflags])
+test.must_not_contain_any_line(test.stdout(), ["is up to date"])
+test.run(arguments=['libname=foo', extraflags, '--debug=explain'])
+test.must_contain_all_lines(test.stdout(), ["is up to date"])
+
test.pass_test()
# Local Variables:
diff --git a/test/Libs/bug2903/SConstruct b/test/Libs/bug2903/SConstruct
new file mode 100644
index 00000000..12919cef
--- /dev/null
+++ b/test/Libs/bug2903/SConstruct
@@ -0,0 +1,13 @@
+# SConstruct for testing but #2903.
+# The test changes the lib name to make sure it rebuilds
+# when the name changes, even if the content of the lib is the same.
+# Also, the test passes in extra shlinkflags to ensure things rebuild
+# when other linker options change, and not when they don't.
+# (This doesn't specifically test LIBPATH, but there's a test for
+# that already.)
+env=Environment()
+libname=ARGUMENTS.get('libname', 'foo')
+env.Append(SHLINKFLAGS=' $EXTRA_SHLINKFLAGS')
+shlinkflags=ARGUMENTS.get('shlinkflags', '')
+env.SharedLibrary('myshared', ['main.c'],
+ LIBS=[libname], LIBPATH='.', EXTRA_SHLINKFLAGS=shlinkflags)
diff --git a/test/Libs/bug2903/SConstruct-libs b/test/Libs/bug2903/SConstruct-libs
new file mode 100644
index 00000000..15900628
--- /dev/null
+++ b/test/Libs/bug2903/SConstruct-libs
@@ -0,0 +1,5 @@
+env=Environment()
+libfoo = env.SharedLibrary('foo', 'lib.c')
+env.InstallAs('${SHLIBPREFIX}bar${SHLIBSUFFIX}', libfoo[0])
+if len(libfoo) > 1: # on Windows, there's an import lib (also a .exp, but we don't want that)
+ env.InstallAs('${LIBPREFIX}bar${LIBSUFFIX}', libfoo[1])
diff --git a/test/Libs/bug2903/lib.c b/test/Libs/bug2903/lib.c
new file mode 100644
index 00000000..65f4cd29
--- /dev/null
+++ b/test/Libs/bug2903/lib.c
@@ -0,0 +1,4 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int i;
diff --git a/test/Libs/bug2903/main.c b/test/Libs/bug2903/main.c
new file mode 100644
index 00000000..a4b1ecc1
--- /dev/null
+++ b/test/Libs/bug2903/main.c
@@ -0,0 +1,6 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void func()
+{
+}
diff --git a/test/Libs/bug2909/SConstruct b/test/Libs/bug2909/SConstruct
deleted file mode 100644
index 2c5440b9..00000000
--- a/test/Libs/bug2909/SConstruct
+++ /dev/null
@@ -1,3 +0,0 @@
-env=Environment()
-libname=ARGUMENTS.get('libname', 'foo')
-env.SharedLibrary('myshared', ['main.c'], LIBS=[libname], LIBPATH='.') \ No newline at end of file
diff --git a/test/Libs/bug2909/SConstruct-libs b/test/Libs/bug2909/SConstruct-libs
deleted file mode 100644
index 3f59f9c5..00000000
--- a/test/Libs/bug2909/SConstruct-libs
+++ /dev/null
@@ -1,3 +0,0 @@
-env=Environment()
-libfoo = env.SharedLibrary('foo', 'lib.c')
-env.InstallAs('${SHLIBPREFIX}bar${SHLIBSUFFIX}', libfoo) \ No newline at end of file
diff --git a/test/Libs/bug2909/lib.c b/test/Libs/bug2909/lib.c
deleted file mode 100644
index 048f715b..00000000
--- a/test/Libs/bug2909/lib.c
+++ /dev/null
@@ -1 +0,0 @@
-int i;
diff --git a/test/Libs/bug2909/main.c b/test/Libs/bug2909/main.c
deleted file mode 100644
index 3fe7d491..00000000
--- a/test/Libs/bug2909/main.c
+++ /dev/null
@@ -1,3 +0,0 @@
-void func()
-{
-}
diff --git a/test/Pseudo.py b/test/Pseudo.py
new file mode 100644
index 00000000..db3c30c0
--- /dev/null
+++ b/test/Pseudo.py
@@ -0,0 +1,65 @@
+#!/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__"
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+# Firstly, build a pseudo target and make sure we get no warnings it
+# doesn't exist under any circumstances
+test.write('SConstruct', """
+env = Environment()
+env.Pseudo(env.Command('foo.out', [], '@echo boo'))
+""")
+
+test.run(arguments='-Q', stdout = 'boo\n')
+
+test.run(arguments='-Q --warning=target-not-built', stdout = "boo\n")
+
+# Now do the same thing again but create the target and check we get an
+# error if it exists after the build
+test.write('SConstruct', """
+env = Environment()
+env.Pseudo(env.Command('foo.out', [], Touch('$TARGET')))
+""")
+
+test.run(arguments='-Q', stdout = 'Touch("foo.out")\n', stderr = None,
+ status = 2)
+test.must_contain_all_lines(test.stderr(),
+ 'scons: *** Pseudo target foo.out must not exist')
+test.run(arguments='-Q --warning=target-not-built',
+ stdout = 'Touch("foo.out")\n',
+ stderr = None, status = 2)
+test.must_contain_all_lines(test.stderr(),
+ 'scons: *** Pseudo target foo.out must not exist')
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/builderrors.py b/test/builderrors.py
index 0133107f..3d443bf7 100644
--- a/test/builderrors.py
+++ b/test/builderrors.py
@@ -107,9 +107,6 @@ test.fail_test(os.path.exists(test.workpath('f3.out')))
test.write('SConstruct', """
env=Environment()
-if env['PLATFORM'] == 'posix':
- from SCons.Platform.posix import fork_spawn
- env['SPAWN'] = fork_spawn
env['ENV']['PATH'] = ''
env.Command(target='foo.out', source=[], action='not_a_program')
""")
@@ -123,9 +120,6 @@ test.must_not_contain_any_line(test.stderr(), ['Exception', 'Traceback'])
long_cmd = 'xyz ' + "foobarxyz" * 100000
test.write('SConstruct', """
env=Environment()
-if env['PLATFORM'] == 'posix':
- from SCons.Platform.posix import fork_spawn
- env['SPAWN'] = fork_spawn
env.Command(target='longcmd.out', source=[], action='echo %s')
"""%long_cmd)
@@ -147,9 +141,6 @@ test.must_not_contain_any_line(test.stderr(), ['Exception', 'Traceback'])
# with error "Permission denied" or "No such file or directory".
test.write('SConstruct', """
env=Environment()
-if env['PLATFORM'] in ('posix', 'darwin'):
- from SCons.Platform.posix import fork_spawn
- env['SPAWN'] = fork_spawn
env['SHELL'] = 'one'
env.Command(target='badshell.out', source=[], action='foo')
""")
@@ -191,7 +182,7 @@ env2.Install("target", "dir2/myFile")
def print_build_failures():
from SCons.Script import GetBuildFailures
for bf in GetBuildFailures():
- print bf.action
+ print bf.action
atexit.register(print_build_failures)
""")
diff --git a/test/leaky-handles.py b/test/leaky-handles.py
new file mode 100644
index 00000000..9502d1b5
--- /dev/null
+++ b/test/leaky-handles.py
@@ -0,0 +1,60 @@
+#!/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 file handles aren't leaked to child processes
+"""
+
+import os
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+if os.name != 'posix':
+ msg = "Skipping fork leak test on non-posix platform '%s'\n" % os.name
+ test.skip_test(msg)
+
+test.write('SConstruct', """
+
+#Leak a file handle
+open('/dev/null')
+
+#Check it gets closed
+test2 = Command('test2', [], '@ls /proc/$$$$/fd|wc -l')
+""")
+
+# In theory that should have 3 lines (handles 0/1/2). This is v. unix specific
+
+test.run(arguments = '-Q', stdout='3\n')
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/option/debug-multiple.py b/test/option/debug-multiple.py
new file mode 100644
index 00000000..f5bbdf09
--- /dev/null
+++ b/test/option/debug-multiple.py
@@ -0,0 +1,86 @@
+#!/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 that --debug can take multiple options
+"""
+
+import re
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+def cat(target, source, env):
+ open(str(target[0]), 'wb').write(open(str(source[0]), 'rb').read())
+env = Environment(BUILDERS={'Cat':Builder(action=Action(cat))})
+env.Cat('file.out', 'file.in')
+""")
+
+test.write('file.in', "file.in\n")
+
+# Just check that object counts for some representative classes
+# show up in the output.
+
+def find_object_count(s, stdout):
+ re_string = '\d+ +\d+ %s' % re.escape(s)
+ return re.search(re_string, stdout)
+
+objects = [
+ 'Action.CommandAction',
+ 'Builder.BuilderBase',
+ 'Environment.Base',
+ 'Executor.Executor',
+ 'Node.FS',
+ 'Node.FS.Base',
+ 'Node.Node',
+]
+
+for args in ['--debug=prepare,count', '--debug=count,prepare']:
+ test.run(arguments = args)
+ stdout = test.stdout()
+ missing = [o for o in objects if find_object_count(o, stdout) is None]
+
+ if missing:
+ print "Missing the following object lines from '%s' output:" % args
+ print "\t", ' '.join(missing)
+ print "STDOUT =========="
+ print stdout
+ test.fail_test(1)
+
+ if 'Preparing target file.out...' not in stdout:
+ print "Missing 'Preparing' lines from '%s' output:" % args
+ print "STDOUT =========="
+ print stdout
+ test.fail_test(1)
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/option/warn-dependency.py b/test/option/warn-dependency.py
index 95d8fed3..ca0c2aa8 100644
--- a/test/option/warn-dependency.py
+++ b/test/option/warn-dependency.py
@@ -43,6 +43,7 @@ env=Environment()
env['BUILDERS']['test'] = Builder(action=build,
source_scanner=SCons.Defaults.ObjSourceScan)
env.test(target='foo', source='foo.c')
+env.Pseudo('foo')
""")
test.write("foo.c","""
diff --git a/test/warning-TargetNotBuiltWarning.py b/test/warning-TargetNotBuiltWarning.py
new file mode 100644
index 00000000..76fdc5cd
--- /dev/null
+++ b/test/warning-TargetNotBuiltWarning.py
@@ -0,0 +1,55 @@
+#!/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__"
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+foo = Command('foo.out', [], '@echo boo')
+bill = Command('bill.out', [], Touch('$TARGET'))
+Depends(bill, foo)
+Alias('jim', bill)
+""")
+
+test.run(arguments='-Q jim', stdout = 'boo\nTouch("bill.out")\n')
+
+test.run(arguments='-Q jim --warning=target-not-built',
+ stdout = "boo\nscons: `jim' is up to date.\n",
+ stderr = None)
+test.must_contain_all_lines(test.stderr(),
+ 'scons: warning: Cannot find target foo.out after building')
+
+test.run(arguments='-Q jim --warning=target-not-built -n',
+ stdout = "scons: `jim' is up to date.\n")
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: