3 Builders and other things for the local site. Here's where we'll
4 duplicate the functionality of autoconf until we move it into the
5 installation procedure or use something like qmconf.
7 The code that reads the registry to find MSVC components was borrowed
8 from distutils.msvccompiler.
15 # Permission is hereby granted, free of charge, to any person obtaining
16 # a copy of this software and associated documentation files (the
17 # "Software"), to deal in the Software without restriction, including
18 # without limitation the rights to use, copy, modify, merge, publish,
19 # distribute, sublicense, and/or sell copies of the Software, and to
20 # permit persons to whom the Software is furnished to do so, subject to
21 # the following conditions:
23 # The above copyright notice and this permission notice shall be included
24 # in all copies or substantial portions of the Software.
26 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
27 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
28 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
51 import SCons.Environment
56 # A placeholder for a default Environment (for fetching source files
57 # from source code management systems and the like). This must be
58 # initialized later, after the top-level directory is set by the calling
62 # Lazily instantiate the default environment so the overhead of creating
63 # it doesn't apply when it's not needed.
64 def _fetch_DefaultEnvironment(*args, **kw):
66 Returns the already-created default construction environment.
71 def DefaultEnvironment(*args, **kw):
73 Initial public entry point for creating the default construction
76 After creating the environment, we overwrite our name
77 (DefaultEnvironment) with the _fetch_DefaultEnvironment() function,
78 which more efficiently returns the initialized default construction
79 environment without checking for its existence.
81 (This function still exists with its _default_check because someone
82 else (*cough* Script/__init__.py *cough*) may keep a reference
83 to this function. So we can't use the fully functional idiom of
84 having the name originally be a something that *only* creates the
85 construction environment and then overwrites the name.)
90 _default_env = SCons.Environment.Environment(*args, **kw)
92 _default_env.Decider('MD5')
94 _default_env.Decider('timestamp-match')
95 global DefaultEnvironment
96 DefaultEnvironment = _fetch_DefaultEnvironment
97 _default_env._CacheDir_path = None
100 # Emitters for setting the shared attribute on object files,
101 # and an action for checking that all of the source files
102 # going into a shared library are, in fact, shared.
103 def StaticObjectEmitter(target, source, env):
105 tgt.attributes.shared = None
106 return (target, source)
108 def SharedObjectEmitter(target, source, env):
110 tgt.attributes.shared = 1
111 return (target, source)
113 def SharedFlagChecker(source, target, env):
114 same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME')
115 if same == '0' or same == '' or same == 'False':
118 shared = src.attributes.shared
119 except AttributeError:
122 raise SCons.Errors.UserError, "Source file: %s is static and is not compatible with shared target: %s" % (src, target[0])
124 SharedCheck = SCons.Action.Action(SharedFlagChecker, None)
126 # Some people were using these variable name before we made
127 # SourceFileScanner part of the public interface. Don't break their
128 # SConscript files until we've given them some fair warning and a
130 CScan = SCons.Tool.CScanner
131 DScan = SCons.Tool.DScanner
132 LaTeXScan = SCons.Tool.LaTeXScanner
133 ObjSourceScan = SCons.Tool.SourceFileScanner
134 ProgScan = SCons.Tool.ProgramScanner
136 # These aren't really tool scanners, so they don't quite belong with
137 # the rest of those in Tool/__init__.py, but I'm not sure where else
138 # they should go. Leave them here for now.
139 import SCons.Scanner.Dir
140 DirScanner = SCons.Scanner.Dir.DirScanner()
141 DirEntryScanner = SCons.Scanner.Dir.DirEntryScanner()
143 # Actions for common languages.
144 CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR")
145 ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR")
146 CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR")
147 ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR")
149 ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR")
150 ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR")
152 LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR")
153 ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR")
155 LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR")
157 # Common tasks that we allow users to perform in platform-independent
158 # ways by creating ActionFactory instances.
159 ActionFactory = SCons.Action.ActionFactory
161 def get_paths_str(dest):
162 # If dest is a list, we need to manually call str() on each element
163 if SCons.Util.is_List(dest):
166 elem_strs.append('"' + str(element) + '"')
167 return '[' + ', '.join(elem_strs) + ']'
169 return '"' + str(dest) + '"'
171 def chmod_func(dest, mode):
172 SCons.Node.FS.invalidate_node_memos(dest)
173 if not SCons.Util.is_List(dest):
176 os.chmod(str(element), mode)
178 def chmod_strfunc(dest, mode):
179 return 'Chmod(%s, 0%o)' % (get_paths_str(dest), mode)
181 Chmod = ActionFactory(chmod_func, chmod_strfunc)
183 def copy_func(dest, src):
184 SCons.Node.FS.invalidate_node_memos(dest)
185 if SCons.Util.is_List(src) and os.path.isdir(dest):
187 shutil.copy2(file, dest)
189 elif os.path.isfile(src):
190 return shutil.copy2(src, dest)
192 return shutil.copytree(src, dest, 1)
194 Copy = ActionFactory(copy_func,
195 lambda dest, src: 'Copy("%s", "%s")' % (dest, src),
198 def delete_func(dest, must_exist=0):
199 SCons.Node.FS.invalidate_node_memos(dest)
200 if not SCons.Util.is_List(dest):
204 if not must_exist and not os.path.exists(entry):
206 if not os.path.exists(entry) or os.path.isfile(entry):
210 shutil.rmtree(entry, 1)
213 def delete_strfunc(dest, must_exist=0):
214 return 'Delete(%s)' % get_paths_str(dest)
216 Delete = ActionFactory(delete_func, delete_strfunc)
218 def mkdir_func(dest):
219 SCons.Node.FS.invalidate_node_memos(dest)
220 if not SCons.Util.is_List(dest):
224 os.makedirs(str(entry))
227 if (e[0] == errno.EEXIST or (sys.platform=='win32' and e[0]==183)) \
228 and os.path.isdir(str(entry)):
229 pass # not an error if already exists
233 Mkdir = ActionFactory(mkdir_func,
234 lambda dir: 'Mkdir(%s)' % get_paths_str(dir))
236 def move_func(dest, src):
237 SCons.Node.FS.invalidate_node_memos(dest)
238 SCons.Node.FS.invalidate_node_memos(src)
239 shutil.move(src, dest)
241 Move = ActionFactory(move_func,
242 lambda dest, src: 'Move("%s", "%s")' % (dest, src),
245 def touch_func(dest):
246 SCons.Node.FS.invalidate_node_memos(dest)
247 if not SCons.Util.is_List(dest):
251 mtime = int(time.time())
252 if os.path.exists(file):
253 atime = os.path.getatime(file)
257 os.utime(file, (atime, mtime))
259 Touch = ActionFactory(touch_func,
260 lambda file: 'Touch(%s)' % get_paths_str(file))
262 # Internal utility functions
264 def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None):
266 Creates a new list from 'list' by first interpolating each element
267 in the list using the 'env' dictionary and then calling f on the
268 list, and finally calling _concat_ixes to concatenate 'prefix' and
269 'suffix' onto each element of the list.
274 l = f(SCons.PathList.PathList(list).subst_path(env, target, source))
278 return _concat_ixes(prefix, list, suffix, env)
280 def _concat_ixes(prefix, list, suffix, env):
282 Creates a new list from 'list' by concatenating the 'prefix' and
283 'suffix' arguments onto each element of the list. A trailing space
284 on 'prefix' or leading space on 'suffix' will cause them to be put
285 into separate list elements rather than being concatenated.
290 # ensure that prefix and suffix are strings
291 prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW))
292 suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW))
295 if isinstance(x, SCons.Node.FS.File):
302 if prefix[-1] == ' ':
303 result.append(prefix[:-1])
304 elif x[:len(prefix)] != prefix:
311 result.append(suffix[1:])
312 elif x[-len(suffix):] != suffix:
313 result[-1] = result[-1]+suffix
317 def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None):
319 This is a wrapper around _concat()/_concat_ixes() that checks for
320 the existence of prefixes or suffixes on list items and strips them
321 where it finds them. This is used by tools (like the GNU linker)
322 that need to turn something like 'libfoo.a' into '-lfoo'.
329 env_c = env['_concat']
330 if env_c != _concat and callable(env_c):
331 # There's a custom _concat() method in the construction
332 # environment, and we've allowed people to set that in
333 # the past (see test/custom-concat.py), so preserve the
334 # backwards compatibility.
339 stripprefixes = list(map(env.subst, SCons.Util.flatten(stripprefixes)))
340 stripsuffixes = list(map(env.subst, SCons.Util.flatten(stripsuffixes)))
343 for l in SCons.PathList.PathList(itms).subst_path(env, None, None):
344 if isinstance(l, SCons.Node.FS.File):
348 if not SCons.Util.is_String(l):
351 for stripprefix in stripprefixes:
352 lsp = len(stripprefix)
353 if l[:lsp] == stripprefix:
355 # Do not strip more than one prefix
358 for stripsuffix in stripsuffixes:
359 lss = len(stripsuffix)
360 if l[-lss:] == stripsuffix:
362 # Do not strip more than one suffix
367 return c(prefix, stripped, suffix, env)
369 def processDefines(defs):
370 """process defines, resolving strings, lists, dictionaries, into a list of
373 if SCons.Util.is_List(defs):
376 if SCons.Util.is_List(d) or type(d) is types.TupleType:
377 l.append(str(d[0]) + '=' + str(d[1]))
380 elif SCons.Util.is_Dict(defs):
381 # The items in a dictionary are stored in random order, but
382 # if the order of the command-line options changes from
383 # invocation to invocation, then the signature of the command
384 # line will change and we'll get random unnecessary rebuilds.
385 # Consequently, we have to sort the keys to ensure a
386 # consistent order...
395 l.append(str(k) + '=' + str(v))
400 def _defines(prefix, defs, suffix, env, c=_concat_ixes):
401 """A wrapper around _concat_ixes that turns a list or string
402 into a list of C preprocessor command-line definitions.
405 return c(prefix, env.subst_path(processDefines(defs)), suffix, env)
407 class NullCmdGenerator:
408 """This is a callable class that can be used in place of other
409 command generators if you don't want them to do anything.
411 The __call__ method for this class simply returns the thing
412 you instantiated it with.
415 env["DO_NOTHING"] = NullCmdGenerator
416 env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}"
419 def __init__(self, cmd):
422 def __call__(self, target, source, env, for_signature=None):
425 class Variable_Method_Caller:
426 """A class for finding a construction variable on the stack and
427 calling one of its methods.
429 We use this to support "construction variables" in our string
430 eval()s that actually stand in for methods--specifically, use
431 of "RDirs" in call to _concat that should actually execute the
432 "TARGET.RDirs" method. (We used to support this by creating a little
433 "build dictionary" that mapped RDirs to the method, but this got in
434 the way of Memoizing construction environments, because we had to
435 create new environment objects to hold the variables.)
437 def __init__(self, variable, method):
438 self.variable = variable
440 def __call__(self, *args, **kw):
442 except ZeroDivisionError:
443 # Don't start iterating with the current stack-frame to
444 # prevent creating reference cycles (f_back is safe).
445 frame = sys.exc_info()[2].tb_frame.f_back
446 variable = self.variable
448 if variable in frame.f_locals:
449 v = frame.f_locals[variable]
451 method = getattr(v, self.method)
452 return method(*args, **kw)
456 ConstructionEnvironment = {
459 'CONFIGUREDIR' : '#/.sconf_temp',
460 'CONFIGURELOG' : '#/config.log',
461 'CPPSUFFIXES' : SCons.Tool.CSuffixes,
462 'DSUFFIXES' : SCons.Tool.DSuffixes,
464 'IDLSUFFIXES' : SCons.Tool.IDLSuffixes,
465 # 'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes, # moved to the TeX tools generate functions
467 '_defines' : _defines,
468 '_stripixes' : _stripixes,
469 '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
470 '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
471 '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
472 '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}',
473 'TEMPFILE' : NullCmdGenerator,
474 'Dir' : Variable_Method_Caller('TARGET', 'Dir'),
475 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'),
476 'File' : Variable_Method_Caller('TARGET', 'File'),
477 'RDirs' : Variable_Method_Caller('TARGET', 'RDirs'),
482 # indent-tabs-mode:nil
484 # vim: set expandtab tabstop=4 shiftwidth=4: