Home | History | Annotate | Download | only in scons
      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