| 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 # List of target groups for printing help; modified by AddTargetGroup(); used |
| 45 # by BuildComponents(). |
| 46 __target_groups = {} |
| 47 |
| 48 |
| 49 def _HostPlatform(): |
| 50 """Returns the current host platform. |
| 51 |
| 52 That is, the platform we're actually running SCons on. You shouldn't use |
| 53 this inside your SConscript files; instead, include the appropriate |
| 54 target_platform tool for your environments. When you call BuildComponents(), |
| 55 only environments with the current host platform will be built. |
| 56 |
| 57 Returns: |
| 58 The host platform name - one of ('WINDOWS', 'LINUX', 'MAC'). |
| 59 """ |
| 60 |
| 61 platform_map = { |
| 62 'win32': 'WINDOWS', |
| 63 'cygwin': 'WINDOWS', |
| 64 'linux': 'LINUX', |
| 65 'linux2': 'LINUX', |
| 66 'darwin': 'MAC', |
| 67 } |
| 68 |
| 69 if sys.platform not in platform_map: |
| 70 print ('site_init.py warning: platform "%s" is not in platfom map.' % |
| 71 sys.platform) |
| 72 |
| 73 return platform_map.get(sys.platform, sys.platform) |
| 74 |
| 75 |
| 76 #------------------------------------------------------------------------------ |
| 77 |
| 78 |
| 79 def _CheckBuildModes(build_modes, environments, host_platform): |
| 80 """Checks the build modes for the environments. |
| 81 |
| 82 Args: |
| 83 build_modes: List of build mode strings. |
| 84 environments: List of SCons environments. |
| 85 host_platform: Host platform string. |
| 86 |
| 87 Raises: |
| 88 ValueError: build groups and/or types invalid. |
| 89 """ |
| 90 # Make sure the list of environments for the current host platform have |
| 91 # unique BUILD_TYPE. This ensures they won't overwrite each others' build |
| 92 # output. (It is ok for build types in different host platforms to overlap; |
| 93 # that is, WINDOWS and MAC can both have a 'dbg' build type.) |
| 94 all_build_types = [] |
| 95 all_build_groups = {} |
| 96 build_desc = {} |
| 97 for e in environments: |
| 98 if not e.Overlap(e['HOST_PLATFORMS'], [host_platform, '*']): |
| 99 continue |
| 100 if e['BUILD_TYPE'] in all_build_types: |
| 101 raise ValueError('Multiple environments have the same BUILD_TYPE=%s' % |
| 102 e['BUILD_TYPE']) |
| 103 else: |
| 104 all_build_types.append(e['BUILD_TYPE']) |
| 105 build_desc[e['BUILD_TYPE']] = e.get('BUILD_TYPE_DESCRIPTION') |
| 106 |
| 107 # Keep track of build groups and the build types which belong to them |
| 108 for g in e['BUILD_GROUPS']: |
| 109 if g not in all_build_groups: |
| 110 all_build_groups[g] = [] |
| 111 # Don't allow build types and groups to share names |
| 112 if g in all_build_types: |
| 113 raise ValueError('Build group %s also specified as BUILD_TYPE.' % g) |
| 114 else: |
| 115 all_build_types.append(g) |
| 116 all_build_groups[g].append(e['BUILD_TYPE']) |
| 117 |
| 118 # Add help for build types |
| 119 xml_help = SCons.Script.GetOption('xml_help') |
| 120 if xml_help: |
| 121 help_mode_format = ' <build_mode name="%s"><![CDATA[%s]]></build_mode>\n' |
| 122 help_text = '<mode_list>\n' |
| 123 else: |
| 124 help_text = ''' |
| 125 Use --mode=type to specify the type of build to perform. The following types |
| 126 may be specified: |
| 127 ''' |
| 128 help_mode_format = ' %-16s %s\n' |
| 129 |
| 130 for build_type in all_build_types: |
| 131 if build_type not in all_build_groups: |
| 132 help_text += help_mode_format % ( |
| 133 build_type, build_desc.get(build_type, '')) |
| 134 |
| 135 if xml_help: |
| 136 help_group_format = (' <build_group name="%s"><![CDATA[%s]]>' |
| 137 '</build_group>\n') |
| 138 help_text += '</mode_list>\n<group_list>\n' |
| 139 else: |
| 140 help_group_format = ' %-16s %s\n' |
| 141 help_text += ''' |
| 142 The following build groups may also be specified via --mode. Build groups |
| 143 build one or more of the other build types. The available build groups are: |
| 144 ''' |
| 145 |
| 146 groups_sorted = all_build_groups.keys() |
| 147 groups_sorted.sort() |
| 148 for g in groups_sorted: |
| 149 help_text += help_group_format % (g, ','.join(all_build_groups[g])) |
| 150 |
| 151 if xml_help: |
| 152 help_text += '</group_list>\n' |
| 153 else: |
| 154 help_text += ''' |
| 155 Multiple modes may be specified, separated by commas: --mode=mode1,mode2. If |
| 156 no mode is specified, the default group will be built. This is equivalent to |
| 157 specifying --mode=default. |
| 158 ''' |
| 159 SCons.Script.Help(help_text) |
| 160 |
| 161 # Make sure all build modes specified by the user are ones which apply to |
| 162 # the current environment. |
| 163 for mode in build_modes: |
| 164 if mode not in all_build_types and mode not in all_build_groups: |
| 165 print ('Warning: Ignoring build mode "%s", which is not defined on this ' |
| 166 'platform.' % mode) |
| 167 |
| 168 |
| 169 def _AddTargetHelp(): |
| 170 """Adds help for the target groups from the global __target_groups.""" |
| 171 xml_help = SCons.Script.GetOption('xml_help') |
| 172 help_text = '' |
| 173 |
| 174 for alias, description in __target_groups.items(): |
| 175 items = map(str, SCons.Script.Alias(alias)[0].sources) |
| 176 # Remove duplicates from multiple environments |
| 177 items = list(set(items)) |
| 178 |
| 179 if items: |
| 180 colwidth = max(map(len, items)) + 2 |
| 181 cols = 77 / colwidth |
| 182 rows = (len(items) + cols - 1) / cols |
| 183 items.sort() |
| 184 if xml_help: |
| 185 help_text += '<target_group name="%s">\n' % alias |
| 186 for i in items: |
| 187 help_text += ' <build_target name="%s"/>\n' % i |
| 188 help_text += '</target_group>\n' |
| 189 else: |
| 190 help_text += '\nThe following %s:' % description |
| 191 for row in range(0, rows): |
| 192 help_text += '\n ' |
| 193 for i in range(row, len(items), rows): |
| 194 help_text += '%-*s' % (colwidth, items[i]) |
| 195 help_text += '\n %s (do all of the above)\n' % alias |
| 196 |
| 197 SCons.Script.Help(help_text) |
| 198 |
| 199 #------------------------------------------------------------------------------ |
| 200 |
| 201 |
| 202 def BuildComponents(environments): |
| 203 """Build a collection of components under a collection of environments. |
| 204 |
| 205 Only environments with HOST_PLATFORMS containing the platform specified by |
| 206 --host-platform (or the native host platform, if --host-platform was not |
| 207 specified) will be matched. |
| 208 |
| 209 Each matching environment is checked against the modes passed to the --mode |
| 210 command line argument (or 'default', if no mode(s) were specified). If any |
| 211 of the modes match the environment's BUILD_TYPE or any of the environment's |
| 212 BUILD_GROUPS, all the BUILD_COMPONENTS and BUILD_SCONSCRIPTS in that |
| 213 environment will be built. |
| 214 |
| 215 Args: |
| 216 environments: List of SCons environments. |
| 217 """ |
| 218 # Get options |
| 219 xml_help = SCons.Script.GetOption('xml_help') |
| 220 build_modes = SCons.Script.GetOption('build_mode') |
| 221 # TODO(rspangler): Remove support legacy MODE= argument, once everyone has |
| 222 # transitioned to --mode. |
| 223 legacy_mode_option = SCons.Script.ARGUMENTS.get('MODE') |
| 224 if legacy_mode_option: |
| 225 build_modes = legacy_mode_option |
| 226 build_modes = build_modes.split(',') |
| 227 |
| 228 host_platform = SCons.Script.GetOption('host_platform') |
| 229 if not host_platform: |
| 230 host_platform = _HostPlatform() |
| 231 |
| 232 # Check build modes |
| 233 _CheckBuildModes(build_modes, environments, host_platform) |
| 234 |
| 235 if xml_help: |
| 236 SCons.Script.Help('<help_from_sconscripts>\n<![CDATA[\n') |
| 237 |
| 238 for e in environments: |
| 239 if not e.Overlap(e['HOST_PLATFORMS'], [host_platform, '*']): |
| 240 continue # Environment requires a host platform which isn't us |
| 241 |
| 242 if e.Overlap([e['BUILD_TYPE'], e['BUILD_GROUPS']], build_modes): |
| 243 # Set up for deferred functions and published resources |
| 244 e._InitializeComponentBuilders() |
| 245 e._InitializeDefer() |
| 246 e._InitializePublish() |
| 247 |
| 248 # Read SConscript for each component |
| 249 # TODO(rspangler): Remove BUILD_COMPONENTS once all projects have |
| 250 # transitioned to the BUILD_SCONSCRIPTS nomenclature. |
| 251 for c in e.get('BUILD_COMPONENTS', []) + e.get('BUILD_SCONSCRIPTS', []): |
| 252 # Clone the environment so components can't interfere with each other |
| 253 ec = e.Clone() |
| 254 |
| 255 if ec.Entry(c).isdir(): |
| 256 # The component is a directory, so assume it contains a SConscript |
| 257 # file. |
| 258 c_dir = ec.Dir(c) |
| 259 |
| 260 # Use 'build.scons' as the default filename, but if that doesn't |
| 261 # exist, fall back to 'SConscript'. |
| 262 c_script = c_dir.File('build.scons') |
| 263 if not c_script.exists(): |
| 264 c_script = c_dir.File('SConscript') |
| 265 else: |
| 266 # The component is a SConscript file. |
| 267 c_script = ec.File(c) |
| 268 c_dir = c_script.dir |
| 269 |
| 270 ec.SConscript(c_script, |
| 271 build_dir='$OBJ_ROOT/' + str(c_dir), |
| 272 exports={'env': ec}, |
| 273 duplicate=0) |
| 274 |
| 275 # Execute deferred functions |
| 276 e._ExecuteDefer() |
| 277 |
| 278 if xml_help: |
| 279 SCons.Script.Help(']]>\n</help_from_sconscripts>\n') |
| 280 |
| 281 _AddTargetHelp() |
| 282 |
| 283 # End final help tag |
| 284 if xml_help: |
| 285 SCons.Script.Help('</help>\n') |
| 286 |
| 287 |
| 288 #------------------------------------------------------------------------------ |
| 289 |
| 290 |
| 291 def _ToolExists(): |
| 292 """Replacement for SCons tool module exists() function, if one isn't present. |
| 293 |
| 294 Returns: |
| 295 True. This enables modules which always exist not to need to include a |
| 296 dummy exists() function. |
| 297 """ |
| 298 return True |
| 299 |
| 300 |
| 301 def _ToolModule(self): |
| 302 """Thunk for SCons.Tool.Tool._tool_module to patch in exists() function. |
| 303 |
| 304 Returns: |
| 305 The module from the original SCons.Tool.Tool._tool_module call, with an |
| 306 exists() method added if it wasn't present. |
| 307 """ |
| 308 module = self._tool_module_orig() |
| 309 if not hasattr(module, 'exists'): |
| 310 module.exists = _ToolExists |
| 311 |
| 312 return module |
| 313 |
| 314 #------------------------------------------------------------------------------ |
| 315 |
| 316 |
| 317 def AddSiteDir(site_dir): |
| 318 """Adds a site directory, as if passed to the --site-dir option. |
| 319 |
| 320 Args: |
| 321 site_dir: Site directory path to add, relative to the location of the |
| 322 SConstruct file. |
| 323 |
| 324 This may be called from the SConscript file to add a local site scons |
| 325 directory for a project. This does the following: |
| 326 * Adds site_dir/site_scons to sys.path. |
| 327 * Imports site_dir/site_init.py. |
| 328 * Adds site_dir/site_scons to the SCons tools path. |
| 329 """ |
| 330 # Call the same function that SCons does for the --site-dir option. |
| 331 SCons.Script.Main._load_site_scons_dir( |
| 332 SCons.Node.FS.get_default_fs().SConstruct_dir, site_dir) |
| 333 |
| 334 |
| 335 def AddTargetGroup(target_group, description): |
| 336 """Adds a target group, used for printing help. |
| 337 |
| 338 Args: |
| 339 target_group: Name of target group. This should be the name of an alias |
| 340 which points to other aliases for the specific targets. |
| 341 description: Description of the target group. |
| 342 """ |
| 343 |
| 344 __target_groups[target_group] = description |
| 345 |
| 346 #------------------------------------------------------------------------------ |
| 347 |
| 348 |
| 349 _new_options_help = ''' |
| 350 Additional options for SCons: |
| 351 |
| 352 --mode=MODE Specify build mode (see below). |
| 353 --host-platform=PLATFORM Force SCons to use PLATFORM as the host platform, |
| 354 instead of the actual platform on which SCons is |
| 355 run. Useful for examining the dependency tree |
| 356 which would be created, but not useful for |
| 357 actually running the build because it'll attempt |
| 358 to use the wrong tools for your actual platform. |
| 359 --site-path=DIRLIST Comma-separated list of additional site |
| 360 directory paths; each is processed as if passed |
| 361 to --site-dir. |
| 362 --xml-help Print help in XML format. |
| 363 ''' |
| 364 |
| 365 def SiteInitMain(): |
| 366 """Main code executed in site_init.""" |
| 367 |
| 368 # Let people use new global methods directly. |
| 369 __builtin__.AddSiteDir = AddSiteDir |
| 370 __builtin__.BuildComponents = BuildComponents |
| 371 __builtin__.AddTargetGroup = AddTargetGroup |
| 372 |
| 373 # Set list of default tools for component_setup |
| 374 __builtin__.component_setup_tools = [ |
| 375 'command_output', |
| 376 'component_bits', |
| 377 'component_builders', |
| 378 'concat_source', |
| 379 'defer', |
| 380 'environment_tools', |
| 381 'publish', |
| 382 'replicate', |
| 383 ] |
| 384 |
| 385 # Patch Tool._tool_module method to fill in an exists() method for the |
| 386 # module if it isn't present. |
| 387 # TODO(sgk): This functionality should be patched into SCons itself by |
| 388 # changing Tool.__init__(). |
| 389 SCons.Tool.Tool._tool_module_orig = SCons.Tool.Tool._tool_module |
| 390 SCons.Tool.Tool._tool_module = _ToolModule |
| 391 |
| 392 # Add our options |
| 393 SCons.Script.AddOption( |
| 394 '--mode', '--build-mode', |
| 395 dest='build_mode', |
| 396 nargs=1, type='string', |
| 397 action='store', |
| 398 metavar='MODE', |
| 399 default='default', |
| 400 help='build mode(s)') |
| 401 SCons.Script.AddOption( |
| 402 '--host-platform', |
| 403 dest='host_platform', |
| 404 nargs=1, type='string', |
| 405 action='store', |
| 406 metavar='PLATFORM', |
| 407 help='build mode(s)') |
| 408 SCons.Script.AddOption( |
| 409 '--site-path', |
| 410 dest='site_path', |
| 411 nargs=1, type='string', |
| 412 action='store', |
| 413 metavar='PATH', |
| 414 help='comma-separated list of site directories') |
| 415 SCons.Script.AddOption( |
| 416 '--xml-help', |
| 417 dest='xml_help', |
| 418 action='store_true', |
| 419 help='print help in XML format') |
| 420 |
| 421 if SCons.Script.GetOption('xml_help'): |
| 422 SCons.Script.Help('<?xml version="1.0" encoding="UTF-8" ?>\n<help>\n') |
| 423 else: |
| 424 SCons.Script.Help(_new_options_help) |
| 425 |
| 426 # Check for site path. This is a list of site directories which each are |
| 427 # processed as if they were passed to --site-dir. |
| 428 site_path = SCons.Script.GetOption('site_path') |
| 429 if site_path: |
| 430 for site_dir in site_path.split(','): |
| 431 AddSiteDir(site_dir) |
| 432 |
| 433 # Since our site dir was specified on the SCons command line, SCons will |
| 434 # normally only look at our site dir. Add back checking for project-local |
| 435 # site_scons directories. |
| 436 if not SCons.Script.GetOption('no_site_dir'): |
| 437 SCons.Script.Main._load_site_scons_dir( |
| 438 SCons.Node.FS.get_default_fs().SConstruct_dir, None) |
| 439 |
| 440 # Run main code |
| 441 SiteInitMain() |
| OLD | NEW |