| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/python2.4 | |
| 2 # Copyright 2008, Google Inc. | |
| 3 # All rights reserved. | |
| 4 # | |
| 5 # Redistribution and use in source and binary forms, with or without | |
| 6 # modification, are permitted provided that the following conditions are | |
| 7 # met: | |
| 8 # | |
| 9 # * Redistributions of source code must retain the above copyright | |
| 10 # notice, this list of conditions and the following disclaimer. | |
| 11 # * Redistributions in binary form must reproduce the above | |
| 12 # copyright notice, this list of conditions and the following disclaimer | |
| 13 # in the documentation and/or other materials provided with the | |
| 14 # distribution. | |
| 15 # * Neither the name of Google Inc. nor the names of its | |
| 16 # contributors may be used to endorse or promote products derived from | |
| 17 # this software without specific prior written permission. | |
| 18 # | |
| 19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 30 | |
| 31 """Software construction toolkit site_scons configuration. | |
| 32 | |
| 33 This module sets up SCons for use with this toolkit. This should contain setup | |
| 34 which occurs outside of environments. If a method operates within the context | |
| 35 of an environment, it should instead go in a tool in site_tools and be invoked | |
| 36 for the target environment. | |
| 37 """ | |
| 38 | |
| 39 import __builtin__ | |
| 40 import sys | |
| 41 import SCons | |
| 42 | |
| 43 | |
| 44 def _HostPlatform(): | |
| 45 """Returns the current host platform. | |
| 46 | |
| 47 That is, the platform we're actually running SCons on. You shouldn't use | |
| 48 this inside your SConscript files; instead, include the appropriate | |
| 49 target_platform tool for your environments. When you call | |
| 50 BuildEnvironments(), only environments with the current host platform will be | |
| 51 built. | |
| 52 | |
| 53 Returns: | |
| 54 The host platform name - one of ('WINDOWS', 'LINUX', 'MAC'). | |
| 55 """ | |
| 56 | |
| 57 platform_map = { | |
| 58 'win32': 'WINDOWS', | |
| 59 'cygwin': 'WINDOWS', | |
| 60 'linux': 'LINUX', | |
| 61 'linux2': 'LINUX', | |
| 62 'darwin': 'MAC', | |
| 63 } | |
| 64 | |
| 65 if sys.platform not in platform_map: | |
| 66 print ('site_init.py warning: platform "%s" is not in platfom map.' % | |
| 67 sys.platform) | |
| 68 | |
| 69 return platform_map.get(sys.platform, sys.platform) | |
| 70 | |
| 71 | |
| 72 #------------------------------------------------------------------------------ | |
| 73 | |
| 74 | |
| 75 def _CheckBuildModes(build_modes, environments, host_platform): | |
| 76 """Checks the build modes for the environments. | |
| 77 | |
| 78 Args: | |
| 79 build_modes: List of build mode strings. | |
| 80 environments: List of SCons environments. | |
| 81 host_platform: Host platform string. | |
| 82 | |
| 83 Raises: | |
| 84 ValueError: build groups and/or types invalid. | |
| 85 """ | |
| 86 # Make sure the list of environments for the current host platform have | |
| 87 # unique BUILD_TYPE. This ensures they won't overwrite each others' build | |
| 88 # output. (It is ok for build types in different host platforms to overlap; | |
| 89 # that is, WINDOWS and MAC can both have a 'dbg' build type.) | |
| 90 all_build_types = [] | |
| 91 all_build_groups = {} | |
| 92 build_desc = {} | |
| 93 for e in environments: | |
| 94 if not e.Overlap(e['HOST_PLATFORMS'], [host_platform, '*']): | |
| 95 continue | |
| 96 if e['BUILD_TYPE'] in all_build_types: | |
| 97 raise ValueError('Multiple environments have the same BUILD_TYPE=%s' % | |
| 98 e['BUILD_TYPE']) | |
| 99 else: | |
| 100 all_build_types.append(e['BUILD_TYPE']) | |
| 101 build_desc[e['BUILD_TYPE']] = e.get('BUILD_TYPE_DESCRIPTION') | |
| 102 | |
| 103 # Keep track of build groups and the build types which belong to them | |
| 104 for g in e['BUILD_GROUPS']: | |
| 105 if g not in all_build_groups: | |
| 106 all_build_groups[g] = [] | |
| 107 # Don't allow build types and groups to share names | |
| 108 if g in all_build_types: | |
| 109 raise ValueError('Build group %s also specified as BUILD_TYPE.' % g) | |
| 110 else: | |
| 111 all_build_types.append(g) | |
| 112 all_build_groups[g].append(e['BUILD_TYPE']) | |
| 113 | |
| 114 # Add help for build types | |
| 115 help_text = ''' | |
| 116 Use --mode=type to specify the type of build to perform. The following types | |
| 117 may be specified: | |
| 118 ''' | |
| 119 | |
| 120 for build_type in all_build_types: | |
| 121 if build_type not in all_build_groups: | |
| 122 help_text += ' %-16s %s\n' % ( | |
| 123 build_type, build_desc.get(build_type, '')) | |
| 124 | |
| 125 help_text += ''' | |
| 126 The following build groups may also be specified via --mode. Build groups | |
| 127 build one or more of the other build types. The available build groups are: | |
| 128 ''' | |
| 129 | |
| 130 groups_sorted = all_build_groups.keys() | |
| 131 groups_sorted.sort() | |
| 132 for g in groups_sorted: | |
| 133 help_text += ' %-16s %s\n' % (g, ','.join(all_build_groups[g])) | |
| 134 | |
| 135 help_text += ''' | |
| 136 Multiple modes may be specified, separated by commas: --mode=mode1,mode2. If | |
| 137 no mode is specified, the default group will be built. This is equivalent to | |
| 138 specifying --mode=default. | |
| 139 ''' | |
| 140 SCons.Script.Help(help_text) | |
| 141 | |
| 142 # Make sure all build modes specified by the user are ones which apply to | |
| 143 # the current environment. | |
| 144 for mode in build_modes: | |
| 145 if mode not in all_build_types and mode not in all_build_groups: | |
| 146 print ('Warning: Ignoring build mode "%s", which is not defined on this ' | |
| 147 'platform.' % mode) | |
| 148 | |
| 149 | |
| 150 #------------------------------------------------------------------------------ | |
| 151 | |
| 152 | |
| 153 def BuildEnvironmentSConscripts(env): | |
| 154 """Evaluates SConscripts for the environment. | |
| 155 | |
| 156 Called by BuildEnvironments(). | |
| 157 """ | |
| 158 # Read SConscript for each component | |
| 159 # TODO(rspangler): Remove BUILD_COMPONENTS once all projects have | |
| 160 # transitioned to the BUILD_SCONSCRIPTS nomenclature. | |
| 161 for c in env.SubstList2('$BUILD_SCONSCRIPTS', '$BUILD_COMPONENTS'): | |
| 162 # Clone the environment so components can't interfere with each other | |
| 163 ec = env.Clone() | |
| 164 | |
| 165 if ec.Entry(c).isdir(): | |
| 166 # The component is a directory, so assume it contains a SConscript | |
| 167 # file. | |
| 168 c_dir = ec.Dir(c) | |
| 169 | |
| 170 # Use 'build.scons' as the default filename, but if that doesn't | |
| 171 # exist, fall back to 'SConscript'. | |
| 172 c_script = c_dir.File('build.scons') | |
| 173 if not c_script.exists(): | |
| 174 c_script = c_dir.File('SConscript') | |
| 175 else: | |
| 176 # The component is a SConscript file. | |
| 177 c_script = ec.File(c) | |
| 178 c_dir = c_script.dir | |
| 179 | |
| 180 # Make c_dir a string. | |
| 181 c_dir = str(c_dir) | |
| 182 | |
| 183 # Use build_dir differently depending on where the SConscript is. | |
| 184 if not ec.RelativePath('$TARGET_ROOT', c_dir).startswith('..'): | |
| 185 # The above expression means: if c_dir is $TARGET_ROOT or anything | |
| 186 # under it. Going from c_dir to $TARGET_ROOT and dropping the not fails | |
| 187 # to include $TARGET_ROOT. | |
| 188 # We want to be able to allow people to use addRepository to back things | |
| 189 # under $TARGET_ROOT/$OBJ_ROOT with things from above the current | |
| 190 # directory. When we are passed a SConscript that is already under | |
| 191 # $TARGET_ROOT, we should not use build_dir. | |
| 192 ec.SConscript(c_script, exports={'env': ec}, duplicate=0) | |
| 193 elif not ec.RelativePath('$MAIN_DIR', c_dir).startswith('..'): | |
| 194 # The above expression means: if c_dir is $MAIN_DIR or anything | |
| 195 # under it. Going from c_dir to $TARGET_ROOT and dropping the not fails | |
| 196 # to include $MAIN_DIR. | |
| 197 # Also, if we are passed a SConscript that | |
| 198 # is not under $MAIN_DIR, we should fail loudly, because it is unclear how | |
| 199 # this will correspond to things under $OBJ_ROOT. | |
| 200 ec.SConscript(c_script, build_dir='$OBJ_ROOT/' + c_dir, | |
| 201 exports={'env': ec}, duplicate=0) | |
| 202 else: | |
| 203 raise SCons.Error.UserError( | |
| 204 'Bad location for a SConscript. "%s" is not under ' | |
| 205 '\$TARGET_ROOT or \$MAIN_DIR' % c_script) | |
| 206 | |
| 207 | |
| 208 def BuildEnvironments(environments): | |
| 209 """Build a collection of SConscripts under a collection of environments. | |
| 210 | |
| 211 Only environments with HOST_PLATFORMS containing the platform specified by | |
| 212 --host-platform (or the native host platform, if --host-platform was not | |
| 213 specified) will be matched. | |
| 214 | |
| 215 Each matching environment is checked against the modes passed to the --mode | |
| 216 command line argument (or 'default', if no mode(s) were specified). If any | |
| 217 of the modes match the environment's BUILD_TYPE or any of the environment's | |
| 218 BUILD_GROUPS, all the BUILD_SCONSCRIPTS (and for legacy reasons, | |
| 219 BUILD_COMPONENTS) in that environment will be built. | |
| 220 | |
| 221 Args: | |
| 222 environments: List of SCons environments. | |
| 223 | |
| 224 Returns: | |
| 225 List of environments which were actually evaluated (built). | |
| 226 """ | |
| 227 # Get options | |
| 228 build_modes = SCons.Script.GetOption('build_mode') | |
| 229 # TODO(rspangler): Remove support legacy MODE= argument, once everyone has | |
| 230 # transitioned to --mode. | |
| 231 legacy_mode_option = SCons.Script.ARGUMENTS.get('MODE') | |
| 232 if legacy_mode_option: | |
| 233 build_modes = legacy_mode_option | |
| 234 build_modes = build_modes.split(',') | |
| 235 | |
| 236 host_platform = SCons.Script.GetOption('host_platform') | |
| 237 if not host_platform: | |
| 238 host_platform = _HostPlatform() | |
| 239 | |
| 240 # Check build modes | |
| 241 _CheckBuildModes(build_modes, environments, host_platform) | |
| 242 | |
| 243 environments_to_evaluate = [] | |
| 244 for e in environments: | |
| 245 if not e.Overlap(e['HOST_PLATFORMS'], [host_platform, '*']): | |
| 246 continue # Environment requires a host platform which isn't us | |
| 247 | |
| 248 if e.Overlap([e['BUILD_TYPE'], e['BUILD_GROUPS']], build_modes): | |
| 249 environments_to_evaluate.append(e) | |
| 250 | |
| 251 for e in environments_to_evaluate: | |
| 252 # Make this the root environment for deferred functions, so they don't | |
| 253 # execute until our call to ExecuteDefer(). | |
| 254 e.SetDeferRoot() | |
| 255 | |
| 256 # Defer building the SConscripts, so that other tools can do | |
| 257 # per-environment setup first. | |
| 258 e.Defer(BuildEnvironmentSConscripts) | |
| 259 | |
| 260 # Execute deferred functions | |
| 261 e.ExecuteDefer() | |
| 262 | |
| 263 # Add help on targets. | |
| 264 AddTargetHelp() | |
| 265 | |
| 266 # Return list of environments actually evaluated | |
| 267 return environments_to_evaluate | |
| 268 | |
| 269 | |
| 270 #------------------------------------------------------------------------------ | |
| 271 | |
| 272 | |
| 273 def _ToolExists(): | |
| 274 """Replacement for SCons tool module exists() function, if one isn't present. | |
| 275 | |
| 276 Returns: | |
| 277 True. This enables modules which always exist not to need to include a | |
| 278 dummy exists() function. | |
| 279 """ | |
| 280 return True | |
| 281 | |
| 282 | |
| 283 def _ToolModule(self): | |
| 284 """Thunk for SCons.Tool.Tool._tool_module to patch in exists() function. | |
| 285 | |
| 286 Returns: | |
| 287 The module from the original SCons.Tool.Tool._tool_module call, with an | |
| 288 exists() method added if it wasn't present. | |
| 289 """ | |
| 290 module = self._tool_module_orig() | |
| 291 if not hasattr(module, 'exists'): | |
| 292 module.exists = _ToolExists | |
| 293 | |
| 294 return module | |
| 295 | |
| 296 #------------------------------------------------------------------------------ | |
| 297 | |
| 298 | |
| 299 def AddSiteDir(site_dir): | |
| 300 """Adds a site directory, as if passed to the --site-dir option. | |
| 301 | |
| 302 Args: | |
| 303 site_dir: Site directory path to add, relative to the location of the | |
| 304 SConstruct file. | |
| 305 | |
| 306 This may be called from the SConscript file to add a local site scons | |
| 307 directory for a project. This does the following: | |
| 308 * Adds site_dir/site_scons to sys.path. | |
| 309 * Imports site_dir/site_init.py. | |
| 310 * Adds site_dir/site_scons to the SCons tools path. | |
| 311 """ | |
| 312 # Call the same function that SCons does for the --site-dir option. | |
| 313 SCons.Script.Main._load_site_scons_dir( | |
| 314 SCons.Node.FS.get_default_fs().SConstruct_dir, site_dir) | |
| 315 | |
| 316 | |
| 317 #------------------------------------------------------------------------------ | |
| 318 | |
| 319 | |
| 320 _new_options_help = ''' | |
| 321 Additional options for SCons: | |
| 322 | |
| 323 --mode=MODE Specify build mode (see below). | |
| 324 --host-platform=PLATFORM Force SCons to use PLATFORM as the host platform, | |
| 325 instead of the actual platform on which SCons is | |
| 326 run. Useful for examining the dependency tree | |
| 327 which would be created, but not useful for | |
| 328 actually running the build because it'll attempt | |
| 329 to use the wrong tools for your actual platform. | |
| 330 --site-path=DIRLIST Comma-separated list of additional site | |
| 331 directory paths; each is processed as if passed | |
| 332 to --site-dir. | |
| 333 ''' | |
| 334 | |
| 335 def SiteInitMain(): | |
| 336 """Main code executed in site_init.""" | |
| 337 | |
| 338 # Bail out if we've been here before. This is needed to handle the case where | |
| 339 # this site_init.py has been dropped into a project directory. | |
| 340 if hasattr(__builtin__, 'BuildEnvironments'): | |
| 341 return | |
| 342 | |
| 343 # Let people use new global methods directly. | |
| 344 __builtin__.AddSiteDir = AddSiteDir | |
| 345 __builtin__.BuildEnvironments = BuildEnvironments | |
| 346 # Legacy method names | |
| 347 # TODO(rspangler): Remove these once they're no longer used anywhere. | |
| 348 __builtin__.BuildComponents = BuildEnvironments | |
| 349 | |
| 350 | |
| 351 # Set list of default tools for component_setup | |
| 352 __builtin__.component_setup_tools = [ | |
| 353 # Defer must be first so other tools can register environment | |
| 354 # setup/cleanup functions. | |
| 355 'defer', | |
| 356 # Component_targets must precede component_builders so builders can | |
| 357 # define target groups. | |
| 358 'component_targets', | |
| 359 'command_output', | |
| 360 'component_bits', | |
| 361 'component_builders', | |
| 362 'concat_source', | |
| 363 'environment_tools', | |
| 364 'publish', | |
| 365 'replicate', | |
| 366 ] | |
| 367 | |
| 368 # Patch Tool._tool_module method to fill in an exists() method for the | |
| 369 # module if it isn't present. | |
| 370 # TODO(sgk): This functionality should be patched into SCons itself by | |
| 371 # changing Tool.__init__(). | |
| 372 SCons.Tool.Tool._tool_module_orig = SCons.Tool.Tool._tool_module | |
| 373 SCons.Tool.Tool._tool_module = _ToolModule | |
| 374 | |
| 375 # Add our options | |
| 376 SCons.Script.AddOption( | |
| 377 '--mode', '--build-mode', | |
| 378 dest='build_mode', | |
| 379 nargs=1, type='string', | |
| 380 action='store', | |
| 381 metavar='MODE', | |
| 382 default='default', | |
| 383 help='build mode(s)') | |
| 384 SCons.Script.AddOption( | |
| 385 '--host-platform', | |
| 386 dest='host_platform', | |
| 387 nargs=1, type='string', | |
| 388 action='store', | |
| 389 metavar='PLATFORM', | |
| 390 help='build mode(s)') | |
| 391 SCons.Script.AddOption( | |
| 392 '--site-path', | |
| 393 dest='site_path', | |
| 394 nargs=1, type='string', | |
| 395 action='store', | |
| 396 metavar='PATH', | |
| 397 help='comma-separated list of site directories') | |
| 398 | |
| 399 SCons.Script.Help(_new_options_help) | |
| 400 | |
| 401 # Check for site path. This is a list of site directories which each are | |
| 402 # processed as if they were passed to --site-dir. | |
| 403 site_path = SCons.Script.GetOption('site_path') | |
| 404 if site_path: | |
| 405 for site_dir in site_path.split(','): | |
| 406 AddSiteDir(site_dir) | |
| 407 | |
| 408 # Since our site dir was specified on the SCons command line, SCons will | |
| 409 # normally only look at our site dir. Add back checking for project-local | |
| 410 # site_scons directories. | |
| 411 if not SCons.Script.GetOption('no_site_dir'): | |
| 412 SCons.Script.Main._load_site_scons_dir( | |
| 413 SCons.Node.FS.get_default_fs().SConstruct_dir, None) | |
| 414 | |
| 415 # Run main code | |
| 416 SiteInitMain() | |
| OLD | NEW |