1 # -*- Python -*- 2 # Copyright 2008 Google Inc. All Rights Reserved. 3 # 4 # Redistribution and use in source and binary forms, with or without 5 # modification, are permitted provided that the following conditions are 6 # met: 7 # 8 # * Redistributions of source code must retain the above copyright 9 # notice, this list of conditions and the following disclaimer. 10 # * Redistributions in binary form must reproduce the above 11 # copyright notice, this list of conditions and the following disclaimer 12 # in the documentation and/or other materials provided with the 13 # distribution. 14 # * Neither the name of Google Inc. nor the names of its 15 # contributors may be used to endorse or promote products derived from 16 # this software without specific prior written permission. 17 # 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 # 30 # Author: joi (a] google.com (Joi Sigurdsson) 31 # Author: vladl (a] google.com (Vlad Losev) 32 # 33 # Shared SCons utilities for building Google Test inside and outside of 34 # Google's environment. 35 # 36 37 EnsurePythonVersion(2, 3) 38 39 40 BUILD_DIR_PREFIX = 'build' 41 42 43 class SConstructHelper: 44 def __init__(self): 45 # A dictionary to look up an environment by its name. 46 self.env_dict = {} 47 48 def Initialize(self, build_root_path, support_multiple_win_builds=False): 49 test_env = Environment() 50 platform = test_env['PLATFORM'] 51 if platform == 'win32': 52 if support_multiple_win_builds: 53 available_build_types = ['win-dbg8', 'win-opt8', 'win-dbg', 'win-opt'] 54 else: 55 available_build_types = ['win-dbg', 'win-opt'] 56 elif platform == 'darwin': # MacOSX 57 available_build_types = ['mac-dbg', 'mac-opt'] 58 else: 59 available_build_types = ['dbg', 'opt'] # Assuming POSIX-like environment 60 # with GCC by default. 61 62 vars = Variables() 63 vars.Add(ListVariable('BUILD', 'Build type', available_build_types[0], 64 available_build_types)) 65 vars.Add(BoolVariable('GTEST_BUILD_SAMPLES', 'Build samples', False)) 66 67 # Create base environment. 68 self.env_base = Environment(variables=vars, 69 BUILD_MODE={'BUILD' : '"${BUILD}"'}) 70 71 # Leave around a variable pointing at the build root so that SConscript 72 # files from outside our project root can find their bearings. Trick 73 # borrowed from Hammer in Software Construction Toolkit 74 # (http://code.google.com/p/swtoolkit/); if/when we switch to using the 75 # Hammer idioms instead of just Hammer's version of SCons, we should be 76 # able to remove this line. 77 self.env_base['SOURCE_ROOT'] = self.env_base.Dir(build_root_path) 78 79 # And another that definitely always points to the project root. 80 self.env_base['PROJECT_ROOT'] = self.env_base.Dir('.').abspath 81 82 # Enable scons -h 83 Help(vars.GenerateHelpText(self.env_base)) 84 85 class EnvCreator: 86 """Creates new customized environments from a base one.""" 87 88 def _Remove(cls, env, attribute, value): 89 """Removes the given attribute value from the environment.""" 90 91 attribute_values = env[attribute] 92 if value in attribute_values: 93 attribute_values.remove(value) 94 _Remove = classmethod(_Remove) 95 96 def Create(cls, base_env, modifier=None): 97 # User should NOT create more than one environment with the same 98 # modifier (including None). 99 env = base_env.Clone() 100 if modifier: 101 modifier(env) 102 else: 103 env['OBJ_SUFFIX'] = '' # Default suffix for unchanged environment. 104 return env; 105 Create = classmethod(Create) 106 107 # Each of the following methods modifies the environment for a particular 108 # purpose and can be used by clients for creating new environments. Each 109 # one needs to set the OBJ_SUFFIX variable to a unique suffix to 110 # differentiate targets built with that environment. Otherwise, SCons may 111 # complain about same target built with different settings. 112 113 def UseOwnTuple(cls, env): 114 """Instructs Google Test to use its internal implementation of tuple.""" 115 116 env['OBJ_SUFFIX'] = '_use_own_tuple' 117 env.Append(CPPDEFINES = 'GTEST_USE_OWN_TR1_TUPLE=1') 118 UseOwnTuple = classmethod(UseOwnTuple) 119 120 def WarningOk(cls, env): 121 """Does not treat warnings as errors. 122 123 Necessary for compiling gtest_unittest.cc, which triggers a gcc 124 warning when testing EXPECT_EQ(NULL, ptr).""" 125 126 env['OBJ_SUFFIX'] = '_warning_ok' 127 if env['PLATFORM'] == 'win32': 128 cls._Remove(env, 'CCFLAGS', '-WX') 129 else: 130 cls._Remove(env, 'CCFLAGS', '-Werror') 131 WarningOk = classmethod(WarningOk) 132 133 def WithExceptions(cls, env): 134 """Re-enables exceptions.""" 135 136 env['OBJ_SUFFIX'] = '_ex' 137 if env['PLATFORM'] == 'win32': 138 env.Append(CCFLAGS=['/EHsc']) 139 env.Append(CPPDEFINES='_HAS_EXCEPTIONS=1') 140 # Undoes the _TYPEINFO_ hack, which is unnecessary and only creates 141 # trouble when exceptions are enabled. 142 cls._Remove(env, 'CPPDEFINES', '_TYPEINFO_') 143 cls._Remove(env, 'CPPDEFINES', '_HAS_EXCEPTIONS=0') 144 else: 145 env.Append(CCFLAGS='-fexceptions') 146 cls._Remove(env, 'CCFLAGS', '-fno-exceptions') 147 WithExceptions = classmethod(WithExceptions) 148 149 def LessOptimized(cls, env): 150 """Disables certain optimizations on Windows. 151 152 We need to disable some optimization flags for some tests on 153 Windows; otherwise the redirection of stdout does not work 154 (apparently because of a compiler bug).""" 155 156 env['OBJ_SUFFIX'] = '_less_optimized' 157 if env['PLATFORM'] == 'win32': 158 for flag in ['/O1', '/Os', '/Og', '/Oy']: 159 cls._Remove(env, 'LINKFLAGS', flag) 160 LessOptimized = classmethod(LessOptimized) 161 162 def WithThreads(cls, env): 163 """Allows use of threads. 164 165 Currently only enables pthreads under GCC.""" 166 167 env['OBJ_SUFFIX'] = '_with_threads' 168 if env['PLATFORM'] != 'win32': 169 # Assuming POSIX-like environment with GCC. 170 # TODO(vladl (a] google.com): sniff presence of pthread_atfork instead of 171 # selecting on a platform. 172 env.Append(CCFLAGS=['-pthread']) 173 env.Append(LINKFLAGS=['-pthread']) 174 WithThreads = classmethod(WithThreads) 175 176 def NoRtti(cls, env): 177 """Disables RTTI support.""" 178 179 env['OBJ_SUFFIX'] = '_no_rtti' 180 if env['PLATFORM'] == 'win32': 181 env.Append(CCFLAGS=['/GR-']) 182 else: 183 env.Append(CCFLAGS=['-fno-rtti']) 184 env.Append(CPPDEFINES='GTEST_HAS_RTTI=0') 185 NoRtti = classmethod(NoRtti) 186 187 def AllowVc71StlWithoutExceptions(self, env): 188 env.Append( 189 CPPDEFINES = [# needed for using some parts of STL with exception 190 # disabled. The scoop is given here, with comments 191 # from P.J. Plauger at 192 # http://groups.google.com/group/microsoft.public.vc.stl/browse_thread/thread/5e719833c6bdb177?q=_HAS_EXCEPTIONS+using+namespace+std&pli=1 193 '_TYPEINFO_']) 194 195 def MakeWinBaseEnvironment(self): 196 win_base = self.env_base.Clone( 197 platform='win32', 198 CCFLAGS=['-GS', # Enable buffer security check 199 '-W4', # Warning level 200 201 # Disables warnings that are either uninteresting or 202 # hard to fix. 203 204 '-WX', # Treat warning as errors 205 #'-GR-', # Disable runtime type information 206 '-RTCs', # Enable stack-frame run-time error checks 207 '-RTCu', # Report when variable used without init. 208 #'-EHs', # enable C++ EH (no SEH exceptions) 209 '-nologo', # Suppress logo line 210 '-J', # All chars unsigned 211 #'-Wp64', # Detect 64-bit portability issues. This 212 # flag has been deprecated by VS 2008. 213 '-Zi', # Produce debug information in PDB files. 214 ], 215 CCPDBFLAGS='', 216 CPPDEFINES=['_UNICODE', 'UNICODE', 217 'WIN32', '_WIN32', 218 'STRICT', 219 'WIN32_LEAN_AND_MEAN', 220 '_HAS_EXCEPTIONS=0', 221 ], 222 LIBPATH=['#/$MAIN_DIR/lib'], 223 LINKFLAGS=['-MACHINE:x86', # Enable safe SEH (not supp. on x64) 224 '-DEBUG', # Generate debug info 225 '-NOLOGO', # Suppress logo line 226 ], 227 # All strings in string tables zero terminated. 228 RCFLAGS=['-n']) 229 230 return win_base 231 232 def SetBuildNameAndDir(self, env, name): 233 env['BUILD_NAME'] = name; 234 env['BUILD_DIR'] = '%s/%s' % (BUILD_DIR_PREFIX, name) 235 self.env_dict[name] = env 236 237 def MakeWinDebugEnvironment(self, base_environment, name): 238 """Takes a VC71 or VC80 base environment and adds debug settings.""" 239 debug_env = base_environment.Clone() 240 self.SetBuildNameAndDir(debug_env, name) 241 debug_env.Append( 242 CCFLAGS = ['-Od', # Disable optimizations 243 '-MTd', # Multithreaded, static link (debug) 244 # Path for PDB files 245 '-Fd%s\\' % debug_env.Dir(debug_env['BUILD_DIR']), 246 ], 247 CPPDEFINES = ['DEBUG', 248 '_DEBUG', 249 ], 250 LIBPATH = [], 251 LINKFLAGS = ['-INCREMENTAL:yes', 252 '/OPT:NOICF', 253 ] 254 ) 255 return debug_env 256 257 def MakeWinOptimizedEnvironment(self, base_environment, name): 258 """Takes a VC71 or VC80 base environment and adds release settings.""" 259 optimized_env = base_environment.Clone() 260 self.SetBuildNameAndDir(optimized_env, name) 261 optimized_env.Append( 262 CCFLAGS = ['-GL', # Enable link-time code generation (/GL) 263 '-GF', # Enable String Pooling (/GF) 264 '-MT', # Multithreaded, static link 265 # Path for PDB files 266 '-Fd%s\\' % optimized_env.Dir(optimized_env['BUILD_DIR']), 267 268 # Favor small code (this is /O1 minus /Og) 269 '-Os', 270 '-Oy', 271 '-Ob2', 272 '-Gs', 273 '-GF', 274 '-Gy', 275 ], 276 CPPDEFINES = ['NDEBUG', 277 '_NDEBUG', 278 ], 279 LIBPATH = [], 280 ARFLAGS = ['-LTCG'], # Link-time Code Generation 281 LINKFLAGS = ['-LTCG', # Link-time Code Generation 282 '-OPT:REF', # Optimize by reference. 283 '-OPT:ICF=32', # Optimize by identical COMDAT folding 284 '-OPT:NOWIN98', # Optimize by not aligning section for 285 # Win98 286 '-INCREMENTAL:NO', # No incremental linking as we don't 287 # want padding bytes in release build. 288 ], 289 ) 290 return optimized_env 291 292 def AddGccFlagsTo(self, env, optimized): 293 env.Append(CCFLAGS=['-fno-exceptions', 294 '-Wall', 295 '-Werror', 296 ]) 297 if optimized: 298 env.Append(CCFLAGS=['-O2'], CPPDEFINES=['NDEBUG', '_NDEBUG']) 299 else: 300 env.Append(CCFLAGS=['-g'], CPPDEFINES=['DEBUG', '_DEBUG']) 301 302 def ConfigureGccEnvironments(self): 303 # Mac environments. 304 mac_base = self.env_base.Clone(platform='darwin') 305 306 mac_dbg = mac_base.Clone() 307 self.AddGccFlagsTo(mac_dbg, optimized=False) 308 self.SetBuildNameAndDir(mac_dbg, 'mac-dbg') 309 310 mac_opt = mac_base.Clone() 311 self.AddGccFlagsTo(mac_opt, optimized=True) 312 self.SetBuildNameAndDir(mac_opt, 'mac-opt') 313 314 # Generic GCC environments. 315 gcc_dbg = self.env_base.Clone() 316 self.AddGccFlagsTo(gcc_dbg, optimized=False) 317 self.SetBuildNameAndDir(gcc_dbg, 'dbg') 318 319 gcc_opt = self.env_base.Clone() 320 self.AddGccFlagsTo(gcc_opt, optimized=True) 321 self.SetBuildNameAndDir(gcc_opt, 'opt') 322 323 def BuildSelectedEnvironments(self): 324 EnvCreator = SConstructHelper.EnvCreator 325 Export('EnvCreator') 326 # Build using whichever environments the 'BUILD' option selected 327 for build_name in self.env_base['BUILD']: 328 print 'BUILDING %s' % build_name 329 env = self.env_dict[build_name] 330 331 # Make sure SConscript files can refer to base build dir 332 env['MAIN_DIR'] = env.Dir(env['BUILD_DIR']) 333 334 #print 'CCFLAGS: %s' % env.subst('$CCFLAGS') 335 #print 'LINK: %s' % env.subst('$LINK') 336 #print 'AR: %s' % env.subst('$AR') 337 #print 'CC: %s' % env.subst('$CC') 338 #print 'CXX: %s' % env.subst('$CXX') 339 #print 'LIBPATH: %s' % env.subst('$LIBPATH') 340 #print 'ENV:PATH: %s' % env['ENV']['PATH'] 341 #print 'ENV:INCLUDE: %s' % env['ENV']['INCLUDE'] 342 #print 'ENV:LIB: %s' % env['ENV']['LIB'] 343 #print 'ENV:TEMP: %s' % env['ENV']['TEMP'] 344 345 Export('env') 346 # Invokes SConscript with variant_dir being build/<config name>. 347 # Counter-intuitively, src_dir is relative to the build dir and has 348 # to be '..' to point to the scons directory. 349 SConscript('SConscript', 350 src_dir='..', 351 variant_dir=env['BUILD_DIR'], 352 duplicate=0) 353 354 355 sconstruct_helper = SConstructHelper() 356 Return('sconstruct_helper') 357