Add MSVC PCH and PDB support. (Anthony Roach)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 3 Oct 2002 21:01:02 +0000 (21:01 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 3 Oct 2002 21:01:02 +0000 (21:01 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@473 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/man/scons.1
src/CHANGES.txt
src/engine/SCons/ActionTests.py
src/engine/SCons/Defaults.py
src/engine/SCons/Environment.py
src/engine/SCons/EnvironmentTests.py
src/engine/SCons/Tool/mslink.py
src/engine/SCons/Tool/msvc.py
test/msvc.py [new file with mode: 0644]
test/option--debug.py
test/win32pathmadness.py

index 47e21410908550c37af7b8274ff91594eaa72a33..34cbcd4cc45ae14d167c49a5bff2d2b780aa1b74 100644 (file)
@@ -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
index 0d50628714c787a0437dcac156ed5c2ee3c02914..838e178ecc2e4ab594ba5229ceb927897ba88b37 100644 (file)
@@ -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
index 6ce52d56219a8fd465acc8b8497b000deea14fa1..289e7fe4b4bb8847739934c4bf0312f93b4f9cba 100644 (file)
@@ -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):
 
index d70be0d28195be477e31058a17ae457ad53f6c7f..4e8cf4e8aff0cd2218d658ca9782ac7375890c1e 100644 (file)
@@ -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',
index 24315a10306365fb17e5b9bdf2721d8eee8b1d08..1a1dd49ca7c0791a335221d4c8263f5adfcc38e0 100644 (file)
@@ -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
index 70e28ffe2b3d157b1810041acc5893175f42bb92..40c4f932f8f6bfbad2bc502bd3bfda48f306f37e 100644 (file)
@@ -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
index 35bd86860a14526e32a6005f43316cb8dbfdc780..3fdd4d8bf3dd3fc68e8ba37834849f0f8478d2ae 100644 (file)
@@ -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']=''
index 37d3e1dcc080d412b7e73c24d9296fbfc9577aea..1ba9ede570b120c4fe695e005503db63a3d36338 100644 (file)
@@ -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 (file)
index 0000000..0e3c3c2
--- /dev/null
@@ -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()
+
+
+
+
+
index 6561a057fe9b5407323ded9ff4b450b0bc4bf55c..d70cfc995f1e0a0f277392ec6d6270a9f4ac5962 100644 (file)
@@ -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)
index cb6cb57a4b43e5f1f18bf8097e042dc629a2f70e..2e5b6e59ed2ace1ef462d217bde9395fd139045e 100644 (file)
@@ -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.
-""")
+"""))