2 # Copyright 2008, Google Inc.
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
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
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.
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.
31 """Software construction toolkit site_scons configuration.
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.
45 """Returns the current host platform.
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
54 The host platform name - one of ('WINDOWS', 'LINUX', 'MAC').
65 if sys
.platform
not in platform_map
:
66 print ('site_init.py warning: platform "%s" is not in platfom map.' %
69 return platform_map
.get(sys
.platform
, sys
.platform
)
72 #------------------------------------------------------------------------------
75 def _CheckBuildModes(build_modes
, environments
, host_platform
):
76 """Checks the build modes for the environments.
79 build_modes: List of build mode strings.
80 environments: List of SCons environments.
81 host_platform: Host platform string.
84 ValueError: build groups and/or types invalid.
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.)
93 for e
in environments
:
94 if not e
.Overlap(e
['HOST_PLATFORMS'], [host_platform
, '*']):
96 if e
['BUILD_TYPE'] in all_build_types
:
97 raise ValueError('Multiple environments have the same BUILD_TYPE=%s' %
100 all_build_types
.append(e
['BUILD_TYPE'])
101 build_desc
[e
['BUILD_TYPE']] = e
.get('BUILD_TYPE_DESCRIPTION')
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
)
111 all_build_types
.append(g
)
112 all_build_groups
[g
].append(e
['BUILD_TYPE'])
114 # Add help for build types
116 Use --mode=type to specify the type of build to perform. The following types
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
, ''))
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:
130 groups_sorted
= all_build_groups
.keys()
132 for g
in groups_sorted
:
133 help_text
+= ' %-16s %s\n' % (g
, ','.join(all_build_groups
[g
]))
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.
140 SCons
.Script
.Help(help_text
)
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 '
150 #------------------------------------------------------------------------------
153 def BuildEnvironmentSConscripts(env
):
154 """Evaluates SConscripts for the environment.
156 Called by BuildEnvironments().
158 # Read SConscript for each component
159 # TODO: 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
165 if ec
.Entry(c
).isdir():
166 # The component is a directory, so assume it contains a SConscript
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')
176 # The component is a SConscript file.
177 c_script
= ec
.File(c
)
180 # Make c_dir a string.
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)
203 raise SCons
.Error
.UserError(
204 'Bad location for a SConscript. "%s" is not under '
205 '\$TARGET_ROOT or \$MAIN_DIR' % c_script
)
208 def BuildEnvironments(environments
):
209 """Build a collection of SConscripts under a collection of environments.
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.
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.
222 environments: List of SCons environments.
225 List of environments which were actually evaluated (built).
228 build_modes
= SCons
.Script
.GetOption('build_mode')
229 # TODO: 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(',')
236 host_platform
= SCons
.Script
.GetOption('host_platform')
237 if not host_platform
:
238 host_platform
= _HostPlatform()
241 _CheckBuildModes(build_modes
, environments
, host_platform
)
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
248 if e
.Overlap([e
['BUILD_TYPE'], e
['BUILD_GROUPS']], build_modes
):
249 environments_to_evaluate
.append(e
)
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().
256 # Defer building the SConscripts, so that other tools can do
257 # per-environment setup first.
258 e
.Defer(BuildEnvironmentSConscripts
)
260 # Execute deferred functions
263 # Add help on targets.
266 # Return list of environments actually evaluated
267 return environments_to_evaluate
270 #------------------------------------------------------------------------------
274 """Replacement for SCons tool module exists() function, if one isn't present.
277 True. This enables modules which always exist not to need to include a
278 dummy exists() function.
283 def _ToolModule(self
):
284 """Thunk for SCons.Tool.Tool._tool_module to patch in exists() function.
287 The module from the original SCons.Tool.Tool._tool_module call, with an
288 exists() method added if it wasn't present.
290 module
= self
._tool
_module
_orig
()
291 if not hasattr(module
, 'exists'):
292 module
.exists
= _ToolExists
296 #------------------------------------------------------------------------------
299 def AddSiteDir(site_dir
):
300 """Adds a site directory, as if passed to the --site-dir option.
303 site_dir: Site directory path to add, relative to the location of the
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.
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
)
317 #------------------------------------------------------------------------------
320 _new_options_help
= '''
321 Additional options for SCons:
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
336 """Main code executed in site_init."""
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'):
343 # Let people use new global methods directly.
344 __builtin__
.AddSiteDir
= AddSiteDir
345 __builtin__
.BuildEnvironments
= BuildEnvironments
346 # Legacy method names
347 # TODO: Remove these once they're no longer used anywhere.
348 __builtin__
.BuildComponents
= BuildEnvironments
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.
356 # Component_targets must precede component_builders so builders can
357 # define target groups.
361 'component_builders',
368 # Patch Tool._tool_module method to fill in an exists() method for the
369 # module if it isn't present.
370 # TODO: 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
376 SCons
.Script
.AddOption(
377 '--mode', '--build-mode',
379 nargs
=1, type='string',
383 help='build mode(s)')
384 SCons
.Script
.AddOption(
386 dest
='host_platform',
387 nargs
=1, type='string',
390 help='build mode(s)')
391 SCons
.Script
.AddOption(
394 nargs
=1, type='string',
397 help='comma-separated list of site directories')
399 SCons
.Script
.Help(_new_options_help
)
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')
405 for site_dir
in site_path
.split(','):
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)