summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/man/scons.1120
-rw-r--r--src/CHANGES.txt3
-rw-r--r--src/engine/SCons/ActionTests.py3
-rw-r--r--src/engine/SCons/Defaults.py3
-rw-r--r--src/engine/SCons/Environment.py76
-rw-r--r--src/engine/SCons/EnvironmentTests.py5
-rw-r--r--src/engine/SCons/Tool/mslink.py26
-rw-r--r--src/engine/SCons/Tool/msvc.py75
-rw-r--r--test/msvc.py223
-rw-r--r--test/option--debug.py2
-rw-r--r--test/win32pathmadness.py8
11 files changed, 486 insertions, 58 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index 47e2141..34cbcd4 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -902,6 +902,19 @@ A synonym for the
.B StaticObject
builder.
+.IP PCH
+Builds a Microsoft Visual C++ precompiled header. Calling this builder
+returns a list of two targets: the PCH as the first element, and the object
+file as the second element. Normally the object file is ignored. This builder is only
+provided when Microsoft Visual C++ is being used as the compiler.
+The PCH builder is generally used in
+conjuction with the PCH construction variable to force object files to use
+the precompiled header:
+
+.ES
+env['PCH'] = env.PCH('StdAfx.cpp')[0]
+.EE
+
.IP Program
Builds an executable given one or more object files or C, C++
or Fortran source files.
@@ -1787,6 +1800,47 @@ The prefix used for (static) object file names.
.IP OBJSUFFIX
The suffix used for (static) object file names.
+.IP PCH
+The Microsoft Visual C++ precompiled header that will be used when compiling
+object files. This variable is ignored by tools other than Microsoft Visual C++.
+When this variable is
+defined SCons will add options to the compiler command line to
+cause it to use the precompiled header, and will also set up the
+dependencies for the PCH file. This variable must reference a File instance created either
+with File() or returned by the PCH builder:
+
+.ES
+env['PCH'] = env.PCH('StdAfx.cpp')[0]
+.EE
+
+.IP PCHSTOP
+This variable specifies how much of a source file is precompiled. This
+variable is ignored by tools other than Microsoft Visual C++, or when
+the PCH variable is not being used. When this variable is define it
+must be a string that is the name of the header that
+is included at the end of the precompiled portion of the source files, or
+the empty string if the "#pragma hrdstop" construct is being used:
+
+.ES
+env['PCHSTOP'] = File('StdAfx.h')
+.EE
+
+
+
+.IP PDB
+The Microsoft Visual C++ PDB file that will store debugging information for
+object files, shared libraries, and programs. This variable is ignored by
+tools other than Microsoft Visual C++.
+When this variable is
+defined SCons will add options to the compiler and linker command line to
+cause them to generate external debugging information, and will also set up the
+dependencies for the PDB file. This variable must reference
+a File instance created with File():
+
+.ES
+env['PDB'] = File('hello.pdb')
+.EE
+
.IP PDFCOM
The command line used to convert TeX DVI files into a PDF file.
@@ -3202,6 +3256,72 @@ CC: The C compiler.
.EE
+.SS Using Microsoft Visual C++ precompiled headers
+
+Since windows.h includes everything and the kitchen sink, it can take quite
+some time to compile it over and over again for a bunch of object files, so
+Microsoft provides a mechanism to compile a set of headers once and then
+include the previously compiled headers in any object file. This
+technology is called precompiled headers. The general recipe is to create a
+file named "StdAfx.cpp" that includes a single header named "StdAfx.h", and
+then include every header you want to precompile in "StdAfx.h", and finally
+include "StdAfx.h" as the first header in all the source files you are
+compiling to object files. For example:
+
+StdAfx.h:
+.ES
+#include <windows.h>
+#include <my_big_header.h>
+.EE
+
+StdAfx.cpp:
+.ES
+#include <StdAfx.h>
+.EE
+
+Foo.cpp:
+.ES
+#include <StdAfx.h>
+
+/* do some stuff */
+.EE
+
+Bar.cpp:
+.ES
+#include <StdAfx.h>
+
+/* do some other stuff */
+.EE
+
+SConstruct:
+.ES
+env=Environment()
+env['PCHSTOP'] = File('StdAfx.h')
+env['PCH'] = env.PCH('StdAfx.cpp')[0]
+env.Program('MyApp', ['Foo.cpp', 'Bar.cpp'])
+.EE
+
+For more information see the document for the PCH builder, and the PCH and
+PCHSTOP construction variables. To learn about the details of precompiled
+headers consult the MSDN documention for /Yc, /Yu, and /Yp.
+
+.SS Using Microsoft Visual C++ external debugging information
+
+Since including debugging information in programs and shared libraries can
+cause their size to increase significantly, Microsoft provides a mechanism
+for including the debugging information in an external file called a PDB
+file. SCons supports PDB files through the PDB construction
+variable.
+
+SConstruct:
+.ES
+env=Environment()
+env['PDB'] = File('MyApp.pdb')
+env.Program('MyApp', ['Foo.cpp', 'Bar.cpp'])
+.EE
+
+For more information see the document for the PDB construction variable.
+
.SH ENVIRONMENT
.IP SCONS_LIB_DIR
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 0d50628..838e178 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -79,6 +79,9 @@ RELEASE 0.09 -
- Add an Options() object for friendlier accomodation of command-
line arguments.
+ - Add support for Microsoft VC++ precompiled header (.pch)
+ and debugger (.pdb) files.
+
From sam th:
- Dynamically check for the existence of utilities with which to
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index 6ce52d5..289e7fe 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -40,7 +40,8 @@ import TestCmd
import UserDict
import SCons.Environment
-Environment = SCons.Environment.EnvProxy
+def Environment(dict):
+ return apply(SCons.Environment.Environment, (), dict)
class ActionTestCase(unittest.TestCase):
diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py
index d70be0d..4e8cf4e 100644
--- a/src/engine/SCons/Defaults.py
+++ b/src/engine/SCons/Defaults.py
@@ -133,6 +133,7 @@ ASPPAction = SCons.Action.Action([ StaticCheckSet, "$ASPPCOM" ])
def StaticObject():
"""A function for generating the static object Builder."""
return SCons.Builder.Builder(action = {},
+ emitter="$OBJEMITTER",
prefix = '$OBJPREFIX',
suffix = '$OBJSUFFIX',
src_builder = ['CFile', 'CXXFile'])
@@ -142,6 +143,7 @@ def SharedObject():
return SCons.Builder.Builder(action = {},
prefix = '$SHOBJPREFIX',
suffix = '$SHOBJSUFFIX',
+ emitter="$OBJEMITTER",
src_builder = ['CFile', 'CXXFile'])
ProgScan = SCons.Scanner.Prog.ProgScan()
@@ -176,6 +178,7 @@ def PDF():
suffix = '$PDFSUFFIX')
Program = SCons.Builder.Builder(action=[ StaticCheck, '$LINKCOM' ],
+ emitter='$PROGEMITTER',
prefix='$PROGPREFIX',
suffix='$PROGSUFFIX',
src_suffix='$OBJSUFFIX',
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 24315a1..1a1dd49 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -111,42 +111,6 @@ class BuilderDict(UserDict):
_rm = re.compile(r'\$[()]')
-class EnvProxy(UserDict):
- """This is a dictionary-like class that is returned
- by Environment.Override().
-
- In addition to providing
- normal dictionary-like access to the variables in the
- Environment, it also exposes the functions subst()
- and subst_list(), allowing users to easily do variable
- interpolation when writing their FunctionActions
- and CommandGeneratorActions."""
-
- def __init__(self, env):
- UserDict.__init__(self, env)
-
- def subst(self, string, raw=0):
- if raw:
- regex_remove = None
- else:
- regex_remove = _rm
- return SCons.Util.scons_subst(string, self.data, {}, regex_remove)
-
- def subst_list(self, string, raw=0):
- if raw:
- regex_remove = None
- else:
- regex_remove = _rm
- return SCons.Util.scons_subst_list(string, self.data, {}, regex_remove)
-
- def Override(self, overrides):
- if overrides:
- proxy = EnvProxy(self)
- proxy.update(overrides)
- return proxy
- else:
- return self
-
class Environment:
"""Base class for construction Environments. These are
the primary objects used to communicate dependency and
@@ -453,8 +417,8 @@ class Environment:
return side_effects[0]
else:
return side_effects
-
- def subst(self, string):
+
+ def subst(self, string, raw=0):
"""Recursively interpolates construction variables from the
Environment into the specified string, returning the expanded
result. Construction variables are specified by a $ prefix
@@ -464,12 +428,20 @@ class Environment:
may be surrounded by curly braces to separate the name from
trailing characters.
"""
- return SCons.Util.scons_subst(string, self._dict, {})
-
- def subst_list(self, string):
+ if raw:
+ regex_remove = None
+ else:
+ regex_remove = _rm
+ return SCons.Util.scons_subst(string, self._dict, {}, regex_remove)
+
+ def subst_list(self, string, raw=0):
"""Calls through to SCons.Util.scons_subst_list(). See
the documentation for that function."""
- return SCons.Util.scons_subst_list(string, self._dict, {})
+ if raw:
+ regex_remove = None
+ else:
+ regex_remove = _rm
+ return SCons.Util.scons_subst_list(string, self._dict, {}, regex_remove)
def get_scanner(self, skey):
"""Find the appropriate scanner given a key (usually a file suffix).
@@ -518,17 +490,23 @@ class Environment:
def Override(self, overrides):
"""
- Produce a modified psuedo-environment whose variables
+ Produce a modified environment whose variables
are overriden by the overrides dictionaries.
- overrides - a dictionaru that will override
+ overrides - a dictionary that will override
the variables of this environment.
+
+ This function is much more efficient than Copy()
+ or creating a new Environment because it doesn't do
+ a deep copy of the dictionary, and doesn't do a copy
+ at all if there are no overrides.
"""
if overrides:
- proxy = EnvProxy(self._dict)
- proxy.update(overrides)
- return proxy
+ env = copy.copy(self)
+ env._dict = copy.copy(self._dict)
+ env._dict.update(overrides)
+ return env
else:
return self
@@ -536,6 +514,10 @@ class Environment:
"Emulates the get() method of dictionaries."""
return self._dict.get(key, default)
+ def items(self):
+ "Emulates the items() method of dictionaries."""
+ return self._dict.items()
+
class VarInterpolator:
def __init__(self, dest, src, prefix, suffix):
self.dest = dest
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index 70e28ff..40c4f93 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -95,12 +95,17 @@ class Scanner:
class EnvironmentTestCase(unittest.TestCase):
def test_Override(self):
+ "Test overriding construction variables"
env = Environment(ONE=1, TWO=2)
assert env['ONE'] == 1
assert env['TWO'] == 2
env2 = env.Override({'TWO':'10'})
assert env2['ONE'] == 1
assert env2['TWO'] == '10'
+ assert env['TWO'] == 2
+ env2.Replace(ONE = "won")
+ assert env2['ONE'] == "won"
+ assert env['ONE'] == 1
def test_Builder_calls(self):
"""Test Builder calls through different environments
diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py
index 35bd868..3fdd4d8 100644
--- a/src/engine/SCons/Tool/mslink.py
+++ b/src/engine/SCons/Tool/mslink.py
@@ -40,6 +40,7 @@ import SCons.Defaults
import SCons.Errors
import SCons.Action
import SCons.Util
+import msvc
from SCons.Tool.msvc import get_msdev_paths
@@ -68,6 +69,10 @@ def win32TempFileMunge(env, cmd_list, for_signature):
def win32LinkGenerator(env, target, source, for_signature):
args = [ '$LINK', '$LINKFLAGS', '/OUT:%s' % target[0],
'$(', '$_LIBDIRFLAGS', '$)', '$_LIBFLAGS' ]
+
+ if env.has_key('PDB') and env['PDB']:
+ args.extend(['/PDB:%s'%env['PDB'], '/DEBUG'])
+
args.extend(map(SCons.Util.to_String, source))
return win32TempFileMunge(env, args, for_signature)
@@ -75,6 +80,9 @@ def win32LibGenerator(target, source, env, for_signature):
listCmd = [ "$SHLINK", "$SHLINKFLAGS" ]
no_import_lib = env.get('no_import_lib', 0)
+ if env.has_key('PDB') and env['PDB']:
+ listCmd.extend(['/PDB:%s'%env['PDB'], '/DEBUG'])
+
for tgt in target:
ext = os.path.splitext(str(tgt))[1]
if ext == env.subst("$LIBSUFFIX"):
@@ -94,9 +102,12 @@ def win32LibGenerator(target, source, env, for_signature):
else:
# Just treat it as a generic source file.
listCmd.append(str(src))
+
return win32TempFileMunge(env, listCmd, for_signature)
def win32LibEmitter(target, source, env):
+ msvc.validate_vars(env)
+
dll = None
no_import_lib = env.get('no_import_lib', 0)
@@ -116,6 +127,11 @@ def win32LibEmitter(target, source, env):
# append a def file to the list of sources
source.append("%s%s" % (os.path.splitext(str(dll))[0],
env.subst("$WIN32DEFSUFFIX")))
+
+ if env.has_key('PDB') and env['PDB']:
+ env.SideEffect(env['PDB'], target)
+ env.Precious(env['PDB'])
+
if not no_import_lib and \
not env.subst("$LIBSUFFIX") in \
map(lambda x: os.path.split(str(x))[1], target):
@@ -125,6 +141,15 @@ def win32LibEmitter(target, source, env):
env.subst("$LIBSUFFIX")))
return (target, source)
+def prog_emitter(target, source, env):
+ msvc.validate_vars(env)
+
+ if env.has_key('PDB') and env['PDB']:
+ env.SideEffect(env['PDB'], target)
+ env.Precious(env['PDB'])
+
+ return (target,source)
+
ShLibAction = SCons.Action.CommandGenerator(win32LibGenerator)
LinkAction = SCons.Action.CommandGenerator(win32LinkGenerator)
@@ -140,6 +165,7 @@ def generate(env, platform):
env['LINK'] = 'link'
env['LINKFLAGS'] = '/nologo'
env['LINKCOM'] = LinkAction
+ env['PROGEMITTER'] = prog_emitter
env['LIBDIRPREFIX']='/LIBPATH:'
env['LIBDIRSUFFIX']=''
env['LIBLINKPREFIX']=''
diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py
index 37d3e1d..1ba9ede 100644
--- a/src/engine/SCons/Tool/msvc.py
+++ b/src/engine/SCons/Tool/msvc.py
@@ -39,6 +39,8 @@ import string
import SCons.Action
import SCons.Tool
import SCons.Errors
+import SCons.Builder
+import SCons.Util
CSuffixes = ['.c', '.C']
CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
@@ -187,6 +189,62 @@ def get_msdev_paths(version=None):
exe_path = ''
return (include_path, lib_path, exe_path)
+def validate_vars(env):
+ """Validate the PDB, PCH, and PCHSTOP construction variables."""
+ if env.has_key('PDB') and env['PDB']:
+ if not isinstance(env['PDB'], SCons.Node.FS.File):
+ raise SCons.Errors.UserError, "The PDB construction variable must be a File instance: %s"%env['PDB']
+
+ if env.has_key('PCH') and env['PCH']:
+ if not isinstance(env['PCH'], SCons.Node.FS.File):
+ raise SCons.Errors.UserError, "The PCH construction variable must be a File instance: %s"%env['PCH']
+ if not env.has_key('PCHSTOP'):
+ raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
+ if not SCons.Util.is_String(env['PCHSTOP']):
+ raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']
+
+def pch_emitter(target, source, env):
+ """Sets up the PDB dependencies for a pch file, and adds the object
+ file target."""
+
+ validate_vars(env)
+
+ pch = None
+ obj = None
+
+ for t in target:
+ if os.path.splitext(str(t))[1] == '.pch':
+ pch = t
+ if os.path.splitext(str(t))[1] == '.obj':
+ obj = t
+
+ if not obj:
+ obj = os.path.splitext(str(pch))[0]+'.obj'
+
+ target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
+
+ if env.has_key('PDB') and env['PDB']:
+ env.SideEffect(env['PDB'], target)
+ env.Precious(env['PDB'])
+
+ return (target, source)
+
+def object_emitter(target, source, env):
+ """Sets up the PDB and PCH dependencies for an object file."""
+
+ validate_vars(env)
+
+ if env.has_key('PDB') and env['PDB']:
+ env.SideEffect(env['PDB'], target)
+ env.Precious(env['PDB'])
+
+ if env.has_key('PCH') and env['PCH']:
+ env.Depends(target, env['PCH'])
+
+ return (target, source)
+
+pch_builder = SCons.Builder.Builder(action='$PCHCOM', suffix='.pch', emitter=pch_emitter)
+
def generate(env, platform):
"""Add Builders and construction variables for MSVC++ to an Environment."""
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
@@ -199,20 +257,24 @@ def generate(env, platform):
static_obj.add_action(suffix, SCons.Defaults.CXXAction)
shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
+ env['CCPDBFLAGS'] = '${(PDB and "/Zi /Fd%s"%PDB) or ""}'
+ env['CCPCHFLAGS'] = '${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",PCH)) or ""}'
+ env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS'
env['CC'] = 'cl'
env['CCFLAGS'] = '/nologo'
- env['CCCOM'] = '$CC $CCFLAGS $CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET'
+ env['CCCOM'] = '$CC $CCFLAGS $CCCOMFLAGS'
env['SHCC'] = '$CC'
env['SHCCFLAGS'] = '$CCFLAGS'
- env['SHCCCOM'] = '$SHCC $SHCCFLAGS $CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET'
+ env['SHCCCOM'] = '$SHCC $SHCCFLAGS $CCCOMFLAGS'
env['CXX'] = '$CC'
env['CXXFLAGS'] = '$CCFLAGS'
- env['CXXCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET'
+ env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS'
env['SHCXX'] = '$CXX'
env['SHCXXFLAGS'] = '$CXXFLAGS'
- env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET'
+ env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS'
env['INCPREFIX'] = '/I'
- env['INCSUFFIX'] = ''
+ env['INCSUFFIX'] = ''
+ env['OBJEMITTER'] = object_emitter
include_path, lib_path, exe_path = get_msdev_paths()
env['ENV']['INCLUDE'] = include_path
@@ -221,5 +283,8 @@ def generate(env, platform):
env['CFILESUFFIX'] = '.c'
env['CXXFILESUFFIX'] = '.cc'
+ env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS'
+ env['BUILDERS']['PCH'] = pch_builder
+
def exists(env):
return env.Detect('cl')
diff --git a/test/msvc.py b/test/msvc.py
new file mode 100644
index 0000000..0e3c3c2
--- /dev/null
+++ b/test/msvc.py
@@ -0,0 +1,223 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import TestSCons
+import sys
+import os.path
+import os
+import TestCmd
+import time
+
+test = TestSCons.TestSCons(match = TestCmd.match_re)
+
+if sys.platform != 'win32':
+ test.pass_test()
+
+#####
+# Test the basics
+
+test.write('SConstruct',"""
+env=Environment()
+env['PCH'] = env.PCH('StdAfx.cpp')[0]
+env['PDB'] = File('test.pdb')
+env['PCHSTOP'] = 'StdAfx.h'
+env.Program('test', 'test.cpp')
+
+env.Object('fast', 'foo.cpp')
+env.Object('slow', 'foo.cpp', PCH=0)
+""")
+
+test.write('test.cpp', '''
+#include "StdAfx.h"
+
+int main(void)
+{
+ return 1;
+}
+''')
+
+test.write('foo.cpp', '''
+#include "StdAfx.h"
+''')
+
+test.write('StdAfx.h', '''
+#include <windows.h>
+''')
+
+test.write('StdAfx.cpp', '''
+#include "StdAfx.h"
+''')
+
+test.run(arguments='test.exe')
+
+test.fail_test(not os.path.exists(test.workpath('test.pdb')))
+test.fail_test(not os.path.exists(test.workpath('StdAfx.pch')))
+test.fail_test(not os.path.exists(test.workpath('StdAfx.obj')))
+
+test.run(arguments='-c .')
+
+test.fail_test(os.path.exists(test.workpath('test.pdb')))
+test.fail_test(os.path.exists(test.workpath('StdAfx.pch')))
+test.fail_test(os.path.exists(test.workpath('StdAfx.obj')))
+
+test.run(arguments='test.exe')
+
+test.fail_test(not os.path.exists(test.workpath('test.pdb')))
+test.fail_test(not os.path.exists(test.workpath('StdAfx.pch')))
+test.fail_test(not os.path.exists(test.workpath('StdAfx.obj')))
+
+test.run(arguments='-c test.pdb')
+test.fail_test(os.path.exists(test.workpath('test.exe')))
+test.fail_test(os.path.exists(test.workpath('test.obj')))
+test.fail_test(os.path.exists(test.workpath('test.pdb')))
+test.fail_test(os.path.exists(test.workpath('StdAfx.pch')))
+test.fail_test(os.path.exists(test.workpath('StdAfx.obj')))
+
+test.run(arguments='StdAfx.pch')
+
+test.fail_test(not os.path.exists(test.workpath('test.pdb')))
+test.fail_test(not os.path.exists(test.workpath('StdAfx.pch')))
+test.fail_test(not os.path.exists(test.workpath('StdAfx.obj')))
+
+start = time.time()
+test.run(arguments='fast.obj')
+fast = time.time() - start
+
+start = time.time()
+test.run(arguments='slow.obj')
+slow = time.time() - start
+
+# using precompiled headers should be significantly faster
+assert fast < slow*0.75
+
+
+##########
+# Test a hierarchical build
+
+test.subdir('src', 'build', 'out')
+
+test.write('SConstruct',"""
+BuildDir('build', 'src', duplicate=0)
+SConscript('build/SConscript')
+""")
+
+test.write('src/SConscript',"""
+env=Environment()
+env['PCH'] = env.PCH('StdAfx.cpp')[0]
+env['PDB'] = File('#out/test.pdb')
+env['PCHSTOP'] = 'StdAfx.h'
+env.Program('#out/test.exe', 'test.cpp')
+""")
+
+test.write('src/test.cpp', '''
+#include "StdAfx.h"
+
+int main(void)
+{
+ return 1;
+}
+''')
+
+test.write('src/StdAfx.h', '''
+#include <windows.h>
+''')
+
+test.write('src/StdAfx.cpp', '''
+#include "StdAfx.h"
+''')
+
+test.run(arguments='out')
+
+test.fail_test(not os.path.exists(test.workpath('out/test.pdb')))
+test.fail_test(not os.path.exists(test.workpath('build/StdAfx.pch')))
+test.fail_test(not os.path.exists(test.workpath('build/StdAfx.obj')))
+
+test.run(arguments='-c out')
+
+test.fail_test(os.path.exists(test.workpath('out/test.pdb')))
+test.fail_test(os.path.exists(test.workpath('build/StdAfx.pch')))
+test.fail_test(os.path.exists(test.workpath('build/StdAfx.obj')))
+
+#####
+# Test error reporting
+
+test.write('SConstruct',"""
+env=Environment()
+env['PCH'] = env.PCH('StdAfx.cpp')
+env['PDB'] = File('test.pdb')
+env['PCHSTOP'] = 'StdAfx.h'
+env.Program('test', 'test.cpp')
+""")
+
+test.run(status=2, stderr=r'''
+SCons error: The PCH construction variable must be a File instance: .+
+File "SConstruct", line 6, in \?
+''')
+
+test.write('SConstruct',"""
+env=Environment()
+env['PCH'] = env.PCH('StdAfx.cpp')[0]
+env['PDB'] = 'test.pdb'
+env['PCHSTOP'] = 'StdAfx.h'
+env.Program('test', 'test.cpp')
+""")
+
+test.run(status=2, stderr='''
+SCons error: The PDB construction variable must be a File instance: test.pdb
+File "SConstruct", line 6, in \?
+''')
+
+test.write('SConstruct',"""
+env=Environment()
+env['PCH'] = env.PCH('StdAfx.cpp')[0]
+env['PDB'] = File('test.pdb')
+env.Program('test', 'test.cpp')
+""")
+
+test.run(status=2, stderr='''
+SCons error: The PCHSTOP construction must be defined if PCH is defined.
+File "SConstruct", line 5, in \?
+''')
+
+test.write('SConstruct',"""
+env=Environment()
+env['PCH'] = env.PCH('StdAfx.cpp')[0]
+env['PDB'] = File('test.pdb')
+env['PCHSTOP'] = File('StdAfx.h')
+env.Program('test', 'test.cpp')
+""")
+
+test.run(status=2, stderr='''
+SCons error: The PCHSTOP construction variable must be a string: .+
+File "SConstruct", line 6, in \?
+''')
+
+test.pass_test()
+
+
+
+
+
diff --git a/test/option--debug.py b/test/option--debug.py
index 6561a05..d70cfc9 100644
--- a/test/option--debug.py
+++ b/test/option--debug.py
@@ -121,7 +121,7 @@ tree = """scons: \".\" is up to date.
+-foo.h
"""
test.run(arguments = "--debug=tree .")
-test.fail_test(string.find(test.stdout(), tree) != 0)
+test.fail_test(string.find(test.stdout(), tree) == -1)
test.run(arguments = "--debug=pdb", stdin = "n\ns\nq\n")
test.fail_test(string.find(test.stdout(), "(Pdb)") == -1)
diff --git a/test/win32pathmadness.py b/test/win32pathmadness.py
index cb6cb57..2e5b6e5 100644
--- a/test/win32pathmadness.py
+++ b/test/win32pathmadness.py
@@ -91,11 +91,11 @@ upper = os.path.join(string.upper(drive),rest)
lower = os.path.join(string.lower(drive),rest)
test.run(chdir=upper)
-test.run(chdir=lower, stdout="""\
+test.run(chdir=lower, stdout=test.wrap_stdout("""\
scons: .* is up to date.
scons: .* is up to date.
scons: .* is up to date.
-""")
+"""))
test.write('SConstruct', """
env=Environment()
@@ -122,10 +122,10 @@ test.write('b.h', """
""")
test.run(arguments='a.lib b.lib')
-test.run(arguments='b.lib a.lib', stdout="""\
+test.run(arguments='b.lib a.lib', stdout=test.wrap_stdout("""\
scons: .* is up to date.
scons: .* is up to date.
-""")
+"""))