# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
__author__ = "Steven Knight <knight at baldmt dot com>"
-__revision__ = "TestCmd.py 0.30.D001 2007/10/01 16:53:55 knight"
-__version__ = "0.30"
+__revision__ = "TestCmd.py 0.31.D001 2008/01/01 09:05:59 knight"
+__version__ = "0.31"
+import errno
import os
import os.path
-import popen2
import re
import shutil
import stat
default_sleep_seconds = 1
+
+
+try:
+ import subprocess
+except ImportError:
+ # The subprocess module doesn't exist in this version of Python,
+ # so we're going to cobble up something that looks just enough
+ # like its API for our purposes below.
+ import new
+
+ subprocess = new.module('subprocess')
+
+ subprocess.PIPE = 'PIPE'
+ subprocess.STDOUT = 'STDOUT'
+ subprocess.mswindows = (sys.platform == 'win32')
+
+ try:
+ import popen2
+ popen2.Popen3
+ except AttributeError:
+ class Popen3:
+ universal_newlines = 1
+ def __init__(self, command, **kw):
+ if sys.platform == 'win32' and command[0] == '"':
+ command = '"' + command + '"'
+ (stdin, stdout, stderr) = os.popen3(' ' + command)
+ self.stdin = stdin
+ self.stdout = stdout
+ self.stderr = stderr
+ def close_output(self):
+ self.stdout.close()
+ self.resultcode = self.stderr.close()
+ def wait(self):
+ return self.resultcode
+
+ else:
+ try:
+ popen2.Popen4
+ except AttributeError:
+ # A cribbed Popen4 class, with some retrofitted code from
+ # the Python 1.5 Popen3 class methods to do certain things
+ # by hand.
+ class Popen4(popen2.Popen3):
+ childerr = None
+
+ def __init__(self, cmd, bufsize=-1):
+ p2cread, p2cwrite = os.pipe()
+ c2pread, c2pwrite = os.pipe()
+ self.pid = os.fork()
+ if self.pid == 0:
+ # Child
+ os.dup2(p2cread, 0)
+ os.dup2(c2pwrite, 1)
+ os.dup2(c2pwrite, 2)
+ for i in range(3, popen2.MAXFD):
+ try:
+ os.close(i)
+ except: pass
+ try:
+ os.execvp(cmd[0], cmd)
+ finally:
+ os._exit(1)
+ # Shouldn't come here, I guess
+ os._exit(1)
+ os.close(p2cread)
+ self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
+ os.close(c2pwrite)
+ self.fromchild = os.fdopen(c2pread, 'r', bufsize)
+ popen2._active.append(self)
+
+ popen2.Popen4 = Popen4
+
+ class Popen3(popen2.Popen3, popen2.Popen4):
+ universal_newlines = 1
+ def __init__(self, command, **kw):
+ if kw.get('stderr') == 'STDOUT':
+ apply(popen2.Popen4.__init__, (self, command, 1))
+ else:
+ apply(popen2.Popen3.__init__, (self, command, 1))
+ self.stdin = self.tochild
+ self.stdout = self.fromchild
+ self.stderr = self.childerr
+
+ subprocess.Popen = Popen3
+
+
+
+# From Josiah Carlson,
+# ASPN : Python Cookbook : Module to allow Asynchronous subprocess use on Windows and Posix platforms
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554
+
+PIPE = subprocess.PIPE
+
+if subprocess.mswindows:
+ from win32file import ReadFile, WriteFile
+ from win32pipe import PeekNamedPipe
+ import msvcrt
+else:
+ import select
+ import fcntl
+
+ try: fcntl.F_GETFL
+ except AttributeError: fcntl.F_GETFL = 3
+
+ try: fcntl.F_SETFL
+ except AttributeError: fcntl.F_SETFL = 4
+
+class Popen(subprocess.Popen):
+ def recv(self, maxsize=None):
+ return self._recv('stdout', maxsize)
+
+ def recv_err(self, maxsize=None):
+ return self._recv('stderr', maxsize)
+
+ def send_recv(self, input='', maxsize=None):
+ return self.send(input), self.recv(maxsize), self.recv_err(maxsize)
+
+ def get_conn_maxsize(self, which, maxsize):
+ if maxsize is None:
+ maxsize = 1024
+ elif maxsize < 1:
+ maxsize = 1
+ return getattr(self, which), maxsize
+
+ def _close(self, which):
+ getattr(self, which).close()
+ setattr(self, which, None)
+
+ if subprocess.mswindows:
+ def send(self, input):
+ if not self.stdin:
+ return None
+
+ try:
+ x = msvcrt.get_osfhandle(self.stdin.fileno())
+ (errCode, written) = WriteFile(x, input)
+ except ValueError:
+ return self._close('stdin')
+ except (subprocess.pywintypes.error, Exception), why:
+ if why[0] in (109, errno.ESHUTDOWN):
+ return self._close('stdin')
+ raise
+
+ return written
+
+ def _recv(self, which, maxsize):
+ conn, maxsize = self.get_conn_maxsize(which, maxsize)
+ if conn is None:
+ return None
+
+ try:
+ x = msvcrt.get_osfhandle(conn.fileno())
+ (read, nAvail, nMessage) = PeekNamedPipe(x, 0)
+ if maxsize < nAvail:
+ nAvail = maxsize
+ if nAvail > 0:
+ (errCode, read) = ReadFile(x, nAvail, None)
+ except ValueError:
+ return self._close(which)
+ except (subprocess.pywintypes.error, Exception), why:
+ if why[0] in (109, errno.ESHUTDOWN):
+ return self._close(which)
+ raise
+
+ #if self.universal_newlines:
+ # read = self._translate_newlines(read)
+ return read
+
+ else:
+ def send(self, input):
+ if not self.stdin:
+ return None
+
+ if not select.select([], [self.stdin], [], 0)[1]:
+ return 0
+
+ try:
+ written = os.write(self.stdin.fileno(), input)
+ except OSError, why:
+ if why[0] == errno.EPIPE: #broken pipe
+ return self._close('stdin')
+ raise
+
+ return written
+
+ def _recv(self, which, maxsize):
+ conn, maxsize = self.get_conn_maxsize(which, maxsize)
+ if conn is None:
+ return None
+
+ try:
+ flags = fcntl.fcntl(conn, fcntl.F_GETFL)
+ except TypeError:
+ flags = None
+ else:
+ if not conn.closed:
+ fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK)
+
+ try:
+ if not select.select([conn], [], [], 0)[0]:
+ return ''
+
+ r = conn.read(maxsize)
+ if not r:
+ return self._close(which)
+
+ #if self.universal_newlines:
+ # r = self._translate_newlines(r)
+ return r
+ finally:
+ if not conn.closed and not flags is None:
+ fcntl.fcntl(conn, fcntl.F_SETFL, flags)
+
+disconnect_message = "Other end disconnected!"
+
+def recv_some(p, t=.1, e=1, tr=5, stderr=0):
+ if tr < 1:
+ tr = 1
+ x = time.time()+t
+ y = []
+ r = ''
+ pr = p.recv
+ if stderr:
+ pr = p.recv_err
+ while time.time() < x or r:
+ r = pr()
+ if r is None:
+ if e:
+ raise Exception(disconnect_message)
+ else:
+ break
+ elif r:
+ y.append(r)
+ else:
+ time.sleep(max((x-time.time())/tr, 0))
+ return ''.join(y)
+
+def send_all(p, data):
+ while len(data):
+ sent = p.send(data)
+ if sent is None:
+ raise Exception(disconnect_message)
+ data = buffer(data, sent)
+
+
+
class TestCmd:
"""Class TestCmd
"""
dir = self.canonicalize(dir)
os.rmdir(dir)
- def run(self, program = None,
- interpreter = None,
- arguments = None,
- chdir = None,
- stdin = None,
- universal_newlines = None):
- """Runs a test of the program or script for the test
- environment. Standard output and error output are saved for
- future retrieval via the stdout() and stderr() methods.
+ def start(self, program = None,
+ interpreter = None,
+ arguments = None,
+ universal_newlines = None,
+ **kw):
+ """
+ Starts a program or script for the test environment.
The specified program will have the original directory
- prepending unless it is enclosed in a [list].
+ prepended unless it is enclosed in a [list].
"""
- if chdir:
- oldcwd = os.getcwd()
- if not os.path.isabs(chdir):
- chdir = os.path.join(self.workpath(chdir))
- if self.verbose:
- sys.stderr.write("chdir(" + chdir + ")\n")
- os.chdir(chdir)
if program:
if type(program) == type('') and not os.path.isabs(program):
program = os.path.join(self._cwd, program)
if universal_newlines is None:
universal_newlines = self.universal_newlines
- try:
- import subprocess
- except ImportError:
- try:
- Popen3 = popen2.Popen3
- except AttributeError:
- class Popen3:
- def __init__(self, command):
- (stdin, stdout, stderr) = os.popen3(' ' + command)
- self.stdin = stdin
- self.stdout = stdout
- self.stderr = stderr
- def close_output(self):
- self.stdout.close()
- self.resultcode = self.stderr.close()
- def wait(self):
- return self.resultcode
- if sys.platform == 'win32' and cmd_string[0] == '"':
- cmd_string = '"' + cmd_string + '"'
- p = Popen3(cmd_string)
- else:
- p = Popen3(cmd, 1)
- p.stdin = p.tochild
- p.stdout = p.fromchild
- p.stderr = p.childerr
+ combine = kw.get('combine', self.combine)
+ if combine:
+ stderr_value = subprocess.STDOUT
else:
- p = subprocess.Popen(cmd,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- universal_newlines=universal_newlines)
+ stderr_value = subprocess.PIPE
+
+ return Popen(cmd,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=stderr_value,
+ universal_newlines=universal_newlines)
+ def finish(self, popen, **kw):
+ """
+ Finishes and waits for the process being run under control of
+ the specified popen argument, recording the exit status,
+ standard output and error output.
+ """
+ popen.stdin.close()
+ self.status = popen.wait()
+ if not self.status:
+ self.status = 0
+ self._stdout.append(popen.stdout.read())
+ if popen.stderr:
+ stderr = popen.stderr.read()
+ else:
+ stderr = ''
+ self._stderr.append(stderr)
+
+ def run(self, program = None,
+ interpreter = None,
+ arguments = None,
+ chdir = None,
+ stdin = None,
+ universal_newlines = None):
+ """Runs a test of the program or script for the test
+ environment. Standard output and error output are saved for
+ future retrieval via the stdout() and stderr() methods.
+
+ The specified program will have the original directory
+ prepended unless it is enclosed in a [list].
+ """
+ if chdir:
+ oldcwd = os.getcwd()
+ if not os.path.isabs(chdir):
+ chdir = os.path.join(self.workpath(chdir))
+ if self.verbose:
+ sys.stderr.write("chdir(" + chdir + ")\n")
+ os.chdir(chdir)
+ p = self.start(program, interpreter, arguments, universal_newlines)
if stdin:
if is_List(stdin):
for line in stdin:
p.stdin.close()
out = p.stdout.read()
- err = p.stderr.read()
+ if p.stderr is None:
+ err = ''
+ else:
+ err = p.stderr.read()
try:
- p.close_output()
+ close_output = p.close_output
except AttributeError:
p.stdout.close()
- p.stderr.close()
+ if not p.stderr is None:
+ p.stderr.close()
+ else:
+ close_output()
+
+ self._stdout.append(out)
+ self._stderr.append(err)
self.status = p.wait()
if not self.status:
self.status = 0
- if self.combine:
- self._stdout.append(out + err)
- else:
- self._stdout.append(out)
- self._stderr.append(err)
-
if chdir:
os.chdir(oldcwd)
if self.verbose >= 2:
def readable(self, top, read=1):
"""Make the specified directory tree readable (read == 1)
or not (read == None).
+
+ This method has no effect on Windows systems, which use a
+ completely different mechanism to control file readability.
"""
+ if sys.platform == 'win32':
+ return
+
if read:
def do_chmod(fname):
try: st = os.stat(fname)
except OSError: pass
- else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0400))
+ else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|stat.S_IREAD))
else:
def do_chmod(fname):
try: st = os.stat(fname)
except OSError: pass
- else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0400))
+ else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~stat.S_IREAD))
if os.path.isfile(top):
# If it's a file, that's easy, just chmod it.
or not (write == None).
"""
- if write:
- def do_chmod(fname):
- try: st = os.stat(fname)
- except OSError: pass
- else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0200))
+ if sys.platform == 'win32':
+
+ if write:
+ def do_chmod(fname):
+ try: os.chmod(fname, stat.S_IWRITE)
+ except OSError: pass
+ else:
+ def do_chmod(fname):
+ try: os.chmod(fname, stat.S_IREAD)
+ except OSError: pass
+
else:
- def do_chmod(fname):
- try: st = os.stat(fname)
- except OSError: pass
- else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0200))
+
+ if write:
+ def do_chmod(fname):
+ try: st = os.stat(fname)
+ except OSError: pass
+ else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0200))
+ else:
+ def do_chmod(fname):
+ try: st = os.stat(fname)
+ except OSError: pass
+ else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0200))
if os.path.isfile(top):
do_chmod(top)
def executable(self, top, execute=1):
"""Make the specified directory tree executable (execute == 1)
or not (execute == None).
+
+ This method has no effect on Windows systems, which use a
+ completely different mechanism to control file executability.
"""
+ if sys.platform == 'win32':
+ return
+
if execute:
def do_chmod(fname):
try: st = os.stat(fname)
except OSError: pass
- else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0100))
+ else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|stat.S_IEXEC))
else:
def do_chmod(fname):
try: st = os.stat(fname)
except OSError: pass
- else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0100))
+ else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~stat.S_IEXEC))
if os.path.isfile(top):
# If it's a file, that's easy, just chmod it.
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
__author__ = "Steven Knight <knight at baldmt dot com>"
-__revision__ = "TestCommon.py 0.30.D001 2007/10/01 16:53:55 knight"
-__version__ = "0.30"
+__revision__ = "TestCommon.py 0.31.D001 2008/01/01 09:05:59 knight"
+__version__ = "0.31"
+import copy
import os
import os.path
import stat
print "Writable files: `%s'" % string.join(writable, "', `")
self.fail_test(missing + writable)
+ def _complete(self, actual_stdout, expected_stdout,
+ actual_stderr, expected_stderr, status, match):
+ """
+ Post-processes running a subcommand, checking for failure
+ status and displaying output appropriately.
+ """
+ if _failed(self, status):
+ expect = ''
+ if status != 0:
+ expect = " (expected %s)" % str(status)
+ print "%s returned %s%s" % (self.program, str(_status(self)), expect)
+ print self.banner('STDOUT ')
+ print actual_stdout
+ print self.banner('STDERR ')
+ print actual_stderr
+ self.fail_test()
+ if not expected_stdout is None and not match(actual_stdout, expected_stdout):
+ self.diff(expected_stdout, actual_stdout, 'STDOUT ')
+ if actual_stderr:
+ print self.banner('STDERR ')
+ print actual_stderr
+ self.fail_test()
+ if not expected_stderr is None and not match(actual_stderr, expected_stderr):
+ print self.banner('STDOUT ')
+ print actual_stdout
+ self.diff(expected_stderr, actual_stderr, 'STDERR ')
+ self.fail_test()
+
+ def start(self, program = None,
+ interpreter = None,
+ arguments = None,
+ universal_newlines = None,
+ **kw):
+ """
+ Starts a program or script for the test environment.
+
+ This handles the "options" keyword argument and exceptions.
+ """
+ try:
+ options = kw['options']
+ del kw['options']
+ except KeyError:
+ pass
+ else:
+ if options:
+ if arguments is None:
+ arguments = options
+ else:
+ arguments = options + " " + arguments
+ try:
+ return apply(TestCmd.start,
+ (self, program, interpreter, arguments, universal_newlines),
+ kw)
+ except KeyboardInterrupt:
+ raise
+ except Exception, e:
+ print self.banner('STDOUT ')
+ try:
+ print self.stdout()
+ except IndexError:
+ pass
+ print self.banner('STDERR ')
+ try:
+ print self.stderr()
+ except IndexError:
+ pass
+ raise e
+
+ def finish(self, popen, stdout = None, stderr = '', status = 0, **kw):
+ """
+ Finishes and waits for the process being run under control of
+ the specified popen argument. Additional arguments are similar
+ to those of the run() method:
+
+ stdout The expected standard output from
+ the command. A value of None means
+ don't test standard output.
+
+ stderr The expected error output from
+ the command. A value of None means
+ don't test error output.
+
+ status The expected exit status from the
+ command. A value of None means don't
+ test exit status.
+ """
+ apply(TestCmd.finish, (self, popen,), kw)
+ match = kw.get('match', self.match)
+ self._complete(self.stdout(), stdout,
+ self.stderr(), stderr, status, match)
+
def run(self, options = None, arguments = None,
stdout = None, stderr = '', status = 0, **kw):
"""Runs the program under test, checking that the test succeeded.
del kw['match']
except KeyError:
match = self.match
- try:
- apply(TestCmd.run, [self], kw)
- except KeyboardInterrupt:
- raise
- except Exception, e:
- print self.banner('STDOUT ')
- try:
- print self.stdout()
- except IndexError:
- pass
- print self.banner('STDERR ')
- try:
- print self.stderr()
- except IndexError:
- pass
- raise e
- if _failed(self, status):
- expect = ''
- if status != 0:
- expect = " (expected %s)" % str(status)
- print "%s returned %s%s" % (self.program, str(_status(self)), expect)
- print self.banner('STDOUT ')
- print self.stdout()
- print self.banner('STDERR ')
- print self.stderr()
- self.fail_test()
- if not stdout is None and not match(self.stdout(), stdout):
- self.diff(stdout, self.stdout(), 'STDOUT ')
- stderr = self.stderr()
- if stderr:
- print self.banner('STDERR ')
- print stderr
- self.fail_test()
- if not stderr is None and not match(self.stderr(), stderr):
- print self.banner('STDOUT ')
- print self.stdout()
- self.diff(stderr, self.stderr(), 'STDERR ')
- self.fail_test()
+ apply(TestCmd.run, [self], kw)
+ self._complete(self.stdout(), stdout,
+ self.stderr(), stderr, status, match)
def skip_test(self, message="Skipping test.\n"):
"""Skips a test.
import re
import string
import sys
+import time
import __builtin__
try:
return str
-
class TestSCons(TestCommon):
"""Class for testing SCons.
return x
def normalize_pdf(self, s):
- s = re.sub(r'/CreationDate \(D:[^)]*\)',
- r'/CreationDate (D:XXXX)', s)
+ s = re.sub(r'/(Creation|Mod)Date \(D:[^)]*\)',
+ r'/\1Date (D:XXXX)', s)
s = re.sub(r'/ID \[<[0-9a-fA-F]*> <[0-9a-fA-F]*>\]',
r'/ID [<XXXX> <XXXX>]', s)
s = re.sub(r'/(BaseFont|FontName) /[A-Z]{6}',
return s
- def java_ENV(self):
+ def java_ENV(self, version=None):
"""
- Return a default external environment that uses a local Java SDK
- in preference to whatever's found in the default PATH.
+ Initialize with a default external environment that uses a local
+ Java SDK in preference to whatever's found in the default PATH.
"""
+ try:
+ return self._java_env[version]['ENV']
+ except AttributeError:
+ self._java_env = {}
+ except KeyError:
+ pass
+
import SCons.Environment
env = SCons.Environment.Environment()
- java_path = [
- '/usr/local/j2sdk1.4.2/bin',
- '/usr/local/j2sdk1.4.1/bin',
- '/usr/local/j2sdk1.3.1/bin',
- '/usr/local/j2sdk1.3.0/bin',
- '/usr/local/j2sdk1.2.2/bin',
- '/usr/local/j2sdk1.2/bin',
- '/usr/local/j2sdk1.1.8/bin',
- '/usr/local/j2sdk1.1.7/bin',
- '/usr/local/j2sdk1.1.6/bin',
- '/usr/local/j2sdk1.1.5/bin',
- '/usr/local/j2sdk1.1.4/bin',
- '/usr/local/j2sdk1.1.3/bin',
- '/usr/local/j2sdk1.1.2/bin',
- '/usr/local/j2sdk1.1.1/bin',
- env['ENV']['PATH'],
- ]
+ self._java_env[version] = env
+
+ def paths(patterns):
+ import glob
+ result = []
+ for p in patterns:
+ paths = glob.glob(p)
+ paths.sort()
+ result.extend(paths)
+ return result
+
+ if version:
+ patterns = [
+ '/usr/lib/jvm/*-%s*/bin' % version,
+ '/usr/local/j2sdk%s*/bin' % version,
+ ]
+ java_path = paths(patterns) + [env['ENV']['PATH']]
+ else:
+ patterns = [
+ '/usr/lib/jvm/*/bin',
+ '/usr/local/j2sdk*/bin',
+ ]
+ java_path = paths(patterns) + [env['ENV']['PATH']]
+
env['ENV']['PATH'] = string.join(java_path, os.pathsep)
return env['ENV']
+ def java_where_jar(self, version=None):
+ ENV = self.java_ENV(version)
+ if self.detect_tool('jar', ENV=ENV):
+ where_jar = self.detect('JAR', 'jar', ENV=ENV)
+ else:
+ where_jar = self.where_is('jar', ENV['PATH'])
+ if not where_jar:
+ self.skip_test("Could not find Java jar, skipping test(s).\n")
+ return where_jar
+
+ def java_where_java(self, version=None):
+ """
+ Return a path to the java executable.
+ """
+ ENV = self.java_ENV(version)
+ where_java = self.where_is('java', ENV['PATH'])
+ if not where_java:
+ self.skip_test("Could not find Java java, skipping test(s).\n")
+ return where_java
+
+ def java_where_javac(self, version=None):
+ """
+ Return a path to the javac compiler.
+ """
+ ENV = self.java_ENV(version)
+ if self.detect_tool('javac'):
+ where_javac = self.detect('JAVAC', 'javac', ENV=ENV)
+ else:
+ where_javac = self.where_is('javac', ENV['PATH'])
+ if not where_javac:
+ self.skip_test("Could not find Java javac, skipping test(s).\n")
+ self.run(program = where_javac,
+ arguments = '-version',
+ stderr=None,
+ status=None)
+ if version:
+ if string.find(self.stderr(), 'javac %s' % version) == -1:
+ fmt = "Could not find javac for Java version %s, skipping test(s).\n"
+ self.skip_test(fmt % version)
+ else:
+ m = re.search(r'javac (\d\.\d)', self.stderr())
+ if m:
+ version = m.group(1)
+ else:
+ version = None
+ return where_javac, version
+
+ def java_where_javah(self, version=None):
+ ENV = self.java_ENV(version)
+ if self.detect_tool('javah'):
+ where_javah = self.detect('JAVAH', 'javah', ENV=ENV)
+ else:
+ where_javah = self.where_is('javah', ENV['PATH'])
+ if not where_javah:
+ self.skip_test("Could not find Java javah, skipping test(s).\n")
+ return where_javah
+
+ def java_where_rmic(self, version=None):
+ ENV = self.java_ENV(version)
+ if self.detect_tool('rmic'):
+ where_rmic = self.detect('RMIC', 'rmic', ENV=ENV)
+ else:
+ where_rmic = self.where_is('rmic', ENV['PATH'])
+ if not where_rmic:
+ self.skip_test("Could not find Java rmic, skipping non-simulated test(s).\n")
+ return where_rmic
+
def Qt_dummy_installation(self, dir='qt'):
# create a dummy qt installation
else:
return distutils.sysconfig.get_python_inc()
+ def wait_for(self, fname, timeout=10.0, popen=None):
+ """
+ Waits for the specified file name to exist.
+ """
+ waited = 0.0
+ while not os.path.exists(fname):
+ if timeout and waited >= timeout:
+ sys.stderr.write('timed out waiting for %s to exist\n' % fname)
+ if popen:
+ popen.stdin.close()
+ self.status = 1
+ self.finish(popen)
+ self.fail_test()
+ time.sleep(1.0)
+ waited = waited + 1.0
+
# In some environments, $AR will generate a warning message to stderr
# if the library doesn't previously exist and is being created. One
# way to fix this is to tell AR to be quiet (sometimes the 'c' flag),
self.write(python_name, profile_py % d)
self.run(program = python_name, interpreter = sys.executable)
+ def tempdir_re(self, *args):
+ """
+ Returns a regular expression to match a scons-time
+ temporary directory.
+ """
+ import re
+ import tempfile
+
+ sep = re.escape(os.sep)
+ tempdir = tempfile.gettempdir()
+
+ try:
+ realpath = os.path.realpath
+ except AttributeError:
+ pass
+ else:
+ tempdir = realpath(tempdir)
+
+ args = (tempdir, 'scons-time-',) + args
+ x = apply(os.path.join, args)
+ x = re.escape(x)
+ x = string.replace(x, 'time\\-', 'time\\-[^%s]*' % sep)
+ return x
+
def write_fake_aegis_py(self, name):
name = self.workpath(name)
self.write(name, aegis_py)
.B --aegis=
command-line option.
.TP
+.B archive_list
+A list of archives (files or directories)
+that will be copied to the temporary directory
+in which SCons will be invoked.
+.BR .tar ,
+.BR .tar.gz ,
+.BR .tgz
+and
+.BR .zip
+files will have their contents unpacked in
+the temporary directory.
+Directory trees and files will be copied as-is.
+.TP
.B initial_commands
A list of commands that will be executed
before the actual timed
This implies
.BR --implicit-cache .
+.TP
+--interactive
+Starts SCons in interactive mode.
+The SConscript files are read once and a
+.B "scons>>>"
+prompt is printed.
+Targets may now be rebuilt by typing commands at interactive prompt
+without having to re-read the SConscript files
+and re-initialize the dependency graph from scratch.
+
+SCons interactive mode supports the following commands:
+
+.RS 10
+.TP 6
+.BI build "[OPTIONS] [TARGETS] ..."
+Builds the specified
+.I TARGETS
+(and their dependencies)
+with the specified
+SCons command-line
+.IR OPTIONS .
+.B b
+and
+.B scons
+are synonyms.
+
+The following SCons command-line options affect the
+.B build
+command:
+
+.ES
+--cache-debug=FILE
+--cache-disable, --no-cache
+--cache-force, --cache-populate
+--cache-show
+--debug=TYPE
+-i, --ignore-errors
+-j N, --jobs=N
+-k, --keep-going
+-n, --no-exec, --just-print, --dry-run, --recon
+-Q
+-s, --silent, --quiet
+-s, --silent, --quiet
+--taskmastertrace=FILE
+--tree=OPTIONS
+.EE
+
+.IP "" 6
+Any other SCons command-line options that are specified
+do not cause errors
+but have no effect on the
+.B build
+command
+(mainly because they affect how the SConscript files are read,
+which only happens once at the beginning of interactive mode).
+
+.TP 6
+.BI clean "[OPTIONS] [TARGETS] ..."
+Cleans the specified
+.I TARGETS
+(and their dependencies)
+with the specified options.
+.B c
+is a synonym.
+This command is itself a synonym for
+.B "build --clean"
+
+.TP 6
+.BI exit
+Exits SCons interactive mode.
+You can also exit by terminating input
+(CTRL+D on UNIX or Linux systems,
+CTRL+Z on Windows systems).
+
+.TP 6
+.BI help "[COMMAND]"
+Provides a help message about
+the commands available in SCons interactive mode.
+If
+.I COMMAND
+is specified,
+.B h
+and
+.B ?
+are synonyms.
+
+.TP 6
+.BI shell "[COMMANDLINE]"
+Executes the specified
+.I COMMANDLINE
+in a subshell.
+If no
+.I COMMANDLINE
+is specified,
+executes the interactive command interpreter
+specified in the
+.B SHELL
+environment variable
+(on UNIX and Linux systems)
+or the
+.B COMSPEC
+environment variable
+(on Windows systems).
+.B sh
+and
+.B !
+are synonyms.
+
+.TP 6
+.B version
+Prints SCons version information.
+.RE
+
+An empty line repeats the last typed command.
+Command-line editing can be used if the
+.B readline
+module is available.
+
+.ES
+$ scons --interactive
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+scons>>> build -n prog
+scons>>> exit
+.EE
+
.TP
.RI -j " N" ", --jobs=" N
Specifies the number of jobs (commands) to run simultaneously.
.IR dir /site_tools
will get added to the default toolpath.
+.TP
+.RI --stack-size= KILOBYTES
+Set the size stack used to run threads to
+.IR KILOBYTES .
+This value determines the stack size of the threads used to run jobs.
+These are the threads that execute the actions of the builders for the
+nodes that are out-of-date.
+Note that this option has no effect unless the
+.B num_jobs
+option, which corresponds to -j and --jobs, is larger than one. Using
+a stack size that is too small may cause stack overflow errors. This
+usually shows up as segmentation faults that cause scons to abort
+before building anything. Using a stack size that is too large will
+cause scons to use more memory than required and may slow down the entire
+build process.
+
+The default value is to use a stack size of 256 kilobytes, which should
+be appropriate for most uses. You should not need to increase this value
+unless you encounter stack overflow errors.
+
.TP
-t, --touch
Ignored for compatibility with GNU
.BR SOURCES .
These warnings are disabled by default.
+.TP
+--warn=stack-size, --warn=no-stack-size
+Enables or disables warnings about requests to set the stack size
+that could not be honored.
+These warnings are enabled by default.
+
.\" .TP
.\" .RI --write-filenames= file
.\" Write all filenames considered into
g77
gas
gcc
+gfortran
gnulink
gs
hpc++
to be performed:
.RS 10
+.HP 6
.B timestamp-newer
Specifies that a target shall be considered out of date and rebuilt
if the dependency's timestamp is newer than the target file's timestamp.
.B no_exec
which corresponds to -n, --no-exec, --just-print, --dry-run and --recon;
.B num_jobs
-which corresponds to -j and --jobs.
+which corresponds to -j and --jobs;
.B random
-which corresponds to --random.
+which corresponds to --random; and
+.B stack_size
+which corresponds to --stack-size.
See the documentation for the
corresponding command line object for information about each specific
option.
'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.TP
-.RI env.subst( string ", [" raw ", " target ", " source ", " conv ])
+.RI env.subst( input ", [" raw ", " target ", " source ", " conv ])
Performs construction variable interpolation
-on the specified string argument.
+on the specified string or sequence argument
+.IR input .
By default,
leading or trailing white space will
pairs
(as is done for signature calculation).
+If the input is a sequence
+(list or tuple),
+the individual elements of
+the sequence will be expanded,
+and the results will be returned as a list.
+
The optional
.I target
and
from within a Python function used
as an SCons action.
-By default,
-all returned values are converted
-to their string representation.
+Returned string values or sequence elements
+are converted to their string representation by default.
The optional
.I conv
argument
.ES
.EE
+.TP
+.RI Configure.CheckDeclaration( self ", " symbol ", [" includes ", " language ])
+Checks if the specified
+.I symbol
+is declared.
+.I includes
+is a string containing one or more
+.B #include
+lines that will be inserted into the program
+that will be run to test for the existence of the type.
+The optional
+.I language
+argument should be
+.B C
+or
+.B C++
+and selects the compiler to be used for the check;
+the default is "C".
+
+.TP
+.RI Configure.Define(self ", " symbol ", [" value ", " comment ])
+This function does not check for anything, but defines a
+preprocessor symbol that will be added to the configuration header file.
+It is the equivalent of AC_DEFINE,
+and defines the symbol
+.I name
+with the optional
+.B value
+and the optional comment
+.BR comment .
+
+.IP
+Examples:
+
+.ES
+env = Environment()
+conf = Configure( env )
+
+# Puts the following line in the config header file:
+# #define A_SYMBOL
+conf.Define('A_SYMBOL')
+
+# Puts the following line in the config header file:
+# #define A_SYMBOL 1
+conf.Define('A_SYMBOL', 1)
+.EE
+
+.IP
+Be careful about quoting string values, though:
+
+.ES
+env = Environment()
+conf = Configure( env )
+
+# Puts the following line in the config header file:
+# #define A_SYMBOL YA
+conf.Define('A_SYMBOL', "YA")
+
+# Puts the following line in the config header file:
+# #define A_SYMBOL "YA"
+conf.Define('A_SYMBOL', '"YA"')
+.EE
+
+.IP
+For comment:
+
+.ES
+env = Environment()
+conf = Configure( env )
+
+# Puts the following lines in the config header file:
+# /* Set to 1 if you have a symbol */
+# #define A_SYMBOL 1
+conf.Define('A_SYMBOL', 1, 'Set to 1 if you have a symbol')
+.EE
+
.EE
You can define your own custom checks.
in addition to the predefined checks.
<!ENTITY as "<application>as</application>">
<!ENTITY Autoconf "<application>Autoconf</application>">
<!ENTITY Automake "<application>Automake</application>">
+<!ENTITY bison "<application>bison</application>">
<!ENTITY cc "<application>cc</application>">
<!ENTITY Cons "<application>Cons</application>">
<!ENTITY cp "<application>cp</application>">
+RELEASE 0.XX - XXX
+
+ From Benoit Belley:
+
+ - Fix the --keep-going flag so it builds all possible targets even when
+ a later top-level target depends on a child that failed its build.
+
+ - Fix being able to use $PDB and $WINDWOWS_INSERT_MANIFEST together.
+
+ - Don't crash if un-installing the Intel C compiler leaves left-over,
+ dangling entries in the Windows registry.
+
+ - Improve support for non-standard library prefixes and suffixes by
+ stripping all prefixes/suffixes from file name string as appropriate.
+
+ - Reduce the default stack size for -j worker threads to 256 Kbytes.
+ Provide user control over this value by adding --stack-size and
+ --warn=stack-size options, and a SetOption('stack_size') function.
+
+ - Fix a crash on Linux systems when trying to use the Intel C compiler
+ and no /opt/intel_cc_* directories are found.
+
+ - Improve using Python functions as actions by incorporating into
+ a FunctionAction's signature:
+ - literal values referenced by the byte code.
+ - values of default arguments
+ - code of nested functions
+ - values of variables captured by closures
+ - names of referenced global variables and functions
+
+ - Fix the closing message when --clean and --keep-going are both
+ used and no errors occur.
+
+ - Add support for the Intel C compiler on Mac OS X.
+
+ From Jérôme Berger:
+
+ - Have the D language scanner search for .di files as well as .d files.
+
+ - Add a find_include_names() method to the Scanner.Classic class to
+ abstract out how included names can be generated by subclasses.
+
+ - Allow the D language scanner to detect multiple modules imported by
+ a single statement.
+
+ From Konstantin Bozhikov:
+
+ - Support expansion of construction variables that contain or refer
+ to lists of other variables or Nodes within expansions like $PCPPATH.
+
+ - Change variable substitution (the env.subst() method) so that an
+ input sequence (list or tuple) is preserved as a list in the output.
+
+ From David Cournapeau:
+
+ - Add a CheckDeclaration() call to configure contexts.
+
+ - Improve the CheckTypeSize() code.
+
+ - Add a Define() call to configure contexts, to add arbitrary #define
+ lines to a generated configure header file.
+
+ - Add a "gfortran" Tool module for the GNU F95/F2003 compiler.
+
+ - Avoid use of -rpath with the Mac OS X linker.
+
+ From Steven Knight:
+
+ - Support the ability to subclass the new-style "str" class as input
+ to Builders.
+
+ - Improve the performance of our type-checking by using isinstance()
+ with new-style classes.
+
+ - Fix #include (and other $*PATH variables searches) of files with
+ absolute path names. Don't die if they don't exist (due to being
+ #ifdef'ed out or the like).
+
+ - Fix --interactive mode when Default(None) is used.
+
+ - Fix --debug=memoizer to work around a bug in base Python 2.2 metaclass
+ initialization (by just not allowing Memoization in Python versions
+ that have the bug).
+
+ - Have the "scons-time time" subcommand handle empty log files, and
+ log files that contain no results specified by the --which option.
+
+ - Fix the max Y of vertical bars drawn by "scons-time --fmt=gnuplot".
+
+ - On Mac OS X, account for the fact that the header file generated
+ from a C++ file will be named (e.g.) file.cpp.h, not file.hpp.
+
+ From Rob Managan:
+
+ - Enhance TeX and LaTeX support to work with BuildDir(duplicate=0).
+
+ - Re-run LaTeX when it issues a package warning that it must be re-run.
+
+ From Jan Nijtmans:
+
+ - If $JARCHDIR isn't set explicitly, use the .java_classdir attribute
+ that was set when the Java() Builder built the .class files.
+
+ From Gary Oberbrunner:
+
+ - Fix the ability to build an Alias in --interactive mode.
+
+ - Fix the ability to hash the contents of actions for nested Python
+ functions on Python versions where the inability to pickle them
+ returns a TypeError (instead of the documented PicklingError).
+
+ From Jonas Olsson:
+
+ - Fix use of the Intel C compiler when the top compiler directory,
+ but not the compiler version, is specified.
+
+ - Handle Intel C compiler network license files (port@system).
+
+ From Adam Simpkins:
+
+ - Add a --interactive option that starts a session for building (or
+ cleaning) targets without re-reading the SConscript files every time.
+
+ - Fix use of readline command-line editing in --interactive mode.
+
+ - Have the --interactive mode "build" command with no arguments
+ build the specified Default() targets.
+
+ From Ben Webb:
+
+ - Support the SWIG %module statement with following modifiers in
+ parenthese (e.g., '%module(directors="1")').
+
+
+
RELEASE 0.97.0d20071212 - Wed, 12 Dec 2007 09:29:32 -0600
From Benoit Belley:
This is the eighth beta release of SCons. Please consult the
CHANGES.txt file for a list of specific changes since last release.
+ Please note the following important changes since release 0.97.0d20071212:
+
+ -- THE env.subst() METHOD NOW RETURNS A LIST WHEN THE INPUT IS A SEQUENCE
+
+ The env.subst() method now returns a list with the elements
+ expanded when given a list as input. Previously, the env.subst()
+ method would always turn its result into a string.
+
+ This behavior was changed because way it interfered with
+ being able to include things like lists within the expansion
+ of variables like $CPPPATH and have SCons understand that the
+ elements of the "internal" lists still needed to be treated
+ separately. This would show up as a list like ['subdir1',
+ 'subdir'] showing up in a command line as "-Isubdir1 subdir".
+
+ -- THE Jar() BUILDER NOW USES THE Java() BUILDER CLASSDIR BY DEFAULT
+
+ By default, the Jar() Builder will now use the class directory
+ specified when the Java() builder is called. So the following
+ input:
+
+ classes = env.Java('classes', 'src')
+ env.Jar('out.jar', classes)
+
+ Will cause "-C classes" to be passed the "jar" command invocation,
+ and the Java classes in the "out.jar" file will not be prefixed
+ "classes/".
+
+ Explicitly setting the $JARCHDIR variable overrides this default
+ behavior. The old behavior of not passing any -C option to the
+ "jar" command can be preserved by explicitly setting $JARCHDIR
+ to None:
+
+ env = Environment(JARCHDIR = None)
+
+ The above setting is compatible with older versions of SCons.
+
Please note the following important changes since release 0.97.0d20070918:
-- SCons REDEFINES PYTHON open() AND file() ON Windows TO NOT PASS
SCons/compat/_scons_optparse.py
SCons/compat/_scons_sets.py
SCons/compat/_scons_sets15.py
+SCons/compat/_scons_shlex.py
SCons/compat/_scons_subprocess.py
SCons/compat/_scons_textwrap.py
SCons/compat/_scons_UserString.py
SCons/SConf.py
SCons/SConsign.py
SCons/Script/__init__.py
+SCons/Script/Interactive.py
SCons/Script/Main.py
SCons/Script/SConscript.py
SCons/Script/SConsOptions.py
SCons/Tool/g77.py
SCons/Tool/gas.py
SCons/Tool/gcc.py
+SCons/Tool/gfortran.py
SCons/Tool/gnulink.py
SCons/Tool/gs.py
SCons/Tool/hpc++.py
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import cPickle
import dis
import os
import os.path
i = i+1
return string.join(result, '')
+
+def _callable_contents(obj):
+ """Return the signature contents of a callable Python object.
+ """
+ try:
+ # Test if obj is a method.
+ return _function_contents(obj.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a callable object.
+ return _function_contents(obj.__call__.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a code object.
+ return _code_contents(obj)
+
+ except AttributeError:
+ # Test if obj is a function object.
+ return _function_contents(obj)
+
+
+def _object_contents(obj):
+ """Return the signature contents of any Python object.
+
+ We have to handle the case where object contains a code object
+ since it can be pickled directly.
+ """
+ try:
+ # Test if obj is a method.
+ return _function_contents(obj.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a callable object.
+ return _function_contents(obj.__call__.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a code object.
+ return _code_contents(obj)
+
+ except AttributeError:
+ try:
+ # Test if obj is a function object.
+ return _function_contents(obj)
+
+ except AttributeError:
+ # Should be a pickable Python object.
+ try:
+ return cPickle.dumps(obj)
+ except (cPickle.PicklingError, TypeError):
+ # This is weird, but it seems that nested classes
+ # are unpickable. The Python docs say it should
+ # always be a PicklingError, but some Python
+ # versions seem to return TypeError. Just do
+ # the best we can.
+ return str(obj)
+
+
+def _code_contents(code):
+ """Return the signature contents of a code object.
+
+ By providing direct access to the code object of the
+ function, Python makes this extremely easy. Hooray!
+
+ Unfortunately, older versions of Python include line
+ number indications in the compiled byte code. Boo!
+ So we remove the line number byte codes to prevent
+ recompilations from moving a Python function.
+ """
+
+ contents = []
+
+ # The code contents depends on the number of local variables
+ # but not their actual names.
+ contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames)))
+ try:
+ contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))
+ except AttributeError:
+ # Older versions of Python do not support closures.
+ contents.append(",0,0")
+
+ # The code contents depends on any constants accessed by the
+ # function. Note that we have to call _object_contents on each
+ # constants because the code object of nested functions can
+ # show-up among the constants.
+ #
+ # Note that we also always ignore the first entry of co_consts
+ # which contains the function doc string. We assume that the
+ # function does not access its doc string.
+ contents.append(',(' + string.join(map(_object_contents,code.co_consts[1:]),',') + ')')
+
+ # The code contents depends on the variable names used to
+ # accessed global variable, as changing the variable name changes
+ # the variable actually accessed and therefore changes the
+ # function result.
+ contents.append(',(' + string.join(map(_object_contents,code.co_names),',') + ')')
+
+
+ # The code contents depends on its actual code!!!
+ contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
+
+ return string.join(contents, '')
+
+
+def _function_contents(func):
+ """Return the signature contents of a function."""
+
+ contents = [_code_contents(func.func_code)]
+
+ # The function contents depends on the value of defaults arguments
+ if func.func_defaults:
+ contents.append(',(' + string.join(map(_object_contents,func.func_defaults),',') + ')')
+ else:
+ contents.append(',()')
+
+ # The function contents depends on the closure captured cell values.
+ try:
+ closure = func.func_closure or []
+ except AttributeError:
+ # Older versions of Python do not support closures.
+ closure = []
+
+ #xxx = [_object_contents(x.cell_contents) for x in closure]
+ xxx = map(lambda x: _object_contents(x.cell_contents), closure)
+ contents.append(',(' + string.join(xxx, ',') + ')')
+
+ return string.join(contents, '')
+
+
def _actionAppend(act1, act2):
# This function knows how to slap two actions together.
# Mainly, it handles ListActions by concatenating into
'accepts (target, source, env) as parameters.')
self.execfunction = execfunction
+ try:
+ self.funccontents = _callable_contents(execfunction)
+ except AttributeError:
+ try:
+ # See if execfunction will do the heavy lifting for us.
+ self.gc = execfunction.get_contents
+ except AttributeError:
+ # This is weird, just do the best we can.
+ self.funccontents = _object_contents(execfunction)
+
apply(_ActionAction.__init__, (self,)+args, kw)
self.varlist = kw.get('varlist', [])
self.cmdstr = cmdstr
return result
def get_contents(self, target, source, env):
- """Return the signature contents of this callable action.
-
- By providing direct access to the code object of the
- function, Python makes this extremely easy. Hooray!
-
- Unfortunately, older versions of Python include line
- number indications in the compiled byte code. Boo!
- So we remove the line number byte codes to prevent
- recompilations from moving a Python function.
- """
- execfunction = self.execfunction
+ """Return the signature contents of this callable action."""
try:
- # Test if execfunction is a function.
- code = execfunction.func_code.co_code
+ contents = self.gc(target, source, env)
except AttributeError:
- try:
- # Test if execfunction is a method.
- code = execfunction.im_func.func_code.co_code
- except AttributeError:
- try:
- # Test if execfunction is a callable object.
- code = execfunction.__call__.im_func.func_code.co_code
- except AttributeError:
- try:
- # See if execfunction will do the heavy lifting for us.
- gc = self.execfunction.get_contents
- except AttributeError:
- # This is weird, just do the best we can.
- contents = str(self.execfunction)
- else:
- contents = gc(target, source, env)
- else:
- contents = str(code)
- else:
- contents = str(code)
- else:
- contents = str(code)
- contents = remove_set_lineno_codes(contents)
+ contents = self.funccontents
+
return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
- self.varlist)))
+ self.varlist)))
def get_implicit_deps(self, target, source, env):
return []
def LocalFunc():
pass
- matches = [
- "d\000\000S",
- "d\x00\x00S",
+ func_matches = [
+ "0,0,0,0,(),(),(d\000\000S),(),()",
+ "0,0,0,0,(),(),(d\x00\x00S),(),()",
+ ]
+
+ meth_matches = [
+ "1,1,0,0,(),(),(d\000\000S),(),()",
+ "1,1,0,0,(),(),(d\x00\x00S),(),()",
]
a = SCons.Action.FunctionAction(GlobalFunc)
c = a.get_contents(target=[], source=[], env=Environment())
- assert c in matches, repr(c)
+ assert c in func_matches, repr(c)
a = SCons.Action.FunctionAction(LocalFunc)
c = a.get_contents(target=[], source=[], env=Environment())
- assert c in matches, repr(c)
+ assert c in func_matches, repr(c)
a = SCons.Action.FunctionAction(GlobalFunc, varlist=['XYZ'])
- matches_foo = map(lambda x: x + "foo", matches)
+ matches_foo = map(lambda x: x + "foo", func_matches)
c = a.get_contents(target=[], source=[], env=Environment())
- assert c in matches, repr(c)
+ assert c in func_matches, repr(c)
c = a.get_contents(target=[], source=[], env=Environment(XYZ = 'foo'))
assert c in matches_foo, repr(c)
lc = LocalClass()
a = SCons.Action.FunctionAction(lc.LocalMethod)
c = a.get_contents(target=[], source=[], env=Environment())
- assert c in matches, repr(c)
+ assert c in meth_matches, repr(c)
def test_strfunction(self):
"""Test the FunctionAction.strfunction() method
import SCons.Action
+cache_enabled = True
cache_debug = False
cache_force = False
cache_show = False
except ImportError:
msg = "No hashlib or MD5 module available, CacheDir() not supported"
SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg)
+ self.path = None
else:
self.path = path
+ self.current_cache_debug = None
+ self.debugFP = None
- def CacheDebugWrite(self, fmt, target, cachefile):
- self.debugFP.write(fmt % (target, os.path.split(cachefile)[1]))
-
- def CacheDebugQuiet(self, fmt, target, cachefile):
- pass
-
- def CacheDebugInit(self, fmt, target, cachefile):
- if cache_debug:
+ def CacheDebug(self, fmt, target, cachefile):
+ if cache_debug != self.current_cache_debug:
if cache_debug == '-':
self.debugFP = sys.stdout
- else:
+ elif cache_debug:
self.debugFP = open(cache_debug, 'w')
- self.CacheDebug = self.CacheDebugWrite
- self.CacheDebug(fmt, target, cachefile)
- else:
- self.CacheDebug = self.CacheDebugQuiet
+ else:
+ self.debugFP = None
+ self.current_cache_debug = cache_debug
+ if self.debugFP:
+ self.debugFP.write(fmt % (target, os.path.split(cachefile)[1]))
- CacheDebug = CacheDebugInit
+ def is_enabled(self):
+ return (cache_enabled and not self.path is None)
def cachepath(self, node):
"""
"""
+ if not self.is_enabled():
+ return None, None
+
sig = node.get_cachedir_bsig()
subdir = string.upper(sig[0])
dir = os.path.join(self.path, subdir)
execute the CacheRetrieveFunc and then have the latter
explicitly check SCons.Action.execute_actions itself.
"""
+ if not self.is_enabled():
+ return False
+
retrieved = False
if cache_show:
return retrieved
def push(self, node):
+ if not self.is_enabled():
+ return
return CachePush(node, [], node.get_build_env())
def push_if_forced(self, node):
if cache_force:
return self.push(node)
-
-class Null(SCons.Util.Null):
- def repr(self):
- return 'CacheDir.Null()'
- def cachepath(self, node):
- return None, None
- def retrieve(self, node):
- return False
}
"""
- # XXX: Try* vs CompileProg ?
- st = context.TryCompile(src % (type_name, expect), suffix)
- if st:
- _Have(context, "SIZEOF_" + type_name, str(expect))
+ st = context.CompileProg(src % (type_name, expect), suffix)
+ if not st:
context.Display("yes\n")
+ _Have(context, "SIZEOF_%s" % type_name, expect)
return expect
else:
context.Display("no\n")
return 0;
}
"""
- ret = context.TryRun(src, suffix)
- st = ret[0]
+ st, out = context.RunProg(src, suffix)
try:
- size = int(ret[1])
- _Have(context, "SIZEOF_" + type_name, str(size))
- context.Display("%d\n" % size)
+ size = int(out)
except ValueError:
+ # If cannot convert output of test prog to an integer (the size),
+ # something went wront, so just fail
+ st = 1
size = 0
- _LogFailed(context, src, st)
- context.Display(" Failed !\n")
- if st:
+
+ if not st:
+ context.Display("yes\n")
+ _Have(context, "SIZEOF_%s" % type_name, size)
return size
else:
+ context.Display("no\n")
+ _LogFailed(context, src, st)
return 0
+ return 0
+
+def CheckDeclaration(context, symbol, includes = None, language = None):
+ """Checks whether symbol is declared.
+
+ Use the same test as autoconf, that is test whether the symbol is defined
+ as a macro or can be used as an r-value.
+
+ Arguments:
+ symbol : str
+ the symbol to check
+ includes : str
+ Optional "header" can be defined to include a header file.
+ language : str
+ only C and C++ supported.
+
+ Returns:
+ status : bool
+ True if the check failed, False if succeeded."""
+
+ # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
+ if context.headerfilename:
+ includetext = '#include "%s"' % context.headerfilename
+ else:
+ includetext = ''
+
+ if not includes:
+ includes = ""
+
+ lang, suffix, msg = _lang2suffix(language)
+ if msg:
+ context.Display("Cannot check for declaration %s: %s\n" % (type_name, msg))
+ return msg
+
+ src = includetext + includes
+ context.Display('Checking whether %s is declared... ' % symbol)
+
+ src = src + r"""
+int main()
+{
+#ifndef %s
+ (void) %s;
+#endif
+ ;
+ return 0;
+}
+""" % (symbol, symbol)
+
+ st = context.CompileProg(src, suffix)
+ _YesNoResult(context, st, "HAVE_DECL_" + symbol, src)
+ return st
+
def CheckLib(context, libs, func_name = None, header = None,
extra_libs = None, call = None, language = None, autoadd = 1):
"""
_default_env.Decider('timestamp-match')
global DefaultEnvironment
DefaultEnvironment = _fetch_DefaultEnvironment
- _default_env._CacheDir = SCons.CacheDir.Null()
+ _default_env._CacheDir_path = None
return _default_env
# Emitters for setting the shared attribute on object files,
return result
-def _stripixes(prefix, list, suffix, stripprefix, stripsuffix, env, c=None):
+def _stripixes(prefix, list, suffix, stripprefixes, stripsuffixes, env, c=None):
"""
This is a wrapper around _concat()/_concat_ixes() that checks for the
existence of prefixes or suffixes on list elements and strips them
if SCons.Util.is_List(list):
list = SCons.Util.flatten(list)
- lsp = len(stripprefix)
- lss = len(stripsuffix)
+ if SCons.Util.is_List(stripprefixes):
+ stripprefixes = map(env.subst, SCons.Util.flatten(stripprefixes))
+ else:
+ stripprefixes = [env.subst(stripprefixes)]
+
+ if SCons.Util.is_List(stripsuffixes):
+ stripsuffixes = map(env.subst, SCons.Util.flatten(stripsuffixes))
+ else:
+ stripsuffixes = [stripsuffixes]
+
stripped = []
for l in SCons.PathList.PathList(list).subst_path(env, None, None):
if isinstance(l, SCons.Node.FS.File):
stripped.append(l)
continue
+
if not SCons.Util.is_String(l):
l = str(l)
- if l[:lsp] == stripprefix:
- l = l[lsp:]
- if l[-lss:] == stripsuffix:
- l = l[:-lss]
+
+ for stripprefix in stripprefixes:
+ lsp = len(stripprefix)
+ if l[:lsp] == stripprefix:
+ l = l[lsp:]
+ # Do not strip more than one prefix
+ break
+
+ for stripsuffix in stripsuffixes:
+ lss = len(stripsuffix)
+ if l[-lss:] == stripsuffix:
+ l = l[:-lss]
+ # Do not strip more than one suffix
+ break
+
stripped.append(l)
return c(prefix, stripped, suffix, env)
# We have an object plus a string, or multiple
# objects that we need to smush together. No choice
# but to make them into a string.
- p = string.join(map(SCons.Util.to_String, p), '')
+ p = string.join(map(SCons.Util.to_String_for_subst, p), '')
else:
p = s(p)
r.append(p)
def get_CacheDir(self):
try:
- return self._CacheDir
+ path = self._CacheDir_path
except AttributeError:
- cd = SCons.Defaults.DefaultEnvironment()._CacheDir
- self._CacheDir = cd
- return cd
+ path = SCons.Defaults.DefaultEnvironment()._CacheDir_path
+ try:
+ if path == self._last_CacheDir_path:
+ return self._last_CacheDir
+ except AttributeError:
+ pass
+ cd = SCons.CacheDir.CacheDir(path)
+ self._last_CacheDir_path = path
+ self._last_CacheDir = cd
+ return cd
def get_factory(self, factory, default='File'):
"""Return a factory function for creating Nodes for this
def CacheDir(self, path):
import SCons.CacheDir
- if path is None:
- self._CacheDir = SCons.CacheDir.Null()
- else:
- self._CacheDir = SCons.CacheDir.CacheDir(self.subst(path))
+ if not path is None:
+ path = self.subst(path)
+ self._CacheDir_path = path
def Clean(self, targets, files):
global CleanTargets
BAR=StringableObj("bar"))
r = env.subst_path([ "${FOO}/bar", "${BAR}/baz" ])
- assert r == [ "foo/bar", "bar/baz" ]
+ assert r == [ "foo/bar", "bar/baz" ], r
r = env.subst_path([ "bar/${FOO}", "baz/${BAR}" ])
- assert r == [ "bar/foo", "baz/bar" ]
+ assert r == [ "bar/foo", "baz/bar" ], r
r = env.subst_path([ "bar/${FOO}/bar", "baz/${BAR}/baz" ])
- assert r == [ "bar/foo/bar", "baz/bar/baz" ]
+ assert r == [ "bar/foo/bar", "baz/bar/baz" ], r
def test_subst_target_source(self):
"""Test the base environment subst_target_source() method"""
d = env.ParseFlags(s)
- if sys.version[:3] in ('1.5', '1.6', '2.0', '2.1', '2.2'):
- # Pre-2.3 Python has no shlex.split() function.
- # The compatibility layer does its best can by wrapping
- # the old shlex.shlex class, but that class doesn't really
- # understand quoting within the body of a token. We're just
- # going to live with this; it's the behavior they'd
- # have anyway if they use the shlex module...
- #
- # (Note that we must test the actual Python version numbers
- # above, not just test for whether trying to use shlex.split()
- # throws an AttributeError, because the compatibility layer
- # adds our wrapper function to the module as shlex.split().)
-
- expect_CPPPATH = ['/usr/include/fum',
- 'bar',
- '"C:\\Program']
- expect_LIBPATH = ['/usr/fax',
- 'foo',
- '"C:\\Program']
- expect_LIBS = ['Files\\ASCEND\\include"',
- 'xxx',
- 'yyy',
- 'Files\\ASCEND"',
- 'ascend']
- else:
- expect_CPPPATH = ['/usr/include/fum',
- 'bar',
- 'C:\\Program Files\\ASCEND\\include']
- expect_LIBPATH = ['/usr/fax',
- 'foo',
- 'C:\\Program Files\\ASCEND']
- expect_LIBS = ['xxx', 'yyy', 'ascend']
-
-
assert d['ASFLAGS'] == ['-as'], d['ASFLAGS']
assert d['CFLAGS'] == ['-std=c99']
assert d['CCFLAGS'] == ['-X', '-Wa,-as',
'+DD64'], d['CCFLAGS']
assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value'], 'BAZ'], d['CPPDEFINES']
assert d['CPPFLAGS'] == ['-Wp,-cpp'], d['CPPFLAGS']
- assert d['CPPPATH'] == expect_CPPPATH, d['CPPPATH']
+ assert d['CPPPATH'] == ['/usr/include/fum',
+ 'bar',
+ 'C:\\Program Files\\ASCEND\\include'], d['CPPPATH']
assert d['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], d['FRAMEWORKPATH']
assert d['FRAMEWORKS'] == ['Carbon'], d['FRAMEWORKS']
- assert d['LIBPATH'] == expect_LIBPATH, d['LIBPATH']
+ assert d['LIBPATH'] == ['/usr/fax',
+ 'foo',
+ 'C:\\Program Files\\ASCEND'], d['LIBPATH']
LIBS = map(str, d['LIBS'])
- assert LIBS == expect_LIBS, (d['LIBS'], LIBS)
+ assert LIBS == ['xxx', 'yyy', 'ascend'], (d['LIBS'], LIBS)
assert d['LINKFLAGS'] == ['-Wl,-link', '-pthread',
'-mno-cygwin', '-mwindows',
('-arch', 'i386'),
env = self.TestEnvironment(CD = 'CacheDir')
env.CacheDir('foo')
- assert env._CacheDir.path == 'foo', env._CacheDir.path
+ assert env._CacheDir_path == 'foo', env._CacheDir_path
env.CacheDir('$CD')
- assert env._CacheDir.path == 'CacheDir', env._CacheDir.path
+ assert env._CacheDir_path == 'CacheDir', env._CacheDir_path
def test_Clean(self):
"""Test the Clean() method"""
def get_build_env(self):
import SCons.Util
class NullEnvironment(SCons.Util.Null):
- #def get_scanner(self, key):
- # return None
- #def changed_since_last_build(self, dependency, target, prev_ni):
- # return dependency.changed_since_last_buld(target, prev_ni)
+ import SCons.CacheDir
+ _CacheDir_path = None
+ _CacheDir = SCons.CacheDir.CacheDir(None)
def get_CacheDir(self):
- import SCons.CacheDir
- return SCons.CacheDir.Null()
+ return self._CacheDir
return NullEnvironment()
def get_build_scanner_path(self):
return None
import SCons.compat
+
+# The default stack size (in kilobytes) of the threads used to execute
+# jobs in parallel.
+#
+# We use a stack size of 256 kilobytes. The default on some platforms
+# is too large and prevents us from creating enough threads to fully
+# parallelized the build. For example, the default stack size on linux
+# is 8 MBytes.
+
+default_stack_size = 256
+
+
class Jobs:
"""An instance of this class initializes N jobs, and provides
methods for starting, stopping, and waiting on all N jobs.
self.job = None
if num > 1:
try:
- self.job = Parallel(taskmaster, num)
+ stack_size = SCons.Job.stack_size
+ except AttributeError:
+ stack_size = default_stack_size
+
+ try:
+ self.job = Parallel(taskmaster, num, stack_size)
self.num_jobs = num
except NameError:
pass
class ThreadPool:
"""This class is responsible for spawning and managing worker threads."""
- def __init__(self, num):
- """Create the request and reply queues, and 'num' worker threads."""
+ def __init__(self, num, stack_size):
+ """Create the request and reply queues, and 'num' worker threads.
+
+ One must specify the stack size of the worker threads. The
+ stack size is specified in kilobytes.
+ """
self.requestQueue = Queue.Queue(0)
self.resultsQueue = Queue.Queue(0)
+ try:
+ prev_size = threading.stack_size(stack_size*1024)
+ except AttributeError, e:
+ # Only print a warning if the stack size has been
+ # explicitely set.
+ if hasattr(SCons.Job, 'stack_size'):
+ msg = "Setting stack size is unsupported by this version of Python:\n " + \
+ e.args[0]
+ SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg)
+ except ValueError, e:
+ msg = "Setting stack size failed:\n " + \
+ e.message
+ SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg)
+
# Create worker threads
self.workers = []
for _ in range(num):
worker = Worker(self.requestQueue, self.resultsQueue)
self.workers.append(worker)
+ # Once we drop Python 1.5 we can change the following to:
+ #if 'prev_size' in locals():
+ if 'prev_size' in locals().keys():
+ threading.stack_size(prev_size)
+
def put(self, obj):
"""Put task into request queue."""
self.requestQueue.put(obj)
This class is thread safe.
"""
- def __init__(self, taskmaster, num):
+ def __init__(self, taskmaster, num, stack_size):
"""Create a new parallel job given a taskmaster.
The taskmaster's next_task() method should return the next
multiple tasks simultaneously. """
self.taskmaster = taskmaster
- self.tp = ThreadPool(num)
+ self.tp = ThreadPool(num, stack_size)
self.maxjobs = num
class NoParallelTestCase(unittest.TestCase):
def runTest(self):
"test handling lack of parallel support"
- def NoParallel(tm, num):
+ def NoParallel(tm, num, stack_size):
raise NameError
save_Parallel = SCons.Job.Parallel
SCons.Job.Parallel = NoParallel
class M:
def __init__(cls, name, bases, cls_dict):
- cls.has_metaclass = 1
-
-class A:
- __metaclass__ = M
+ cls.use_metaclass = 1
+ def fake_method(self):
+ pass
+ new.instancemethod(fake_method, None, cls)
try:
- has_metaclass = A.has_metaclass
+ class A:
+ __metaclass__ = M
+
+ use_metaclass = A.use_metaclass
except AttributeError:
- has_metaclass = None
+ use_metaclass = None
+ reason = 'no metaclasses'
+except TypeError:
+ use_metaclass = None
+ reason = 'new.instancemethod() bug'
+else:
+ del A
del M
-del A
-if not has_metaclass:
+if not use_metaclass:
def Dump(title):
pass
- class Memoized_Metaclass:
- # Just a place-holder so pre-metaclass Python versions don't
- # have to have special code for the Memoized classes.
- pass
+ try:
+ class Memoized_Metaclass(type):
+ # Just a place-holder so pre-metaclass Python versions don't
+ # have to have special code for the Memoized classes.
+ pass
+ except TypeError:
+ class Memoized_Metaclass:
+ # A place-holder so pre-metaclass Python versions don't
+ # have to have special code for the Memoized classes.
+ pass
def EnableMemoization():
import SCons.Warnings
- msg = 'memoization is not supported in this version of Python (no metaclasses)'
- raise SCons.Warnings.NoMetaclassSupportWarning, msg
+ msg = 'memoization is not supported in this version of Python (%s)'
+ raise SCons.Warnings.NoMetaclassSupportWarning, msg % reason
else:
c = obj.get_memoizer_counter('dict')
- if SCons.Memoize.has_metaclass:
+ if SCons.Memoize.use_metaclass:
assert c.hit == 3, c.hit
assert c.miss == 2, c.miss
else:
c = obj.get_memoizer_counter('value')
- if SCons.Memoize.has_metaclass:
+ if SCons.Memoize.use_metaclass:
assert c.hit == 3, c.hit
assert c.miss == 1, c.miss
else:
self.get_build_env().get_CacheDir().push_if_forced(self)
ninfo = self.get_ninfo()
- old = self.get_stored_info()
-
- csig = None
- mtime = self.get_timestamp()
- size = self.get_size()
-
- max_drift = self.fs.max_drift
- if max_drift > 0:
- if (time.time() - mtime) > max_drift:
- try:
- n = old.ninfo
- if n.timestamp and n.csig and n.timestamp == mtime:
- csig = n.csig
- except AttributeError:
- pass
- elif max_drift == 0:
- try:
- csig = old.ninfo.csig
- except AttributeError:
- pass
+ csig = self.get_max_drift_csig()
if csig:
ninfo.csig = csig
- ninfo.timestamp = mtime
- ninfo.size = size
+ ninfo.timestamp = self.get_timestamp()
+ ninfo.size = self.get_size()
if not self.has_builder():
# This is a source file, but it might have been a target file
# in another build that included more of the DAG. Copy
# any build information that's stored in the .sconsign file
# into our binfo object so it doesn't get lost.
+ old = self.get_stored_info()
self.get_binfo().__dict__.update(old.binfo.__dict__)
self.store_info()
# SIGNATURE SUBSYSTEM
#
+ def get_max_drift_csig(self):
+ """
+ Returns the content signature currently stored for this node
+ if it's been unmodified longer than the max_drift value, or the
+ max_drift value is 0. Returns None otherwise.
+ """
+ old = self.get_stored_info()
+ mtime = self.get_timestamp()
+
+ csig = None
+ max_drift = self.fs.max_drift
+ if max_drift > 0:
+ if (time.time() - mtime) > max_drift:
+ try:
+ n = old.ninfo
+ if n.timestamp and n.csig and n.timestamp == mtime:
+ csig = n.csig
+ except AttributeError:
+ pass
+ elif max_drift == 0:
+ try:
+ csig = old.ninfo.csig
+ except AttributeError:
+ pass
+
+ return csig
+
def get_csig(self):
"""
Generate a node's content signature, the digested signature
except AttributeError:
pass
- try:
- contents = self.get_contents()
- except IOError:
- # This can happen if there's actually a directory on-disk,
- # which can be the case if they've disabled disk checks,
- # or if an action with a File target actually happens to
- # create a same-named directory by mistake.
- csig = ''
- else:
- csig = SCons.Util.MD5signature(contents)
+ csig = self.get_max_drift_csig()
+ if csig is None:
+
+ try:
+ contents = self.get_contents()
+ except IOError:
+ # This can happen if there's actually a directory on-disk,
+ # which can be the case if they've disabled disk checks,
+ # or if an action with a File target actually happens to
+ # create a same-named directory by mistake.
+ csig = ''
+ else:
+ csig = SCons.Util.MD5signature(contents)
ninfo.csig = csig
It would be more compact to just use this as a nested function
with a default keyword argument (see the commented-out version
below), but that doesn't work unless you have nested scopes,
- so we define it here just this works work under Python 1.5.2.
+ so we define it here just so this work under Python 1.5.2.
"""
if fd is None:
fd = self.default_filedir
dir, name = os.path.split(fd)
drive, d = os.path.splitdrive(dir)
if d in ('/', os.sep):
- return p
+ return p.fs.get_root(drive).dir_on_disk(name)
if dir:
p = self.filedir_lookup(p, dir)
if not p:
# XXX additional tests for the guts of the functionality some day
- def test_del_binfo(self):
- """Test deleting the build information from a Node
- """
- node = SCons.Node.Node()
- node.binfo = None
- node.del_binfo()
- assert not hasattr(node, 'binfo'), node
+ #def test_del_binfo(self):
+ # """Test deleting the build information from a Node
+ # """
+ # node = SCons.Node.Node()
+ # node.binfo = None
+ # node.del_binfo()
+ # assert not hasattr(node, 'binfo'), node
def test_store_info(self):
"""Test calling the method to store build information
# waiting for this Node to be built.
for parent in self.waiting_parents.keys():
parent.implicit = None
- parent.del_binfo()
self.clear()
can be re-evaluated by interfaces that do continuous integration
builds).
"""
- # Note in case it's important in the future: We also used to clear
- # the build information (the lists of dependencies) here like this:
- #
- # self.del_binfo()
- #
- # But we now rely on the fact that we're going to look at that
- # once before the build, and then store the results in the
- # .sconsign file after the build.
+ # The del_binfo() call here isn't necessary for normal execution,
+ # but is for interactive mode, where we might rebuild the same
+ # target and need to start from scratch.
+ self.del_binfo()
self.clear_memoized_values()
self.ninfo = self.new_ninfo()
self.executor_cleanup()
# so we must recalculate the implicit deps:
self.implicit = []
self.implicit_dict = {}
- self._children_reset()
- self.del_binfo()
# Have the executor scan the sources.
executor.scan_sources(self.builder.source_scanner)
# entries to equal the new dependency list, for the benefit
# of the loop below that updates node information.
then.extend([None] * diff)
+ if t: Trace(': old %s new %s' % (len(then), len(children)))
result = True
for child, prev_ni in zip(children, then):
try:
get = obj.get
except AttributeError:
- if isinstance(obj, SCons.Node.Node):
+ if isinstance(obj, SCons.Node.Node) or SCons.Util.is_Sequence( obj ):
result = obj
else:
result = str(obj)
value = env.subst(value, target=target, source=source,
conv=node_conv)
if SCons.Util.is_Sequence(value):
- # It came back as a string or tuple, which in this
- # case usually means some variable expanded to an
- # actually Dir node. Concatenate the values.
- value = string.join(map(str, value), '')
+ result.extend(value)
+ continue
+
elif type == TYPE_OBJECT:
value = node_conv(value)
if value:
env['LIBSUFFIX'] = '.a'
env['SHLIBPREFIX'] = '$LIBPREFIX'
env['SHLIBSUFFIX'] = '.so'
- env['LIBPREFIXES'] = '$LIBPREFIX'
+ env['LIBPREFIXES'] = [ '$LIBPREFIX' ]
env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
env['PSPAWN'] = pspawn
env['SPAWN'] = spawn
'CheckFunc' : CheckFunc,
'CheckType' : CheckType,
'CheckTypeSize' : CheckTypeSize,
+ 'CheckDeclaration' : CheckDeclaration,
'CheckHeader' : CheckHeader,
'CheckCHeader' : CheckCHeader,
'CheckCXXHeader' : CheckCXXHeader,
'CheckLib' : CheckLib,
- 'CheckLibWithHeader' : CheckLibWithHeader
+ 'CheckLibWithHeader' : CheckLibWithHeader,
}
self.AddTests(default_tests)
self.AddTests(custom_tests)
self._shutdown()
return self.env
+ def Define(self, name, value = None, comment = None):
+ """
+ Define a pre processor symbol name, with the optional given value in the
+ current config header.
+
+ If value is None (default), then #define name is written. If value is not
+ none, then #define name value is written.
+
+ comment is a string which will be put as a C comment in the
+ header, to explain the meaning of the value (appropriate C comments /* and
+ */ will be put automatically."""
+ lines = []
+ if comment:
+ comment_str = "/* %s */" % comment
+ lines.append(comment_str)
+
+ if value is not None:
+ define_str = "#define %s %s" % (name, value)
+ else:
+ define_str = "#define %s" % name
+ lines.append(define_str)
+ lines.append('')
+
+ self.config_h_text = self.config_h_text + string.join(lines, '\n')
+
def BuildNodes(self, nodes):
"""
Tries to build the given nodes immediately. Returns 1 on success,
# TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
return not self.TryBuild(self.env.Object, text, ext)
+ def RunProg(self, text, ext):
+ self.sconf.cached = 1
+ # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
+ st, out = self.TryRun(text, ext)
+ return not st, out
+
def AppendLIBS(self, lib_name_list):
oldLIBS = self.env.get( 'LIBS', [] )
self.env.Append(LIBS = lib_name_list)
context.did_show_result = 1
return res
+def CheckDeclaration(context, declaration, includes = "", language = None):
+ res = SCons.Conftest.CheckDeclaration(context, declaration,
+ includes = includes,
+ language = language)
+ context.did_show_result = 1
+ return not res
+
def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'):
# used by CheckHeader and CheckLibWithHeader to produce C - #include
# statements from the specified header (list)
finally:
sconf.Finish()
+ def test_Define(self):
+ """Test SConf.Define()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'),
+ config_h = self.test.workpath('config.h'))
+ try:
+ # XXX: we test the generated config.h string. This is not so good,
+ # ideally, we would like to test if the generated file included in
+ # a test program does what we want.
+
+ # Test defining one symbol wo value
+ sconf.config_h_text = ''
+ sconf.Define('YOP')
+ assert sconf.config_h_text == '#define YOP\n'
+
+ # Test defining one symbol with integer value
+ sconf.config_h_text = ''
+ sconf.Define('YOP', 1)
+ assert sconf.config_h_text == '#define YOP 1\n'
+
+ # Test defining one symbol with string value
+ sconf.config_h_text = ''
+ sconf.Define('YOP', '"YIP"')
+ assert sconf.config_h_text == '#define YOP "YIP"\n'
+
+ # Test defining one symbol with string value
+ sconf.config_h_text = ''
+ sconf.Define('YOP', "YIP")
+ assert sconf.config_h_text == '#define YOP YIP\n'
+
+ finally:
+ sconf.Finish()
+
def test_CheckTypeSize(self):
"""Test SConf.CheckTypeSize()
"""
finally:
sconf.Finish()
+ def test_CheckDeclaration(self):
+ """Test SConf.CheckDeclaration()
+ """
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ try:
+ # In ANSI C, malloc should be available in stdlib
+ r = sconf.CheckDeclaration('malloc', includes = "#include <stdlib.h>")
+ assert r, "malloc not declared ??"
+ # For C++, __cplusplus should be declared
+ r = sconf.CheckDeclaration('__cplusplus', language = 'C++')
+ assert r, "__cplusplus not declared in C++ ??"
+ r = sconf.CheckDeclaration('__cplusplus', language = 'C')
+ assert not r, "__cplusplus declared in C ??"
+ finally:
+ sconf.Finish()
+
def test_(self):
"""Test SConf.CheckType()
"""
import SCons.Node.FS
import SCons.Scanner
+import SCons.Util
+
+import SCons.cpp
+
+class SConsCPPScanner(SCons.cpp.PreProcessor):
+ """
+ SCons-specific subclass of the cpp.py module's processing.
+
+ We subclass this so that: 1) we can deal with files represented
+ by Nodes, not strings; 2) we can keep track of the files that are
+ missing.
+ """
+ def __init__(self, *args, **kw):
+ apply(SCons.cpp.PreProcessor.__init__, (self,)+args, kw)
+ self.missing = []
+ def initialize_result(self, fname):
+ self.result = SCons.Util.UniqueList([fname])
+ def finalize_result(self, fname):
+ return self.result[1:]
+ def find_include_file(self, t):
+ keyword, quote, fname = t
+ result = SCons.Node.FS.find_file(fname, self.searchpath[quote])
+ if not result:
+ self.missing.append((fname, self.current_file))
+ return result
+ def read_file(self, file):
+ try:
+ fp = open(str(file.rfile()))
+ except EnvironmentError, e:
+ self.missing.append((file, self.current_file))
+ return ''
+ else:
+ return fp.read()
+
+def dictify_CPPDEFINES(env):
+ cppdefines = env.get('CPPDEFINES', {})
+ if cppdefines is None:
+ return {}
+ if SCons.Util.is_Sequence(cppdefines):
+ result = {}
+ for c in cppdefines:
+ if SCons.Util.is_Sequence(c):
+ result[c[0]] = c[1]
+ else:
+ result[c] = None
+ return result
+ if not SCons.Util.is_Dict(cppdefines):
+ return {cppdefines : None}
+ return cppdefines
+
+class SConsCPPScannerWrapper:
+ """
+ The SCons wrapper around a cpp.py scanner.
+
+ This is the actual glue between the calling conventions of generic
+ SCons scanners, and the (subclass of) cpp.py class that knows how
+ to look for #include lines with reasonably real C-preprocessor-like
+ evaluation of #if/#ifdef/#else/#elif lines.
+ """
+ def __init__(self, name, variable):
+ self.name = name
+ self.path = SCons.Scanner.FindPathDirs(variable)
+ def __call__(self, node, env, path = ()):
+ cpp = SConsCPPScanner(current = node.get_dir(),
+ cpppath = path,
+ dict = dictify_CPPDEFINES(env))
+ result = cpp(node)
+ for included, includer in cpp.missing:
+ fmt = "No dependency generated for file: %s (included from: %s) -- file not found"
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
+ fmt % (included, includer))
+ return result
+
+ def recurse_nodes(self, nodes):
+ return nodes
+ def select(self, node):
+ return self
def CScanner():
"""Return a prototype Scanner instance for scanning source files
that use the C pre-processor"""
+
+ # Here's how we would (or might) use the CPP scanner code above that
+ # knows how to evaluate #if/#ifdef/#else/#elif lines when searching
+ # for #includes. This is commented out for now until we add the
+ # right configurability to let users pick between the scanners.
+ #return SConsCPPScannerWrapper("CScanner", "CPPPATH")
+
cs = SCons.Scanner.ClassicCPP("CScanner",
"$CPPSUFFIXES",
"CPPPATH",
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import re
import string
import SCons.Scanner
def DScanner():
"""Return a prototype Scanner instance for scanning D source files"""
- ds = D(name = "DScanner",
- suffixes = '$DSUFFIXES',
- path_variable = 'DPATH',
- regex = 'import\s+([^\;]*)\;')
+ ds = D()
return ds
class D(SCons.Scanner.Classic):
+ def __init__ (self):
+ SCons.Scanner.Classic.__init__ (self,
+ name = "DScanner",
+ suffixes = '$DSUFFIXES',
+ path_variable = 'DPATH',
+ regex = 'import\s+(?:[a-zA-Z0-9_.]+)\s*(?:,\s*(?:[a-zA-Z0-9_.]+)\s*)*;')
+
+ self.cre2 = re.compile ('(?:import\s)?\s*([a-zA-Z0-9_.]+)\s*(?:,|;)', re.M)
+
def find_include(self, include, source_dir, path):
# translate dots (package separators) to slashes
inc = string.replace(include, '.', '/')
i = SCons.Node.FS.find_file(inc + '.d', (source_dir,) + path)
+ if i is None:
+ i = SCons.Node.FS.find_file (inc + '.di', (source_dir,) + path)
return i, include
+
+ def find_include_names(self, node):
+ includes = []
+ for i in self.cre.findall(node.get_contents()):
+ includes = includes + self.cre2.findall(i)
+ return includes
but leave the file name untouched for "includegraphics." For
the "bibliography" keyword we need to add .bib if there is
no extension. (This need to be revisited since if there
- is no extension for an :includegraphics" keyword latex will
+ is no extension for an "includegraphics" keyword latex will
append .ps or .eps to find the file; while pdftex will use
other extensions.)
"""
env = DummyEnvironment(LIBPATH = [ 'foo' ])
env.fs = DummyFS()
+ env.fs._cwd = DummyNode('cwd')
dir = DummyNode('dir', ['xxx'])
fpd = SCons.Scanner.FindPathDirs('LIBPATH')
+ result = fpd(env)
+ assert str(result) == "('foo',)", result
result = fpd(env, dir)
assert str(result) == "('xxx', 'foo')", result
will return all of the *path directories."""
def __init__(self, variable):
self.variable = variable
- def __call__(self, env, dir, target=None, source=None, argument=None):
+ def __call__(self, env, dir=None, target=None, source=None, argument=None):
import SCons.PathList
try:
path = env[self.variable]
def sort_key(self, include):
return SCons.Node.FS._my_normcase(include)
+ def find_include_names(self, node):
+ return self.cre.findall(node.get_contents())
+
def scan(self, node, path=()):
# cache the includes list in node so we only scan it once:
if node.includes != None:
includes = node.includes
else:
- includes = self.cre.findall(node.get_contents())
+ includes = self.find_include_names (node)
node.includes = includes
# This is a hand-coded DSU (decorate-sort-undecorate, or
--- /dev/null
+#
+# __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__"
+
+__doc__ = """
+SCons interactive mode
+"""
+
+# TODO:
+#
+# This has the potential to grow into something with a really big life
+# of its own, which might or might not be a good thing. Nevertheless,
+# here are some enhancements that will probably be requested some day
+# and are worth keeping in mind (assuming this takes off):
+#
+# - A command to re-read / re-load the SConscript files. This may
+# involve allowing people to specify command-line options (e.g. -f,
+# -I, --no-site-dir) that affect how the SConscript files are read.
+#
+# - Additional command-line options on the "build" command.
+#
+# Of the supported options that seemed to make sense (after a quick
+# pass through the list), the ones that seemed likely enough to be
+# used are listed in the man page and have explicit test scripts.
+#
+# These had code changed in Script/Main.py to support them, but didn't
+# seem likely to be used regularly, so had no test scripts added:
+#
+# build --diskcheck=*
+# build --implicit-cache=*
+# build --implicit-deps-changed=*
+# build --implicit-deps-unchanged=*
+#
+# These look like they should "just work" with no changes to the
+# existing code, but like those above, look unlikely to be used and
+# therefore had no test scripts added:
+#
+# build --random
+#
+# These I'm not sure about. They might be useful for individual
+# "build" commands, and may even work, but they seem unlikely enough
+# that we'll wait until they're requested before spending any time on
+# writing test scripts for them, or investigating whether they work.
+#
+# build -q [??? is there a useful analog to the exit status?]
+# build --duplicate=
+# build --profile=
+# build --max-drift=
+# build --warn=*
+# build --Y
+#
+# - Most of the SCons command-line options that the "build" command
+# supports should be settable as default options that apply to all
+# subsequent "build" commands. Maybe a "set {option}" command that
+# maps to "SetOption('{option}')".
+#
+# - Need something in the 'help' command that prints the -h output.
+#
+# - A command to run the configure subsystem separately (must see how
+# this interacts with the new automake model).
+#
+# - Command-line completion of target names; maybe even of SCons options?
+# Completion is something that's supported by the Python cmd module,
+# so this should be doable without too much trouble.
+#
+
+import cmd
+import copy
+import os
+import re
+import shlex
+import string
+import sys
+
+try:
+ import readline
+except ImportError:
+ pass
+
+from SCons.Debug import Trace
+
+class SConsInteractiveCmd(cmd.Cmd):
+ """\
+ build [TARGETS] Build the specified TARGETS and their dependencies.
+ 'b' is a synonym.
+ clean [TARGETS] Clean (remove) the specified TARGETS and their
+ dependencies. 'c' is a synonym.
+ exit Exit SCons interactive mode.
+ help [COMMAND] Prints help for the specified COMMAND. 'h' and
+ '?' are synonyms.
+ shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!'
+ are synonyms.
+ version Prints SCons version information.
+ """
+
+ synonyms = {
+ 'b' : 'build',
+ 'c' : 'clean',
+ 'h' : 'help',
+ 'scons' : 'build',
+ 'sh' : 'shell',
+ }
+
+ def __init__(self, **kw):
+ cmd.Cmd.__init__(self)
+ for key, val in kw.items():
+ setattr(self, key, val)
+
+ if sys.platform == 'win32':
+ self.shell_variable = 'COMSPEC'
+ else:
+ self.shell_variable = 'SHELL'
+
+ def default(self, argv):
+ print "*** Unknown command: %s" % argv[0]
+
+ def onecmd(self, line):
+ line = string.strip(line)
+ if not line:
+ print self.lastcmd
+ return self.emptyline()
+ self.lastcmd = line
+ if line[0] == '!':
+ line = 'shell ' + line[1:]
+ elif line[0] == '?':
+ line = 'help ' + line[1:]
+ argv = shlex.split(line)
+ argv[0] = self.synonyms.get(argv[0], argv[0])
+ if not argv[0]:
+ return self.default(line)
+ else:
+ try:
+ func = getattr(self, 'do_' + argv[0])
+ except AttributeError:
+ return self.default(argv)
+ return func(argv)
+
+ def do_build(self, argv):
+ """\
+ build [TARGETS] Build the specified TARGETS and their
+ dependencies. 'b' is a synonym.
+ """
+ import SCons.SConsign
+ import SCons.Script.Main
+
+ options = copy.deepcopy(self.options)
+
+ options, targets = self.parser.parse_args(argv[1:], values=options)
+
+ SCons.Script.COMMAND_LINE_TARGETS = targets
+
+ if targets:
+ SCons.Script.BUILD_TARGETS = targets
+ else:
+ # If the user didn't specify any targets on the command line,
+ # use the list of default targets.
+ SCons.Script.BUILD_TARGETS = SCons.Script._build_plus_default
+
+ nodes = SCons.Script.Main._build_targets(self.fs,
+ options,
+ targets,
+ self.target_top)
+
+ if not nodes:
+ return
+
+ # Clean up so that we can perform the next build correctly.
+ #
+ # We do this by walking over all the children of the targets,
+ # and clearing their state.
+ #
+ # We currently have to re-scan each node to find their
+ # children, because built nodes have already been partially
+ # cleared and don't remember their children. (In scons
+ # 0.96.1 and earlier, this wasn't the case, and we didn't
+ # have to re-scan the nodes.)
+ #
+ # Because we have to re-scan each node, we can't clear the
+ # nodes as we walk over them, because we may end up rescanning
+ # a cleared node as we scan a later node. Therefore, only
+ # store the list of nodes that need to be cleared as we walk
+ # the tree, and clear them in a separate pass.
+ #
+ # XXX: Someone more familiar with the inner workings of scons
+ # may be able to point out a more efficient way to do this.
+
+ SCons.Script.Main.progress_display("scons: Clearing cached node information ...")
+
+ seen_nodes = {}
+
+ def get_unseen_children(node, parent, seen_nodes=seen_nodes):
+ def is_unseen(node, seen_nodes=seen_nodes):
+ return not seen_nodes.has_key(node)
+ return filter(is_unseen, node.children(scan=1))
+
+ def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes):
+ seen_nodes[node] = 1
+
+ # If this file is in a BuildDir and has a
+ # corresponding source file in the source tree, remember the
+ # node in the source tree, too. This is needed in
+ # particular to clear cached implicit dependencies on the
+ # source file, since the scanner will scan it if the
+ # BuildDir was created with duplicate=0.
+ try:
+ rfile_method = node.rfile
+ except AttributeError:
+ return
+ else:
+ rfile = rfile_method()
+ if rfile != node:
+ seen_nodes[rfile] = 1
+
+ for node in nodes:
+ walker = SCons.Node.Walker(node,
+ kids_func=get_unseen_children,
+ eval_func=add_to_seen_nodes)
+ n = walker.next()
+ while n:
+ n = walker.next()
+
+ for node in seen_nodes.keys():
+ # Call node.clear() to clear most of the state
+ node.clear()
+ # node.clear() doesn't reset node.state, so call
+ # node.set_state() to reset it manually
+ node.set_state(SCons.Node.no_state)
+ node.implicit = None
+
+ SCons.SConsign.Reset()
+ SCons.Script.Main.progress_display("scons: done clearing node information.")
+
+ def do_clean(self, argv):
+ """\
+ clean [TARGETS] Clean (remove) the specified TARGETS
+ and their dependencies. 'c' is a synonym.
+ """
+ return self.do_build(['build', '--clean'] + argv[1:])
+
+ def do_EOF(self, argv):
+ print
+ self.do_exit(argv)
+
+ def _do_one_help(self, arg):
+ try:
+ # If help_<arg>() exists, then call it.
+ func = getattr(self, 'help_' + arg)
+ except AttributeError:
+ try:
+ func = getattr(self, 'do_' + arg)
+ except AttributeError:
+ doc = None
+ else:
+ doc = self._doc_to_help(func)
+ if doc:
+ sys.stdout.write(doc + '\n')
+ sys.stdout.flush()
+ else:
+ doc = self.strip_initial_spaces(func())
+ if doc:
+ sys.stdout.write(doc + '\n')
+ sys.stdout.flush()
+
+ def _doc_to_help(self, obj):
+ doc = obj.__doc__
+ if doc is None:
+ return ''
+ return self._strip_initial_spaces(doc)
+
+ def _strip_initial_spaces(self, s):
+ #lines = s.split('\n')
+ lines = string.split(s, '\n')
+ spaces = re.match(' *', lines[0]).group(0)
+ #def strip_spaces(l):
+ # if l.startswith(spaces):
+ # l = l[len(spaces):]
+ # return l
+ #return '\n'.join([ strip_spaces(l) for l in lines ])
+ def strip_spaces(l, spaces=spaces):
+ if l[:len(spaces)] == spaces:
+ l = l[len(spaces):]
+ return l
+ lines = map(strip_spaces, lines)
+ return string.join(lines, '\n')
+
+ def do_exit(self, argv):
+ """\
+ exit Exit SCons interactive mode.
+ """
+ sys.exit(0)
+
+ def do_help(self, argv):
+ """\
+ help [COMMAND] Prints help for the specified COMMAND. 'h'
+ and '?' are synonyms.
+ """
+ if argv[1:]:
+ for arg in argv[1:]:
+ if self._do_one_help(arg):
+ break
+ else:
+ # If bare 'help' is called, print this class's doc
+ # string (if it has one).
+ doc = self._doc_to_help(self.__class__)
+ if doc:
+ sys.stdout.write(doc + '\n')
+ sys.stdout.flush()
+
+ def do_shell(self, argv):
+ """\
+ shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and
+ '!' are synonyms.
+ """
+ import subprocess
+ argv = argv[1:]
+ if not argv:
+ argv = os.environ[self.shell_variable]
+ try:
+ p = subprocess.Popen(argv)
+ except EnvironmentError, e:
+ sys.stderr.write('scons: %s: %s\n' % (argv[0], e.strerror))
+ else:
+ p.wait()
+
+ def do_version(self, argv):
+ """\
+ version Prints SCons version information.
+ """
+ sys.stdout.write(self.parser.version + '\n')
+
+def interact(fs, parser, options, targets, target_top):
+ c = SConsInteractiveCmd(prompt = 'scons>>> ',
+ fs = fs,
+ parser = parser,
+ options = options,
+ targets = targets,
+ target_top = target_top)
+ c.cmdloop()
import SCons.Util
import SCons.Warnings
+import SCons.Script.Interactive
+
+def fetch_win32_parallel_msg():
+ # A subsidiary function that exists solely to isolate this import
+ # so we don't have to pull it in on all platforms, and so that an
+ # in-line "import" statement in the _main() function below doesn't
+ # cause warnings about local names shadowing use of the 'SCons'
+ # globl in nest scopes and UnboundLocalErrors and the like in some
+ # versions (2.1) of Python.
+ import SCons.Platform.win32
+ SCons.Platform.win32.parallel_msg
+
#
class SConsPrintHelpException(Exception):
module.__buildsys__)
def _main(parser):
- import SCons
global exit_status
options = parser.values
SCons.Warnings.NoMetaclassSupportWarning,
SCons.Warnings.NoObjectCountWarning,
SCons.Warnings.NoParallelSupportWarning,
- SCons.Warnings.MisleadingKeywordsWarning, ]
+ SCons.Warnings.MisleadingKeywordsWarning,
+ SCons.Warnings.StackSizeWarning, ]
for warning in default_warnings:
SCons.Warnings.enableWarningClass(warning)
SCons.Warnings._warningOut = _scons_internal_warning
SCons.Node.implicit_cache = options.implicit_cache
SCons.Node.implicit_deps_changed = options.implicit_deps_changed
SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged
+
if options.no_exec:
SCons.SConf.dryrun = 1
SCons.Action.execute_actions = None
- CleanTask.execute = CleanTask.show
if options.question:
SCons.SConf.dryrun = 1
if options.clean:
if options.no_progress or options.silent:
progress_display.set_mode(0)
- if options.silent:
- display.set_mode(0)
- if options.silent:
- SCons.Action.print_actions = None
-
- if options.cache_disable:
- SCons.CacheDir.CacheDir = SCons.Util.Null()
- if options.cache_debug:
- SCons.CacheDir.cache_debug = options.cache_debug
- if options.cache_force:
- SCons.CacheDir.cache_force = True
- if options.cache_show:
- SCons.CacheDir.cache_show = True
if options.site_dir:
_load_site_scons_dir(d, options.site_dir)
SCons.Script._Add_Targets(targets + parser.rargs)
SCons.Script._Add_Arguments(xmit_args)
- sys.stdout = SCons.Util.Unbuffered(sys.stdout)
+ # If stdout is not a tty, replace it with a wrapper object to call flush
+ # after every write.
+ #
+ # Tty devices automatically flush after every newline, so the replacement
+ # isn't necessary. Furthermore, if we replace sys.stdout, the readline
+ # module will no longer work. This affects the behavior during
+ # --interactive mode. --interactive should only be used when stdin and
+ # stdout refer to a tty.
+ if not sys.stdout.isatty():
+ sys.stdout = SCons.Util.Unbuffered(sys.stdout)
+ if not sys.stderr.isatty():
+ sys.stderr = SCons.Util.Unbuffered(sys.stderr)
memory_stats.append('before reading SConscript files:')
count_stats.append(('pre-', 'read'))
SCons.Node.implicit_cache = options.implicit_cache
SCons.Node.FS.set_duplicate(options.duplicate)
fs.set_max_drift(options.max_drift)
+ if not options.stack_size is None:
+ SCons.Job.stack_size = options.stack_size
+
+ platform = SCons.Platform.platform_module()
+
+ if options.interactive:
+ SCons.Script.Interactive.interact(fs, OptionsParser, options,
+ targets, target_top)
+
+ else:
+
+ # Build the targets
+ nodes = _build_targets(fs, options, targets, target_top)
+ if not nodes:
+ exit_status = 2
+
+def _build_targets(fs, options, targets, target_top):
+
+ progress_display.set_mode(not (options.no_progress or options.silent))
+ display.set_mode(not options.silent)
+ SCons.Action.print_actions = not options.silent
+ SCons.Action.execute_actions = not options.no_exec
+ SCons.SConf.dryrun = options.no_exec
+
+ if options.diskcheck:
+ SCons.Node.FS.set_diskcheck(options.diskcheck)
+
+ _set_debug_values(options)
+ SCons.Node.implicit_cache = options.implicit_cache
+ SCons.Node.implicit_deps_changed = options.implicit_deps_changed
+ SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged
+
+ SCons.CacheDir.cache_enabled = not options.cache_disable
+ SCons.CacheDir.cache_debug = options.cache_debug
+ SCons.CacheDir.cache_force = options.cache_force
+ SCons.CacheDir.cache_show = options.cache_show
+
+ if options.no_exec:
+ CleanTask.execute = CleanTask.show
+ else:
+ CleanTask.execute = CleanTask.remove
lookup_top = None
if targets or SCons.Script.BUILD_TARGETS != SCons.Script._build_plus_default:
if not targets:
sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n")
- sys.exit(2)
+ return None
def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs):
if isinstance(x, SCons.Node.Node):
opening_message = "Cleaning targets ..."
closing_message = "done cleaning targets."
if options.keep_going:
- closing_message = "done cleaning targets (errors occurred during clean)."
+ failure_message = "done cleaning targets (errors occurred during clean)."
else:
failure_message = "cleaning terminated because of errors."
except AttributeError:
msg = "parallel builds are unsupported by this version of Python;\n" + \
"\tignoring -j or num_jobs option.\n"
elif sys.platform == 'win32':
- import SCons.Platform.win32
- msg = SCons.Platform.win32.parallel_msg
+ msg = fetch_win32_parallel_msg()
if msg:
SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg)
try:
progress_display("scons: " + opening_message)
- jobs.run()
+ try:
+ jobs.run()
+ except KeyboardInterrupt:
+ # If we are in interactive mode, a KeyboardInterrupt
+ # interrupts only this current run. Return 'nodes' normally
+ # so that the outer loop can clean up the nodes and continue.
+ if options.interactive:
+ print "Build interrupted."
+ # Continue and return normally
finally:
jobs.cleanup()
if exit_status:
memory_stats.append('after building targets:')
count_stats.append(('post-', 'build'))
+ return nodes
+
def _exec_main(parser, values):
sconsflags = os.environ.get('SCONSFLAGS', '')
all_args = string.split(sconsflags) + sys.argv[1:]
'no_exec',
'num_jobs',
'random',
+ 'stack_size',
]
def set_option(self, name, value):
# Set this right away so it can affect the rest of the
# file/Node lookups while processing the SConscript files.
SCons.Node.FS.set_diskcheck(value)
+ elif name == 'stack_size':
+ try:
+ value = int(value)
+ except ValueError:
+ raise SCons.Errors.UserError, "An integer is required: %s"%repr(value)
self.__SConscript_settings__[name] = value
usage="usage: scons [OPTION] [TARGET] ...",)
op.preserve_unknown_options = True
+ op.version = version
# Add the options to the parser we just created.
#
action="callback", callback=opt_implicit_deps,
help="Ignore changes in implicit dependencies.")
+ op.add_option('--interact', '--interactive',
+ dest='interactive', default=False,
+ action="store_true",
+ help="Run in interactive mode.")
+
op.add_option('-j', '--jobs',
nargs=1, type="int",
dest="num_jobs", default=1,
help="Use DIR instead of the usual site_scons dir.",
metavar="DIR")
+ op.add_option('--stack-size',
+ nargs=1, type="int",
+ dest='stack_size',
+ action="store",
+ help="Set the stack size of the threads used to run jobs to N kilobytes.",
+ metavar="N")
+
op.add_option('--taskmastertrace',
nargs=1,
dest="taskmastertrace_file", default=None,
help="Search up directory tree for SConstruct, "
"build Default() targets from local SConscript.")
- def opt_version(option, opt, value, parser, version=version):
- sys.stdout.write(version + '\n')
+ def opt_version(option, opt, value, parser):
+ sys.stdout.write(parser.version + '\n')
sys.exit(0)
op.add_option("-v", "--version",
action="callback", callback=opt_version,
import SCons.Errors
-from SCons.Util import is_String, is_List, is_Tuple
+from SCons.Util import is_String, is_Sequence
# Indexed by the SUBST_* constants below.
-_strconv = [SCons.Util.to_String,
- SCons.Util.to_String,
+_strconv = [SCons.Util.to_String_for_subst,
+ SCons.Util.to_String_for_subst,
SCons.Util.to_String_for_signature]
list = self.list
if list is None:
list = []
- elif not is_List(list) and not is_Tuple(list):
+ elif not is_Sequence(list):
list = [list]
# The map(self.func) call is what actually turns
# a list into appropriate proxies.
wrapping a NLWrapper. This class handles the different methods used
to access the list, calling the NLWrapper to create proxies on demand.
- Note that we subclass UserList.UserList purely so that the is_List()
- function will identify an object of this class as a list during
- variable expansion. We're not really using any UserList.UserList
- methods in practice.
+ Note that we subclass UserList.UserList purely so that the
+ is_Sequence() function will identify an object of this class as
+ a list during variable expansion. We're not really using any
+ UserList.UserList methods in practice.
"""
def __init__(self, nl):
self.nl = nl
# Indexed by the SUBST_* constants above.
_regex_remove = [ _rm, None, _remove ]
+def _rm_list(list):
+ #return [ l for l in list if not l in ('$(', '$)') ]
+ return filter(lambda l: not l in ('$(', '$)'), list)
+
+def _remove_list(list):
+ result = []
+ do_append = result.append
+ for l in list:
+ if l == '$(':
+ do_append = lambda x: None
+ elif l == '$)':
+ do_append = result.append
+ else:
+ do_append(l)
+ return result
+
+# Indexed by the SUBST_* constants above.
+_list_remove = [ _rm_list, None, _remove_list ]
+
# Regular expressions for splitting strings and handling substitutions,
# for use by the scons_subst() and scons_subst_list() functions:
#
_space_sep = re.compile(r'[\t ]+(?![^{]*})')
def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None):
- """Expand a string containing construction variable substitutions.
+ """Expand a string or list containing construction variable
+ substitutions.
This is the work-horse function for substitutions in file names
and the like. The companion scons_subst_list() function (below)
var = string.split(key, '.')[0]
lv[var] = ''
return self.substitute(s, lv)
- elif is_List(s) or is_Tuple(s):
+ elif is_Sequence(s):
def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars):
return conv(substitute(l, lvars))
- r = map(func, s)
- return string.join(r)
+ return map(func, s)
elif callable(s):
try:
s = s(target=self.target,
separate tokens.
"""
if is_String(args) and not isinstance(args, CmdStringHolder):
+ args = str(args) # In case it's a UserString.
try:
def sub_match(match, conv=self.conv, expand=self.expand, lvars=lvars):
return conv(expand(match.group(1), lvars))
result = []
for a in args:
result.append(self.conv(self.expand(a, lvars)))
- try:
- result = string.join(result, '')
- except TypeError:
- if len(result) == 1:
- result = result[0]
+ if len(result) == 1:
+ result = result[0]
+ else:
+ result = string.join(map(str, result), '')
return result
else:
return self.expand(args, lvars)
# Compress strings of white space characters into
# a single space.
result = string.strip(_space_sep.sub(' ', result))
+ elif is_Sequence(result):
+ remove = _list_remove[mode]
+ if remove:
+ result = remove(result)
return result
lv[var] = ''
self.substitute(s, lv, 0)
self.this_word()
- elif is_List(s) or is_Tuple(s):
+ elif is_Sequence(s):
for a in s:
self.substitute(a, lvars, 1)
self.next_word()
"""
if is_String(args) and not isinstance(args, CmdStringHolder):
+ args = str(args) # In case it's a UserString.
args = _separate_args.findall(args)
for a in args:
if a[0] in ' \t\n\r\f\v':
a = match.group(1)
if a in matchlist:
a = val
- if is_List(a) or is_Tuple(a):
+ if is_Sequence(a):
return string.join(map(str, a))
else:
return str(a)
- if is_List(strSubst) or is_Tuple(strSubst):
+ if is_Sequence(strSubst):
result = []
for arg in strSubst:
if is_String(arg):
if arg in matchlist:
arg = val
- if is_List(arg) or is_Tuple(arg):
+ if is_Sequence(arg):
result.extend(arg)
else:
result.append(arg)
'T' : ('x', 'y'),
'CS' : cs,
'CL' : cl,
+ 'US' : UserString.UserString('us'),
# Test function calls within ${}.
'FUNCCALL' : '${FUNC1("$AAA $FUNC2 $BBB")}',
'$CS', 'cs',
'$CL', 'cl',
+ # Various uses of UserString.
+ UserString.UserString('x'), 'x',
+ UserString.UserString('$X'), 'x',
+ UserString.UserString('$US'), 'us',
+ '$US', 'us',
+
# Test function calls within ${}.
'$FUNCCALL', 'a xc b',
"This is test",
["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1],
- "| $( a | b $) | c 1",
- "| a | b | c 1",
- "| | c 1",
+ ["|", "$(", "a", "|", "b", "$)", "|", "c", "1"],
+ ["|", "a", "|", "b", "|", "c", "1"],
+ ["|", "|", "c", "1"],
]
gvars = env.Dictionary()
cmd = SCons.Util.CLVar("test $FOO $BAR $CALL test")
newcmd = scons_subst(cmd, env, gvars=env.Dictionary())
- assert newcmd == 'test foo bar call test', newcmd
+ assert newcmd == ['test', 'foo', 'bar', 'call', 'test'], newcmd
cmd_list = scons_subst_list(cmd, env, gvars=env.Dictionary())
assert len(cmd_list) == 1, cmd_list
'L' : ['x', 'y'],
'CS' : cs,
'CL' : cl,
+ 'US' : UserString.UserString('us'),
# Test function calls within ${}.
'FUNCCALL' : '${FUNC1("$AAA $FUNC2 $BBB")}',
'$CL', [['cl']],
['$CL'], [['cl']],
+ # Various uses of UserString.
+ UserString.UserString('x'), [['x']],
+ [UserString.UserString('x')], [['x']],
+ UserString.UserString('$X'), [['x']],
+ [UserString.UserString('$X')], [['x']],
+ UserString.UserString('$US'), [['us']],
+ [UserString.UserString('$US')], [['us']],
+ '$US', [['us']],
+ ['$US'], [['us']],
+
# Test function calls within ${}.
'$FUNCCALL', [['a', 'xc', 'b']],
childstate = map(lambda N: (N, N.get_state()), children)
- # Skip this node if any of its children have failed. This
- # catches the case where we're descending a top-level target
- # and one of our children failed while trying to be built
- # by a *previous* descent of an earlier top-level target.
- failed_children = filter(lambda I: I[1] == SCons.Node.failed,
- childstate)
- if failed_children:
- node.set_state(SCons.Node.failed)
- if S: S.child_failed = S.child_failed + 1
- if T:
- c = map(str, failed_children)
- c.sort()
- T.write(' children failed:\n %s\n' % c)
- continue
-
# Detect dependency cycles:
pending_nodes = filter(lambda I: I[1] == SCons.Node.pending, childstate)
if pending_nodes:
T.write(' waiting on side effects:\n %s\n' % c)
continue
+ # Skip this node if any of its children have failed.
+ #
+ # This catches the case where we're descending a top-level
+ # target and one of our children failed while trying to be
+ # built by a *previous* descent of an earlier top-level
+ # target.
+ #
+ # It can also occur if a node is reused in multiple
+ # targets. One first descends though the one of the
+ # target, the next time occurs through the other target.
+ #
+ # Note that we can only have failed_children if the
+ # --keep-going flag was used, because without it the build
+ # will stop before diving in the other branch.
+ #
+ # Note that even if one of the children fails, we still
+ # added the other children to the list of candidate nodes
+ # to keep on building (--keep-going).
+ failed_children = filter(lambda I: I[1] == SCons.Node.failed,
+ childstate)
+ if failed_children:
+ node.set_state(SCons.Node.failed)
+ if S: S.child_failed = S.child_failed + 1
+ if T:
+ c = map(lambda I: str(I[0]), failed_children)
+ c.sort()
+ T.write(' children failed:\n %s\n' % c)
+ continue
+
# The default when we've gotten through all of the checks above:
# this node is ready to be built.
if S: S.build = S.build + 1
import SCons.Util
-import gnulink
+# Even though the Mac is based on the GNU toolchain, it doesn't understand
+# the -rpath option, so we use the "link" tool instead of "gnulink".
+import link
def generate(env):
"""Add Builders and construction variables for applelink to an
Environment."""
- gnulink.generate(env)
+ link.generate(env)
env['FRAMEWORKPATHPREFIX'] = '-F'
env['_FRAMEWORKPATH'] = '${_concat(FRAMEWORKPATHPREFIX, FRAMEWORKPATH, "", __env__)}'
--- /dev/null
+"""SCons.Tool.gfortran
+
+Tool-specific initialization for gfortran, the GNU Fortran 95/Fortran
+2003 compiler.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# __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 SCons.Util
+
+import fortran
+
+def generate(env):
+ """Add Builders and construction variables for gfortran to an
+ Environment."""
+ fortran.generate(env)
+
+ # which one is the good one ? ifort uses _FORTRAND, ifl FORTRAN,
+ # aixf77 F77 ...
+ #env['_FORTRAND'] = 'gfortran'
+ env['FORTRAN'] = 'gfortran'
+
+ # XXX does this need to be set too ?
+ #env['SHFORTRAN'] = 'gfortran'
+
+ if env['PLATFORM'] in ['cygwin', 'win32']:
+ env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS')
+ else:
+ env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -fPIC')
+
+ # XXX; Link problems: we need to add -lgfortran somewhere...
+
+def exists(env):
+ return env.Detect('gfortran')
--- /dev/null
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="gfortran">
+<summary>
+Sets construction variables for the GNU F95/F2003 GNU compiler.
+</summary>
+<sets>
+FORTRAN
+SHFORTRANFLAGS
+</sets>
+</tool>
(os.environ.has_key('PROCESSOR_ARCHITEW6432') and
os.environ['PROCESSOR_ARCHITEW6432'] == 'AMD64'))
is_linux = sys.platform == 'linux2'
+is_mac = sys.platform == 'darwin'
if is_windows:
import SCons.Tool.msvc
elif is_linux:
import SCons.Tool.gcc
+elif is_mac:
+ import SCons.Tool.gcc
import SCons.Util
import SCons.Warnings
'x86_64' : 'x86_64',
'em64t' : 'x86_64',
'amd64' : 'x86_64'}
+ if is_mac:
+ valid_abis = {'ia32' : 'ia32',
+ 'x86' : 'ia32',
+ 'x86_64' : 'x86_64',
+ 'em64t' : 'x86_64'}
try:
abi = valid_abis[abi]
except KeyError:
if ok:
versions.append(subkey)
else:
- # Registry points to nonexistent dir. Ignore this version.
- print "Ignoring "+str(get_intel_registry_value('ProductDir', subkey, 'IA32'))
+ try:
+ # Registry points to nonexistent dir. Ignore this
+ # version.
+ value = get_intel_registry_value('ProductDir', subkey, 'IA32')
+ except MissingRegistryError, e:
+
+ # Registry key is left dangling (potentially
+ # after uninstalling).
+
+ print \
+ "scons: *** Ignoring the registry key for the Intel compiler version %s.\n" \
+ "scons: *** It seems that the compiler was uninstalled and that the registry\n" \
+ "scons: *** was not cleaned up properly.\n" % subkey
+ else:
+ print "scons: *** Ignoring "+str(value)
+
i = i + 1
except EnvironmentError:
# no more subkeys
elif is_linux:
for d in glob.glob('/opt/intel_cc_*'):
# Typical dir here is /opt/intel_cc_80.
- versions.append(re.search(r'cc_(.*)$', d).group(1))
+ m = re.search(r'cc_(.*)$', d)
+ if m:
+ versions.append(m.group(1))
+ for d in glob.glob('/opt/intel/cc*/*'):
+ # Typical dir here is /opt/intel/cc/9.0 for IA32,
+ # /opt/intel/cce/9.0 for EMT64 (AMD64)
+ m = re.search(r'([0-9.]+)$', d)
+ if m:
+ versions.append(m.group(1))
+ elif is_mac:
for d in glob.glob('/opt/intel/cc*/*'):
# Typical dir here is /opt/intel/cc/9.0 for IA32,
# /opt/intel/cce/9.0 for EMT64 (AMD64)
- versions.append(re.search(r'([0-9.]+)$', d).group(1))
+ m = re.search(r'([0-9.]+)$', d)
+ if m:
+ versions.append(m.group(1))
versions = uniquify(versions) # remove dups
versions.sort(vercmp)
return versions
if not os.path.exists(os.path.join(top, "Bin", "icl.exe")):
raise MissingDirError, \
"Can't find Intel compiler in %s"%(top)
- elif is_linux:
+ elif is_mac or is_linux:
# first dir is new (>=9.0) style, second is old (8.0) style.
dirs=('/opt/intel/cc/%s', '/opt/intel_cc_%s')
if abi == 'x86_64':
If topdir is used, version and abi are ignored.
verbose: (int) if >0, prints compiler version used.
"""
- if not (is_linux or is_windows):
+ if not (is_mac or is_linux or is_windows):
# can't handle this platform
return
SCons.Tool.msvc.generate(env)
elif is_linux:
SCons.Tool.gcc.generate(env)
+ elif is_mac:
+ SCons.Tool.gcc.generate(env)
# if version is unspecified, use latest
vlist = get_all_compiler_versions()
# alternatives are ia64 for Itanium, or amd64 or em64t or x86_64 (all synonyms here)
abi = check_abi(abi)
if abi is None:
- if is_linux:
+ if is_mac or is_linux:
# Check if we are on 64-bit linux, default to 64 then.
uname_m = os.uname()[4]
if uname_m == 'x86_64':
# on $PATH and the user is importing their env.
class ICLTopDirWarning(SCons.Warnings.Warning):
pass
- if is_linux and not env.Detect('icc') or \
+ if (is_mac or is_linux) and not env.Detect('icc') or \
is_windows and not env.Detect('icl'):
SCons.Warnings.enableWarningClass(ICLTopDirWarning)
if topdir:
if verbose:
- print "Intel C compiler: using version '%s' (%g), abi %s, in '%s'"%\
- (version, linux_ver_normalize(version),abi,topdir)
+ print "Intel C compiler: using version %s (%g), abi %s, in '%s'"%\
+ (repr(version), linux_ver_normalize(version),abi,topdir)
if is_linux:
# Show the actual compiler version by running the compiler.
os.system('%s/bin/icc --version'%topdir)
+ if is_mac:
+ # Show the actual compiler version by running the compiler.
+ os.system('%s/bin/icc --version'%topdir)
env['INTEL_C_COMPILER_TOP'] = topdir
if is_linux:
'LD_LIBRARY_PATH' : 'lib'}
for p in paths:
env.PrependENVPath(p, os.path.join(topdir, paths[p]))
+ if is_mac:
+ paths={'INCLUDE' : 'include',
+ 'LIB' : 'lib',
+ 'PATH' : 'bin',
+ 'LD_LIBRARY_PATH' : 'lib'}
+ for p in paths:
+ env.PrependENVPath(p, os.path.join(topdir, paths[p]))
if is_windows:
# env key reg valname default subdir of top
paths=(('INCLUDE', 'IncludeDir', 'Include'),
('LIB' , 'LibDir', 'Lib'),
('PATH' , 'BinDir', 'Bin'))
+ # We are supposed to ignore version if topdir is set, so set
+ # it to the emptry string if it's not already set.
+ if version is None:
+ version = ''
# Each path has a registry entry, use that or default to subdir
for p in paths:
try:
licdir = None
for ld in [envlicdir, reglicdir]:
- if ld and os.path.exists(ld):
+ # If the string contains an '@', then assume it's a network
+ # license (port@system) and good by definition.
+ if ld and (string.find(ld, '@') != -1 or os.path.exists(ld)):
licdir = ld
break
if not licdir:
env['ENV']['INTEL_LICENSE_FILE'] = licdir
def exists(env):
- if not (is_linux or is_windows):
+ if not (is_mac or is_linux or is_windows):
# can't handle this platform
return 0
return env.Detect('icl')
elif is_linux:
return env.Detect('icc')
+ elif is_mac:
+ return env.Detect('icc')
return detected
# end of file
def jarSources(target, source, env, for_signature):
"""Only include sources that are not a manifest file."""
- jarchdir = env.subst('$JARCHDIR', target=target, source=source)
- if jarchdir:
- jarchdir = env.fs.Dir(jarchdir)
+ try:
+ env['JARCHDIR']
+ except KeyError:
+ jarchdir_set = False
+ else:
+ jarchdir_set = True
+ jarchdir = env.subst('$JARCHDIR', target=target, source=source)
+ if jarchdir:
+ jarchdir = env.fs.Dir(jarchdir)
result = []
for src in source:
contents = src.get_contents()
if contents[:16] != "Manifest-Version":
- if jarchdir:
+ if jarchdir_set:
+ _chdir = jarchdir
+ else:
+ try:
+ _chdir = src.attributes.java_classdir
+ except AttributeError:
+ _chdir = None
+ if _chdir:
# If we are changing the dir with -C, then sources should
# be relative to that directory.
- src = SCons.Subst.Literal(src.get_path(jarchdir))
+ src = SCons.Subst.Literal(src.get_path(_chdir))
result.append('-C')
- result.append(jarchdir)
+ result.append(_chdir)
result.append(src)
return result
command will change to the specified directory using the
<option>-C</option>
option.
+If &cv-JARCHDIR; is not set explicitly,
+&SCons; will use the top of any subdirectory tree
+in which Java <filename>.class</filename>
+were built by the &b-link-Java; Builder.
+
If the contents any of the source files begin with the string
<literal>Manifest-Version</literal>,
the file is assumed to be a manifest
return '$CXX'
return '$CC'
+def shlib_emitter(target, source, env):
+ for tgt in target:
+ tgt.attributes.shared = 1
+ return (target, source)
+
def generate(env):
"""Add Builders and construction variables for gnulink to an Environment."""
SCons.Tool.createSharedLibBuilder(env)
env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
# don't set up the emitter, cause AppendUnique will generate a list
# starting with None :-(
- #env['SHLIBEMITTER']= None
+ env.Append(SHLIBEMITTER = [shlib_emitter])
env['SMARTLINK'] = smart_link
env['LINK'] = "$SMARTLINK"
env['LINKFLAGS'] = SCons.Util.CLVar('')
env['LINKCOM'] = '$LINK -o $TARGET $LINKFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
env['LIBDIRPREFIX']='-L'
env['LIBDIRSUFFIX']=''
- env['_LIBFLAGS']='${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIX, LIBSUFFIX, __env__)}'
+ env['_LIBFLAGS']='${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__)}'
env['LIBLINKPREFIX']='-l'
env['LIBLINKSUFFIX']=''
def windowsLibEmitter(target, source, env):
SCons.Tool.msvc.validate_vars(env)
+ extratargets = []
+ extrasources = []
+
dll = env.FindIxes(target, "SHLIBPREFIX", "SHLIBSUFFIX")
no_import_lib = env.get('no_import_lib', 0)
not env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"):
# append a def file to the list of sources
- source.append(env.ReplaceIxes(dll,
- "SHLIBPREFIX", "SHLIBSUFFIX",
- "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"))
+ extrasources.append(
+ env.ReplaceIxes(dll,
+ "SHLIBPREFIX", "SHLIBSUFFIX",
+ "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"))
version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0'))
if version_num >= 8.0 and env.get('WINDOWS_INSERT_MANIFEST', 0):
# MSVC 8 automatically generates .manifest files that must be installed
- target.append(env.ReplaceIxes(dll,
- "SHLIBPREFIX", "SHLIBSUFFIX",
- "WINDOWSSHLIBMANIFESTPREFIX", "WINDOWSSHLIBMANIFESTSUFFIX"))
+ extratargets.append(
+ env.ReplaceIxes(dll,
+ "SHLIBPREFIX", "SHLIBSUFFIX",
+ "WINDOWSSHLIBMANIFESTPREFIX", "WINDOWSSHLIBMANIFESTSUFFIX"))
if env.has_key('PDB') and env['PDB']:
pdb = env.arg2nodes('$PDB', target=target, source=source)[0]
- target.append(pdb)
+ extratargets.append(pdb)
target[0].attributes.pdb = pdb
if not no_import_lib and \
not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"):
# Append an import library to the list of targets.
- target.append(env.ReplaceIxes(dll,
- "SHLIBPREFIX", "SHLIBSUFFIX",
- "LIBPREFIX", "LIBSUFFIX"))
+ extratargets.append(
+ env.ReplaceIxes(dll,
+ "SHLIBPREFIX", "SHLIBSUFFIX",
+ "LIBPREFIX", "LIBSUFFIX"))
# and .exp file is created if there are exports from a DLL
- target.append(env.ReplaceIxes(dll,
- "SHLIBPREFIX", "SHLIBSUFFIX",
- "WINDOWSEXPPREFIX", "WINDOWSEXPSUFFIX"))
+ extratargets.append(
+ env.ReplaceIxes(dll,
+ "SHLIBPREFIX", "SHLIBSUFFIX",
+ "WINDOWSEXPPREFIX", "WINDOWSEXPSUFFIX"))
- return (target, source)
+ return (target+extratargets, source+extrasources)
def prog_emitter(target, source, env):
SCons.Tool.msvc.validate_vars(env)
+ extratargets = []
+
exe = env.FindIxes(target, "PROGPREFIX", "PROGSUFFIX")
if not exe:
raise SCons.Errors.UserError, "An executable should have exactly one target with the suffix: %s" % env.subst("$PROGSUFFIX")
version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0'))
if version_num >= 8.0 and env.get('WINDOWS_INSERT_MANIFEST', 0):
# MSVC 8 automatically generates .manifest files that have to be installed
- target.append(env.ReplaceIxes(exe,
- "PROGPREFIX", "PROGSUFFIX",
- "WINDOWSPROGMANIFESTPREFIX", "WINDOWSPROGMANIFESTSUFFIX"))
+ extratargets.append(
+ env.ReplaceIxes(exe,
+ "PROGPREFIX", "PROGSUFFIX",
+ "WINDOWSPROGMANIFESTPREFIX", "WINDOWSPROGMANIFESTSUFFIX"))
if env.has_key('PDB') and env['PDB']:
pdb = env.arg2nodes('$PDB', target=target, source=source)[0]
- target.append(pdb)
+ extratargets.append(pdb)
target[0].attributes.pdb = pdb
- return (target,source)
+ return (target+extratargets,source)
def RegServerFunc(target, source, env):
if env.has_key('register') and env['register']:
cpp = source[0]
# looks like cpp.includes is cleared before the build stage :-(
# not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/
- path = SCons.Defaults.CScan.path_function(env, moc.cwd)
+ path = SCons.Defaults.CScan.path(env, moc.cwd)
includes = SCons.Defaults.CScan(cpp, env, path)
if not moc in includes:
SCons.Warnings.warn(
s.attributes.java_classname = classname
slist.append(s)
+ stub_suffixes = ['_Stub']
+ if env.get('JAVAVERSION') == '1.4':
+ stub_suffixes.append('_Skel')
+
tlist = []
for s in source:
- for suff in ['_Skel', '_Stub']:
+ for suff in stub_suffixes:
fname = string.replace(s.attributes.java_classname, '.', os.sep) + \
suff + class_suffix
t = target[0].File(fname)
else:
return '$SWIGCFILESUFFIX'
-_reModule = re.compile(r'%module\s+(.+)')
+# Match '%module test', as well as '%module(directors="1") test'
+_reModule = re.compile(r'%module(?:\s*\(.*\))?\s+(.+)')
def _swigEmitter(target, source, env):
swigflags = env.subst("$SWIGFLAGS", target=target, source=source)
import SCons.Node.FS
import SCons.Util
-warning_rerun_re = re.compile("^LaTeX Warning:.*Rerun", re.MULTILINE)
+warning_rerun_re = re.compile('(^LaTeX Warning:.*Rerun)|(^Package \w+ Warning:.*Rerun)', re.MULTILINE)
rerun_citations_str = "^LaTeX Warning:.*\n.*Rerun to get citations correct"
rerun_citations_re = re.compile(rerun_citations_str, re.MULTILINE)
basename = SCons.Util.splitext(str(source[0]))[0]
basedir = os.path.split(str(source[0]))[0]
-
- # Notice that all the filenames are not prefixed with the basedir.
- # That's because the *COM variables have the cd command in the prolog.
-
- bblfilename = basename + '.bbl'
+ basefile = os.path.split(str(basename))[1]
+ abspath = os.path.abspath(basedir)
+ targetbase = SCons.Util.splitext(str(target[0]))[0]
+ targetdir = os.path.split(str(target[0]))[0]
+
+ # Not sure if these environment changes should go here or make the
+ # user do them I undo all but TEXPICTS but there is still the side
+ # effect of creating the empty (':') entries in the environment.
+
+ def modify_env_var(env, var, abspath):
+ try:
+ save = env['ENV'][var]
+ except KeyError:
+ save = ':'
+ env['ENV'][var] = ''
+ if SCons.Util.is_List(env['ENV'][var]):
+ env['ENV'][var] = [abspath] + env['ENV'][var]
+ else:
+ env['ENV'][var] = abspath + os.pathsep + env['ENV'][var]
+ return save
+
+ texinputs_save = modify_env_var(env, 'TEXINPUTS', abspath)
+ bibinputs_save = modify_env_var(env, 'BIBINPUTS', abspath)
+ bstinputs_save = modify_env_var(env, 'BSTINPUTS', abspath)
+ texpicts_save = modify_env_var(env, 'TEXPICTS', abspath)
+
+ # Create these file names with the target directory since they will
+ # be made there. That's because the *COM variables have the cd
+ # command in the prolog.
+
+ bblfilename = os.path.join(targetdir, basefile + '.bbl')
bblContents = ""
if os.path.exists(bblfilename):
bblContents = open(bblfilename, "rb").read()
- idxfilename = basename + '.idx'
+ idxfilename = os.path.join(targetdir, basefile + '.idx')
idxContents = ""
if os.path.exists(idxfilename):
idxContents = open(idxfilename, "rb").read()
- tocfilename = basename + '.toc'
+ tocfilename = os.path.join(targetdir, basefile + '.toc')
tocContents = ""
if os.path.exists(tocfilename):
tocContents = open(tocfilename, "rb").read()
- # Run LaTeX once to generate a new aux file.
+ # Run LaTeX once to generate a new aux file and log file.
XXXLaTeXAction(target, source, env)
# Decide if various things need to be run, or run again. We check
# with stubs that don't necessarily generate all of the same files.
# Read the log file to find all .aux files
- logfilename = basename + '.log'
+ logfilename = os.path.join(targetbase + '.log')
auxfiles = []
if os.path.exists(logfilename):
content = open(logfilename, "rb").read()
# Now decide if bibtex will need to be run.
for auxfilename in auxfiles:
- if os.path.exists(os.path.join(basedir, auxfilename)):
- content = open(os.path.join(basedir, auxfilename), "rb").read()
+ target_aux = os.path.join(targetdir, auxfilename)
+ if os.path.exists(target_aux):
+ content = open(target_aux, "rb").read()
if string.find(content, "bibdata") != -1:
- bibfile = env.fs.File(basename)
+ bibfile = env.fs.File(targetbase)
BibTeXAction(bibfile, bibfile, env)
break
# Now decide if latex will need to be run again due to index.
if os.path.exists(idxfilename) and idxContents != open(idxfilename, "rb").read():
# We must run makeindex
- idxfile = env.fs.File(basename)
+ idxfile = env.fs.File(targetbase)
MakeIndexAction(idxfile, idxfile, env)
must_rerun_latex = 1
XXXLaTeXAction(target, source, env)
# Now decide if latex needs to be run yet again to resolve warnings.
- logfilename = basename + '.log'
+ logfilename = targetbase + '.log'
for _ in range(int(env.subst('$LATEXRETRIES'))):
if not os.path.exists(logfilename):
break
not undefined_references_re.search(content):
break
XXXLaTeXAction(target, source, env)
+
+ env['ENV']['TEXINPUTS'] = texinputs_save
+ env['ENV']['BIBINPUTS'] = bibinputs_save
+ env['ENV']['BSTINPUTS'] = bibinputs_save
+
+ # The TEXPICTS enviroment variable is needed by a dvi -> pdf step
+ # later on Mac OSX so leave it,
+ # env['ENV']['TEXPICTS'] = texpicts_save
+
return 0
def LaTeXAuxAction(target = None, source= None, env=None):
def tex_emitter(target, source, env):
base = SCons.Util.splitext(str(source[0]))[0]
- target.append(base + '.aux')
- env.Precious(base + '.aux')
- target.append(base + '.log')
+ targetbase = SCons.Util.splitext(str(target[0]))[0]
+
+ target.append(targetbase + '.aux')
+ env.Precious(targetbase + '.aux')
+ target.append(targetbase + '.log')
for f in source:
content = f.get_contents()
if tableofcontents_re.search(content):
- target.append(base + '.toc')
- env.Precious(base + '.toc')
+ target.append(targetbase + '.toc')
+ env.Precious(targetbase + '.toc')
if makeindex_re.search(content):
- target.append(base + '.ilg')
- target.append(base + '.ind')
- target.append(base + '.idx')
- env.Precious(base + '.idx')
+ target.append(targetbase + '.ilg')
+ target.append(targetbase + '.ind')
+ target.append(targetbase + '.idx')
+ env.Precious(targetbase + '.idx')
if bibliography_re.search(content):
- target.append(base + '.bbl')
- env.Precious(base + '.bbl')
- target.append(base + '.blg')
+ target.append(targetbase + '.bbl')
+ env.Precious(targetbase + '.bbl')
+ target.append(targetbase + '.blg')
- # read log file to get all output file (include .aux files)
- logfilename = base + '.log'
- dir, base_nodir = os.path.split(base)
+ # read log file to get all .aux files
+ logfilename = targetbase + '.log'
+ dir, base_nodir = os.path.split(targetbase)
if os.path.exists(logfilename):
content = open(logfilename, "rb").read()
out_files = openout_re.findall(content)
# If -d is specified on the command line, yacc will emit a .h
# or .hpp file with the same name as the .c or .cpp output file.
if '-d' in flags:
- target.append(targetBase + env.subst(hsuf))
+ target.append(targetBase + env.subst(hsuf, target=target, source=source))
# If -g is specified on the command line, yacc will emit a .vcg
# file with the same base name as the .y, .yacc, .ym or .yy file.
env['YACCFLAGS'] = SCons.Util.CLVar('')
env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES'
env['YACCHFILESUFFIX'] = '.h'
- env['YACCHXXFILESUFFIX'] = '.hpp'
+
+ if env['PLATFORM'] == 'darwin':
+ # Bison on Mac OS X just appends ".h" to the generated target .cc
+ # or .cpp file name. Hooray for delayed expansion of variables.
+ env['YACCHXXFILESUFFIX'] = '${TARGET.suffix}.h'
+ else:
+ env['YACCHXXFILESUFFIX'] = '.hpp'
+
env['YACCVCGFILESUFFIX'] = '.vcg'
def exists(env):
it exists to allow you to specify
what suffix the parser generator will use of its own accord.
The default value is
-<filename>.hpp</filename>.
+<filename>.hpp</filename>,
+except on Mac OS X,
+where the default is
+<filename>${TARGET.suffix}.h</filename>.
+because the default &bison; parser generator just
+appends <filename>.h</filename>
+to the name of the generated C++ file.
</summary>
</cvar>
try:
f = obj.for_signature
except AttributeError:
- return to_String(obj)
+ return to_String_for_subst(obj)
else:
return f()
+def to_String_for_subst(s):
+ if is_Sequence( s ):
+ return string.join( map(to_String_for_subst, s) )
+
+ return to_String( s )
+
+
class CallableComposite(UserList):
"""A simple composite callable class that, when called, will invoke all
of its contained callables with the same arguments."""
# Yes, all of this manual testing breaks polymorphism, and the real
# Pythonic way to do all of this would be to just try it and handle the
# exception, but handling the exception when it's not the right type is
-# too slow.
-#
-# The actual implementations here have been selected after timings
-# coded up in in bench/is_types.py (from the SCons source tree, see the
-# scons-src distribution). Key results from those timings:
-#
-# -- Storing the type of the object in a variable (t = type(obj))
-# slows down the case where it's a native type and the first
-# comparison will match, but nicely speeds up the case where
-# it's a different native type. Since that's going to be common,
-# it's a good tradeoff.
-#
-# -- The data show that calling isinstance() on an object that's
-# a native type (dict, list or string) is expensive enough that
-# checking up front for whether the object is of type InstanceType
-# is a pretty big win, even though it does slow down the case
-# where it really *is* an object instance a little bit.
-
-def is_Dict(obj):
- t = type(obj)
- return t is DictType or \
- (t is InstanceType and isinstance(obj, UserDict))
-
-def is_List(obj):
- t = type(obj)
- return t is ListType \
- or (t is InstanceType and isinstance(obj, UserList))
-
-def is_Sequence(obj):
- t = type(obj)
- return t is ListType \
- or t is TupleType \
- or (t is InstanceType and isinstance(obj, UserList))
-
-def is_Tuple(obj):
- t = type(obj)
- return t is TupleType
+# often too slow.
-if hasattr(types, 'UnicodeType'):
- def is_String(obj):
+try:
+ class mystr(str):
+ pass
+except TypeError:
+ # An older Python version without new-style classes.
+ #
+ # The actual implementations here have been selected after timings
+ # coded up in in bench/is_types.py (from the SCons source tree,
+ # see the scons-src distribution), mostly against Python 1.5.2.
+ # Key results from those timings:
+ #
+ # -- Storing the type of the object in a variable (t = type(obj))
+ # slows down the case where it's a native type and the first
+ # comparison will match, but nicely speeds up the case where
+ # it's a different native type. Since that's going to be
+ # common, it's a good tradeoff.
+ #
+ # -- The data show that calling isinstance() on an object that's
+ # a native type (dict, list or string) is expensive enough
+ # that checking up front for whether the object is of type
+ # InstanceType is a pretty big win, even though it does slow
+ # down the case where it really *is* an object instance a
+ # little bit.
+ def is_Dict(obj):
+ t = type(obj)
+ return t is DictType or \
+ (t is InstanceType and isinstance(obj, UserDict))
+
+ def is_List(obj):
t = type(obj)
- return t is StringType \
- or t is UnicodeType \
- or (t is InstanceType and isinstance(obj, UserString))
+ return t is ListType \
+ or (t is InstanceType and isinstance(obj, UserList))
+
+ def is_Sequence(obj):
+ t = type(obj)
+ return t is ListType \
+ or t is TupleType \
+ or (t is InstanceType and isinstance(obj, UserList))
+
+ def is_Tuple(obj):
+ t = type(obj)
+ return t is TupleType
+
+ if hasattr(types, 'UnicodeType'):
+ def is_String(obj):
+ t = type(obj)
+ return t is StringType \
+ or t is UnicodeType \
+ or (t is InstanceType and isinstance(obj, UserString))
+ else:
+ def is_String(obj):
+ t = type(obj)
+ return t is StringType \
+ or (t is InstanceType and isinstance(obj, UserString))
else:
+ # A modern Python version with new-style classes, so we can just use
+ # isinstance().
+ def is_Dict(obj):
+ return isinstance(obj, (dict, UserDict))
+
+ def is_List(obj):
+ return isinstance(obj, (list, UserList))
+
+ def is_Sequence(obj):
+ return isinstance(obj, (list, UserList, tuple))
+
+ def is_Tuple(obj):
+ return isinstance(obj, (tuple))
+
def is_String(obj):
- t = type(obj)
- return t is StringType \
- or (t is InstanceType and isinstance(obj, UserString))
+ # Empirically, Python versions with new-style classes all have unicode.
+ return isinstance(obj, (str, unicode, UserString))
def test_is_Dict(self):
assert is_Dict({})
assert is_Dict(UserDict())
+ try:
+ class mydict(dict):
+ pass
+ except TypeError:
+ pass
+ else:
+ assert is_Dict(mydict({}))
assert not is_Dict([])
assert not is_Dict(())
assert not is_Dict("")
assert is_List([])
import UserList
assert is_List(UserList.UserList())
+ try:
+ class mylist(list):
+ pass
+ except TypeError:
+ pass
+ else:
+ assert is_List(mylist([]))
assert not is_List(())
assert not is_List({})
assert not is_List("")
pass
else:
assert is_String(UserString.UserString(''))
+ try:
+ class mystr(str):
+ pass
+ except TypeError:
+ pass
+ else:
+ assert is_String(mystr(''))
assert not is_String({})
assert not is_String([])
assert not is_String(())
def test_is_Tuple(self):
assert is_Tuple(())
+ try:
+ class mytuple(tuple):
+ pass
+ except TypeError:
+ pass
+ else:
+ assert is_Tuple(mytuple(()))
assert not is_Tuple([])
assert not is_Tuple({})
assert not is_Tuple("")
class ReservedVariableWarning(Warning):
pass
+class StackSizeWarning(Warning):
+ pass
+
_warningAsException = 0
# The below is a list of 2-tuples. The first element is a class object.
try:
shlex.split
except AttributeError:
- # Pre-2.3 Python has no shlex.split function.
- def split(s, comments=False):
- import StringIO
- lex = shlex.shlex(StringIO.StringIO(s))
- lex.wordchars = lex.wordchars + '/\\-+,=:'
- result = []
- while True:
- tt = lex.get_token()
- if not tt:
- break
- result.append(tt)
- return result
- shlex.split = split
- del split
+ # Pre-2.3 Python has no shlex.split() function.
+ #
+ # The full white-space splitting semantics of shlex.split() are
+ # complicated to reproduce by hand, so just use a compatibility
+ # version of the shlex module cribbed from Python 2.5 with some
+ # minor modifications for older Python versions.
+ del shlex
+ import_as('_scons_shlex', 'shlex')
try:
import subprocess
--- /dev/null
+# -*- coding: iso-8859-1 -*-
+"""A lexical analyzer class for simple shell-like syntaxes."""
+
+# Module and documentation by Eric S. Raymond, 21 Dec 1998
+# Input stacking and error message cleanup added by ESR, March 2000
+# push_source() and pop_source() made explicit by ESR, January 2001.
+# Posix compliance, split(), string arguments, and
+# iterator interface by Gustavo Niemeyer, April 2003.
+
+import os.path
+import sys
+#from collections import deque
+
+class deque:
+ def __init__(self):
+ self.data = []
+ def __len__(self):
+ return len(self.data)
+ def appendleft(self, item):
+ self.data.insert(0, item)
+ def popleft(self):
+ return self.data.pop(0)
+
+try:
+ basestring
+except NameError:
+ import types
+ def is_basestring(s):
+ return type(s) is types.StringType
+else:
+ def is_basestring(s):
+ return isinstance(s, basestring)
+
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
+__all__ = ["shlex", "split"]
+
+class shlex:
+ "A lexical analyzer class for simple shell-like syntaxes."
+ def __init__(self, instream=None, infile=None, posix=False):
+ if is_basestring(instream):
+ instream = StringIO(instream)
+ if instream is not None:
+ self.instream = instream
+ self.infile = infile
+ else:
+ self.instream = sys.stdin
+ self.infile = None
+ self.posix = posix
+ if posix:
+ self.eof = None
+ else:
+ self.eof = ''
+ self.commenters = '#'
+ self.wordchars = ('abcdfeghijklmnopqrstuvwxyz'
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_')
+ if self.posix:
+ self.wordchars = self.wordchars + ('ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ'
+ 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ')
+ self.whitespace = ' \t\r\n'
+ self.whitespace_split = False
+ self.quotes = '\'"'
+ self.escape = '\\'
+ self.escapedquotes = '"'
+ self.state = ' '
+ self.pushback = deque()
+ self.lineno = 1
+ self.debug = 0
+ self.token = ''
+ self.filestack = deque()
+ self.source = None
+ if self.debug:
+ print 'shlex: reading from %s, line %d' \
+ % (self.instream, self.lineno)
+
+ def push_token(self, tok):
+ "Push a token onto the stack popped by the get_token method"
+ if self.debug >= 1:
+ print "shlex: pushing token " + repr(tok)
+ self.pushback.appendleft(tok)
+
+ def push_source(self, newstream, newfile=None):
+ "Push an input source onto the lexer's input source stack."
+ if is_basestring(newstream):
+ newstream = StringIO(newstream)
+ self.filestack.appendleft((self.infile, self.instream, self.lineno))
+ self.infile = newfile
+ self.instream = newstream
+ self.lineno = 1
+ if self.debug:
+ if newfile is not None:
+ print 'shlex: pushing to file %s' % (self.infile,)
+ else:
+ print 'shlex: pushing to stream %s' % (self.instream,)
+
+ def pop_source(self):
+ "Pop the input source stack."
+ self.instream.close()
+ (self.infile, self.instream, self.lineno) = self.filestack.popleft()
+ if self.debug:
+ print 'shlex: popping to %s, line %d' \
+ % (self.instream, self.lineno)
+ self.state = ' '
+
+ def get_token(self):
+ "Get a token from the input stream (or from stack if it's nonempty)"
+ if self.pushback:
+ tok = self.pushback.popleft()
+ if self.debug >= 1:
+ print "shlex: popping token " + repr(tok)
+ return tok
+ # No pushback. Get a token.
+ raw = self.read_token()
+ # Handle inclusions
+ if self.source is not None:
+ while raw == self.source:
+ spec = self.sourcehook(self.read_token())
+ if spec:
+ (newfile, newstream) = spec
+ self.push_source(newstream, newfile)
+ raw = self.get_token()
+ # Maybe we got EOF instead?
+ while raw == self.eof:
+ if not self.filestack:
+ return self.eof
+ else:
+ self.pop_source()
+ raw = self.get_token()
+ # Neither inclusion nor EOF
+ if self.debug >= 1:
+ if raw != self.eof:
+ print "shlex: token=" + repr(raw)
+ else:
+ print "shlex: token=EOF"
+ return raw
+
+ def read_token(self):
+ quoted = False
+ escapedstate = ' '
+ while True:
+ nextchar = self.instream.read(1)
+ if nextchar == '\n':
+ self.lineno = self.lineno + 1
+ if self.debug >= 3:
+ print "shlex: in state", repr(self.state), \
+ "I see character:", repr(nextchar)
+ if self.state is None:
+ self.token = '' # past end of file
+ break
+ elif self.state == ' ':
+ if not nextchar:
+ self.state = None # end of file
+ break
+ elif nextchar in self.whitespace:
+ if self.debug >= 2:
+ print "shlex: I see whitespace in whitespace state"
+ if self.token or (self.posix and quoted):
+ break # emit current token
+ else:
+ continue
+ elif nextchar in self.commenters:
+ self.instream.readline()
+ self.lineno = self.lineno + 1
+ elif self.posix and nextchar in self.escape:
+ escapedstate = 'a'
+ self.state = nextchar
+ elif nextchar in self.wordchars:
+ self.token = nextchar
+ self.state = 'a'
+ elif nextchar in self.quotes:
+ if not self.posix:
+ self.token = nextchar
+ self.state = nextchar
+ elif self.whitespace_split:
+ self.token = nextchar
+ self.state = 'a'
+ else:
+ self.token = nextchar
+ if self.token or (self.posix and quoted):
+ break # emit current token
+ else:
+ continue
+ elif self.state in self.quotes:
+ quoted = True
+ if not nextchar: # end of file
+ if self.debug >= 2:
+ print "shlex: I see EOF in quotes state"
+ # XXX what error should be raised here?
+ raise ValueError, "No closing quotation"
+ if nextchar == self.state:
+ if not self.posix:
+ self.token = self.token + nextchar
+ self.state = ' '
+ break
+ else:
+ self.state = 'a'
+ elif self.posix and nextchar in self.escape and \
+ self.state in self.escapedquotes:
+ escapedstate = self.state
+ self.state = nextchar
+ else:
+ self.token = self.token + nextchar
+ elif self.state in self.escape:
+ if not nextchar: # end of file
+ if self.debug >= 2:
+ print "shlex: I see EOF in escape state"
+ # XXX what error should be raised here?
+ raise ValueError, "No escaped character"
+ # In posix shells, only the quote itself or the escape
+ # character may be escaped within quotes.
+ if escapedstate in self.quotes and \
+ nextchar != self.state and nextchar != escapedstate:
+ self.token = self.token + self.state
+ self.token = self.token + nextchar
+ self.state = escapedstate
+ elif self.state == 'a':
+ if not nextchar:
+ self.state = None # end of file
+ break
+ elif nextchar in self.whitespace:
+ if self.debug >= 2:
+ print "shlex: I see whitespace in word state"
+ self.state = ' '
+ if self.token or (self.posix and quoted):
+ break # emit current token
+ else:
+ continue
+ elif nextchar in self.commenters:
+ self.instream.readline()
+ self.lineno = self.lineno + 1
+ if self.posix:
+ self.state = ' '
+ if self.token or (self.posix and quoted):
+ break # emit current token
+ else:
+ continue
+ elif self.posix and nextchar in self.quotes:
+ self.state = nextchar
+ elif self.posix and nextchar in self.escape:
+ escapedstate = 'a'
+ self.state = nextchar
+ elif nextchar in self.wordchars or nextchar in self.quotes \
+ or self.whitespace_split:
+ self.token = self.token + nextchar
+ else:
+ self.pushback.appendleft(nextchar)
+ if self.debug >= 2:
+ print "shlex: I see punctuation in word state"
+ self.state = ' '
+ if self.token:
+ break # emit current token
+ else:
+ continue
+ result = self.token
+ self.token = ''
+ if self.posix and not quoted and result == '':
+ result = None
+ if self.debug > 1:
+ if result:
+ print "shlex: raw token=" + repr(result)
+ else:
+ print "shlex: raw token=EOF"
+ return result
+
+ def sourcehook(self, newfile):
+ "Hook called on a filename to be sourced."
+ if newfile[0] == '"':
+ newfile = newfile[1:-1]
+ # This implements cpp-like semantics for relative-path inclusion.
+ if is_basestring(self.infile) and not os.path.isabs(newfile):
+ newfile = os.path.join(os.path.dirname(self.infile), newfile)
+ return (newfile, open(newfile, "r"))
+
+ def error_leader(self, infile=None, lineno=None):
+ "Emit a C-compiler-like, Emacs-friendly error-message leader."
+ if infile is None:
+ infile = self.infile
+ if lineno is None:
+ lineno = self.lineno
+ return "\"%s\", line %d: " % (infile, lineno)
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ token = self.get_token()
+ if token == self.eof:
+ raise StopIteration
+ return token
+
+def split(s, comments=False):
+ lex = shlex(s, posix=True)
+ lex.whitespace_split = True
+ if not comments:
+ lex.commenters = ''
+ #return list(lex)
+ result = []
+ while True:
+ token = lex.get_token()
+ if token == lex.eof:
+ break
+ result.append(token)
+ return result
+
+if __name__ == '__main__':
+ if len(sys.argv) == 1:
+ lexer = shlex()
+ else:
+ file = sys.argv[1]
+ lexer = shlex(open(file), file)
+ while 1:
+ tt = lexer.get_token()
+ if tt:
+ print "Token: " + repr(tt)
+ else:
+ break
# that we want to fetch, using the regular expressions to which the lists
# of preprocessor directives map.
cpp_lines_dict = {
- # Fetch the rest of a #if/#elif/#ifdef/#ifndef/#import/#include/
- # #include_next line as one argument.
- ('if', 'elif', 'ifdef', 'ifndef', 'import', 'include', 'include_next',)
+ # Fetch the rest of a #if/#elif/#ifdef/#ifndef as one argument,
+ # separated from the keyword by white space.
+ ('if', 'elif', 'ifdef', 'ifndef',)
: '\s+(.+)',
+ # Fetch the rest of a #import/#include/#include_next line as one
+ # argument, with white space optional.
+ ('import', 'include', 'include_next',)
+ : '\s*(.+)',
+
# We don't care what comes after a #else or #endif line.
('else', 'endif',) : '',
"""
self.name = name
self.args = function_arg_separator.split(args)
- self.expansion = string.split(expansion, '##')
+ try:
+ expansion = string.split(expansion, '##')
+ except (AttributeError, TypeError):
+ # Python 1.5 throws TypeError if "expansion" isn't a string,
+ # later versions throw AttributeError.
+ pass
+ self.expansion = expansion
def __call__(self, *values):
"""
Evaluates the expansion of a #define macro function called
"""
The main workhorse class for handling C pre-processing.
"""
- def __init__(self, current='.', cpppath=[], dict={}, all=0):
+ def __init__(self, current=os.curdir, cpppath=(), dict={}, all=0):
global Table
+ cpppath = tuple(cpppath)
+
self.searchpath = {
- '"' : [current] + cpppath,
- '<' : cpppath + [current],
+ '"' : (current,) + cpppath,
+ '<' : cpppath + (current,),
}
# Initialize our C preprocessor namespace for tracking the
# stack and changing what method gets called for each relevant
# directive we might see next at this level (#else, #elif).
# #endif will simply pop the stack.
- d = {}
+ d = {
+ 'scons_current_file' : self.scons_current_file
+ }
for op in Table.keys():
d[op] = getattr(self, 'do_' + op)
self.default_table = d
(m[0],) + t[m[0]].match(m[1]).groups(),
cpp_tuples)
- def __call__(self, contents):
+ def __call__(self, file):
+ """
+ Pre-processes a file.
+
+ This is the main public entry point.
+ """
+ self.current_file = file
+ return self.process_contents(self.read_file(file), file)
+
+ def process_contents(self, contents, fname=None):
"""
Pre-processes a file contents.
- This is the main entry point, which
+ This is the main internal entry point.
"""
self.stack = []
self.dispatch_table = self.default_table.copy()
+ self.current_file = fname
self.tuples = self.tupleize(contents)
- self.result = []
+ self.initialize_result(fname)
while self.tuples:
t = self.tuples.pop(0)
# Uncomment to see the list of tuples being processed (e.g.,
# to validate the CPP lines are being translated correctly).
#print t
self.dispatch_table[t[0]](t)
-
- return self.result
+ return self.finalize_result(fname)
# Dispatch table stack manipulation methods.
"""
pass
+ def scons_current_file(self, t):
+ self.current_file = t[1]
+
def eval_expression(self, t):
"""
Evaluates a C preprocessor expression.
try: return eval(t, self.cpp_namespace)
except (NameError, TypeError): return 0
+ def initialize_result(self, fname):
+ self.result = [fname]
+
+ def finalize_result(self, fname):
+ return self.result[1:]
+
def find_include_file(self, t):
"""
Finds the #include file for a given preprocessor tuple.
"""
fname = t[2]
for d in self.searchpath[t[1]]:
- f = os.path.join(d, fname)
+ if d == os.curdir:
+ f = fname
+ else:
+ f = os.path.join(d, fname)
if os.path.isfile(f):
return f
return None
+ def read_file(self, file):
+ return open(file).read()
+
# Start and stop processing include lines.
def start_handling_includes(self, t=None):
if include_file:
#print "include_file =", include_file
self.result.append(include_file)
- contents = open(include_file).read()
- new_tuples = self.tupleize(contents)
+ contents = self.read_file(include_file)
+ new_tuples = [('scons_current_file', include_file)] + \
+ self.tupleize(contents) + \
+ [('scons_current_file', self.current_file)]
self.tuples[:] = new_tuples + self.tuples
# Date: Tue, 22 Nov 2005 20:26:09 -0500
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import string
import sys
import unittest
-print sys.path
import cpp
#include FUNC39c(ZERO, ONE)
#include FUNC40c(ZERO, ONE)
+
+/* Make sure we don't die if the expansion isn't a string. */
+#define FUNC_INTEGER(x) 1
"""
"""
+no_space_input = """
+#include<file43-yes>
+#include"file44-yes"
+"""
+
+
# pp_class = PreProcessor
# #pp_class = DumbPreProcessor
def test_basic(self):
"""Test basic #include scanning"""
expect = self.basic_expect
- result = self.cpp(basic_input)
+ result = self.cpp.process_contents(basic_input)
assert expect == result, (expect, result)
def test_substitution(self):
"""Test substitution of #include files using CPP variables"""
expect = self.substitution_expect
- result = self.cpp(substitution_input)
+ result = self.cpp.process_contents(substitution_input)
assert expect == result, (expect, result)
def test_ifdef(self):
"""Test basic #ifdef processing"""
expect = self.ifdef_expect
- result = self.cpp(ifdef_input)
+ result = self.cpp.process_contents(ifdef_input)
assert expect == result, (expect, result)
def test_if_boolean(self):
"""Test #if with Boolean values"""
expect = self.if_boolean_expect
- result = self.cpp(if_boolean_input)
+ result = self.cpp.process_contents(if_boolean_input)
assert expect == result, (expect, result)
def test_if_defined(self):
"""Test #if defined() idioms"""
expect = self.if_defined_expect
- result = self.cpp(if_defined_input)
+ result = self.cpp.process_contents(if_defined_input)
assert expect == result, (expect, result)
def test_expression(self):
"""Test #if with arithmetic expressions"""
expect = self.expression_expect
- result = self.cpp(expression_input)
+ result = self.cpp.process_contents(expression_input)
assert expect == result, (expect, result)
def test_undef(self):
"""Test #undef handling"""
expect = self.undef_expect
- result = self.cpp(undef_input)
+ result = self.cpp.process_contents(undef_input)
assert expect == result, (expect, result)
def test_macro_function(self):
"""Test using macro functions to express file names"""
expect = self.macro_function_expect
- result = self.cpp(macro_function_input)
+ result = self.cpp.process_contents(macro_function_input)
assert expect == result, (expect, result)
def test_token_pasting(self):
- """Test taken-pasting to construct file names"""
+ """Test token-pasting to construct file names"""
expect = self.token_pasting_expect
- result = self.cpp(token_pasting_input)
+ result = self.cpp.process_contents(token_pasting_input)
+ assert expect == result, (expect, result)
+
+ def test_no_space(self):
+ """Test no space between #include and the quote"""
+ expect = self.no_space_expect
+ result = self.cpp.process_contents(no_space_input)
assert expect == result, (expect, result)
class cppAllTestCase(cppTestCase):
('include', '<', 'file42-yes'),
]
+ no_space_expect = [
+ ('include', '<', 'file43-yes'),
+ ('include', '"', 'file44-yes'),
+ ]
+
class DumbPreProcessorTestCase(cppAllTestCase):
cpp_class = cpp.DumbPreProcessor
('include', '<', 'file42-yes'),
]
+ no_space_expect = [
+ ('include', '<', 'file43-yes'),
+ ('include', '"', 'file44-yes'),
+ ]
+
+
+
+import os
+import re
+import shutil
+import tempfile
+
+tempfile.template = 'cppTests.'
+if os.name in ('posix', 'nt'):
+ tempfile.template = 'cppTests.' + str(os.getpid()) + '.'
+else:
+ tempfile.template = 'cppTests.'
+
+_Cleanup = []
+
+def _clean():
+ for dir in _Cleanup:
+ if os.path.exists(dir):
+ shutil.rmtree(dir)
+
+sys.exitfunc = _clean
+
+class fileTestCase(unittest.TestCase):
+ cpp_class = cpp.DumbPreProcessor
+
+ def setUp(self):
+ try:
+ path = tempfile.mktemp(prefix=tempfile.template)
+ except TypeError:
+ # The tempfile.mktemp() function in earlier versions of Python
+ # has no prefix argument, but uses the tempfile.template
+ # value that we set above.
+ path = tempfile.mktemp()
+ _Cleanup.append(path)
+ os.mkdir(path)
+ self.tempdir = path
+ self.orig_cwd = os.getcwd()
+ os.chdir(path)
+
+ def tearDown(self):
+ shutil.rmtree(self.tempdir)
+ _Cleanup.remove(self.tempdir)
+ os.chdir(self.orig_cwd)
+
+ def strip_initial_spaces(self, s):
+ #lines = s.split('\n')
+ lines = string.split(s, '\n')
+ spaces = re.match(' *', lines[0]).group(0)
+ def strip_spaces(l, spaces=spaces):
+ #if l.startswith(spaces):
+ if l[:len(spaces)] == spaces:
+ l = l[len(spaces):]
+ return l
+ #return '\n'.join([ strip_spaces(l) for l in lines ])
+ return string.join(map(strip_spaces, lines), '\n')
+
+ def write(self, file, contents):
+ open(file, 'w').write(self.strip_initial_spaces(contents))
+
+ def test_basic(self):
+ """Test basic file inclusion"""
+ self.write('f1.h', """\
+ #include "f2.h"
+ """)
+ self.write('f2.h', """\
+ #include <f3.h>
+ """)
+ self.write('f3.h', """\
+ """)
+ p = cpp.DumbPreProcessor(current = os.curdir,
+ cpppath = [os.curdir])
+ result = p('f1.h')
+ assert result == ['f2.h', 'f3.h'], result
+
+ def test_current_file(self):
+ """Test use of the .current_file attribute"""
+ self.write('f1.h', """\
+ #include <f2.h>
+ """)
+ self.write('f2.h', """\
+ #include "f3.h"
+ """)
+ self.write('f3.h', """\
+ """)
+ class MyPreProcessor(cpp.DumbPreProcessor):
+ def __init__(self, *args, **kw):
+ apply(cpp.DumbPreProcessor.__init__, (self,) + args, kw)
+ self.files = []
+ def __call__(self, file):
+ self.files.append(file)
+ return cpp.DumbPreProcessor.__call__(self, file)
+ def scons_current_file(self, t):
+ r = cpp.DumbPreProcessor.scons_current_file(self, t)
+ self.files.append(self.current_file)
+ return r
+ p = MyPreProcessor(current = os.curdir, cpppath = [os.curdir])
+ result = p('f1.h')
+ assert result == ['f2.h', 'f3.h'], result
+ assert p.files == ['f1.h', 'f2.h', 'f3.h', 'f2.h', 'f1.h'], p.files
+
+
+
if __name__ == '__main__':
suite = unittest.TestSuite()
tclasses = [ PreProcessorTestCase,
DumbPreProcessorTestCase,
+ fileTestCase,
]
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
+ try:
+ names = list(set(names))
+ except NameError:
+ pass
+ names.sort()
suite.addTests(map(tclass, names))
if not unittest.TextTestRunner().run(suite).wasSuccessful():
sys.exit(1)
-
import getopt
import glob
import os
+import os.path
import re
import shutil
import string
def make_temp_file(**kw):
try:
result = tempfile.mktemp(**kw)
+ try:
+ result = os.path.realpath(result)
+ except AttributeError:
+ # Python 2.1 has no os.path.realpath() method.
+ pass
except TypeError:
try:
save_template = tempfile.template
if self.comment:
print '# %s' % self.comment
for x, y in self.points:
- print fmt % (x, y)
+ # If y is None, it usually represents some kind of break
+ # in the line's index number. We might want to represent
+ # this some way rather than just drawing the line straight
+ # between the two points on either side.
+ if not y is None:
+ print fmt % (x, y)
print 'e'
def get_x_values(self):
def vertical_bar(self, x, type, label, comment):
if self.get_min_x() <= x and x <= self.get_max_x():
- points = [(x, 0), (x, self.get_max_x())]
+ points = [(x, 0), (x, self.max_graph_value(self.get_max_y()))]
self.line(points, type, label, comment)
def get_all_x_values(self):
result = []
for line in self.lines:
result.extend(line.get_x_values())
- return result
+ return filter(None, result)
def get_all_y_values(self):
result = []
for line in self.lines:
result.extend(line.get_y_values())
- return result
+ return filter(None, result)
def get_min_x(self):
try:
return self.min_x
except AttributeError:
- self.min_x = min(self.get_all_x_values())
+ try:
+ self.min_x = min(self.get_all_x_values())
+ except ValueError:
+ self.min_x = 0
return self.min_x
def get_max_x(self):
try:
return self.max_x
except AttributeError:
- self.max_x = max(self.get_all_x_values())
+ try:
+ self.max_x = max(self.get_all_x_values())
+ except ValueError:
+ self.max_x = 0
return self.max_x
def get_min_y(self):
try:
return self.min_y
except AttributeError:
- self.min_y = min(self.get_all_y_values())
+ try:
+ self.min_y = min(self.get_all_y_values())
+ except ValueError:
+ self.min_y = 0
return self.min_y
def get_max_y(self):
try:
return self.max_y
except AttributeError:
- self.max_y = max(self.get_all_y_values())
+ try:
+ self.max_y = max(self.get_all_y_values())
+ except ValueError:
+ self.max_y = 0
return self.max_y
def draw(self):
print 'set title "%s"' % self.title
print 'set key %s' % self.key_location
+ min_y = self.get_min_y()
+ max_y = self.max_graph_value(self.get_max_y())
+ range = max_y - min_y
+ incr = range / 10.0
+ start = min_y + (max_y / 2.0) + (2.0 * incr)
+ position = [ start - (i * incr) for i in xrange(5) ]
+
inx = 1
- max_y = self.max_graph_value(self.get_max_y())/2
for line in self.lines:
- line.print_label(inx, line.points[0][0]-1, max_y)
+ line.print_label(inx, line.points[0][0]-1,
+ position[(inx-1) % len(position)])
inx += 1
plot_strings = [ self.plot_string(l) for l in self.lines ]
for file in files:
t = line_function(file, *args, **kw)
+ if t is None:
+ t = []
diff = len(columns) - len(t)
if diff > 0:
t += [''] * diff
except ValueError:
x, type, label = bar_tuple
comment = label
- gp.vertical_bar(x, type, None, label, comment)
+ gp.vertical_bar(x, type, label, comment)
gp.draw()
else:
search_string = time_string
contents = open(file).read()
+ if not contents:
+ sys.stderr.write('file %s has no contents!\n' % repr(file))
+ return None
result = re.findall(r'%s: ([\d\.]*)' % search_string, contents)[-4:]
result = [ float(r) for r in result ]
if not time_string is None:
- result = result[0]
+ try:
+ result = result[0]
+ except IndexError:
+ sys.stderr.write('file %s has no results!\n' % repr(file))
+ return None
return result
def get_function_profile(self, file, function):
commands.append((shutil.copytree, 'cp -r %%s %%s', archive, dest))
else:
suffix = self.archive_splitext(archive)[1]
- commands.append(self.unpack_map[suffix] + (archive,))
+ unpack_command = self.unpack_map.get(suffix)
+ if not unpack_command:
+ dest = os.path.split(archive)[1]
+ commands.append((shutil.copyfile, 'cp %%s %%s', archive, dest))
+ else:
+ commands.append(unpack_command + (archive,))
commands.extend([
(os.chdir, 'cd %%s', self.subdir),
'engine/SCons/compat/_scons_optparse.py',
'engine/SCons/compat/_scons_sets.py',
'engine/SCons/compat/_scons_sets15.py',
+ 'engine/SCons/compat/_scons_shlex.py',
'engine/SCons/compat/_scons_subprocess.py',
'engine/SCons/compat/_scons_textwrap.py',
'engine/SCons/Conftest.py',
'engine/SCons/compat/_scons_optparse.py',
'engine/SCons/compat/_scons_sets.py',
'engine/SCons/compat/_scons_sets15.py',
+ 'engine/SCons/compat/_scons_shlex.py',
'engine/SCons/compat/_scons_subprocess.py',
'engine/SCons/compat/_scons_textwrap.py',
'engine/SCons/Conftest.py',
'SCons/compat/_scons_optparse.py',
'SCons/compat/_scons_sets.py',
'SCons/compat/_scons_sets15.py',
+ 'SCons/compat/_scons_shlex.py',
'SCons/compat/_scons_subprocess.py',
'SCons/compat/_scons_textwrap.py',
'SCons/Conftest.py',
'src/engine/SCons/compat/_scons_optparse.py',
'src/engine/SCons/compat/_scons_sets.py',
'src/engine/SCons/compat/_scons_sets15.py',
+ 'src/engine/SCons/compat/_scons_shlex.py',
'src/engine/SCons/compat/_scons_subprocess.py',
'src/engine/SCons/compat/_scons_textwrap.py',
'src/engine/SCons/Conftest.py',
--- /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__"
+
+import TestSCons
+
+_python_ = TestSCons._python_
+
+test = TestSCons.TestSCons()
+
+#
+# Test that the signature of function action includes all the
+# necessary pieces.
+#
+
+test.write('SConstruct', r"""
+import re
+
+import SCons.Action
+import SCons.Builder
+
+options = Options()
+options.AddOptions(
+ ('header', 'Header string (default cell argument)', 'Head:'),
+ ('trailer', 'Trailer string (default cell argument)', 'Tail'),
+ ('NbDeps', 'Number of dependencies', '2'),
+ ('separator', 'Separator for the dependencies (function constant)', ':'),
+ ('closure_cell_value', 'Value of a closure cell', '25'),
+ ('b', 'Value of b (value default argument', '7'),
+ ('regexp', 'Regexp (object as a default argument', 'a(a*)'),
+ ('docstring', 'Docstring', 'docstring'),
+ ('extracode', 'Extra code for the builder function', ''),
+ ('extraarg', 'Extra arg builder function', ''),
+ ('nestedfuncexp', 'Expression for the nested function', 'xxx - b'),
+)
+
+optEnv = Environment(options=options, tools=[])
+
+r = re.compile(optEnv['regexp'])
+
+toto = \
+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, xxx=xxx):
+ """+'"""%(docstring)s"""'+"""
+ def foo(b=b, xxx=xxx):
+ 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
+'''
+
+exec( toto % optEnv )
+
+genHeaderBld = SCons.Builder.Builder(
+ action = SCons.Action.Action(
+ toto(),
+ 'Generating $TARGET',
+ varlist=['ENVDEPS']
+ ),
+ suffix = '.gen.h'
+ )
+
+env = Environment()
+env.Append(BUILDERS = {'GenHeader' : genHeaderBld})
+
+envdeps = map(str, range(int(optEnv['NbDeps'])))
+
+env.GenHeader('Out', None, ENVDEPS=envdeps)
+""")
+
+
+rebuildstr = """\
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+scons: Building targets ...
+Generating Out.gen.h
+scons: done building targets.
+"""
+
+nobuildstr = """\
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+scons: Building targets ...
+scons: `.' is up to date.
+scons: done building targets.
+"""
+
+def runtest( arguments, expectedOutFile, expectedRebuild=True):
+ test.run(arguments=arguments,
+ stdout=expectedRebuild and rebuildstr or nobuildstr)
+ 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.must_match('Out.gen.h', expectedOutFile)
+
+
+# Original build.
+runtest('', """Head:0:1:Tail\n18\naaa\n""")
+
+# Changing a docstring should not cause a rebuild
+runtest('docstring=ThisBuilderDoesXAndY', """Head:0:1:Tail\n18\naaa\n""", False)
+runtest('docstring=SuperBuilder', """Head:0:1:Tail\n18\naaa\n""", False)
+runtest('docstring=', """Head:0:1:Tail\n18\naaa\n""", False)
+
+# Changing a variable listed in the varlist should cause a rebuild
+runtest('NbDeps=3', """Head:0:1:2:Tail\n18\naaa\n""")
+runtest('NbDeps=4', """Head:0:1:2:3:Tail\n18\naaa\n""")
+runtest('', """Head:0:1:Tail\n18\naaa\n""")
+
+# Changing the function code should cause a rebuild
+runtest('extracode=f.write("XX\\n")', """Head:0:1:Tail\n18\naaa\nXX\n""")
+runtest('extracode=a=2', """Head:0:1:Tail\n18\naaa\n""")
+runtest('', """Head:0:1:Tail\n18\naaa\n""")
+
+# Changing a constant used in the function code should cause a rebuild
+runtest('separator=,', """Head:0,1,Tail\n18\naaa\n""")
+runtest('separator=;', """Head:0;1;Tail\n18\naaa\n""")
+runtest('', """Head:0:1:Tail\n18\naaa\n""")
+
+# Changing the code of a nested function should cause a rebuild
+runtest('nestedfuncexp=b-xxx', """Head:0:1:Tail\n-18\naaa\n""")
+runtest('nestedfuncexp=b+xxx', """Head:0:1:Tail\n32\naaa\n""")
+runtest('', """Head:0:1:Tail\n18\naaa\n""")
+
+# Adding an extra argument should cause a rebuild.
+runtest('extraarg=,xarg=2', """Head:0:1:Tail\n18\naaa\n2\n""")
+runtest('extraarg=,xarg=5', """Head:0:1:Tail\n18\naaa\n5\n""")
+runtest('', """Head:0:1:Tail\n18\naaa\n""")
+
+# Changing the value of a default argument should cause a rebuild
+# case 1: a value
+runtest('b=0', """Head:0:1:Tail\n25\naaa\n""")
+runtest('b=9', """Head:0:1:Tail\n16\naaa\n""")
+runtest('', """Head:0:1:Tail\n18\naaa\n""")
+
+# case 2: an object
+runtest('regexp=(aaaa)', """Head:0:1:Tail\n18\naaaa\n""")
+runtest('regexp=aa(a+)', """Head:0:1:Tail\n18\naa\n""")
+runtest('', """Head:0:1:Tail\n18\naaa\n""")
+
+# Changing the value of a closure cell value should cause a rebuild
+# case 1: a value
+runtest('closure_cell_value=32', """Head:0:1:Tail\n25\naaa\n""")
+runtest('closure_cell_value=7', """Head:0:1:Tail\n0\naaa\n""")
+runtest('', """Head:0:1:Tail\n18\naaa\n""")
+
+# case 2: a default argument
+runtest('header=MyHeader:', """MyHeader:0:1:Tail\n18\naaa\n""")
+runtest('trailer=MyTrailer', """Head:0:1:MyTrailer\n18\naaa\n""")
+runtest('', """Head:0:1:Tail\n18\naaa\n""")
+
+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 basic use of CPPPDEFINES with various data types.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+test_list = [
+ 'xyz',
+ ['x', 'y', 'z'],
+ ['x', ['y', 123], 'z', ('int', '$INTEGER')],
+ { 'c' : 3, 'b': None, 'a' : 1 },
+]
+env = Environment(CPPDEFPREFIX='-D', CPPDEFSUFFIX='', INTEGER=0)
+for i in test_list:
+ print env.Clone(CPPDEFINES=i).subst('$_CPPDEFFLAGS')
+env = Environment(CPPDEFPREFIX='|', CPPDEFSUFFIX='|', INTEGER=1)
+for i in test_list:
+ print env.Clone(CPPDEFINES=i).subst('$_CPPDEFFLAGS')
+""")
+
+expect = test.wrap_stdout(build_str="scons: `.' is up to date.\n",
+ read_str = """\
+-Dxyz
+-Dx -Dy -Dz
+-Dx -Dy=123 -Dz -Dint=0
+-Da=1 -Db -Dc=3
+|xyz|
+|x| |y| |z|
+|x| |y=123| |z| |int=1|
+|a=1| |b| |c=3|
+""")
+
+test.run(arguments = '.', stdout=expect)
+
+test.pass_test()
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
-XXX Put a description of the test here.
+Verify basic use of CPPDEFINES with live compilation.
"""
-import string
-
import TestSCons
test = TestSCons.TestSCons()
-# Make sure $_CPPDEFFLAGS doesn't barf when CPPDEFINES isn't defined.
-test.write('SConstruct', """\
-env = Environment()
-print env.subst('$_CPPDEFFLAGS')
-""")
-
-expect = test.wrap_stdout(build_str="scons: `.' is up to date.\n",
- read_str = "\n")
-
-test.run(arguments = '.', stdout=expect)
-
-# Test CPPDEFINES as a string and a list.
-test.write('SConstruct', """\
-test_list = [
- 'xyz',
- ['x', 'y', 'z'],
- ['x', ['y', 123], 'z', ('int', '$INTEGER')],
- { 'c' : 3, 'b': None, 'a' : 1 },
-]
-env = Environment(CPPDEFPREFIX='-D', CPPDEFSUFFIX='', INTEGER=0)
-for i in test_list:
- print env.Clone(CPPDEFINES=i).subst('$_CPPDEFFLAGS')
-env = Environment(CPPDEFPREFIX='|', CPPDEFSUFFIX='|', INTEGER=1)
-for i in test_list:
- print env.Clone(CPPDEFINES=i).subst('$_CPPDEFFLAGS')
-""")
-
-expect = test.wrap_stdout(build_str="scons: `.' is up to date.\n",
- read_str = """\
--Dxyz
--Dx -Dy -Dz
--Dx -Dy=123 -Dz -Dint=0
--Da=1 -Db -Dc=3
-|xyz|
-|x| |y| |z|
-|x| |y=123| |z| |int=1|
-|a=1| |b| |c=3|
-""")
-
-test.run(arguments = '.', stdout=expect)
-
test.write('SConstruct', """\
foo = Environment(CPPDEFINES = ['FOO', ('VAL', '$VALUE')], VALUE=7)
bar = Environment(CPPDEFINES = {'BAR':None, 'VAL':8})
--- /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 the Scanner that evaluates CPP lines works as expected.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+m = 'Scanner evaluation of CPP lines not yet supported; skipping test.\n'
+test.skip_test(m)
+
+f1_exe = 'f1' + TestSCons._exe
+f2_exe = 'f2' + TestSCons._exe
+f3_exe = 'f3' + TestSCons._exe
+f4_exe = 'f4' + TestSCons._exe
+
+test.write('SConstruct', """\
+env = Environment(CPPPATH = ['.'])
+
+f1 = env.Object('f1', 'fff.c', CPPDEFINES = ['F1'])
+f2 = env.Object('f2', 'fff.c', CPPDEFINES = [('F2', 1)])
+f3 = env.Object('f3', 'fff.c', CPPDEFINES = {'F3':None})
+f4 = env.Object('f4', 'fff.c', CPPDEFINES = {'F4':1})
+
+env.Program('f1', ['prog.c', f1])
+env.Program('f2', ['prog.c', f2])
+env.Program('f3', ['prog.c', f3])
+env.Program('f4', ['prog.c', f4])
+""")
+
+test.write('f1.h', """
+#define STRING "F1"
+""")
+
+test.write('f2.h', """
+#define STRING "F2"
+""")
+
+test.write('f3.h', """
+#define STRING "F3"
+""")
+
+test.write('f4.h', """
+#define STRING "F4"
+""")
+
+test.write('fff.c', """
+#ifdef F1
+#include <f1.h>
+#endif
+#if F2
+#include <f2.h>
+#endif
+#ifdef F3
+#include <f3.h>
+#endif
+#ifdef F4
+#include <f4.h>
+#endif
+
+char *
+foo(void)
+{
+ return (STRING);
+}
+""")
+
+
+test.write('prog.c', r"""
+#include <stdio.h>
+#include <stdlib.h>
+
+extern char *foo(void);
+
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("prog.c: %s\n", foo());
+ exit (0);
+}
+""")
+
+
+
+test.run(arguments = '.')
+
+test.run(program = test.workpath('f1'), stdout = "prog.c: F1\n")
+test.run(program = test.workpath('f2'), stdout = "prog.c: F2\n")
+test.run(program = test.workpath('f3'), stdout = "prog.c: F3\n")
+test.run(program = test.workpath('f4'), stdout = "prog.c: F4\n")
+
+
+
+test.write('f1.h', """
+#define STRING "F1 again"
+""")
+
+test.up_to_date(arguments = '%(f2_exe)s %(f3_exe)s %(f4_exe)s' % locals())
+
+test.not_up_to_date(arguments = '.')
+
+test.run(program = test.workpath('f1'), stdout = "prog.c: F1 again\n")
+test.run(program = test.workpath('f2'), stdout = "prog.c: F2\n")
+test.run(program = test.workpath('f3'), stdout = "prog.c: F3\n")
+test.run(program = test.workpath('f4'), stdout = "prog.c: F4\n")
+
+
+
+test.write('f2.h', """
+#define STRING "F2 again"
+""")
+
+test.up_to_date(arguments = '%(f1_exe)s %(f3_exe)s %(f4_exe)s' % locals())
+
+test.not_up_to_date(arguments = '.')
+
+test.run(program = test.workpath('f1'), stdout = "prog.c: F1 again\n")
+test.run(program = test.workpath('f2'), stdout = "prog.c: F2 again\n")
+test.run(program = test.workpath('f3'), stdout = "prog.c: F3\n")
+test.run(program = test.workpath('f4'), stdout = "prog.c: F4\n")
+
+
+
+test.write('f3.h', """
+#define STRING "F3 again"
+""")
+
+test.up_to_date(arguments = '%(f1_exe)s %(f2_exe)s %(f4_exe)s' % locals())
+
+test.not_up_to_date(arguments = '.')
+
+test.run(program = test.workpath('f1'), stdout = "prog.c: F1 again\n")
+test.run(program = test.workpath('f2'), stdout = "prog.c: F2 again\n")
+test.run(program = test.workpath('f3'), stdout = "prog.c: F3 again\n")
+test.run(program = test.workpath('f4'), stdout = "prog.c: F4\n")
+
+
+
+test.write('f4.h', """
+#define STRING "F4 again"
+""")
+
+test.up_to_date(arguments = '%(f1_exe)s %(f2_exe)s %(f3_exe)s' % locals())
+
+test.not_up_to_date(arguments = '.')
+
+test.run(program = test.workpath('f1'), stdout = "prog.c: F1 again\n")
+test.run(program = test.workpath('f2'), stdout = "prog.c: F2 again\n")
+test.run(program = test.workpath('f3'), stdout = "prog.c: F3 again\n")
+test.run(program = test.workpath('f4'), stdout = "prog.c: F4 again\n")
+
+
+
+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 $_CPPDEFFLAGS doesn't barf when CPPDEFINES isn't defined.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+env = Environment()
+print env.subst('$_CPPDEFFLAGS')
+""")
+
+expect = test.wrap_stdout(build_str="scons: `.' is up to date.\n",
+ read_str = "\n")
+
+test.run(arguments = '.', stdout=expect)
+
+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 the ability to #include a file with an absolute path name. (Which
+is not strictly a test of using $CPPPATH, but it's in the ball park...)
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('include', 'work')
+
+inc_h = test.workpath('include', 'inc.h')
+does_not_exist_h = test.workpath('include', 'does_not_exist.h')
+
+test.write(['work', 'SConstruct'], """\
+Program('prog.c')
+""")
+
+test.write(['work', 'prog.c'], """\
+#include <stdio.h>
+#include "%(inc_h)s"
+#if 0
+#include "%(does_not_exist_h)s"
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("%%s\\n", STRING);
+ return 0;
+}
+""" % locals())
+
+test.write(['include', 'inc.h'], """\
+#define STRING "include/inc.h 1\\n"
+""")
+
+test.run(chdir = 'work', arguments = '.')
+
+test.up_to_date(chdir = 'work', arguments = '.')
+
+test.write(['include', 'inc.h'], """\
+#define STRING "include/inc.h 2\\n"
+""")
+
+test.not_up_to_date(chdir = 'work', arguments = '.')
+
+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 expansion of construction variables whose values are functions
+(that return lists that contain Nodes, even) works as expected within
+a $CPPPATH list definition.
+
+This used to cause TypeErrors when the _concat expansion tried to
+join the Nodes with the strings.
+
+Test courtesy Konstantin Bozhikov.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('list_inc1',
+ 'list_inc2',
+ 'inc1',
+ 'inc2',
+ 'inc3',
+ ['inc3', 'subdir'])
+
+test.write('SConstruct', """\
+env = Environment()
+def my_cpppaths( target, source, env, for_signature ):
+ return [ Dir('list_inc1'), Dir('list_inc2') ]
+
+env = Environment( CPPPATH = [Dir('inc1'), '$INC2', '$INC3/subdir', '$MY_CPPPATHS' ],
+ INC2 = Dir('inc2'),
+ INC3 = Dir('inc3'),
+ MY_CPPPATHS = my_cpppaths )
+
+env.Program('prog.c')
+""")
+
+test.write('prog.c', """\
+#include <stdio.h>
+#include <stdlib.h>
+#include "string_list_1.h"
+#include "string_list_2.h"
+#include "string_1.h"
+#include "string_2.h"
+#include "string_3.h"
+
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("prog.c\\n");
+ printf("%s\\n", STRING_LIST_1);
+ printf("%s\\n", STRING_LIST_2);
+ printf("%s\\n", STRING_1);
+ printf("%s\\n", STRING_2);
+ printf("%s\\n", STRING_3);
+ exit (0);
+}
+""")
+
+test.write(['list_inc1', 'string_list_1.h'], """\
+#define STRING_LIST_1 "list_inc1/string_list_1.h"
+""")
+
+test.write(['list_inc2', 'string_list_2.h'], """\
+#define STRING_LIST_2 "list_inc2/string_list_2.h"
+""")
+
+test.write(['inc1', 'string_1.h'], """\
+#define STRING_1 "inc1/string_1.h"
+""")
+
+test.write(['inc2', 'string_2.h'], """\
+#define STRING_2 "inc2/string_2.h"
+""")
+
+test.write(['inc3', 'subdir', 'string_3.h'], """\
+#define STRING_3 "inc3/subdir/string_3.h"
+""")
+
+test.run()
+
+test.up_to_date(arguments = '.')
+
+expect = """\
+prog.c
+list_inc1/string_list_1.h
+list_inc2/string_list_2.h
+inc1/string_1.h
+inc2/string_2.h
+inc3/subdir/string_3.h
+"""
+
+test.run(program = test.workpath('prog' + TestSCons._exe), stdout=expect)
+
+test.write(['inc3', 'subdir', 'string_3.h'], """\
+#define STRING_3 "inc3/subdir/string_3.h 2"
+""")
+
+test.not_up_to_date(arguments = '.')
+
+expect = """\
+prog.c
+list_inc1/string_list_1.h
+list_inc2/string_list_2.h
+inc1/string_1.h
+inc2/string_2.h
+inc3/subdir/string_3.h 2
+"""
+
+test.run(program = test.workpath('prog' + TestSCons._exe), stdout=expect)
+
+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 expansion of construction variables whose values are
+lists works as expected within a $CPPPATH list definition.
+
+Previously, the stringification of the expansion of the individual
+variables would turn a list like ['sub1', 'sub2'] below into "-Isub1 sub2"
+on the command line.
+
+Test case courtesy Daniel Svensson.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('sub1', 'sub2', 'sub3', 'sub4')
+
+test.write('SConstruct', """\
+class _inc_test:
+ def __init__(self, name):
+ self.name = name
+
+ def __call__(self, target, source, env, for_signature):
+ return env.something[self.name]
+
+env = Environment()
+
+env.something = {}
+env.something['test'] = ['sub1', 'sub2']
+
+env['INC_PATHS1'] = _inc_test
+
+env['INC_PATHS2'] = ['sub3', 'sub4']
+
+env.Append(CPPPATH = ['${INC_PATHS1("test")}', '$INC_PATHS2'])
+env.Program('test', 'test.c')
+""")
+
+test.write('test.c', """\
+#include <stdio.h>
+#include <stdlib.h>
+#include "string1.h"
+#include "string2.h"
+#include "string3.h"
+#include "string4.h"
+
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("test.c\\n");
+ printf("%s\\n", STRING1);
+ printf("%s\\n", STRING2);
+ printf("%s\\n", STRING3);
+ printf("%s\\n", STRING4);
+ exit (0);
+}
+""")
+
+test.write(['sub1', 'string1.h'], """\
+#define STRING1 "sub1/string1.h"
+""")
+
+test.write(['sub2', 'string2.h'], """\
+#define STRING2 "sub2/string2.h"
+""")
+
+test.write(['sub3', 'string3.h'], """\
+#define STRING3 "sub3/string3.h"
+""")
+
+test.write(['sub4', 'string4.h'], """\
+#define STRING4 "sub4/string4.h"
+""")
+
+test.run()
+
+test.up_to_date(arguments = '.')
+
+expect = """\
+test.c
+sub1/string1.h
+sub2/string2.h
+sub3/string3.h
+sub4/string4.h
+"""
+
+test.run(program = test.workpath('test' + TestSCons._exe), stdout=expect)
+
+test.write(['sub2', 'string2.h'], """\
+#define STRING2 "sub2/string2.h 2"
+""")
+
+test.not_up_to_date(arguments = '.')
+
+expect = """\
+test.c
+sub1/string1.h
+sub2/string2.h 2
+sub3/string3.h
+sub4/string4.h
+"""
+
+test.run(program = test.workpath('test' + TestSCons._exe), stdout=expect)
+
+test.pass_test()
test = TestSCons.TestSCons()
-test.write('cmd.py', r"""
+test.write('mycommand.py', r"""
import sys
sys.stderr.write( 'Hello World on stderr\n' )
sys.stdout.write( 'Hello World on stdout\n' )
return 0
conf = env.Configure(custom_tests = {'MyTest' : CustomTest})
if not conf.MyTest():
- env.Command("hello", [], '%(_python_)s cmd.py $TARGET')
+ env.Command("hello", [], '%(_python_)s mycommand.py $TARGET')
env = conf.Finish()
""" % locals())
--- /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 the D scanner can return multiple modules imported by
+a single statement.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+_obj = TestSCons._obj
+
+dmd = test.where_is('dmd')
+if not dmd:
+ test.skip_test("Could not find 'dmd'; skipping test.\n")
+
+test.subdir(['p'])
+
+test.write('SConstruct', """
+env = Environment()
+env.Program('test1.d')
+env.Program('test2.d')
+""")
+
+test.write(['test1.d'], """\
+import module1;
+import module2;
+import module3;
+import p.submodule1;
+import p.submodule2;
+
+int main() {
+ return 0;
+}
+""")
+
+test.write(['test2.d'], """\
+import
+ module1,
+ module2,
+ module3;
+import
+ p.submodule1,
+ p.submodule2;
+
+int main() {
+ return 0;
+}
+""")
+
+test.write(['ignored.d'], """\
+module ignored;
+
+int something;
+""")
+
+test.write(['module1.d'], """\
+module module1;
+
+int something;
+""")
+
+test.write(['module2.d'], """\
+module module2;
+
+int something;
+""")
+
+test.write(['module3.di'], """\
+module module3;
+
+int something;
+""")
+
+test.write(['p', 'ignored.d'], """\
+module p.ignored;
+
+int something;
+""")
+
+test.write(['p', 'submodule1.d'], """\
+module p.submodule1;
+
+int something;
+""")
+
+test.write(['p', 'submodule2.d'], """\
+module p.submodule2;
+
+int something;
+""")
+
+arguments = 'test1%(_obj)s test2%(_obj)s' % locals()
+
+test.run(arguments = arguments)
+
+test.up_to_date(arguments = arguments)
+
+test.write(['module2.d'], """\
+module module2;
+
+int something_else;
+""")
+
+test.not_up_to_date(arguments = arguments)
+
+test.up_to_date(arguments = arguments)
+
+test.write(['p', 'submodule2.d'], """\
+module p.submodule2;
+
+int something_else;
+""")
+
+test.not_up_to_date(arguments = arguments)
+
+test.up_to_date(arguments = arguments)
+
+test.pass_test()
a ! x
""")
+# It looks like vanilla Python 2.2 is the only version that
+# puts "<string>" here in place of the file name.
test.run(stdout = "scons: Reading SConscript files ...\n",
- stderr = """ File ".+SConstruct", line 2
+ stderr = """ File "(.+SConstruct|<string>)", line 2
a ! x
--- /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__"
+
+import os
+import string
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+not_executable = test.workpath("not_executable")
+
+test.write(not_executable, "\n")
+
+test.write("f3.in", "\n")
+
+test.write('SConstruct', r"""
+bld = Builder(action = '%s $SOURCES $TARGET')
+env = Environment(BUILDERS = { 'bld' : bld })
+env.bld(target = 'f3', source = 'f3.in')
+""" % string.replace(test.workdir, '\\', '\\\\'))
+
+test.run(arguments='.',
+ stdout = test.wrap_stdout("%s f3.in f3\n" % test.workdir, error=1),
+ stderr = None,
+ status = 2)
+
+bad_command = """\
+Bad command or file name
+"""
+
+unrecognized = """\
+'%s' is not recognized as an internal or external command,
+operable program or batch file.
+scons: *** [%s] Error 1
+"""
+
+unspecified = """\
+The name specified is not recognized as an
+internal or external command, operable program or batch file.
+scons: *** [%s] Error 1
+"""
+
+cannot_execute = """\
+%s: cannot execute
+scons *** [%s] Error 126
+"""
+
+Permission_denied = """\
+%s: Permission denied
+scons: *** [%s] Error 126
+"""
+
+permission_denied = """\
+%s: permission denied
+scons: *** [%s] Error 126
+"""
+
+is_a_directory = """\
+%s: is a directory
+scons: *** [%s] Error 126
+"""
+
+test.description_set("Incorrect STDERR:\n%s\n" % test.stderr())
+if os.name == 'nt':
+ errs = [
+ bad_command,
+ unrecognized % (test.workdir, 'f3'),
+ unspecified % 'f3'
+ ]
+ test.fail_test(not test.stderr() in errs)
+else:
+ errs = [
+ cannot_execute % (not_executable, 'f3'),
+ is_a_directory % (test.workdir, 'f3'),
+ Permission_denied % (test.workdir, 'f3'),
+ Permission_denied % (test.workdir, 'f3'),
+ ]
+ error_message_not_found = 1
+ for err in errs:
+ if string.find(test.stderr(), err) != -1:
+ error_message_not_found = None
+ break
+ test.fail_test(error_message_not_found)
+
+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__"
+
+import os
+import string
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+not_executable = test.workpath("not_executable")
+
+test.write(not_executable, "\n")
+
+test.write("f1.in", "\n")
+
+bad_command = """\
+Bad command or file name
+"""
+
+unrecognized = """\
+'%s' is not recognized as an internal or external command,
+operable program or batch file.
+scons: *** [%s] Error 1
+"""
+
+unspecified = """\
+The name specified is not recognized as an
+internal or external command, operable program or batch file.
+scons: *** [%s] Error 1
+"""
+
+cannot_execute = """\
+%s: cannot execute
+scons *** [%s] Error 126
+"""
+
+Permission_denied = """\
+%s: Permission denied
+scons: *** [%s] Error 126
+"""
+
+permission_denied = """\
+%s: permission denied
+scons: *** [%s] Error 126
+"""
+
+test.write('SConstruct', r"""
+bld = Builder(action = '%s $SOURCES $TARGET')
+env = Environment(BUILDERS = { 'bld': bld })
+env.bld(target = 'f1', source = 'f1.in')
+""" % string.replace(not_executable, '\\', '\\\\'))
+
+test.run(arguments='.',
+ stdout = test.wrap_stdout("%s f1.in f1\n" % not_executable, error=1),
+ stderr = None,
+ status = 2)
+
+test.description_set("Incorrect STDERR:\n%s\n" % test.stderr())
+if os.name == 'nt':
+ errs = [
+ bad_command,
+ unrecognized % (not_executable, 'f1'),
+ unspecified % 'f1'
+ ]
+ test.fail_test(not test.stderr() in errs)
+else:
+ errs = [
+ cannot_execute % (not_executable, 'f1'),
+ Permission_denied % (not_executable, 'f1'),
+ permission_denied % (not_executable, 'f1'),
+ ]
+ error_message_not_found = 1
+ for err in errs:
+ if string.find(test.stderr(), err) != -1:
+ error_message_not_found = None
+ break
+ test.fail_test(error_message_not_found)
+
+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__"
+
+import os
+import string
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+no_such_file = test.workpath("no_such_file")
+
+test.write("f1.in", "\n")
+
+test.write('SConstruct', r"""
+bld = Builder(action = '%s $SOURCES $TARGET')
+env = Environment(BUILDERS = { 'bld' : bld })
+env.bld(target = 'f1', source = 'f1.in')
+""" % string.replace(no_such_file, '\\', '\\\\'))
+
+test.run(arguments='.',
+ stdout = test.wrap_stdout("%s f1.in f1\n" % no_such_file, error=1),
+ stderr = None,
+ status = 2)
+
+bad_command = """\
+Bad command or file name
+"""
+
+unrecognized = """\
+'%s' is not recognized as an internal or external command,
+operable program or batch file.
+scons: *** [%s] Error 1
+"""
+
+unspecified = """\
+The name specified is not recognized as an
+internal or external command, operable program or batch file.
+scons: *** [%s] Error 1
+"""
+
+not_found_1 = """
+sh: %s: not found
+scons: *** [%s] Error 1
+"""
+
+not_found_2 = """
+sh: %s: not found
+scons: *** [%s] Error 1
+"""
+
+not_found_127 = """\
+sh: %s: not found
+scons: *** [%s] Error 127
+"""
+
+No_such = """\
+%s: No such file or directory
+scons: *** [%s] Error 127
+"""
+
+test.description_set("Incorrect STDERR:\n%s\n" % test.stderr())
+if os.name == 'nt':
+ errs = [
+ bad_command,
+ unrecognized % (no_such_file, 'f1'),
+ unspecified % 'f1'
+ ]
+ test.fail_test(not test.stderr() in errs)
+else:
+ errs = [
+ not_found_1 % (no_such_file, 'f1'),
+ not_found_2 % (no_such_file, 'f1'),
+ not_found_127 % (no_such_file, 'f1'),
+ No_such % (no_such_file, 'f1'),
+ ]
+ error_message_not_found = 1
+ for err in errs:
+ if string.find(test.stderr(), err) != -1:
+ error_message_not_found = None
+ break
+ test.fail_test(error_message_not_found)
+
+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__"
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', r"""
+env = Environment()
+env.Command('test.out', 'test.in', Copy('$TARGET', '$SOURCE'))
+env.InstallAs('test2.out', 'test.out')
+# Mark test2.out as precious so we'll handle the exception in
+# FunctionAction() rather than when the target is cleaned before building.
+env.Precious('test2.out')
+env.Default('test2.out')
+""")
+
+test.write('test.in', "test.in 1\n")
+
+test.run(arguments = '.')
+
+test.write('test.in', "test.in 2\n")
+
+test.writable('test2.out', 0)
+f = open(test.workpath('test2.out'))
+
+test.run(arguments = '.',
+ stderr = None,
+ status = 2)
+
+f.close()
+test.writable('test2.out', 1)
+
+test.description_set("Incorrect STDERR:\n%s" % test.stderr())
+errs = [
+ "scons: *** [test2.out] test2.out: Permission denied\n",
+ "scons: *** [test2.out] test2.out: permission denied\n",
+]
+test.fail_test(test.stderr() not in errs)
+
+test.pass_test()
#
test.write('SConstruct', r"""
env = Environment(SUBDIR='subdir')
-env.Install(r'%(destdir)s', 'file1.out')
-env.InstallAs(['file2.out', r'%(_SUBDIR_file3_out)s'],
- ['file2.in', r'%(_SUBDIR_file3_in)s'])
+f1 = env.Install(r'%(destdir)s', 'file1.out')
+f2 = env.InstallAs(['file2.out', r'%(_SUBDIR_file3_out)s'],
+ ['file2.in', r'%(_SUBDIR_file3_in)s'])
+env.Depends(f2, f1)
""" % locals())
test.write('file1.out', "file1.out\n")
test.write(['subdir', 'file3.in'], "subdir/file3.in\n")
expect = test.wrap_stdout("""\
+Install file: "file1.out" as "%(file1_out)s"
Install file: "file2.in" as "%(target_file2_out)s"
Install file: "%(subdir_file3_in)s" as "%(target_subdir_file3_out)s"
-Install file: "file1.out" as "%(file1_out)s"
""" % locals())
test.run(arguments = '--install-sandbox=%s' % destdir, stdout=expect)
--- /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 the ability to build an Alias in --interactive mode.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+foo = Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Alias('foo-alias', foo)
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in 1\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build foo-alias\n")
+
+scons.send("build 1\n")
+
+test.wait_for(test.workpath('1'), popen=scons)
+
+test.must_match(test.workpath('foo.out'), "foo.in 1\n")
+
+
+
+test.write('foo.in', "foo.in 2\n")
+
+# Verify that "scons" can be used as a synonmyn for the "build" command.
+scons.send("scons foo-alias\n")
+
+scons.send("scons 2\n")
+
+test.wait_for(test.workpath('2'), popen=scons)
+
+test.must_match(test.workpath('foo.out'), "foo.in 2\n")
+
+
+
+scons.send("build foo-alias\n")
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+scons>>> Touch("1")
+scons>>> Copy("foo.out", "foo.in")
+scons>>> Touch("2")
+scons>>> scons: `foo-alias' is up to date.
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 we get the expected error message when we use a "build"
+command without arguments and there are no default targets (because they
+explicitly called Default(None) in the SConstruct file).
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons(combine=1)
+
+test.write('SConstruct', """\
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Default(None)
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in 1\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build\n")
+
+scons.send("build foo.out\n")
+
+scons.send("build 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 1\n")
+
+
+
+test.write('foo.in', "foo.in 2\n")
+
+# Verify that "scons" can be used as a synonmyn for the "build" command.
+scons.send("scons\n")
+
+scons.send("scons foo.out\n")
+
+scons.send("build 2\n")
+
+test.wait_for(test.workpath('2'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 2\n")
+
+
+
+scons.send("build\n")
+
+scons.send("build foo.out\n")
+
+expect_stdout = """\
+scons>>> scons: *** No targets specified and no Default() targets found. Stop.
+scons>>> Copy("foo.out", "foo.in")
+scons>>> Touch("1")
+scons>>> scons: *** No targets specified and no Default() targets found. Stop.
+scons>>> Copy("foo.out", "foo.in")
+scons>>> Touch("2")
+scons>>> scons: *** No targets specified and no Default() targets found. Stop.
+scons>>> scons: `foo.out' is up to date.
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 we can use a "build" command without arguments to (re-)build
+the Default() targets, without exiting the command loop.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+foo_out = Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Default(foo_out)
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in 1\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build\n")
+
+scons.send("build 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 1\n")
+
+
+
+test.write('foo.in', "foo.in 2\n")
+
+# Verify that "scons" can be used as a synonmy for the "build" command.
+scons.send("scons\n")
+
+scons.send("build 2\n")
+
+test.wait_for(test.workpath('2'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 2\n")
+
+
+
+scons.send("build\n")
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+scons>>> Touch("1")
+scons>>> Copy("foo.out", "foo.in")
+scons>>> Touch("2")
+scons>>> scons: `foo.out' is up to date.
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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__"
+
+"""
+This verifies the --interactive command line option's ability to
+rebuild a target when an implicit dependency (include line) is
+added to the source file.
+"""
+
+import string
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('foo.h.in', """
+#define FOO_STRING "foo.h.in"
+""")
+
+test.write('foo.c', """
+#include <stdio.h>
+
+int main()
+{
+ printf("foo.c\\n");
+ return 0;
+}
+""")
+
+test.write('SConstruct', """
+env = Environment(CPPPATH=['.'])
+env.Command('foo.h', ['foo.h.in'], Copy('$TARGET', '$SOURCE'))
+env.Program('foo', ['foo.c'])
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+Command('3', [], Touch('$TARGET'))
+""")
+
+foo_exe = test.workpath('foo' + TestSCons._exe)
+_foo_exe_ = '"%s"' % string.replace(foo_exe, '\\', '\\\\')
+
+
+
+# Start scons, to build "foo"
+scons = test.start(arguments = '--interactive')
+
+scons.send("build %(_foo_exe_)s 1\n" % locals())
+
+test.wait_for(test.workpath('1'), popen=scons)
+
+test.run(program = foo_exe, stdout = 'foo.c\n')
+
+
+
+
+# Update foo.c
+# We add a new #include line, to make sure that scons notices
+# the new implicit dependency and builds foo.h first.
+test.write('foo.c', """
+#include <foo.h>
+
+#include <stdio.h>
+
+int main()
+{
+ printf("%s\\n", FOO_STRING);
+ return 0;
+}
+""")
+
+scons.send("build %(_foo_exe_)s 2\n" % locals())
+
+test.wait_for(test.workpath('2'))
+
+# Run foo, and make sure it prints correctly
+test.run(program = foo_exe, stdout = 'foo.h.in\n')
+
+
+
+test.write('foo.h.in', """
+#define FOO_STRING "foo.h.in 3"
+""")
+
+
+
+scons.send("build %(_foo_exe_)s 3\n" % locals())
+
+test.wait_for(test.workpath('3'))
+
+# Run foo, and make sure it prints correctly
+test.run(program = foo_exe, stdout = 'foo.h.in 3\n')
+
+
+
+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 basic operation of the --interactive command line option to build
+a target, rebuild it when the input changes, and not rebuild it when
+the input doesn't change.
+
+Also tests that "scons" can be used as a synonym for "build".
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in 1\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build foo.out 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 1\n")
+
+
+
+test.write('foo.in', "foo.in 2\n")
+
+# Verify that "scons" can be used as a synonmy for the "build" command.
+scons.send("scons foo.out 2\n")
+
+test.wait_for(test.workpath('2'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 2\n")
+
+
+
+scons.send("build foo.out\n")
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+Touch("1")
+scons>>> Copy("foo.out", "foo.in")
+Touch("2")
+scons>>> scons: `foo.out' is up to date.
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 the --interactive command line option to build a target when the
+--cache-debug option is used.
+"""
+
+import TestCmd
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+CacheDir('cache')
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+Command('3', [], Touch('$TARGET'))
+Command('4', [], Touch('$TARGET'))
+Command('5', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build foo.out\n")
+
+scons.send("build 1\n")
+
+test.wait_for(test.workpath('1'), popen=scons)
+
+test.must_match(test.workpath('foo.out'), "foo.in\n")
+
+scons.send("clean foo.out\n")
+
+scons.send("build 2\n")
+
+test.wait_for(test.workpath('2'), popen=scons)
+
+test.must_not_exist(test.workpath('foo.out'))
+
+
+
+scons.send("build foo.out\n")
+
+scons.send("build 3\n")
+
+test.wait_for(test.workpath('3'), popen=scons)
+
+test.must_match(test.workpath('foo.out'), "foo.in\n")
+
+scons.send("clean foo.out\n")
+
+scons.send("build 4\n")
+
+test.wait_for(test.workpath('4'), popen=scons)
+
+test.must_not_exist(test.workpath('foo.out'))
+
+
+
+scons.send("build --cache-debug=- foo.out\n")
+
+scons.send("build 5\n")
+
+test.wait_for(test.workpath('5'), popen=scons)
+
+test.must_match(test.workpath('foo.out'), "foo.in\n")
+
+
+
+expect_stdout = \
+r"""scons>>> Copy\("foo.out", "foo.in"\)
+scons>>> Touch\("1"\)
+scons>>> Removed foo.out
+scons>>> Touch\("2"\)
+scons>>> Retrieved `foo.out' from cache
+scons>>> Touch\("3"\)
+scons>>> Removed foo.out
+scons>>> Touch\("4"\)
+scons>>> Retrieved `foo.out' from cache
+CacheRetrieve\(foo.out\): retrieving from [0-9A-za-z]+
+scons>>> Touch\("5"\)
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout, match=TestCmd.match_re)
+
+
+
+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 the --interactive command line option to build
+a target when the --cache-disable option is used.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+CacheDir('cache')
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+Command('3', [], Touch('$TARGET'))
+Command('4', [], Touch('$TARGET'))
+Command('5', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build foo.out\n")
+
+scons.send("build 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in\n")
+
+scons.send("clean foo.out\n")
+
+scons.send("build 2\n")
+
+test.wait_for(test.workpath('2'))
+
+test.must_not_exist(test.workpath('foo.out'))
+
+
+
+scons.send("build foo.out\n")
+
+scons.send("build 3\n")
+
+test.wait_for(test.workpath('3'))
+
+test.must_match(test.workpath('foo.out'), "foo.in\n")
+
+scons.send("clean foo.out\n")
+
+scons.send("build 4\n")
+
+test.wait_for(test.workpath('4'))
+
+test.must_not_exist(test.workpath('foo.out'))
+
+
+
+scons.send("build --cache-disable foo.out\n")
+
+scons.send("build 5\n")
+
+test.wait_for(test.workpath('5'))
+
+test.must_match(test.workpath('foo.out'), "foo.in\n")
+
+
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+scons>>> Touch("1")
+scons>>> Removed foo.out
+scons>>> Touch("2")
+scons>>> Retrieved `foo.out' from cache
+scons>>> Touch("3")
+scons>>> Removed foo.out
+scons>>> Touch("4")
+scons>>> Copy("foo.out", "foo.in")
+scons>>> Touch("5")
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 the --interactive command line option to build a target when the
+--cache-force option is used.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+CacheDir('cache')
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+Command('3', [], Touch('$TARGET'))
+Command('4', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build foo.out\n")
+
+scons.send("build 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in\n")
+
+scons.send("clean foo.out\n")
+
+scons.send("build 2\n")
+
+test.wait_for(test.workpath('2'))
+
+test.must_not_exist(test.workpath('foo.out'))
+
+
+
+scons.send("build foo.out\n")
+
+scons.send("build 3\n")
+
+test.wait_for(test.workpath('3'))
+
+test.must_match(test.workpath('foo.out'), "foo.in\n")
+
+import shutil
+shutil.rmtree(test.workpath('cache'))
+
+
+
+scons.send("build --cache-force foo.out\n")
+
+scons.send("clean foo.out\n")
+
+scons.send("build foo.out\n")
+
+scons.send("build 4\n")
+
+test.wait_for(test.workpath('4'))
+
+test.must_match(test.workpath('foo.out'), "foo.in\n")
+
+
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+scons>>> Touch("1")
+scons>>> Removed foo.out
+scons>>> Touch("2")
+scons>>> Retrieved `foo.out' from cache
+scons>>> Touch("3")
+scons>>> scons: `foo.out' is up to date.
+scons>>> Removed foo.out
+scons>>> Retrieved `foo.out' from cache
+scons>>> Touch("4")
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 the --interactive command line option to build a target when the
+--cache-show option is used.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+CacheDir('cache')
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+Command('3', [], Touch('$TARGET'))
+Command('4', [], Touch('$TARGET'))
+Command('5', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build foo.out\n")
+
+scons.send("build 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in\n")
+
+scons.send("clean foo.out\n")
+
+scons.send("build 2\n")
+
+test.wait_for(test.workpath('2'))
+
+test.must_not_exist(test.workpath('foo.out'))
+
+
+
+scons.send("build foo.out\n")
+
+scons.send("build 3\n")
+
+test.wait_for(test.workpath('3'))
+
+test.must_match(test.workpath('foo.out'), "foo.in\n")
+
+scons.send("clean foo.out\n")
+
+scons.send("build 4\n")
+
+test.wait_for(test.workpath('4'))
+
+test.must_not_exist(test.workpath('foo.out'))
+
+
+
+scons.send("build --cache-show foo.out\n")
+
+scons.send("build 5\n")
+
+test.wait_for(test.workpath('5'))
+
+test.must_match(test.workpath('foo.out'), "foo.in\n")
+
+
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+scons>>> Touch("1")
+scons>>> Removed foo.out
+scons>>> Touch("2")
+scons>>> Retrieved `foo.out' from cache
+scons>>> Touch("3")
+scons>>> Removed foo.out
+scons>>> Touch("4")
+scons>>> Copy("foo.out", "foo.in")
+scons>>> Touch("5")
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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__"
+
+"""
+Verifies operation of the --interactive command line option
+"clean" subcommand.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+Command('f1.out', 'f1.in', Copy('$TARGET', '$SOURCE'))
+Command('f2.out', 'f2.in', Copy('$TARGET', '$SOURCE'))
+Command('f3.out', 'f3.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+Command('3', [], Touch('$TARGET'))
+""")
+
+test.write('f1.in', "f1.in\n")
+test.write('f2.in', "f2.in\n")
+test.write('f3.in', "f3.in\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build f1.out f2.out f3.out\n")
+
+scons.send("build 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('f1.out'), "f1.in\n")
+test.must_match(test.workpath('f2.out'), "f2.in\n")
+test.must_match(test.workpath('f3.out'), "f3.in\n")
+
+
+
+scons.send("clean f1.out\n")
+
+scons.send("build 2\n")
+
+test.wait_for(test.workpath('2'), popen=scons)
+
+test.must_not_exist('f1.out')
+test.must_exist('f2.out')
+test.must_exist('f3.out')
+
+
+
+scons.send("build -c\n")
+
+scons.send("build 3\n")
+
+test.wait_for(test.workpath('3'))
+
+test.must_not_exist('f1.out')
+test.must_not_exist('f2.out')
+test.must_not_exist('f3.out')
+
+expect_stdout = """\
+scons>>> Copy("f1.out", "f1.in")
+Copy("f2.out", "f2.in")
+Copy("f3.out", "f3.in")
+scons>>> Touch("1")
+scons>>> Removed f1.out
+scons>>> Touch("2")
+scons>>> Removed 1
+Removed 2
+Removed f2.out
+Removed f3.out
+scons>>> Touch("3")
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 use of the "exit" subcommand.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in 1\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build foo.out 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 1\n")
+
+
+
+scons.send('exit\n')
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+Touch("1")
+scons>>> """
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 the behavior of the "help" subcommand (and its "h" and "?" aliases).
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in 1\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build foo.out 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 1\n")
+
+
+
+scons.send('help\n')
+
+scons.send('h\n')
+
+scons.send('?\n')
+
+help_text = """\
+build [TARGETS] Build the specified TARGETS and their dependencies.
+ 'b' is a synonym.
+clean [TARGETS] Clean (remove) the specified TARGETS and their
+ dependencies. 'c' is a synonym.
+exit Exit SCons interactive mode.
+help [COMMAND] Prints help for the specified COMMAND. 'h' and
+ '?' are synonyms.
+shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!'
+ are synonyms.
+version Prints SCons version information.
+"""
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+Touch("1")
+scons>>> %(help_text)s
+scons>>> %(help_text)s
+scons>>> %(help_text)s
+scons>>>
+""" % locals()
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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__"
+
+"""
+This is a regression test for a bug in earlier versions of the
+--interactive command line option (specifically the original prototype
+submitted by Adam Simpkins, who created this test case).
+
+It tests to make sure that cached state is cleared between files for
+nodes in both the build tree and the source tree when BuildDirs are used.
+This is needed especially with BuildDirs created with duplicate=0, since
+the scanners scan the files in the source tree. Any cached implicit
+deps must be cleared on the source files.
+"""
+
+import os.path
+import string
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('src',
+ ['src', 'inc'])
+
+# Create the top-level SConstruct file
+test.write('SConstruct', """
+BUILD_ENV = Environment()
+Export('BUILD_ENV')
+
+hdr_dir = '#build/include'
+BUILD_ENV['HDR_DIR'] = hdr_dir
+BUILD_ENV.Append(CPPPATH = hdr_dir)
+
+BUILD_ENV.BuildDir('build', 'src', duplicate = 0)
+SConscript('build/SConscript')
+
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+""")
+
+# Create the src/SConscript file
+test.write(['src', 'SConscript'], """
+Import('BUILD_ENV')
+BUILD_ENV.Install(BUILD_ENV['HDR_DIR'], ['inc/foo.h'])
+BUILD_ENV.Program('foo', ['foo.c'])
+""")
+
+# Create src/foo.c
+test.write(['src', 'foo.c'], """
+#include <stdio.h>
+
+#define FOO_PRINT_STRING "Hello from foo.c"
+
+int main()
+{
+ printf(FOO_PRINT_STRING "\\n");
+ return 0;
+}
+""")
+
+# Create src/inc/foo.h
+test.write(['src', 'inc', 'foo.h'], """
+#ifndef INCLUDED_foo_h
+#define INCLUDED_foo_h
+
+#define FOO_PRINT_STRING "Hello from foo.h"
+
+#endif /* INCLUDED_foo_h */
+""")
+
+# Start scons, to build only "build/foo"
+build_foo_exe = os.path.join('build', 'foo' + TestSCons._exe)
+_build_foo_exe_ = '"%s"' % string.replace(build_foo_exe, '\\', '\\\\')
+abs_foo_exe = test.workpath(build_foo_exe)
+
+scons = test.start(arguments = '--interactive', combine=1)
+
+
+
+# Build build/foo
+scons.send('build %(_build_foo_exe_)s 1\n' % locals())
+
+test.wait_for(test.workpath('1'))
+
+# Run foo, and make sure it prints correctly
+test.run(program = abs_foo_exe, stdout = 'Hello from foo.c\n')
+
+
+
+# Update foo.c to include foo.h
+test.write(['src', 'foo.c'], """
+#include "foo.h"
+#include <stdio.h>
+
+int main()
+{
+ printf(FOO_PRINT_STRING "\\n");
+ return 0;
+}
+""")
+
+# Build build/foo
+scons.send('build %(_build_foo_exe_)s 2\n' % locals())
+
+test.wait_for(test.workpath('2'))
+
+# Run foo, and make sure it prints correctly
+test.run(program = abs_foo_exe, stdout = 'Hello from foo.h\n')
+
+
+
+scons.send('exit\n')
+
+test.finish(scons)
+
+
+
+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 the -Q option on an individual "build" command
+will suppress the progress messages.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in 1\n")
+
+
+
+scons = test.start(arguments = '--interactive')
+
+scons.send("build foo.out 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 1\n")
+
+
+
+test.write('foo.in', "foo.in 2\n")
+
+scons.send("build -Q foo.out 2\n")
+
+test.wait_for(test.workpath('2'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 2\n")
+
+
+
+expect_stdout = """\
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+scons>>> scons: Building targets ...
+Copy("foo.out", "foo.in")
+Touch("1")
+scons: done building targets.
+scons: Clearing cached node information ...
+scons: done clearing node information.
+scons>>> Copy("foo.out", "foo.in")
+Touch("2")
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 the -i option, specified on the build command, causes
+build errors to be ignored, just for that command.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+def error(target, source, env):
+ return 1
+e1 = Command('e1.out', 'e1.in', Action(error))
+e2 = Command('e2.out', 'e2.in', Action(error))
+f1 = Command('f1.out', 'f1.in', Copy('$TARGET', '$SOURCE'))
+f2 = Command('f2.out', 'f2.in', Copy('$TARGET', '$SOURCE'))
+Depends(f1, e1)
+Depends(f2, e2)
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+Command('3', [], Touch('$TARGET'))
+""")
+
+test.write('e1.in', "e1.in\n")
+test.write('e2.in', "e2.in\n")
+test.write('f1.in', "f1.in\n")
+test.write('f2.in', "f2.in\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive', combine=1)
+
+scons.send("build f1.out\n")
+
+scons.send("build 1\n")
+
+test.wait_for(test.workpath('1'), popen=scons)
+
+test.must_not_exist(test.workpath('f1.out'))
+
+
+
+scons.send("build -i e1.out f1.out\n")
+
+scons.send("build 2\n")
+
+test.wait_for(test.workpath('2'), popen=scons)
+
+test.must_match(test.workpath('f1.out'), "f1.in\n")
+
+
+
+scons.send("build f2.out\n")
+
+scons.send("build 3\n")
+
+test.wait_for(test.workpath('3'), popen=scons)
+
+test.must_not_exist(test.workpath('f2.out'))
+
+
+
+expect_stdout = """\
+scons>>> error(["e1.out"], ["e1.in"])
+scons: *** [e1.out] Error 1
+scons>>> Touch("1")
+scons>>> error(["e1.out"], ["e1.in"])
+scons: *** [e1.out] Error 1
+Copy("f1.out", "f1.in")
+scons>>> Touch("2")
+scons>>> error(["e2.out"], ["e2.in"])
+scons: *** [e2.out] Error 1
+scons>>> Touch("3")
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 "build" command of --interactive mode can take a -j
+option to build things in parallel.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons(combine=1)
+
+test.write('SConstruct', """\
+import os
+import time
+from SCons.Script import *
+def cat(target, source, env):
+ t = str(target[0])
+ os.mkdir(t + '.started')
+ fp = open(t, 'wb')
+ for s in source:
+ fp.write(open(str(s), 'rb').read())
+ fp.close()
+ os.mkdir(t + '.finished')
+
+def must_be_finished(target, source, env, dir):
+ if not os.path.exists(dir):
+ msg = 'build failed, %s does not exist\\n' % dir
+ sys.stderr.write(msg)
+ return 1
+ return cat(target, source, env)
+
+def f1_a_out_must_be_finished(target, source, env):
+ return must_be_finished(target, source, env, 'f1-a.out.finished')
+def f3_a_out_must_be_finished(target, source, env):
+ return must_be_finished(target, source, env, 'f3-a.out.finished')
+
+def must_wait_for_f2_b_out(target, source, env):
+ t = str(target[0])
+ os.mkdir(t + '.started')
+ f2_b_started = 'f2-b.out.started'
+ while not os.path.exists(f2_b_started):
+ time.sleep(1)
+ fp = open(t, 'wb')
+ for s in source:
+ fp.write(open(str(s), 'rb').read())
+ fp.close()
+ os.mkdir(t + '.finished')
+
+def _f2_a_out_must_not_be_finished(target, source, env):
+ f2_a_started = 'f2-a.out.started'
+ f2_a_finished = 'f2-a.out.finished'
+ while not os.path.exists(f2_a_started):
+ time.sleep(1)
+ msg = 'f2_a_out_must_not_be_finished(["%s"], ["%s"])\\n' % (target[0], source[0])
+ sys.stdout.write(msg)
+ if os.path.exists(f2_a_finished):
+ msg = 'build failed, %s exists\\n' % f2_a_finished
+ sys.stderr.write(msg)
+ return 1
+ return cat(target, source, env)
+
+f2_a_out_must_not_be_finished = Action(_f2_a_out_must_not_be_finished,
+ strfunction = None)
+
+Cat = Action(cat)
+f1_a = Command('f1-a.out', 'f1-a.in', cat)
+f1_b = Command('f1-b.out', 'f1-b.in', f1_a_out_must_be_finished)
+f2_a = Command('f2-a.out', 'f2-a.in', must_wait_for_f2_b_out)
+f2_b = Command('f2-b.out', 'f2-b.in', f2_a_out_must_not_be_finished)
+f3_a = Command('f3-a.out', 'f3-a.in', cat)
+f3_b = Command('f3-b.out', 'f3-b.in', f3_a_out_must_be_finished)
+Command('f1.out', f1_a + f1_b, cat)
+Command('f2.out', f2_a + f2_b, cat)
+Command('f3.out', f3_a + f3_b, cat)
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+Command('3', [], Touch('$TARGET'))
+""")
+
+test.write('f1-a.in', "f1-a.in\n")
+test.write('f1-b.in', "f1-b.in\n")
+test.write('f2-a.in', "f2-a.in\n")
+test.write('f2-b.in', "f2-b.in\n")
+test.write('f3-a.in', "f3-a.in\n")
+test.write('f3-b.in', "f3-b.in\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build f1.out\n")
+
+scons.send("build 1\n")
+
+test.wait_for(test.workpath('1'), popen=scons)
+
+test.must_match(test.workpath('f1.out'), "f1-a.in\nf1-b.in\n")
+
+
+
+scons.send("build -j2 f2.out\n")
+
+scons.send("build 2\n")
+
+test.wait_for(test.workpath('2'), popen=scons)
+
+test.must_match(test.workpath('f2.out'), "f2-a.in\nf2-b.in\n")
+
+
+
+scons.send("build f3.out\n")
+
+scons.send("build 3\n")
+
+test.wait_for(test.workpath('3'))
+
+test.must_match(test.workpath('f3.out'), "f3-a.in\nf3-b.in\n")
+
+
+
+expect_stdout = """\
+scons>>> cat(["f1-a.out"], ["f1-a.in"])
+f1_a_out_must_be_finished(["f1-b.out"], ["f1-b.in"])
+cat(["f1.out"], ["f1-a.out", "f1-b.out"])
+scons>>> Touch("1")
+scons>>> must_wait_for_f2_b_out(["f2-a.out"], ["f2-a.in"])
+f2_a_out_must_not_be_finished(["f2-b.out"], ["f2-b.in"])
+cat(["f2.out"], ["f2-a.out", "f2-b.out"])
+scons>>> Touch("2")
+scons>>> cat(["f3-a.out"], ["f3-a.in"])
+f3_a_out_must_be_finished(["f3-b.out"], ["f3-b.in"])
+cat(["f3.out"], ["f3-a.out", "f3-b.out"])
+scons>>> Touch("3")
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 the -k option, specified on the build command, causes
+us to keep going and build additional targets.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+def error(target, source, env):
+ return 1
+e1 = Command('e1.out', 'e1.in', Action(error))
+f1 = Command('f1.out', 'f1.in', Copy('$TARGET', '$SOURCE'))
+Command('f2.out', 'f2.in', Copy('$TARGET', '$SOURCE'))
+Command('f3.out', 'f3.in', Copy('$TARGET', '$SOURCE'))
+Depends(f1, e1)
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+Command('3', [], Touch('$TARGET'))
+""")
+
+test.write('e1.in', "e1.in\n")
+test.write('e2.in', "e2.in\n")
+test.write('f1.in', "f1.in\n")
+test.write('f2.in', "f2.in\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive', combine=1)
+
+scons.send("build f1.out f2.out\n")
+
+scons.send("build 1\n")
+
+test.wait_for(test.workpath('1'), popen=scons)
+
+test.must_not_exist(test.workpath('f1.out'))
+test.must_not_exist(test.workpath('f2.out'))
+
+
+
+scons.send("build -k f1.out f2.out\n")
+
+scons.send("build 2\n")
+
+test.wait_for(test.workpath('2'), popen=scons)
+
+test.must_not_exist(test.workpath('f1.out'))
+test.must_match(test.workpath('f2.out'), "f2.in\n")
+
+
+
+scons.send("build f1.out f3.out\n")
+
+scons.send("build 3\n")
+
+test.wait_for(test.workpath('3'), popen=scons)
+
+test.must_not_exist(test.workpath('f1.out'))
+test.must_not_exist(test.workpath('f3.out'))
+
+
+
+expect_stdout = """\
+scons>>> error(["e1.out"], ["e1.in"])
+scons: *** [e1.out] Error 1
+scons>>> Touch("1")
+scons>>> error(["e1.out"], ["e1.in"])
+scons: *** [e1.out] Error 1
+Copy("f2.out", "f2.in")
+scons>>> Touch("2")
+scons>>> error(["e1.out"], ["e1.in"])
+scons: *** [e1.out] Error 1
+scons>>> Touch("3")
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 the -n option, specified on the build command, reports
+what would be built but doesn't actually build anything.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build -n foo.out\n")
+
+scons.send("build 1\n")
+
+test.wait_for(test.workpath('1'), popen=scons)
+
+test.must_not_exist(test.workpath('foo.out'))
+
+
+
+scons.send("build foo.out\n")
+
+scons.send("build 2\n")
+
+test.wait_for(test.workpath('2'), popen=scons)
+
+test.must_match(test.workpath('foo.out'), "foo.in\n")
+
+
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+scons>>> Touch("1")
+scons>>> Copy("foo.out", "foo.in")
+scons>>> Touch("2")
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 basic operation of the --interactive command line option
+to build a target, rebuild it when the input changes, and not rebuild
+it when the input doesn't change.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in 1\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build foo.out 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 1\n")
+
+
+
+test.write('foo.in', "foo.in 2\n")
+
+scons.send("build -s foo.out 2\n")
+
+test.wait_for(test.workpath('2'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 2\n")
+
+
+
+scons.send("build foo.out\n")
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+Touch("1")
+scons>>> scons>>> scons: `foo.out' is up to date.
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 a blank line repeats the last (build) command.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in 1\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build foo.out 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 1\n")
+
+
+
+test.write('foo.in', "foo.in 2\n")
+
+scons.send("\n")
+
+scons.send("build 2\n")
+
+test.wait_for(test.workpath('2'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 2\n")
+
+
+
+scons.send("build foo.out\n")
+
+scons.send("\n")
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+Touch("1")
+scons>>> build foo.out 1
+Copy("foo.out", "foo.in")
+scons: `1' is up to date.
+scons>>> Touch("2")
+scons>>> scons: `foo.out' is up to date.
+scons>>> build foo.out
+scons: `foo.out' is up to date.
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 the ability of the "shell" command (and its "sh" and "!" aliases)
+to shell out of interactive mode.
+"""
+
+import string
+import sys
+
+import TestSCons
+
+test = TestSCons.TestSCons(combine=1)
+
+_python_ = TestSCons._python_
+
+shell_command_py = test.workpath('shell_command.py')
+_shell_command_py_ = '"%s"' % string.replace(shell_command_py, '\\', '\\\\')
+
+test.write(shell_command_py, """\
+print 'hello from shell_command.py'
+""")
+
+test.write('SConstruct', """\
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in 1\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build foo.out 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 1\n")
+
+
+
+scons.send('!%(_python_)s %(_shell_command_py_)s\n' % locals())
+
+scons.send("\n")
+
+scons.send('shell %(_python_)s %(_shell_command_py_)s\n' % locals())
+
+scons.send("\n")
+
+scons.send('sh %(_python_)s %(_shell_command_py_)s\n' % locals())
+
+scons.send("\n")
+
+scons.send('!no_such_command arg1 arg2\n')
+
+scons.send("\n")
+
+scons.send("build foo.out\n")
+
+scons.send("\n")
+
+if sys.platform == 'win32':
+ no_such_error = 'The system cannot find the file specified'
+else:
+ no_such_error = 'No such file or directory'
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+Touch("1")
+scons>>> hello from shell_command.py
+scons>>> !%(_python_)s %(_shell_command_py_)s
+hello from shell_command.py
+scons>>> hello from shell_command.py
+scons>>> shell %(_python_)s %(_shell_command_py_)s
+hello from shell_command.py
+scons>>> hello from shell_command.py
+scons>>> sh %(_python_)s %(_shell_command_py_)s
+hello from shell_command.py
+scons>>> scons: no_such_command: %(no_such_error)s
+scons>>> !no_such_command arg1 arg2
+scons: no_such_command: %(no_such_error)s
+scons>>> scons: `foo.out' is up to date.
+scons>>> build foo.out
+scons: `foo.out' is up to date.
+scons>>>
+""" % locals()
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 use of the --taskmastertrace= option to the "build" command
+of --interactive mode.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in 1\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build foo.out 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 1\n")
+
+
+
+test.write('foo.in', "foo.in 2\n")
+
+scons.send("build --taskmastertrace=- foo.out\n")
+
+scons.send("build 2\n")
+
+test.wait_for(test.workpath('2'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 2\n")
+
+
+
+scons.send("build foo.out\n")
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+Touch("1")
+scons>>> Taskmaster: 'foo.out': children:
+ ['foo.in']
+ waiting on unfinished children:
+ ['foo.in']
+Taskmaster: 'foo.in': evaluating foo.in
+Taskmaster: 'foo.out': children:
+ ['foo.in']
+ evaluating foo.out
+Copy("foo.out", "foo.in")
+scons>>> Touch("2")
+scons>>> scons: `foo.out' is up to date.
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 basic operation of the --interactive command line option to build
+a target, rebuild it when the input changes, and not rebuild it when
+the input doesn't change.
+
+Also tests that "scons" can be used as a synonym for "build".
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+Command('2', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in 1\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build foo.out\n")
+
+scons.send("build 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 1\n")
+
+
+
+test.write('foo.in', "foo.in 2\n")
+
+scons.send("build --tree=all foo.out\n")
+
+scons.send("scons 2\n")
+
+test.wait_for(test.workpath('2'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 2\n")
+
+
+
+scons.send("build --debug=tree foo.out\n")
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+scons>>> Touch("1")
+scons>>> Copy("foo.out", "foo.in")
++-foo.out
+ +-foo.in
+scons>>> Touch("2")
+scons>>> scons: `foo.out' is up to date.
++-foo.out
+ +-foo.in
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 the error message when an unknown command is typed in.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+Command('foo.out', 'foo.in', Copy('$TARGET', '$SOURCE'))
+Command('1', [], Touch('$TARGET'))
+""")
+
+test.write('foo.in', "foo.in 1\n")
+
+
+
+scons = test.start(arguments = '-Q --interactive')
+
+scons.send("build foo.out 1\n")
+
+test.wait_for(test.workpath('1'))
+
+test.must_match(test.workpath('foo.out'), "foo.in 1\n")
+
+
+
+scons.send('this-is-an-unknown-command hello\n')
+
+expect_stdout = """\
+scons>>> Copy("foo.out", "foo.in")
+Touch("1")
+scons>>> *** Unknown command: this-is-an-unknown-command
+scons>>>
+"""
+
+test.finish(scons, stdout = expect_stdout)
+
+
+
+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 the behavior of the "version" subcommand.
+"""
+
+import TestCmd
+import TestSCons
+
+test = TestSCons.TestSCons(match = TestCmd.match_re)
+
+test.write('SConstruct', "")
+
+
+
+# Construct the standard copyright marker so it doesn't get replaced
+# by the packaging build.
+copyright_marker = '__' + 'COPYRIGHT' + '__'
+
+copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007'
+
+fmt = '(%s|Copyright \\(c\\) %s The SCons Foundation)\n'
+
+copyright_line = fmt % (copyright_marker, copyright_years)
+
+
+
+expect1 = """\
+scons>>>
+scons>>>
+"""
+
+expect2 = """\
+scons>>>
+scons>>>
+"""
+
+test.run(arguments = '-Q --interactive',
+ stdin = "version\nexit\n")
+
+# Windows may or may not print a line for the script version
+# depending on whether it's invoked through scons.py or scons.bat.
+expect1 = r"""scons>>> SCons by Steven Knight et al\.:
+\tengine: v\S+, [^,]*, by \S+ on \S+
+%(copyright_line)sscons>>>
+""" % locals()
+
+expect2 = r"""scons>>> SCons by Steven Knight et al\.:
+\tscript: v\S+, [^,]*, by \S+ on \S+
+\tengine: v\S+, [^,]*, by \S+ on \S+
+%(copyright_line)sscons>>>
+""" % locals()
+
+stdout = test.stdout() + '\n'
+if not test.match_re(stdout, expect1) and not test.match_re(stdout, expect2):
+ print repr(stdout)
+ test.fail_test()
+
+
+
+test.pass_test()
test.must_match('classes.jar',
'cvfm classes.jar foo.mf -C testdir bar.class\n')
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
-
-if test.detect_tool('jar', ENV=ENV):
- where_jar = test.detect('JAR', 'jar', ENV=ENV)
-else:
- where_jar = test.where_is('jar')
-if not where_jar:
- test.skip_test("Could not find Java jar, skipping test(s).\n")
+
+
+where_javac, java_version = test.java_where_javac()
+where_jar = test.java_where_jar()
+
test.write("wrapper.py", """\
test = TestSCons.TestSCons()
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
-
-if test.detect_tool('jar', ENV=ENV):
- where_jar = test.detect('JAR', 'jar', ENV=ENV)
-else:
- where_jar = test.where_is('jar')
-if not where_jar:
- test.skip_test("Could not find Java jar, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac()
+where_jar = test.java_where_jar()
test.subdir('src')
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
-
-if test.detect_tool('jar', ENV=ENV):
- where_jar = test.detect('JAR', 'jar', ENV=ENV)
-else:
- where_jar = test.where_is('jar')
-if not where_jar:
- test.skip_test("Could not find Java jar, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac()
+where_jar = test.java_where_jar()
+
+
test.write('SConstruct', """
env = Environment(tools = ['javac', 'jar'],
expect = test.wrap_stdout("""\
%(where_javac)s -d classes -sourcepath src src/Example1\.java
-%(where_jar)s cvf test.jar classes/src/Example1\.class
+%(where_jar)s cvf test.jar -C classes src/Example1\.class
.*
-adding: classes/src/Example1\.class.*
+adding: src/Example1\.class.*
""" % locals())
expect = string.replace(expect, '/', os.sep)
test = TestSCons.TestSCons()
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
-
-if test.detect_tool('javah', ENV=ENV):
- where_javah = test.detect('JAVAH', 'javah', ENV=ENV)
-else:
- where_javah = test.where_is('javah')
-if not where_javah:
- test.skip_test("Could not find Java javah, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac()
+where_javah = test.java_where_javah()
test.write('SConstruct', """
env = Environment(tools = ['javac', 'javah'],
test = TestSCons.TestSCons()
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac()
test.subdir('src')
test = TestSCons.TestSCons()
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
-
-if test.detect_tool('javah', ENV=ENV):
- where_javah = test.detect('JAVAH', 'javah', ENV=ENV)
-else:
- where_javah = test.where_is('javah')
-if not where_javah:
- test.skip_test("Could not find Java javah, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac()
+where_javah = test.java_where_javah()
test.write('SConstruct', """
env = Environment(tools = ['javac', 'javah'],
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-import os
+import os.path
import string
-import sys
+
import TestSCons
_python_ = TestSCons._python_
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac()
+where_javah = test.java_where_javah()
-if test.detect_tool('javah', ENV=ENV):
- where_javah = test.detect('JAVAH', 'javah', ENV=ENV)
-else:
- where_javah = test.where_is('javah')
-if not where_javah:
- test.skip_test("Could not find Java javah, skipping test(s).\n")
+if java_version:
+ java_version = repr(java_version)
foo = Environment(tools = ['javac', 'javah', 'install'],
JAVAC = r'%(where_javac)s',
JAVAH = r'%(where_javah)s')
+jv = %(java_version)s
+if jv:
+ foo['JAVAVERSION'] = jv
javah = foo.Dictionary('JAVAH')
bar = foo.Clone(JAVAH = r'%(_python_)s wrapper.py ' + javah)
foo.Java(target = 'class1', source = 'com/sub/foo')
test = TestSCons.TestSCons()
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac()
test.write('SConstruct', """
env = Environment(tools = ['javac', 'javah'],
test = TestSCons.TestSCons()
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac('1.4')
{
public NestedExample()
{
- Thread t = new Thread() {
+ new Thread() {
public void start()
{
- Thread t = new Thread() {
+ new Thread() {
public void start()
{
try {Thread.sleep(200);}
public static void main(String argv[])
{
- NestedExample e = new NestedExample();
+ new NestedExample();
}
}
""")
test.write(['src5', 'TestSCons.java'], """\
class TestSCons {
public static void main(String[] args) {
- Foo[] fooArray = new Foo[] { new Foo() };
+ new Foo();
}
}
global failed
got = get_class_files(test.workpath(dir))
if expect != got:
- sys.stderr.write("Expected the following class files in '%s':\n" % dir)
- for c in expect:
- sys.stderr.write(' %s\n' % c)
- sys.stderr.write("Got the following class files in '%s':\n" % dir)
- for c in got:
- sys.stderr.write(' %s\n' % c)
+ missing = set(expect) - set(got)
+ if missing:
+ sys.stderr.write("Missing the following class files from '%s':\n" % dir)
+ for c in missing:
+ sys.stderr.write(' %s\n' % c)
+ unexpected = set(got) - set(expect)
+ if unexpected:
+ sys.stderr.write("Found the following unexpected class files in '%s':\n" % dir)
+ for c in unexpected:
+ sys.stderr.write(' %s\n' % c)
failed = 1
def classes_must_not_exist(dir, expect):
test = TestSCons.TestSCons()
-ENV = test.java_ENV()
-ENV['PATH'] = '/usr/lib/jvm/java-1.5.0-sun-1.5.0.11/bin' + os.pathsep + os.environ['PATH']
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac('1.5')
{
public NestedExample()
{
- Thread t = new Thread() {
+ new Thread() {
public void start()
{
- Thread t = new Thread() {
+ new Thread() {
public void start()
{
try {Thread.sleep(200);}
public static void main(String argv[])
{
- NestedExample e = new NestedExample();
+ new NestedExample();
}
}
""")
test = TestSCons.TestSCons()
-ENV = test.java_ENV()
-ENV['PATH'] = '/usr/lib/jvm/java-6-sun-1.6.0.00/bin' + os.pathsep + os.environ['PATH']
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac('1.6')
{
public NestedExample()
{
- Thread t = new Thread() {
+ new Thread() {
public void start()
{
- Thread t = new Thread() {
+ new Thread() {
public void start()
{
try {Thread.sleep(200);}
public static void main(String argv[])
{
- NestedExample e = new NestedExample();
+ new NestedExample();
}
}
""")
test.fail_test(test.read(['outdir', 'test2.class']) != "test2.JAVA\nline 3\n")
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping non-simulated test(s).\n")
-
-if test.detect_tool('rmic', ENV=ENV):
- where_rmic = test.detect('RMIC', 'rmic', ENV=ENV)
-else:
- where_rmic = test.where_is('rmic')
-if not where_rmic:
- test.skip_test("Could not find Java rmic, skipping non-simulated test(s).\n")
+where_javac, java_version = test.java_where_javac()
+where_rmic = test.java_where_rmic()
test.write("wrapper.py", """\
import os
test.fail_test(test.read('wrapper.out') != "wrapper.py %s -d outdir2 -classpath class2 com.sub.bar.Example3 com.sub.bar.Example4\n" % where_rmic)
-test.fail_test(not os.path.exists(test.workpath('outdir1', 'com', 'sub', 'foo', 'Example1_Skel.class')))
-test.fail_test(not os.path.exists(test.workpath('outdir1', 'com', 'sub', 'foo', 'Example1_Stub.class')))
-test.fail_test(not os.path.exists(test.workpath('outdir1', 'com', 'sub', 'foo', 'Example2_Skel.class')))
-test.fail_test(not os.path.exists(test.workpath('outdir1', 'com', 'sub', 'foo', 'Example2_Stub.class')))
-
-test.fail_test(not os.path.exists(test.workpath('outdir2', 'com', 'sub', 'bar', 'Example3_Skel.class')))
-test.fail_test(not os.path.exists(test.workpath('outdir2', 'com', 'sub', 'bar', 'Example3_Stub.class')))
-test.fail_test(not os.path.exists(test.workpath('outdir2', 'com', 'sub', 'bar', 'Example4_Skel.class')))
-test.fail_test(not os.path.exists(test.workpath('outdir2', 'com', 'sub', 'bar', 'Example4_Stub.class')))
+test.must_exist(test.workpath('outdir1', 'com', 'sub', 'foo', 'Example1_Stub.class'))
+test.must_exist(test.workpath('outdir1', 'com', 'sub', 'foo', 'Example2_Stub.class'))
+test.must_exist(test.workpath('outdir2', 'com', 'sub', 'bar', 'Example3_Stub.class'))
+test.must_exist(test.workpath('outdir2', 'com', 'sub', 'bar', 'Example4_Stub.class'))
+
+# We used to check for _Skel.class files as well, but they're not
+# generated by default starting with Java 1.5, and they apparently
+# haven't been needed for a while. Don't bother looking, even if we're
+# running Java 1.4. If we think they're needed but they don't exist
+# the test.up_to_date() call below will detect it.
+#test.must_exist(test.workpath('outdir1', 'com', 'sub', 'foo', 'Example1_Skel.class'))
+#test.must_exist(test.workpath('outdir1', 'com', 'sub', 'foo', 'Example2_Skel.class'))
+#test.must_exist(test.workpath('outdir2', 'com', 'sub', 'bar', 'Example3_Skel.class'))
+#test.must_exist(test.workpath('outdir2', 'com', 'sub', 'bar', 'Example4_Skel.class'))
test.up_to_date(arguments = '.')
-out_file1 = os.path.join('out', 'file1', 'class_Skel.class')
-out_file2 = os.path.join('out', 'file2', 'class_Skel.class')
-out_file3 = os.path.join('out', 'file3', 'class_Skel.class')
+out_file1 = os.path.join('out', 'file1', 'class_Stub.class')
+out_file2 = os.path.join('out', 'file2', 'class_Stub.class')
+out_file3 = os.path.join('out', 'file3', 'class_Stub.class')
-out_file1 = os.path.join('out', 'file1', 'class_Skel.class')
-out_file2 = os.path.join('out', 'file2', 'class_Skel.class')
-out_file3 = os.path.join('out', 'file3', 'class_Skel.class')
+out_file1 = os.path.join('out', 'file1', 'class_Stub.class')
+out_file2 = os.path.join('out', 'file2', 'class_Stub.class')
+out_file3 = os.path.join('out', 'file3', 'class_Stub.class')
test = TestSCons.TestSCons()
-# This test requires javac and swig
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
-
-if test.detect_tool('javah', ENV=ENV):
- where_javah = test.detect('JAVAH', 'javah', ENV=ENV)
-else:
- where_javah = test.where_is('javah')
-if not where_javah:
- test.skip_test("Could not find Java javah, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac()
+where_javah = test.java_where_javah()
swig = test.where_is('swig')
if not swig:
--- /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 the Jar() behavior when we have no JARCHDIR set (it should
+automatically use the classdir that was deduced from the Java() call)
+and when we explicity set it to None (it should not use the Java()
+classdir attribute at all).
+"""
+
+import string
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+where_javac, java_version = test.java_where_javac()
+where_jar = test.java_where_jar()
+
+test.subdir('src')
+
+
+
+test.write(['src', 'a.java'], """\
+package foo.bar;
+public class a {}
+""")
+
+test.write(['src', 'b.java'], """\
+package foo.bar;
+public class b {}
+""")
+
+
+
+test.write('SConstruct', """\
+env = Environment(tools = ['javac', 'jar'],
+ JAVAC = r'%(where_javac)s',
+ JAR = r'%(where_jar)s')
+
+jar = env.Jar('x.jar', env.Java(target = 'classes', source = 'src'))
+""" % locals())
+
+test.run(arguments = '.')
+
+
+
+test.run(program = where_jar, arguments = 'tf x.jar')
+
+expect = """\
+foo/bar/a.class
+foo/bar/b.class
+"""
+
+if string.find(test.stdout(), expect) == -1:
+ print "Did not find expected string in standard output."
+ print "Expected =========================================================="
+ print expect
+ print "Output ============================================================"
+ print test.stdout()
+ test.fail_test()
+
+
+
+test.run(arguments = '-c')
+
+
+
+test.write('SConstruct', """\
+env = Environment(tools = ['javac', 'jar'],
+ JAVAC = r'%(where_javac)s',
+ JAR = r'%(where_jar)s',
+ JARCHDIR = None)
+
+jar = env.Jar('x.jar', env.Java(target = 'classes', source = 'src'))
+""" % locals())
+
+test.run(arguments = '.')
+
+
+
+test.run(program = where_jar, arguments = 'tf x.jar')
+
+expect = """\
+classes/foo/bar/a.class
+classes/foo/bar/b.class
+"""
+
+if string.find(test.stdout(), expect) == -1:
+ print "Did not find expected string in standard output."
+ print "Expected =========================================================="
+ print expect
+ print "Output ============================================================"
+ print test.stdout()
+ test.fail_test()
+
+
+
+test.pass_test()
test = TestSCons.TestSCons()
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac()
test.write('SConstruct', """
test = TestSCons.TestSCons()
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
-
-if test.detect_tool('javah', ENV=ENV):
- where_javah = test.detect('JAVAH', 'javah', ENV=ENV)
-else:
- where_javah = test.where_is('javah')
-if not where_javah:
- test.skip_test("Could not find Java javah, skipping test(s).\n")
-
-if test.detect_tool('jar', ENV=ENV):
- where_jar = test.detect('JAR', 'jar', ENV=ENV)
-else:
- where_jar = test.where_is('jar')
-if not where_jar:
- test.skip_test("Could not find Java jar, skipping test(s).\n")
+swig = test.where_is('swig')
+if not swig:
+ test.skip_test('Can not find installed "swig", skipping test.\n')
+
+where_javac, java_version = test.java_where_javac()
+where_javah = test.java_where_javah()
+where_jar = test.java_where_jar()
test.subdir(['foo'],
['java'],
--- /dev/null
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Test that we can build shared libraries and link against shared
+libraries that have non-standard library prefixes and suffixes.
+"""
+
+import re
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+import sys
+isWindows = sys.platform == 'win32'
+
+env = Environment()
+
+# Make sure that the shared library can be located at runtime.
+env.Append(RPATH=['.'])
+env.Append(LIBPATH=['.'])
+
+# We first bake the LIBSUFFIXES, so that it will not change as a
+# side-effect of changing SHLIBSUFFIX.
+env['LIBSUFFIXES'] = map( env.subst, env.get('LIBSUFFIXES', []))
+
+weird_prefixes = ['libXX', 'libYY']
+
+if isWindows:
+ weird_suffixes = ['.xxx', '.yyy', '.xxx.dll', '.yyy.dll']
+ env.Append(CCFLAGS = '/MD')
+elif env['PLATFORM'] == 'darwin':
+ weird_suffixes = ['.xxx.dylib', '.yyy.dylib']
+else:
+ weird_suffixes = ['.xxx.so', '.yyy.so']
+
+shlibprefix = env.subst('$SHLIBPREFIX')
+shlibsuffix = env.subst('$SHLIBSUFFIX')
+
+progprefix = env.subst('$PROGPREFIX')
+progsuffix = env.subst('$PROGSUFFIX')
+
+goo_obj = env.SharedObject(source='goo.c')
+foo_obj = env.SharedObject(source='foo.c')
+prog_obj = env.SharedObject(source='prog.c')
+
+#
+# The following functions define all the different way that one can
+# use link againt a shared library.
+#
+def nodeInSrc(source, lib, libname):
+ return (source+lib, '')
+
+def pathInSrc(source, lib, libname):
+ return (source+map(str,lib), '')
+
+def nodeInLib(source, lib, libname):
+ return (source, lib)
+
+def pathInLib(source, lib, libname):
+ return (source, map(str,lib))
+
+def nameInLib(source, lib, libname):
+ # NOTE: libname must contain both the proper prefix and suffix.
+ #
+ # When using non-standard prefixes and suffixes, one has to
+ # provide the full name of the library since scons can not know
+ # which of the non-standard extension to use.
+ #
+ # Note that this is not necessarally SHLIBPREFIX and
+ # SHLIBSUFFIX. These are the ixes of the target library, not the
+ # ixes of the library that we are linking againt.
+ return (source, libname)
+
+libmethods = [
+ nodeInSrc, pathInSrc, nodeInLib, pathInLib,
+ nameInLib ]
+
+def buildAndlinkAgainst(builder, target, source, method, lib, libname, **kw):
+ '''Build a target using a given builder while linking againt a given
+ library using a specified method for linking against the library.'''
+
+ # On Windows, we have to link against the .lib file.
+ if isWindows:
+ for l in lib:
+ if str(l)[-4:] == '.lib':
+ lib = [l]
+ break
+ (source, LIBS) = method(source, lib, libname)
+ #build = builder(target=target, source=source, LIBS=LIBS, **kw)
+ kw = kw.copy()
+ kw['target'] = target
+ kw['source'] = source
+ kw['LIBS'] = LIBS
+ build = apply(builder, (), kw)
+
+ # Check that the build target depends on at least one of the
+ # library target.
+ found_dep = False
+ children = build[0].children()
+ for l in lib:
+ if l in children:
+ found_dep = True
+ break;
+ assert found_dep, \
+ "One of %s not found in %s, method=%s, libname=%s, shlibsuffix=%s" % \
+ (map(str,lib), map(str, build[0].children()), method.__name__, libname, shlibsuffix)
+ return build
+
+def prog(i,
+ goomethod, goolibprefix, goolibsuffix,
+ foomethod, foolibprefix, foolibsuffix):
+ '''Build a program
+
+ The program links against a shared library foo which itself links
+ against a shared library goo. The libraries foo and goo can use
+ arbitrary library prefixes and suffixes.'''
+
+ goo_name = goolibprefix+'goo'+str(i)+goolibsuffix
+ foo_name = foolibprefix+'foo'+str(i)+foolibsuffix
+ prog_name = progprefix+'prog'+str(i)+progsuffix
+
+ print 'Prog: %d, %s, %s, %s' % (i, goo_name, foo_name, prog_name)
+
+ # On Windows, we have to link against the .lib file.
+ if isWindows:
+ goo_libname = goolibprefix+'goo'+str(i)+'.lib'
+ foo_libname = foolibprefix+'foo'+str(i)+'.lib'
+ else:
+ goo_libname = goo_name
+ foo_libname = foo_name
+
+ goo_lib = env.SharedLibrary(
+ goo_name, goo_obj, SHLIBSUFFIX=goolibsuffix)
+ foo_lib = buildAndlinkAgainst(
+ env.SharedLibrary, foo_name, foo_obj,
+ goomethod, goo_lib, goo_libname, SHLIBSUFFIX=foolibsuffix)
+ prog = buildAndlinkAgainst(env.Program, prog_name, prog_obj,
+ foomethod, foo_lib, foo_libname)
+
+
+#
+# Create the list of all possible permutations to test.
+#
+i = 0
+tests = []
+prefixes = [shlibprefix] + weird_prefixes
+suffixes = [shlibsuffix] + weird_suffixes
+for foolibprefix in prefixes:
+ for foolibsuffix in suffixes:
+ for foomethod in libmethods:
+ for goolibprefix in prefixes:
+ for goolibsuffix in suffixes:
+ for goomethod in libmethods:
+ tests.append(
+ (i,
+ goomethod, goolibprefix, goolibsuffix,
+ foomethod, foolibprefix, foolibsuffix))
+ i = i + 1
+
+#
+# Pseudo-randomly choose 200 tests to run out of the possible
+# tests. (Testing every possible permutation would take too long.)
+#
+import random
+random.seed(123456)
+try:
+ random.shuffle(tests)
+except AttributeError:
+ pass
+
+for i in range(200):
+ apply(prog, tests[i])
+
+""")
+
+test.write('goo.c', r"""
+#include <stdio.h>
+
+#ifdef _WIN32
+#define EXPORT __declspec( dllexport )
+#else
+#define EXPORT
+#endif
+
+EXPORT void
+goo(void)
+{
+ printf("goo.c\n");
+}
+""")
+
+test.write('foo.c', r"""
+#include <stdio.h>
+
+#ifdef _WIN32
+#define EXPORT __declspec( dllexport )
+#else
+#define EXPORT
+#endif
+
+EXPORT void
+foo(void)
+{
+ goo();
+ printf("foo.c\n");
+}
+""")
+
+test.write('prog.c', r"""
+#include <stdio.h>
+
+void foo(void);
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ foo();
+ printf("prog.c\n");
+ return 0;
+}
+""")
+
+test.run(arguments = '.',
+ stderr=TestSCons.noisy_ar,
+ match=TestSCons.match_re_dotall)
+
+tests = re.findall(r'Prog: (\d+), (\S+), (\S+), (\S+)', test.stdout())
+expected = "goo.c\nfoo.c\nprog.c\n"
+
+for t in tests:
+ test.must_exist(t[1])
+ test.must_exist(t[2])
+ test.must_exist(t[3])
+ test.run(program = test.workpath(t[3]), stdout=expected)
+
+test.pass_test()
if string.find(sys.platform, 'darwin') != -1:
test.run(program='/usr/bin/file',
arguments = "foo1",
- stdout="foo1: Mach-O bundle ppc\n")
+ match = TestCmd.match_re,
+ stdout="foo1: Mach-O bundle (ppc|i386)\n")
if sys.platform in platforms_with_dlopen:
os.environ['LD_LIBRARY_PATH'] = test.workpath()
--- /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 .pdb files work correctly in conjunction with manifest files.
+"""
+
+import sys
+
+import TestSCons
+
+_exe = TestSCons._exe
+_dll = TestSCons._dll
+_lib = TestSCons._lib
+
+test = TestSCons.TestSCons()
+
+if sys.platform != 'win32':
+ msg = "Skipping Visual C/C++ test on non-Windows platform '%s'\n" % sys.platform
+ test.skip_test(msg)
+
+test.write('SConstruct', """\
+env = Environment()
+
+env['WINDOWS_INSERT_DEF'] = True
+env['WINDOWS_INSERT_MANIFEST'] = True
+env['PDB'] = '${TARGET.base}.pdb'
+env.Program('test', 'test.cpp')
+env.SharedLibrary('sharedlib', 'test.cpp')
+env.StaticLibrary('staticlib', 'test.cpp')
+""")
+
+test.write('test.cpp', """\
+#include <stdio.h>
+#include <stdlib.h>
+int
+main(int argc, char *argv)
+{
+ printf("test.cpp\\n");
+ exit (0);
+}
+""")
+
+test.write('sharedlib.def', """\
+""")
+
+test.run(arguments = '.')
+
+test.must_exist('test%s' % _exe)
+test.must_exist('test.pdb')
+
+test.must_exist('sharedlib%s' % _dll)
+test.must_exist('sharedlib.pdb')
+
+test.must_exist('staticlib%s' % _lib)
+test.must_not_exist('staticlib.pdb')
+
+test.pass_test()
test.write('SConscript', """\
Import("env")
env.Program(target = 'aaa', source = 'aaa.cpp')
+if env['PLATFORM'] == 'darwin':
+ env.Install('.', 'qt/lib/libmyqt.dylib')
""")
test.write('aaa.cpp', r"""
void aaa(void) Q_OBJECT;
""")
-test.run(arguments = aaa_exe)
+test.run()
test.up_to_date(options = '-n', arguments=aaa_exe)
scons: warning: Generated moc file 'aaa.moc' is not included by 'aaa.cpp'
""" + TestSCons.file_expr
-# In case 'ar' gives a warning about creating a library.
-test.fail_test(not test.match_re(test.stderr(), match12) and \
- not test.match_re(test.stderr(), match12 + ".+\n"))
+if not re.search(match12, test.stderr()):
+ print "Did not find expected regular expression in stderr:"
+ print test.stderr()
+ test.fail_test()
os.environ['QTDIR'] = test.QT
test = TestSCons.TestSCons()
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
-
-where_java = test.where_is('java')
-if not where_java:
- test.skip_test("Could not find Java java, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac()
+where_java = test.java_where_java()
java = where_java
#
test.run(chdir = 'work1', options = opts, arguments = ".")
+os.environ['JAVA_HOME'] = '/usr/lib/jvm/java-1.5.0-sun-1.5.0.11'
+
test.run(program = java,
arguments = "-cp %s Foo1" % work1_classes,
stdout = "rep1/src/Foo1.java\n")
test = TestSCons.TestSCons()
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
-
-if test.detect_tool('javah', ENV=ENV):
- where_javah = test.detect('JAVAH', 'javah', ENV=ENV)
-else:
- where_javah = test.where_is('javah')
-if not where_javah:
- test.skip_test("Could not find Java javah, skipping test(s).\n")
-
-where_java = test.where_is('java')
-if not where_java:
- test.skip_test("Could not find Java java, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac()
+where_java = test.java_where_java()
+where_javah = test.java_where_javah()
java = where_java
javac = where_javac
return 0
env_zzz.Command('zzz.out', aaa_exe, write_LIBDIRFLAGS)
env_yyy.Command('yyy.out', bbb_exe, write_LIBDIRFLAGS)
+
+if env_yyy['PLATFORM'] == 'darwin':
+ # The Mac OS X linker complains about nonexistent directories
+ # specified as -L arguments. Suppress its warnings so we don't
+ # treat the warnings on stderr as a failure.
+ env_yyy.Append(LINKFLAGS=['-w'])
+ env_zzz.Append(LINKFLAGS=['-w'])
""")
test.write(['work', 'aaa.c'], r"""
Test building Java applications when using Repositories.
"""
-import os
import string
-import sys
+
import TestSCons
python = TestSCons.python
test = TestSCons.TestSCons()
-ENV = test.java_ENV()
-
-if test.detect_tool('javac', ENV=ENV):
- where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
-else:
- where_javac = test.where_is('javac')
-if not where_javac:
- test.skip_test("Could not find Java javac, skipping test(s).\n")
-
-if test.detect_tool('rmic', ENV=ENV):
- where_rmic = test.detect('RMIC', 'rmic', ENV=ENV)
-else:
- where_rmic = test.where_is('rmic')
-if not where_rmic:
- test.skip_test("Could not find Java rmic, skipping non-simulated test(s).\n")
-
-where_java = test.where_is('java')
-if not where_java:
- test.skip_test("Could not find Java java, skipping test(s).\n")
+where_javac, java_version = test.java_where_javac()
+where_java = test.java_where_java()
+where_rmic = test.java_where_rmic()
java = where_java
javac = where_javac
# see that they were built from the proper rep1 sources, but I don't
# know how to do that with RMI, so punt for now.
-test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class')))
-test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class')))
-test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class')))
-test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class')))
+test.must_not_exist(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class'))
+test.must_not_exist(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class'))
-test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class')))
-test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class')))
-test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class')))
-test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class')))
+test.must_exist (test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class'))
+test.must_exist (test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class'))
+
+# We used to check for _Skel.class files as well, but they're not
+# generated by default starting with Java 1.5, and they apparently
+# haven't been needed for a while. Don't bother looking, even if we're
+# running Java 1.4. If we think they're needed but they don't exist
+# the variou test.up_to_date() calls below will detect it.
+#test.must_not_exist(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class'))
+#test.must_exist (test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class'))
+#test.must_exist (test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class'))
test.up_to_date(chdir = 'work1', options = opts, arguments = ".")
# see that they were built from the proper work1 sources, but I don't
# know how to do that with RMI, so punt for now.
-test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class')))
-test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class')))
-test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class')))
-test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class')))
+test.must_not_exist(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class'))
+test.must_not_exist(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class'))
+test.must_exist (test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class'))
+test.must_exist (test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class'))
-test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class')))
-test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class')))
-test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class')))
-test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class')))
+#test.must_not_exist(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class'))
+#test.must_not_exist(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class'))
+#test.must_exist (test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class'))
+#test.must_exist (test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class'))
test.up_to_date(chdir = 'work1', options = opts, arguments = ".")
# see that they were built from the proper work1 sources, but I don't
# know how to do that with RMI, so punt for now.
-test.fail_test(not os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class')))
-test.fail_test(not os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class')))
-test.fail_test(not os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class')))
-test.fail_test(not os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class')))
+test.must_exist(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class'))
+test.must_exist(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class'))
+
+#test.must_exist(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class'))
+#test.must_exist(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class'))
test.up_to_date(chdir = 'rep1', options = opts, arguments = ".")
test.run(chdir = 'work3', options = opts, arguments = ".")
-test.fail_test(os.path.exists(test.workpath('work3', 'classes', 'com', 'sub', 'foo', 'Hello.class')))
-test.fail_test(os.path.exists(test.workpath('work3', 'classes', 'com', 'sub', 'foo', 'Foo1.class')))
-test.fail_test(os.path.exists(test.workpath('work3', 'classes', 'com', 'sub', 'foo', 'Foo2.class')))
+test.must_not_exist(test.workpath('work3', 'classes', 'com', 'sub', 'foo', 'Hello.class'))
+test.must_not_exist(test.workpath('work3', 'classes', 'com', 'sub', 'foo', 'Foo1.class'))
+test.must_not_exist(test.workpath('work3', 'classes', 'com', 'sub', 'foo', 'Foo2.class'))
+
+test.must_exist (test.workpath('work3', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class'))
+test.must_exist (test.workpath('work3', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class'))
+
+#test.must_exist (test.workpath('work3', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class'))
+#test.must_exist (test.workpath('work3', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class'))
-test.fail_test(not os.path.exists(test.workpath('work3', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class')))
-test.fail_test(not os.path.exists(test.workpath('work3', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class')))
-test.fail_test(not os.path.exists(test.workpath('work3', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class')))
-test.fail_test(not os.path.exists(test.workpath('work3', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class')))
+test.up_to_date(chdir = 'work3', options = opts, arguments = ".")
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 we handle %module(directors="1") statements, both with and
+without white space before the opening parenthesis.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+swig = test.where_is('swig')
+
+if not swig:
+ test.skip_test('Can not find installed "swig", skipping test.\n')
+
+python_include_dir = test.get_python_inc()
+
+test.write(['SConstruct'], """\
+env = Environment(SWIGFLAGS = '-python',
+ CPPPATH=r"%(python_include_dir)s")
+
+import sys
+if sys.version[0] == '1':
+ # SWIG requires the -classic flag on pre-2.0 Python versions.
+ env.Append(SWIGFLAGS = ' -classic')
+
+env.SharedLibrary('test1.so', 'test1.i')
+env.SharedLibrary('test2.so', 'test2.i')
+""" % locals())
+
+test.write(['test1.cc'], """\
+int test1func()
+{
+ return 0;
+}
+""")
+
+test.write(['test1.h'], """\
+int test1func();
+""")
+
+test.write(['test1.i'], """\
+%module(directors="1") test1
+
+%{
+#include "test1.h"
+%}
+
+%include "test1.h"
+""")
+
+test.write(['test2.cc'], """\
+int test2func()
+{
+ return 0;
+}
+""")
+
+test.write(['test2.h'], """\
+int test2func();
+""")
+
+test.write(['test2.i'], """\
+%module (directors="1") test2
+
+%{
+#include "test2.h"
+%}
+
+%include "test2.h"
+""")
+
+test.run(arguments = '.')
+
+test.up_to_date(arguments = '.')
+
+test.pass_test()
f.write(file + ": " + str(dict[k]) + "\\n")
f.close()
-orig_function = CScan.scan
+orig_function = CScan.__call__
-def MyCScan(node, paths, orig_function=orig_function):
- deps = orig_function(node, paths)
+def MyCScan(node, paths, cwd, orig_function=orig_function):
+ deps = orig_function(node, paths, cwd)
global Scanned
n = str(node)
return deps
-CScan.scan = MyCScan
+CScan.__call__ = MyCScan
env = Environment(CPPPATH = ".")
l = env.StaticLibrary("g", Split("libg_1.c libg_2.c libg_3.c"))
--- /dev/null
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Test creation of a fully-featured TeX document (with bibliography
+and index) in a build_dir.
+
+Test courtesy Rob Managan.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+latex = test.where_is('latex')
+dvipdf = test.where_is('dvipdf')
+makeindex = test.where_is('makeindex')
+bibtex = test.where_is('bibtex')
+if not latex or not makeindex or not bibtex or not dvipdf:
+ test.skip_test("Could not find 'latex', 'makeindex', 'bibtex', or dvipdf; skipping test.\n")
+
+test.subdir(['docs'])
+
+
+test.write(['SConstruct'], """\
+import os
+
+env = Environment(ENV = { 'PATH' : os.environ['PATH'] },
+ TOOLS = ['tex', 'latex', 'dvipdf'])
+Export(['env'])
+
+SConscript(os.path.join('docs', 'SConscript'),
+ build_dir=os.path.join('mybuild','docs'),
+ duplicate=0)
+""")
+
+
+test.write(['docs', 'SConscript'], """\
+Import('env')
+
+test_dvi = env.DVI(source='test.tex')
+testpdf = env.PDF(source=test_dvi)
+""")
+
+
+test.write(['docs', 'Fig1.ps'], """\
+%!PS-Adobe-2.0 EPSF-2.0
+%%Title: Fig1.fig
+%%Creator: fig2dev Version 3.2 Patchlevel 4
+%%CreationDate: Tue Apr 25 09:56:11 2006
+%%BoundingBox: 0 0 98 98
+%%Magnification: 1.0000
+%%EndComments
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+
+end
+save
+newpath 0 98 moveto 0 0 lineto 98 0 lineto 98 98 lineto closepath clip newpath
+-24.9 108.2 translate
+1 -1 scale
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/sc {scale} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/tr {translate} bind def
+ /DrawEllipse {
+ /endangle exch def
+ /startangle exch def
+ /yrad exch def
+ /xrad exch def
+ /y exch def
+ /x exch def
+ /savematrix mtrx currentmatrix def
+ x y tr xrad yrad sc 0 0 1 startangle endangle arc
+ closepath
+ savematrix setmatrix
+ } def
+
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+$F2psBegin
+10 setmiterlimit
+ 0.06299 0.06299 sc
+%
+% Fig objects follow
+%
+7.500 slw
+% Ellipse
+n 1170 945 766 766 0 360 DrawEllipse gs col0 s gr
+
+$F2psEnd
+rs
+""")
+
+
+test.write(['docs', 'Fig1.tex'],
+r"""\begin{picture}(0,0)%
+\includegraphics{Fig1.ps}%
+\end{picture}%
+\setlength{\unitlength}{4144sp}%
+%
+\begingroup\makeatletter\ifx\SetFigFont\undefined%
+\gdef\SetFigFont#1#2#3#4#5{%
+ \reset@font\fontsize{#1}{#2pt}%
+ \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
+ \selectfont}%
+\fi\endgroup%
+\begin{picture}(1548,1546)(397,-879)
+\put(856,-196){\makebox(0,0)[lb]{\smash{\SetFigFont{12}{14.4}{\rmdefault}{\mddefault}{\updefault}{\color[rgb]{0,0,0}center $r_0$}%
+}}}
+\end{picture}
+""")
+
+
+test.write(['docs', 'test.bib'], """\
+%% This BibTeX bibliography file was created using BibDesk.
+%% http://bibdesk.sourceforge.net/
+
+%% Saved with string encoding Western (ASCII)
+
+@techreport{AnAuthor:2006fk,
+ Author = {A. N. Author},
+ Date-Added = {2006-11-15 12:51:30 -0800},
+ Date-Modified = {2006-11-15 12:52:35 -0800},
+ Institution = {none},
+ Month = {November},
+ Title = {A Test Paper},
+ Year = {2006}}
+""")
+
+
+test.write(['docs', 'test.tex'],
+r"""\documentclass{report}
+
+\usepackage{graphicx}
+\usepackage{epsfig,color} % for .tex version of figures if we go that way
+
+\usepackage{makeidx}
+\makeindex
+
+\begin{document}
+
+\title{Report Title}
+
+\author{A. N. Author}
+
+\maketitle
+
+\begin{abstract}
+there is no abstract
+\end{abstract}
+
+\tableofcontents
+\listoffigures
+
+\chapter{Introduction}
+
+The introduction is short.
+
+\index{Acknowledgements}
+
+\section{Acknowledgements}
+
+The Acknowledgements are show as well \cite{AnAuthor:2006fk}.
+
+\index{Getting the Report}
+
+To get a hard copy of this report call me.
+
+\begin{figure}[htbp]
+\begin{center}
+\input{Fig1.tex} % testing figure variant that uses TeX labeling
+\caption{Zone and Node indexing}
+\label{fig1}
+\end{center}
+\end{figure}
+
+All done now.
+
+\bibliographystyle{unsrt}
+\bibliography{test}
+\newpage
+
+\printindex
+
+\end{document}
+""")
+
+
+# makeindex will write status messages to stderr (grrr...), so ignore it.
+test.run(arguments = '.', stderr=None)
+
+
+# All (?) the files we expect will get created in the build_dir
+# (mybuild/docs) and not in the srcdir (docs).
+files = [
+ 'test.aux',
+ 'test.bbl',
+ 'test.blg',
+ 'test.dvi',
+ 'test.idx',
+ 'test.ilg',
+ 'test.ind',
+ 'test.lof',
+ 'test.log',
+ 'test.pdf',
+ 'test.toc',
+]
+
+for f in files:
+ test.must_exist(['mybuild', 'docs', f])
+ test.must_not_exist(['docs', f])
+
+
+test.pass_test()
Validate that both .tex and .ltx files can handle a LaTeX-style
bibliography (by calling $BIBTEX to generate a .bbl file) and
correctly re-run to resolve undefined references.
+
+Also verifies that package warnings are caught and re-run as needed.
"""
import string
if not tex and not latex:
test.skip_test("Could not find tex or latex; skipping test(s).\n")
-test.subdir('work1', 'work2', 'work4')
+test.subdir('work1', 'work2', 'work3', 'work4')
input_file = r"""
\end{document}
"""
+input_file3 = r"""
+\documentclass{article}
+\usepackage{longtable}
+
+\begin{document}
+As stated in the last paper, this is a bug-a-boo.
+here is some more junk and another table
+here is some more junk and another table
+
+\begin{longtable}[l]{rlll}
+ Isotope &\multicolumn{1}{c}{Abar} &Name\\
+\\
+ 1001 &1.0078 &Proton &$p$\\
+ 1002 &2.0141 &Deuterium &$d$\\
+ 1003 &3.0170 &Tritium &$t$\\
+ 2003 &3.0160 &Helium 3 &He$^3$\\
+ 2004 &4.0026 &Helium 4 &He$^{4}$\\
+\end{longtable}
+
+and a closing comment
+
+ These parameters and arrays are filled in when the parameter \textbf{iftnrates}
+ is set to 1:
+
+\begin{longtable}[l]{ll}
+\\
+\textbf{nxxxx} &Total number of particles made by xxxx reaction\\
+\textbf{pxxxx} &Total number of particles made by xxxx reaction\\
+\textbf{nxxxxx} &Total number of particles made by xxxxx reaction\\
+\textbf{nxxxx} &Total number of particles made by xxxx reaction\\
+\textbf{pxxxx} &Total number of particles made by xxxx reaction\\
+\textbf{nxxxx} &Total number of particles made by xxxx reaction\\
+\textbf{pxxxx} &Total number of particles made by xxxx reaction\\
+\textbf{nxxxxx} &Total number of particles made by xxxxx reaction\\
+\textbf{nxxxx} &Total number of particles made by xxxx reaction\\
+\textbf{pxxxx} &Total number of particles made by xxxx reaction\\
+\\
+\textbf{rnxxxx} &Regional total of particles made by xxxx reaction\\
+\textbf{rpxxxx} &Regional total of particles made by xxxx reaction\\
+\textbf{rnxxxxx} &Regional total of particles made by xxxxx reaction\\
+\textbf{rnxxxx} &Regional total of particles made by xxxx reaction\\
+\textbf{rpxxxx} &Regional total of particles made by xxxx reaction\\
+\textbf{rnxxxx} &Regional total of particles made by xxxx reaction\\
+\textbf{rpxxxx} &Regional total of particles made by xxxx reaction\\
+\textbf{rnxxxxx} &Regional total of particles made by xxxxx reaction\\
+\textbf{rnxxxx} &Regional total of particles made by xxxx reaction\\
+\textbf{rpxxxx} &Regional total of particles made by xxxx reaction\\
+\\
+\textbf{reactot}(r) &Total number of reactions for reaction r\\
+\textbf{reacreg}(r,ir) &Total number of reactions for reaction r in region ir\\
+\end{longtable}
+
+
+\end{document}
+"""
+
bibfile = r"""
@Article{X,
author = "Mr. X",
print foo_log
test.fail_test(1)
+ test.write(['work3', 'SConstruct'], """\
+DVI( "foo3.tex" )
+""")
+
+ test.write(['work3', 'foo3.tex'], input_file3)
+
+ test.run(chdir = 'work3', arguments = '.')
+
+ foo_log = test.read(['work3', 'foo3.log'])
+ if string.find(foo_log, 'Rerun LaTeX') != -1:
+ print 'foo.log contains "Rerun LaTeX":'
+ print foo_log
+ test.fail_test(1)
+
if latex:
print foo_log
test.fail_test(1)
+ test.write(['work3', 'SConstruct'], """\
+DVI( "foo3.tex" )
+PDF( "foo3.tex" )
+""")
+
+ test.write(['work3', 'foo3.tex'], input_file3)
+
+ test.run(chdir = 'work3', arguments = '.')
+
+ foo_log = test.read(['work3', 'foo3.log'])
+ if string.find(foo_log, 'Rerun LaTeX') != -1:
+ print 'foo.log contains "Rerun LaTeX":'
+ print foo_log
+ test.fail_test(1)
test.write(['work4', 'SConstruct'], """\
%%
""")
+import sys
+if sys.platform == 'darwin':
+ file_hpp = 'file.cpp.h'
+else:
+ file_hpp = 'file.hpp'
+
test.write("hello.cpp", """\
-#include "file.hpp"
+#include "%(file_hpp)s"
int main()
{
}
-""")
+""" % locals())
test.write('foo.y', yacc % 'foo.y')
+++ /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__"
-
-import os
-import string
-import sys
-import TestCmd
-import TestSCons
-
-test = TestSCons.TestSCons()
-
-no_such_file = test.workpath("no_such_file")
-not_executable = test.workpath("not_executable")
-
-test.write(not_executable, "\n")
-
-test.write("f1.in", "\n")
-test.write("f2.in", "\n")
-test.write("f3.in", "\n")
-
-test.write('SConstruct1', r"""
-bld = Builder(action = '%s $SOURCES $TARGET')
-env = Environment(BUILDERS = { 'bld' : bld })
-env.bld(target = 'f1', source = 'f1.in')
-""" % string.replace(no_such_file, '\\', '\\\\'))
-
-test.run(arguments='-f SConstruct1 .',
- stdout = test.wrap_stdout("%s f1.in f1\n" % no_such_file, error=1),
- stderr = None,
- status = 2)
-
-bad_command = """\
-Bad command or file name
-"""
-
-unrecognized = """\
-'%s' is not recognized as an internal or external command,
-operable program or batch file.
-scons: *** [%s] Error 1
-"""
-
-unspecified = """\
-The name specified is not recognized as an
-internal or external command, operable program or batch file.
-scons: *** [%s] Error 1
-"""
-
-not_found_1 = """
-sh: %s: not found
-scons: *** [%s] Error 1
-"""
-
-not_found_2 = """
-sh: %s: not found
-scons: *** [%s] Error 1
-"""
-
-No_such = """\
-%s: No such file or directory
-scons: *** [%s] Error 127
-"""
-
-cannot_execute = """\
-%s: cannot execute
-scons *** [%s] Error 126
-"""
-
-Permission_denied = """\
-%s: Permission denied
-scons: *** [%s] Error 126
-"""
-
-permission_denied = """\
-%s: permission denied
-scons: *** [%s] Error 126
-"""
-
-is_a_directory = """\
-%s: is a directory
-scons: *** [%s] Error 126
-"""
-
-test.description_set("Incorrect STDERR:\n%s\n" % test.stderr())
-if os.name == 'nt':
- errs = [
- bad_command,
- unrecognized % (no_such_file, 'f1'),
- unspecified % 'f1'
- ]
- test.fail_test(not test.stderr() in errs)
-else:
- errs = [
- not_found_1 % (no_such_file, 'f1'),
- not_found_2 % (no_such_file, 'f1'),
- No_such % (no_such_file, 'f1'),
- ]
- error_message_not_found = 1
- for err in errs:
- if string.find(test.stderr(), err) != -1:
- error_message_not_found = None
- break
- test.fail_test(error_message_not_found)
-
-test.write('SConstruct2', r"""
-bld = Builder(action = '%s $SOURCES $TARGET')
-env = Environment(BUILDERS = { 'bld': bld })
-env.bld(target = 'f2', source = 'f2.in')
-""" % string.replace(not_executable, '\\', '\\\\'))
-
-test.run(arguments='-f SConstruct2 .',
- stdout = test.wrap_stdout("%s f2.in f2\n" % not_executable, error=1),
- stderr = None,
- status = 2)
-
-test.description_set("Incorrect STDERR:\n%s\n" % test.stderr())
-if os.name == 'nt':
- errs = [
- bad_command,
- unrecognized % (not_executable, 'f2'),
- unspecified % 'f2'
- ]
- test.fail_test(not test.stderr() in errs)
-else:
- errs = [
- cannot_execute % (not_executable, 'f2'),
- Permission_denied % (not_executable, 'f2'),
- permission_denied % (not_executable, 'f2'),
- ]
- error_message_not_found = 1
- for err in errs:
- if string.find(test.stderr(), err) != -1:
- error_message_not_found = None
- break
- test.fail_test(error_message_not_found)
-
-test.write('SConstruct3', r"""
-bld = Builder(action = '%s $SOURCES $TARGET')
-env = Environment(BUILDERS = { 'bld' : bld })
-env.bld(target = 'f3', source = 'f3.in')
-""" % string.replace(test.workdir, '\\', '\\\\'))
-
-test.run(arguments='-f SConstruct3 .',
- stdout = test.wrap_stdout("%s f3.in f3\n" % test.workdir, error=1),
- stderr = None,
- status = 2)
-
-test.description_set("Incorrect STDERR:\n%s\n" % test.stderr())
-if os.name == 'nt':
- errs = [
- bad_command,
- unrecognized % (test.workdir, 'f3'),
- unspecified % 'f3'
- ]
- test.fail_test(not test.stderr() in errs)
-else:
- errs = [
- cannot_execute % (not_executable, 'f3'),
- is_a_directory % (test.workdir, 'f3'),
- ]
- error_message_not_found = 1
- for err in errs:
- if string.find(test.stderr(), err) != -1:
- error_message_not_found = None
- break
- test.fail_test(error_message_not_found)
-
-test.write('SConstruct4', r"""
-env = Environment()
-env.Command('test.out', 'test.in', Copy('$TARGET', '$SOURCE'))
-env.InstallAs('test2.out', 'test.out')
-# Mark test2.out as precious so we'll handle the exception in
-# FunctionAction() rather than when the target is cleaned before building.
-env.Precious('test2.out')
-env.Default('test2.out')
-""")
-
-test.write('test.in', "test.in 1\n")
-
-test.run(arguments = '-f SConstruct4 .')
-
-test.write('test.in', "test.in 2\n")
-
-test.writable('test2.out', 0)
-f = open(test.workpath('test2.out'))
-
-test.run(arguments = '-f SConstruct4 .',
- stderr = None,
- status = 2)
-
-f.close()
-test.writable('test2.out', 1)
-
-test.description_set("Incorrect STDERR:\n%s" % test.stderr())
-errs = [
- "scons: *** [test2.out] test2.out: Permission denied\n",
- "scons: *** [test2.out] test2.out: permission denied\n",
-]
-test.fail_test(test.stderr() not in errs)
-
-test.pass_test()
# Test bad shell ('./one' is a dir, so it can't be used as a shell).
# This will also give an exit status not in exitvalmap,
-# with error "Permission denied".
+# with error "Permission denied" or "No such file or directory".
test.write('SConstruct', """
env=Environment()
-if env['PLATFORM'] == 'posix':
+if env['PLATFORM'] in ('posix', 'darwin'):
from SCons.Platform.posix import fork_spawn
env['SPAWN'] = fork_spawn
env['SHELL'] = 'one'
test.run(status=2, stderr=None)
err = test.stderr()
-test.fail_test(string.find(err, 'Exception') != -1 or \
- string.find(err, 'Traceback') != -1)
-test.fail_test(string.find(err, "ermission") == -1 and \
- string.find(err, "such file") == -1)
+if string.find(err, 'Exception') != -1 or string.find(err, 'Traceback') != -1:
+ print "Exception or Traceback found in the following error output:"
+ print err
+ test.fail_test()
+if string.find(err, 'ermission') == -1 and string.find(err, 'such file') == -1:
+ print "Missing '[Pp]ermission' or 'such file' string in the following error output:"
+ print err
+ test.fail_test()
# Test command with exit status -1.
'g77',
'gas',
'gcc',
+ 'gfortran',
'gnulink',
'gs',
'hpc++',
test = TestSCons.TestSCons()
-test.subdir('work1', 'work2')
+test.subdir('work1', 'work2', 'work3')
sys.exit(1)
""")
+
+#
+# Test: work1
+#
+
test.write(['work1', 'SConstruct'], """\
Succeed = Builder(action = r'%(_python_)s ../succeed.py $TARGETS')
Fail = Builder(action = r'%(_python_)s ../fail.py $TARGETS')
test.must_not_exist(test.workpath('work1', 'aaa.out'))
test.must_match(['work1', 'bbb.out'], "succeed.py: bbb.out\n")
+expect = """\
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+scons: Cleaning targets ...
+Removed bbb.out
+scons: done cleaning targets.
+"""
+
+test.run(chdir = 'work1',
+ arguments = '--clean --keep-going aaa.out bbb.out',
+ stdout = expect)
+
+test.must_not_exist(test.workpath('work1', 'aaa.1'))
+test.must_not_exist(test.workpath('work1', 'aaa.out'))
+test.must_not_exist(test.workpath('work1', 'bbb.out'))
+
+
+#
+# Test: work2
+#
test.write(['work2', 'SConstruct'], """\
Succeed = Builder(action = r'%(_python_)s ../succeed.py $TARGETS')
+#
+# Test: work3
+#
+# Check that the -k (keep-going) switch works correctly when the Nodes
+# forms a DAG. The test case is the following
+#
+# all
+# |
+# +-----+-----+-------------+
+# | | |
+# a1 a2 a3
+# | | |
+# + +---+---+ +---+---+
+# \ | / | |
+# \ bbb.out / a4 ccc.out
+# \ / /
+# \ / /
+# \ / /
+# aaa.out (fails)
+#
+
+test.write(['work3', 'SConstruct'], """\
+Succeed = Builder(action = r'%(_python_)s ../succeed.py $TARGETS')
+Fail = Builder(action = r'%(_python_)s ../fail.py $TARGETS')
+env = Environment(BUILDERS = { 'Succeed' : Succeed, 'Fail' : Fail })
+a = env.Fail('aaa.out', 'aaa.in')
+b = env.Succeed('bbb.out', 'bbb.in')
+c = env.Succeed('ccc.out', 'ccc.in')
+
+a1 = Alias( 'a1', a )
+a2 = Alias( 'a2', a+b)
+a4 = Alias( 'a4', c)
+a3 = Alias( 'a3', a4+c)
+
+Alias('all', a1+a2+a3)
+""" % locals())
+
+test.write(['work3', 'aaa.in'], "aaa.in\n")
+test.write(['work3', 'bbb.in'], "bbb.in\n")
+test.write(['work3', 'ccc.in'], "ccc.in\n")
+
+
+# Test tegular build (i.e. without -k)
+test.run(chdir = 'work3',
+ arguments = '.',
+ status = 2,
+ stderr = None,
+ stdout = """\
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+scons: Building targets ...
+%(_python_)s ../fail.py aaa.out
+scons: building terminated because of errors.
+""" % locals())
+
+test.must_not_exist(['work3', 'aaa.out'])
+test.must_not_exist(['work3', 'bbb.out'])
+test.must_not_exist(['work3', 'ccc.out'])
+
+
+test.run(chdir = 'work3',
+ arguments = '-c .')
+test.must_not_exist(['work3', 'aaa.out'])
+test.must_not_exist(['work3', 'bbb.out'])
+test.must_not_exist(['work3', 'ccc.out'])
+
+
+# Current directory
+test.run(chdir = 'work3',
+ arguments = '-k .',
+ status = 2,
+ stderr = None,
+ stdout = """\
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+scons: Building targets ...
+%(_python_)s ../fail.py aaa.out
+%(_python_)s ../succeed.py bbb.out
+%(_python_)s ../succeed.py ccc.out
+scons: done building targets (errors occurred during build).
+""" % locals())
+
+test.must_not_exist(['work3', 'aaa.out'])
+test.must_exist(['work3', 'bbb.out'])
+test.must_exist(['work3', 'ccc.out'])
+
+
+test.run(chdir = 'work3',
+ arguments = '-c .')
+test.must_not_exist(['work3', 'aaa.out'])
+test.must_not_exist(['work3', 'bbb.out'])
+test.must_not_exist(['work3', 'ccc.out'])
+
+
+# Single target
+test.run(chdir = 'work3',
+ arguments = '--keep-going all',
+ status = 2,
+ stderr = None,
+ stdout = """\
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+scons: Building targets ...
+%(_python_)s ../fail.py aaa.out
+%(_python_)s ../succeed.py bbb.out
+%(_python_)s ../succeed.py ccc.out
+scons: done building targets (errors occurred during build).
+""" % locals())
+
+test.must_not_exist(['work3', 'aaa.out'])
+test.must_exist(['work3', 'bbb.out'])
+test.must_exist(['work3', 'ccc.out'])
+
+
+test.run(chdir = 'work3',
+ arguments = '-c .')
+test.must_not_exist(['work3', 'aaa.out'])
+test.must_not_exist(['work3', 'bbb.out'])
+test.must_not_exist(['work3', 'ccc.out'])
+
+
+# Separate top-level targets
+test.run(chdir = 'work3',
+ arguments = '-k a1 a2 a3',
+ status = 2,
+ stderr = None,
+ stdout = """\
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+scons: Building targets ...
+%(_python_)s ../fail.py aaa.out
+%(_python_)s ../succeed.py bbb.out
+%(_python_)s ../succeed.py ccc.out
+scons: done building targets (errors occurred during build).
+""" % locals())
+
+test.must_not_exist(['work3', 'aaa.out'])
+test.must_exist(['work3', 'bbb.out'])
+test.must_exist(['work3', 'ccc.out'])
+
+
test.pass_test()
+-bar.h
"""
test.run(arguments = "--debug=includes foo.ooo")
-test.fail_test(string.find(test.stdout(), includes) == -1)
+
+if string.find(test.stdout(), includes) == -1:
+ print "Did not find expected string in standard output."
+ print "Expected =========================================================="
+ print includes
+ print "Actual ============================================================"
+ print test.stdout()
+ test.fail_test()
# In an ideal world, --debug=includes would also work when there's a build
# failure, but this would require even more complicated logic to scan
"""
import os
+import new
import string
import TestSCons
class M:
def __init__(cls, name, bases, cls_dict):
- cls.has_metaclass = 1
-
-class A:
- __metaclass__ = M
+ cls.use_metaclass = 1
+ def fake_method(self):
+ pass
+ new.instancemethod(fake_method, None, cls)
try:
- has_metaclass = A.has_metaclass
+ class A:
+ __metaclass__ = M
+
+ use_metaclass = A.use_metaclass
except AttributeError:
- has_metaclass = None
+ use_metaclass = None
+ reason = 'no metaclasses'
+except TypeError:
+ use_metaclass = None
+ reason = 'new.instancemethod\\(\\) bug'
"Node._children_get()",
]
-expect_no_metaclasses = """
-scons: warning: memoization is not supported in this version of Python \\(no metaclasses\\)
-""" + TestSCons.file_expr
-
-if has_metaclass:
+if use_metaclass:
def run_and_check(test, args, desc):
test.run(arguments = args)
else:
+ expect_no_metaclasses = """
+scons: warning: memoization is not supported in this version of Python \\(%s\\)
+""" % reason
+
+ expect_no_metaclasses = expect_no_metaclasses + TestSCons.file_expr
+
def run_and_check(test, args, desc):
test.run(arguments = args, stderr = expect_no_metaclasses)
stdout = test.stdout()
--- /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__"
+
+import string
+
+import TestSCons
+
+_python_ = TestSCons._python_
+
+test = TestSCons.TestSCons()
+
+isStackSizeAvailable = False
+try:
+ import threading
+ isStackSizeAvailable = hasattr(threading,'stack_size')
+except ImportError:
+ pass
+
+test.subdir('work1', 'work2')
+
+test.write('build.py', r"""
+import sys
+contents = open(sys.argv[2], 'rb').read()
+file = open(sys.argv[1], 'wb')
+file.write(contents)
+file.close()
+""")
+
+
+test.write(['work1', 'SConstruct'], """
+B = Builder(action = r'%(_python_)s ../build.py $TARGETS $SOURCES')
+env = Environment(BUILDERS = { 'B' : B })
+f1 = env.B(target = 'f1.out', source = 'f1.in')
+f2 = env.B(target = 'f2.out', source = 'f2.in')
+Requires(f2, f1)
+""" % locals())
+
+test.write(['work1', 'f1.in'], "f1.in\n")
+test.write(['work1', 'f2.in'], "f2.in\n")
+
+
+test.write(['work2', 'SConstruct'], """
+SetOption('stack_size', 128)
+B = Builder(action = r'%(_python_)s ../build.py $TARGETS $SOURCES')
+env = Environment(BUILDERS = { 'B' : B })
+f1 = env.B(target = 'f1.out', source = 'f1.in')
+f2 = env.B(target = 'f2.out', source = 'f2.in')
+Requires(f2, f1)
+""" % locals())
+
+test.write(['work2', 'f1.in'], "f1.in\n")
+test.write(['work2', 'f2.in'], "f2.in\n")
+
+
+
+expected_stdout = test.wrap_stdout("""\
+%(_python_)s ../build.py f1.out f1.in
+%(_python_)s ../build.py f2.out f2.in
+""" % locals())
+
+re_expected_stdout = string.replace(expected_stdout, '\\', '\\\\')
+
+expect_unsupported = """
+scons: warning: Setting stack size is unsupported by this version of Python:
+ (('module' object|'threading' module) has no attribute 'stack_size'|stack_size)
+File .*
+"""
+
+
+#
+# Test without any options
+#
+test.run(chdir='work1',
+ arguments = '.',
+ stdout=expected_stdout,
+ stderr='')
+test.must_exist(['work1', 'f1.out'])
+test.must_exist(['work1', 'f2.out'])
+
+test.run(chdir='work1',
+ arguments = '-c .')
+test.must_not_exist(['work1', 'f1.out'])
+test.must_not_exist(['work1', 'f2.out'])
+
+#
+# Test with -j2
+#
+test.run(chdir='work1',
+ arguments = '-j2 .',
+ stdout=expected_stdout,
+ stderr='')
+test.must_exist(['work1', 'f1.out'])
+test.must_exist(['work1', 'f2.out'])
+
+test.run(chdir='work1',
+ arguments = '-j2 -c .')
+test.must_not_exist(['work1', 'f1.out'])
+test.must_not_exist(['work1', 'f2.out'])
+
+
+#
+# Test with --stack-size
+#
+test.run(chdir='work1',
+ arguments = '--stack-size=128 .',
+ stdout=expected_stdout,
+ stderr='')
+test.must_exist(['work1', 'f1.out'])
+test.must_exist(['work1', 'f2.out'])
+
+test.run(chdir='work1',
+ arguments = '--stack-size=128 -c .')
+test.must_not_exist(['work1', 'f1.out'])
+test.must_not_exist(['work1', 'f2.out'])
+
+#
+# Test with SetOption('stack_size', 128)
+#
+test.run(chdir='work2',
+ arguments = '.',
+ stdout=expected_stdout,
+ stderr='')
+test.must_exist(['work2', 'f1.out'])
+test.must_exist(['work2', 'f2.out'])
+
+test.run(chdir='work2',
+ arguments = '--stack-size=128 -c .')
+test.must_not_exist(['work2', 'f1.out'])
+test.must_not_exist(['work2', 'f2.out'])
+
+if isStackSizeAvailable:
+ #
+ # Test with -j2 --stack-size=128
+ #
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=128 .',
+ stdout=expected_stdout,
+ stderr='')
+ test.must_exist(['work1', 'f1.out'])
+ test.must_exist(['work1', 'f2.out'])
+
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=128 -c .')
+ test.must_not_exist(['work1', 'f1.out'])
+ test.must_not_exist(['work1', 'f2.out'])
+
+ #
+ # Test with -j2 --stack-size=16
+ #
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=16 .',
+ match=TestSCons.match_re,
+ stdout=re_expected_stdout,
+ stderr="""
+scons: warning: Setting stack size failed:
+ size not valid: 16384 bytes
+File .*
+""")
+ test.must_exist(['work1', 'f1.out'])
+ test.must_exist(['work1', 'f2.out'])
+
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=16 -c .',
+ match=TestSCons.match_re,
+ stderr="""
+scons: warning: Setting stack size failed:
+ size not valid: 16384 bytes
+File .*
+""")
+ test.must_not_exist(['work1', 'f1.out'])
+ test.must_not_exist(['work1', 'f2.out'])
+
+ #
+ # Test with -j2 SetOption('stack_size', 128)
+ #
+ test.run(chdir='work2',
+ arguments = '-j2 .',
+ stdout=expected_stdout,
+ stderr='')
+ test.must_exist(['work2', 'f1.out'])
+ test.must_exist(['work2', 'f2.out'])
+
+ test.run(chdir='work2',
+ arguments = '-j2 -c .')
+ test.must_not_exist(['work2', 'f1.out'])
+ test.must_not_exist(['work2', 'f2.out'])
+
+ #
+ # Test with -j2 --stack-size=128 --warn=no-stack-size
+ #
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=128 --warn=no-stack-size .',
+ stdout=expected_stdout,
+ stderr='')
+ test.must_exist(['work1', 'f1.out'])
+ test.must_exist(['work1', 'f2.out'])
+
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=128 --warn=no-stack-size -c .')
+ test.must_not_exist(['work1', 'f1.out'])
+ test.must_not_exist(['work1', 'f2.out'])
+
+ #
+ # Test with -j2 --stack-size=16 --warn=no-stack-size
+ #
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=16 --warn=no-stack-size .',
+ stdout=expected_stdout,
+ stderr='')
+ test.must_exist(['work1', 'f1.out'])
+ test.must_exist(['work1', 'f2.out'])
+
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=16 --warn=no-stack-size -c .')
+ test.must_not_exist(['work1', 'f1.out'])
+ test.must_not_exist(['work1', 'f2.out'])
+
+ #
+ # Test with -j2 --warn=no-stack-size SetOption('stack_size', 128)
+ #
+ test.run(chdir='work2',
+ arguments = '-j2 --warn=no-stack-size .',
+ stdout=expected_stdout,
+ stderr='')
+ test.must_exist(['work2', 'f1.out'])
+ test.must_exist(['work2', 'f2.out'])
+
+ test.run(chdir='work2',
+ arguments = '-j2 --warn=no-stack-size -c .')
+ test.must_not_exist(['work2', 'f1.out'])
+ test.must_not_exist(['work2', 'f2.out'])
+
+else:
+
+ #
+ # Test with -j2 --stack-size=128
+ #
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=128 .',
+ match=TestSCons.match_re,
+ stdout=re_expected_stdout,
+ stderr=expect_unsupported)
+ test.must_exist(['work1', 'f1.out'])
+ test.must_exist(['work1', 'f2.out'])
+
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=128 -c .',
+ match=TestSCons.match_re,
+ stderr=expect_unsupported)
+ test.must_not_exist(['work1', 'f1.out'])
+ test.must_not_exist(['work1', 'f2.out'])
+
+ #
+ # Test with -j2 --stack-size=16
+ #
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=16 .',
+ match=TestSCons.match_re,
+ stdout=re_expected_stdout,
+ stderr=expect_unsupported)
+ test.must_exist(['work1', 'f1.out'])
+ test.must_exist(['work1', 'f2.out'])
+
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=16 -c .',
+ match=TestSCons.match_re,
+ stderr=expect_unsupported)
+ test.must_not_exist(['work1', 'f1.out'])
+ test.must_not_exist(['work1', 'f2.out'])
+
+ #
+ # Test with -j2 SetOption('stack_size', 128)
+ #
+ test.run(chdir='work2',
+ arguments = '-j2 .',
+ match=TestSCons.match_re,
+ stdout=re_expected_stdout,
+ stderr=expect_unsupported)
+ test.must_exist(['work2', 'f1.out'])
+ test.must_exist(['work2', 'f2.out'])
+
+ test.run(chdir='work2',
+ arguments = '-j2 -c .',
+ match=TestSCons.match_re,
+ stderr=expect_unsupported)
+ test.must_not_exist(['work2', 'f1.out'])
+ test.must_not_exist(['work2', 'f2.out'])
+
+ #
+ # Test with -j2 --stack-size=128 --warn=no-stack-size
+ #
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=128 --warn=no-stack-size .',
+ stdout=expected_stdout,
+ stderr='')
+ test.must_exist(['work1', 'f1.out'])
+ test.must_exist(['work1', 'f2.out'])
+
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=128 --warn=no-stack-size -c .')
+ test.must_not_exist(['work1', 'f1.out'])
+ test.must_not_exist(['work1', 'f2.out'])
+
+ #
+ # Test with -j2 --stack-size=16 --warn=no-stack-size
+ #
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=16 --warn=no-stack-size .',
+ stdout=expected_stdout,
+ stderr='')
+ test.must_exist(['work1', 'f1.out'])
+ test.must_exist(['work1', 'f2.out'])
+
+ test.run(chdir='work1',
+ arguments = '-j2 --stack-size=16 --warn=no-stack-size -c .')
+ test.must_not_exist(['work1', 'f1.out'])
+ test.must_not_exist(['work1', 'f2.out'])
+
+ #
+ # Test with -j2 --warn=no-stack-size SetOption('stack_size', 128)
+ #
+ test.run(chdir='work2',
+ arguments = '-j2 --warn=no-stack-size .',
+ stdout=expected_stdout,
+ stderr='')
+ test.must_exist(['work2', 'f1.out'])
+ test.must_exist(['work2', 'f2.out'])
+
+ test.run(chdir='work2',
+ arguments = '-j2 --warn=no-stack-size -c .')
+ test.must_not_exist(['work2', 'f1.out'])
+ test.must_not_exist(['work2', 'f2.out'])
+
+test.pass_test()
import TestSCons_time
-test = TestSCons_time.TestSCons_time(match = TestSCons_time.match_re)
+test = TestSCons_time.TestSCons_time()
try:
import pstats
expect1 = r'\d.\d\d\d prof1\.py:1\(_main\)' + '\n'
-test.run(arguments = 'func -f st1.conf', stdout = expect1)
+test.run(arguments = 'func -f st1.conf',
+ match = TestSCons_time.match_re,
+ stdout = expect1)
test.write('st2.conf', """\
prefix = 'foo'
title = 'ST2.CONF TITLE'
+vertical_bars = (
+ ( 1.4, 7, None ),
+ ( 1.5, 7, "label 1.5" ),
+ ( 1.6, 7, "label 1.6" ),
+)
""")
expect2 = \
r"""set title "ST2.CONF TITLE"
set key bottom left
-plot '-' title "Startup" with lines lt 1
+set label 3 "label 1.5" at 0.5,1.5 right
+set label 4 "label 1.6" at 0.6,1.5 right
+plot '-' title "Startup" with lines lt 1, \
+ '-' notitle with lines lt 7, \
+ '-' title "label 1.5" with lines lt 7, \
+ '-' title "label 1.6" with lines lt 7
# Startup
1 0.000
2 0.000
e
+1.4 0
+1.4 1
+e
+1.5 0
+1.5 1
+e
+1.6 0
+1.6 1
+e
"""
test.run(arguments = 'func --file st2.conf --fmt gnuplot', stdout = expect2)
test.write('st2.conf', """\
prefix = 'foo'
title = 'ST2.CONF TITLE'
+vertical_bars = (
+ ( 1.5, 7, None ),
+)
""")
expect2 = \
r"""set title "ST2.CONF TITLE"
set key bottom left
-plot '-' title "Startup" with lines lt 1
+plot '-' title "Startup" with lines lt 1, \
+ '-' notitle with lines lt 7
# Startup
1 4000.000
2 4000.000
e
+1.5 0
+1.5 4800
+e
"""
test.run(arguments = 'mem --file st2.conf --fmt gnuplot', stdout = expect2)
test.write('st2.conf', """\
prefix = 'foo'
title = 'ST2.CONF TITLE'
+vertical_bars = (
+ ( 1.5, 7, None ),
+)
""")
expect2 = \
r"""set title "ST2.CONF TITLE"
set key bottom left
-plot '-' title "Startup" with lines lt 1
+plot '-' title "Startup" with lines lt 1, \
+ '-' notitle with lines lt 7
# Startup
1 16040.000
2 16040.000
e
+1.5 0
+1.5 18000
+e
"""
test.run(arguments = 'obj --file st2.conf --fmt gnuplot Node.FS.Base', stdout = expect2)
'foo-329-2.log',
'foo-329-2.prof')
-def tempdir_re(*args):
- import os
- import os.path
- import string
- import tempfile
-
- sep = re.escape(os.sep)
- args = (tempfile.gettempdir(), 'scons-time-aegis-',) + args
- x = apply(os.path.join, args)
- x = re.escape(x)
- x = string.replace(x, 'aegis\\-', 'aegis\\-[^%s]*' % sep)
- return x
-
expect = [
- tempdir_re('src', 'script', 'scons.py'),
- 'SCONS_LIB_DIR = %s' % tempdir_re('src', 'engine'),
+ test.tempdir_re('src', 'script', 'scons.py'),
+ 'SCONS_LIB_DIR = %s' % test.tempdir_re('src', 'engine'),
]
content = test.read(test.workpath('foo-321-2.log'))
test.write_sample_project('foo.tar.gz')
test.write('config', """\
-archive_list = ['foo.tar.gz']
+archive_list = ['foo.tar.gz', 'foo-file']
""")
+test.write('foo-file', "foo-file\n")
+
test.run(arguments = 'run -f config')
test.must_exist('foo-000-0.log',
'foo-000-2.log',
'foo-000-2.prof')
+test.must_exist('foo-file')
+
test.write_sample_project('bar.tar.gz')
test = TestSCons_time.TestSCons_time(match = TestSCons_time.match_re)
test.diff_function = TestSCons_time.diff_re
-
-def tempdir_re(*args):
- import os,sys
- import os.path
- import string
- import tempfile
-
- sep = re.escape(os.sep)
- args = (tempfile.gettempdir(), 'scons-time-',) + args
- x = apply(os.path.join, args)
- x = re.escape(x)
- x = string.replace(x, 'time\\-', 'time\\-[^%s]*' % sep)
- if sys.platform=='darwin':
- # OSX has /tmp in /private/tmp.
- x = '(/private)?' + x
- return x
-
scons_py = re.escape(test.workpath('src', 'script', 'scons.py'))
src_engine = re.escape(test.workpath('src', 'engine'))
-tmp_scons_time = tempdir_re()
-tmp_scons_time_foo = tempdir_re('foo')
+tmp_scons_time = test.tempdir_re()
+tmp_scons_time_foo = test.tempdir_re('foo')
test.write_fake_scons_py()
test = TestSCons_time.TestSCons_time(match = TestSCons_time.match_re)
test.diff_function = TestSCons_time.diff_re
-
-def tempdir_re(*args):
- import os,sys
- import os.path
- import string
- import tempfile
-
- sep = re.escape(os.sep)
- args = (tempfile.gettempdir(), 'scons-time-',) + args
- x = apply(os.path.join, args)
- x = re.escape(x)
- x = string.replace(x, 'time\\-', 'time\\-[^%s]*' % sep)
- if sys.platform=='darwin':
- # OSX has /tmp in /private/tmp.
- x = '(/private)?' + x
- return x
-
scons_py = re.escape(test.workpath('src', 'script', 'scons.py'))
src_engine = re.escape(test.workpath('src', 'engine'))
-tmp_scons_time = tempdir_re()
-tmp_scons_time_foo = tempdir_re('foo')
+tmp_scons_time = test.tempdir_re()
+tmp_scons_time_foo = test.tempdir_re('foo')
test.write_fake_scons_py()
'foo-716-2.log',
'foo-716-2.prof')
-def tempdir_re(*args):
- import os
- import os.path
- import string
- import tempfile
-
- sep = re.escape(os.sep)
- args = (tempfile.gettempdir(), 'scons-time-svn-',) + args
- x = apply(os.path.join, args)
- x = re.escape(x)
- x = string.replace(x, 'svn\\-', 'svn\\-[^%s]*' % sep)
- return x
-
expect = [
- tempdir_re('src', 'script', 'scons.py'),
- 'SCONS_LIB_DIR = %s' % tempdir_re('src', 'engine'),
+ test.tempdir_re('src', 'script', 'scons.py'),
+ 'SCONS_LIB_DIR = %s' % test.tempdir_re('src', 'engine'),
]
content = test.read(test.workpath('foo-617-2.log'), mode='r')
--- /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 the time subcommand doesn't fail and prints an appropriate
+error message if a log file is empty.
+"""
+
+import TestSCons_time
+
+test = TestSCons_time.TestSCons_time()
+
+
+header = ' Total SConscripts SCons commands\n'
+
+lines = []
+
+line_fmt = ' 11.123456 22.234567 33.345678 44.456789 %s\n'
+empty_fmt = ' %s\n'
+
+for i in xrange(9):
+ logfile_name = 'foo-%s-0.log' % i
+ if i == 5:
+ test.write(test.workpath(logfile_name), "")
+ lines.append(empty_fmt % logfile_name)
+ else:
+ test.fake_logfile(logfile_name)
+ lines.append(line_fmt % logfile_name)
+
+expect = [header] + lines
+
+test.run(arguments = 'time foo-*.log',
+ stdout = ''.join(expect),
+ stderr = "file 'foo-5-0.log' has no contents!\n")
+
+expect = """\
+set key bottom left
+plot '-' title "Startup" with lines lt 1
+# Startup
+0 11.123456
+1 11.123456
+2 11.123456
+3 11.123456
+4 11.123456
+6 11.123456
+7 11.123456
+8 11.123456
+e
+"""
+
+stderr = "file 'foo-5-0.log' has no contents!\n"
+
+test.run(arguments = 'time --fmt gnuplot --which total foo-*.log',
+ stdout = expect,
+ stderr = stderr)
+
+expect = """\
+set key bottom left
+plot '-' title "Startup" with lines lt 1
+# Startup
+e
+"""
+
+test.run(arguments = 'time --fmt gnuplot foo-5-0.log',
+ stdout = expect,
+ stderr = stderr)
+
+test.pass_test()
test.write('st2.conf', """\
prefix = 'foo'
title = 'ST2.CONF TITLE'
+vertical_bars = (
+ ( 1.5, 7, None ),
+)
""")
expect2 = \
r"""set title "ST2.CONF TITLE"
set key bottom left
-plot '-' title "Startup" with lines lt 1
+plot '-' title "Startup" with lines lt 1, \
+ '-' notitle with lines lt 7
# Startup
1 11.123456
2 11.123456
e
+1.5 0
+1.5 12
+e
"""
test.run(arguments = 'time --file st2.conf --fmt gnuplot', stdout = expect2)
--- /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 the time subcommand's --which option doesn't fail, and prints
+an appropriate error message, if a log file doesn't have its specific
+requested results.
+"""
+
+import TestSCons_time
+
+test = TestSCons_time.TestSCons_time()
+
+
+header = """\
+set key bottom left
+plot '-' title "Startup" with lines lt 1
+# Startup
+"""
+
+footer = """\
+e
+"""
+
+line_fmt = "%s 11.123456\n"
+
+lines = []
+
+for i in xrange(9):
+ logfile_name = 'foo-%s-0.log' % i
+ if i == 5:
+ test.write(test.workpath(logfile_name), "NO RESULTS HERE!\n")
+ else:
+ test.fake_logfile(logfile_name)
+ lines.append(line_fmt % i)
+
+expect = [header] + lines + [footer]
+
+stderr = "file 'foo-5-0.log' has no results!\n"
+
+
+test.run(arguments = 'time --fmt gnuplot --which total foo*.log',
+ stdout = ''.join(expect),
+ stderr = stderr)
+
+expect = [header] + [footer]
+
+test.run(arguments = 'time --fmt gnuplot foo-5-0.log',
+ stdout = ''.join(expect),
+ stderr = stderr)
+
+test.pass_test()
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
-Verify that we can trivially subclass our "public" classes.
+Verify that we can trivially subclass our "public" classes. Also
+verify that we can use a trivial subclass of new-style str classes
+as well as UserString as Builder input.
"""
import TestSCons
test = TestSCons.TestSCons()
test.write('SConstruct', """
+copy_action = Copy('$TARGET', '$SOURCE')
+
# Some day, we'd probably like people to be able to subclass Action and
# Builder, but that's going to take some serious class-hackery to turn
# our factory function into the class itself.
class my_Environment(Environment):
pass
env = my_Environment()
-env.Program('hello', 'hello.c')
-""")
+env.Command('f0.out', 'f0.in', copy_action)
+
+from UserString import UserString
+try:
+ class mystr(str):
+ pass
+except TypeError:
+ class mystr(UserString):
+ pass
-test.write('hello.c', """\
-#include <stdio.h>
-#include <stdlib.h>
-int
-main(int argc, char *argv[]) {
- printf("hello.c\\n");
-}
+Command(mystr('f1.out'), mystr('f1.in'), copy_action)
+Command(UserString('f2.out'), UserString('f2.in'), copy_action)
+
+Install(mystr('install'), mystr('f1.out'))
+Install(mystr('install'), UserString('f2.out'))
""")
+test.write('f0.in', "f0.in\n")
+test.write('f1.in', "f1.in\n")
+test.write('f2.in', "f2.in\n")
+
test.run(arguments = '.')
+test.must_match('f0.out', "f0.in\n")
+test.must_match(['install', 'f1.out'], "f1.in\n")
+test.must_match(['install', 'f2.out'], "f2.in\n")
+
test.pass_test()
test.write('f3.in', "f3.in\n")
test.write('f4.in', "f4.in\n")
-test.run(arguments = 'f1.out f3.out')
+test.run(arguments = 'f1.out f3.out',
+ stderr = None)
test.run(arguments = 'f1.out f2.out f3.out f4.out',
stdout = test.wrap_stdout("""\
build(["f2.out"], ["f2.in"])
scons: `f3.out' is up to date.
build(["f4.out"], ["f4.in"])
-"""))
+"""),
+ stderr = None)
os.utime(test.workpath('f1.in'),
(os.path.getatime(test.workpath('f1.in')),
scons: `f2.out' is up to date.
build(["f3.out"], ["f3.in"])
scons: `f4.out' is up to date.
-"""))
+"""),
+ stderr = None)
test.pass_test()
--- /dev/null
+#
+# __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.
+#
+
+"""
+This configuration is for testing the timing of searching long lists of
+CPPPATH directories.
+
+We create 100 on-disk directories, with a single .h file in the last
+directory in the list. We set CPPPATH to a list of Dir Nodes for the
+created directories. The .c file we create #includes the .h file to be
+found in the last directory in the list.
+"""
+
+import os
+import os.path
+
+dir_cnt = 100
+
+dir_list = map(lambda t: 'inc_%03d' % t, xrange(dir_cnt))
+
+for dir in dir_list:
+ if not os.path.isdir(dir):
+ os.mkdir(dir)
+
+foo_h = 'inc_099/foo.h'
+
+if not os.path.isfile(foo_h):
+ open(foo_h, 'w').write('#define FOO 1\n')
+
+contents = """\
+#include "foo.h"
+void
+foo(void)
+{
+ ;
+}
+"""
+
+if not os.path.isfile('foo.c'):
+ open('foo.c', 'w').write(contents)
+
+inc_list = map(lambda d: Dir(d), dir_list)
+
+env = Environment(CPPPATH = inc_list)
+
+env.Object( 'foo.c' )
--- /dev/null
+#
+# __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.
+
+"""
+scons-time.py configuration file for the "CPPPATH" timing test.
+"""
+
+archive_list = [ 'SConstruct' ]
+subdir = '.'
+
+import sys
+sys.path.insert(0, '..')
+import SCons_Bars
+
+revs = [
+ 1224, # Don't create a Node for every file we try to find during scan.
+ 1349, # More efficient checking for on-disk file entries.
+ 1407, # Use a Dir scanner instead of a hard-coded method.
+ 1433, # Remove unnecessary creation of RCS and SCCS Node.Dir nodes.
+ 1703, # Lobotomize Memoizer.
+ 2380, # The Big Signature Refactoring hits branches/core.
+]
+
+vertical_bars = SCons_Bars.Release_Bars.gnuplot(labels=True) + \
+ SCons_Bars.Revision_Bars.gnuplot(labels=False, revs=revs)
--- /dev/null
+#
+# __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.
+#
+
+"""
+This configuration is for timing how we evaluate long chains of
+dependencies, specifically when -j is used.
+
+We set up a chain of 100 targets that get built from a Python function
+action with no source files (equivalent to "echo junk > $TARGET").
+Each target explicitly depends on the next target in turn, so the
+Taskmaster will do a deep walk of the dependency graph.
+
+This test case was contributed by Kevin Massey. Prior to revision 1468,
+we had a serious O(N^2) problem in the Taskmaster when handling long
+dependency chains like this. That was fixed by adding reference counts
+to the Taskmaster so it could be smarter about not re-evaluating Nodes.
+"""
+
+target_cnt = 100
+
+env = Environment()
+
+def write_file( env, target, source ):
+ path_target = env.File( target ).path
+ outfile = open( path_target, 'w' )
+ outfile.write( 'junk' )
+ outfile.close()
+
+list = []
+for i in range( target_cnt ):
+ target = 'target_%03d' % i
+ env.Command( target, [], write_file )
+ env.Depends( target, list )
+ list.append( target )
--- /dev/null
+#
+# __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.
+#
+
+"""
+scons-time.py configuration file for the "JTimer" timing test.
+"""
+
+archive_list = [ 'SConstruct' ]
+subdir = '.'
+targets = '-j2'
+
+import sys
+sys.path.insert(0, '..')
+import SCons_Bars
+
+revs = [
+ 1261, # Fix -j re-scanning built files for implicit deps.
+ 1307, # Move signature Node tranlation of rel_paths into the class.
+ 1407, # Use a Dir scanner instead of a hard-coded method.
+ 1435, # Don't prep .sconsign dependencies until needed.
+ 1468, # Use waiting-Node reference counts to speed up Taskmaster.
+ 1703, # Lobotomize Memoizer.
+ 1706, # Fix _doLookup value-cache misspellings.
+ 2380, # The Big Signature Refactoring hits branches/core.
+]
+
+vertical_bars = SCons_Bars.Release_Bars.gnuplot(labels=True) + \
+ SCons_Bars.Revision_Bars.gnuplot(labels=False, revs=revs)
--- /dev/null
+#
+# __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.
+#
+
+"""
+A quick module for central collection of information about which
+Subversion revisions are important for performance implications.
+"""
+
+class Bars(dict):
+ """
+ Dictionary subclass for mapping revision numbers to labels describing
+ each revision.
+
+ We provide two extensions: a .color attribute (for the default
+ color) and a .gnuplot() method (for returning a list of revisions
+ in the tuple format that scons-time uses to describe vertical bars).
+ """
+ def __init__(self, dict=None, color=None, **kwargs):
+ super(Bars, self).__init__(dict, **kwargs)
+ self.color = color
+ def gnuplot(self, color=None, labels=False, revs=None):
+ if color is None:
+ color = self.color
+ if revs is None:
+ revs = self.keys()
+ revs.sort()
+ if labels:
+ result = [ (r, color, None, self[r]) for r in revs ]
+ else:
+ result = [ (r, color, None, None) for r in revs ]
+ return tuple(result)
+
+# The Release_Bars dictionary records the Subversion revisions that
+# correspond to each official SCons release.
+
+Release_Bars = Bars(
+ color = 7,
+ dict = {
+ 1232 : '0.96.90',
+ 1344 : '0.96.91',
+ 1435 : '0.96.92',
+ 1674 : '0.96.93',
+ 1765 : '0.96.94',
+ 1835 : '0.96.95',
+ 1882 : '0.96.96',
+ 1901 : '0.97',
+ 2242 : '0.97.0d20070809',
+ 2454 : '0.97.0d20070918',
+ 2527 : '0.97.0d20071212',
+ },
+)
+
+
+# The Revisions_Bars dictionary records the Subversion revisions that
+# correspond to "interesting" changes in timing. This is essentially the
+# global list of interesting changes. Individual timing configurations
+# typically only display bars for a subset of these, the ones that
+# actually affect their configuration.
+#
+# Note that the default behavior of most of the st.conf files is to
+# *not* display the labels for each of these lines, since they're long
+# and verbose. So in practice they function as comments describing the
+# changes that have timing impacts on various configurations.
+
+Revision_Bars = Bars(
+ color = 5,
+ dict = {
+ 1220 : 'Use WeakValueDicts in the Memoizer to reduce memory use.',
+ 1224 : 'Don\'t create a Node for every file we try to find during scan.',
+ 1231 : 'Don\'t pick same-named directories in a search path.',
+ 1241 : 'Optimize out N*M suffix matching in Builder.py.',
+ 1245 : 'Reduce gen_binfo() time for long source lists.',
+ 1261 : 'Fix -j re-scanning built files for implicit deps.',
+ 1262 : 'Match Entries when searching paths for Files or Dirs.',
+ 1273 : 'Store paths in .sconsign relative to target directory.',
+ 1282 : 'Cache result from rel_path().',
+ 1307 : 'Move signature Node tranlation of rel_paths into the class.',
+ 1346 : 'Give subst logic its own module.',
+ 1349 : 'More efficient checking for on-disk file entries.',
+ 1407 : 'Use a Dir scanner instead of a hard-coded method.',
+ 1433 : 'Remove unnecessary creation of RCS and SCCS Node.Dir nodes.',
+ 1435 : 'Don\'t convert .sconsign dependencies to Nodes until needed.',
+ 1468 : 'Use waiting-Node reference counts to speed up Taskmaster.',
+ 1477 : 'Delay disambiguation of Node.FS.Entry into File/Dir.',
+ 1533 : 'Fix some disambiguation-delay ramifications.',
+ 1655 : 'Reduce unnecessary calls to Node.FS.disambiguate().',
+ 1703 : 'Lobotomize Memoizer.',
+ 1706 : 'Fix _doLookup value-cache misspellings.',
+ 1712 : 'PathList, restore caching of Builder source suffixes.',
+ 1724 : 'Cache Node.FS.find_file() and Node.FS.Dir.srcdir_find_file().',
+ 1727 : 'Cache Executor methods, reduce calls when scanning.',
+ 1752 : 'Don\'t cache Builder source suffixes too early.',
+ 1790 : 'Clean up various module imports (pychecker fixes).',
+ 1794 : 'Un-fix various later-Python-version pychecker "fixes".',
+ 1828 : 'Speed up Builder suffix-matching (SuffixMap).',
+ 2380 : 'The Big Signature Refactoring hits branches/core.',
+ },
+)
--- /dev/null
+#
+# __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.
+#
+
+"""
+This configuration is for timing how we handle the NxM interaction when
+we build a lot of targets from a lot of source files.
+
+We create a list of 100 target files that will each be built by copying
+a file from a corresponding list of 100 source files. The source
+files themselves are each built by a Python function action that's the
+equivalent of "echo contents > $TARGET".
+"""
+
+target_cnt = 100
+
+env = Environment()
+
+def create_file( env, target, source ):
+ t = str(target[0])
+ open( t, 'w' ).write('contents\n')
+
+source_list = map(lambda t: 'source_%03d' % t, xrange(target_cnt))
+target_list = map(lambda t: 'target_%03d' % t, xrange(target_cnt))
+
+for source in source_list:
+ env.Command( source, [], create_file )
+
+def copy_files( env, target, source ):
+ for t, s in zip(target, source):
+ open(str(t), 'w').write(open(str(s), 'r').read())
+
+env.Command( target_list, source_list, copy_files )
--- /dev/null
+#
+# __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.
+#
+
+"""
+scons-time.py configuration file for the "hundred" timing test.
+"""
+
+archive_list = [ 'SConstruct' ]
+subdir = '.'
+
+import sys
+sys.path.insert(0, '..')
+import SCons_Bars
+
+revs = [
+ 1220, # Use WeakValueDicts in the Memoizer to reduce memory use.
+ 1307, # Move signature Node tranlation of rel_paths into the class.
+ 1435, # Fix Debug.caller() directory separators.
+ 1477, # Delay disambiguation of Node.FS.Entry into File/Dir.
+ 1655, # Reduce unnecessary calls to Node.FS.disambiguate().
+ 1703, # Lobotomize Memoizer.
+ 1727, # Cache Executor methods, reduce calls when scanning.
+ 2380, # The Big Signature Refactoring hits branches/core.
+]
+
+vertical_bars = SCons_Bars.Release_Bars.gnuplot(labels=True) + \
+ SCons_Bars.Revision_Bars.gnuplot(labels=False, revs=revs)