OLD | NEW |
(Empty) | |
| 1 """SCons.Defaults |
| 2 |
| 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. |
| 6 |
| 7 The code that reads the registry to find MSVC components was borrowed |
| 8 from distutils.msvccompiler. |
| 9 |
| 10 """ |
| 11 |
| 12 # |
| 13 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The S
Cons Foundation |
| 14 # |
| 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: |
| 22 # |
| 23 # The above copyright notice and this permission notice shall be included |
| 24 # in all copies or substantial portions of the Software. |
| 25 # |
| 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. |
| 33 # |
| 34 from __future__ import division |
| 35 |
| 36 __revision__ = "src/engine/SCons/Defaults.py 5134 2010/08/16 23:02:40 bdeegan" |
| 37 |
| 38 |
| 39 import os |
| 40 import errno |
| 41 import shutil |
| 42 import stat |
| 43 import time |
| 44 import sys |
| 45 |
| 46 import SCons.Action |
| 47 import SCons.Builder |
| 48 import SCons.CacheDir |
| 49 import SCons.Environment |
| 50 import SCons.PathList |
| 51 import SCons.Subst |
| 52 import SCons.Tool |
| 53 |
| 54 # A placeholder for a default Environment (for fetching source files |
| 55 # from source code management systems and the like). This must be |
| 56 # initialized later, after the top-level directory is set by the calling |
| 57 # interface. |
| 58 _default_env = None |
| 59 |
| 60 # Lazily instantiate the default environment so the overhead of creating |
| 61 # it doesn't apply when it's not needed. |
| 62 def _fetch_DefaultEnvironment(*args, **kw): |
| 63 """ |
| 64 Returns the already-created default construction environment. |
| 65 """ |
| 66 global _default_env |
| 67 return _default_env |
| 68 |
| 69 def DefaultEnvironment(*args, **kw): |
| 70 """ |
| 71 Initial public entry point for creating the default construction |
| 72 Environment. |
| 73 |
| 74 After creating the environment, we overwrite our name |
| 75 (DefaultEnvironment) with the _fetch_DefaultEnvironment() function, |
| 76 which more efficiently returns the initialized default construction |
| 77 environment without checking for its existence. |
| 78 |
| 79 (This function still exists with its _default_check because someone |
| 80 else (*cough* Script/__init__.py *cough*) may keep a reference |
| 81 to this function. So we can't use the fully functional idiom of |
| 82 having the name originally be a something that *only* creates the |
| 83 construction environment and then overwrites the name.) |
| 84 """ |
| 85 global _default_env |
| 86 if not _default_env: |
| 87 import SCons.Util |
| 88 _default_env = SCons.Environment.Environment(*args, **kw) |
| 89 if SCons.Util.md5: |
| 90 _default_env.Decider('MD5') |
| 91 else: |
| 92 _default_env.Decider('timestamp-match') |
| 93 global DefaultEnvironment |
| 94 DefaultEnvironment = _fetch_DefaultEnvironment |
| 95 _default_env._CacheDir_path = None |
| 96 return _default_env |
| 97 |
| 98 # Emitters for setting the shared attribute on object files, |
| 99 # and an action for checking that all of the source files |
| 100 # going into a shared library are, in fact, shared. |
| 101 def StaticObjectEmitter(target, source, env): |
| 102 for tgt in target: |
| 103 tgt.attributes.shared = None |
| 104 return (target, source) |
| 105 |
| 106 def SharedObjectEmitter(target, source, env): |
| 107 for tgt in target: |
| 108 tgt.attributes.shared = 1 |
| 109 return (target, source) |
| 110 |
| 111 def SharedFlagChecker(source, target, env): |
| 112 same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME') |
| 113 if same == '0' or same == '' or same == 'False': |
| 114 for src in source: |
| 115 try: |
| 116 shared = src.attributes.shared |
| 117 except AttributeError: |
| 118 shared = None |
| 119 if not shared: |
| 120 raise SCons.Errors.UserError("Source file: %s is static and is n
ot compatible with shared target: %s" % (src, target[0])) |
| 121 |
| 122 SharedCheck = SCons.Action.Action(SharedFlagChecker, None) |
| 123 |
| 124 # Some people were using these variable name before we made |
| 125 # SourceFileScanner part of the public interface. Don't break their |
| 126 # SConscript files until we've given them some fair warning and a |
| 127 # transition period. |
| 128 CScan = SCons.Tool.CScanner |
| 129 DScan = SCons.Tool.DScanner |
| 130 LaTeXScan = SCons.Tool.LaTeXScanner |
| 131 ObjSourceScan = SCons.Tool.SourceFileScanner |
| 132 ProgScan = SCons.Tool.ProgramScanner |
| 133 |
| 134 # These aren't really tool scanners, so they don't quite belong with |
| 135 # the rest of those in Tool/__init__.py, but I'm not sure where else |
| 136 # they should go. Leave them here for now. |
| 137 import SCons.Scanner.Dir |
| 138 DirScanner = SCons.Scanner.Dir.DirScanner() |
| 139 DirEntryScanner = SCons.Scanner.Dir.DirEntryScanner() |
| 140 |
| 141 # Actions for common languages. |
| 142 CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR") |
| 143 ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR") |
| 144 CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR") |
| 145 ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR") |
| 146 |
| 147 ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR") |
| 148 ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR") |
| 149 |
| 150 LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR") |
| 151 ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR") |
| 152 |
| 153 LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR") |
| 154 |
| 155 # Common tasks that we allow users to perform in platform-independent |
| 156 # ways by creating ActionFactory instances. |
| 157 ActionFactory = SCons.Action.ActionFactory |
| 158 |
| 159 def get_paths_str(dest): |
| 160 # If dest is a list, we need to manually call str() on each element |
| 161 if SCons.Util.is_List(dest): |
| 162 elem_strs = [] |
| 163 for element in dest: |
| 164 elem_strs.append('"' + str(element) + '"') |
| 165 return '[' + ', '.join(elem_strs) + ']' |
| 166 else: |
| 167 return '"' + str(dest) + '"' |
| 168 |
| 169 def chmod_func(dest, mode): |
| 170 SCons.Node.FS.invalidate_node_memos(dest) |
| 171 if not SCons.Util.is_List(dest): |
| 172 dest = [dest] |
| 173 for element in dest: |
| 174 os.chmod(str(element), mode) |
| 175 |
| 176 def chmod_strfunc(dest, mode): |
| 177 return 'Chmod(%s, 0%o)' % (get_paths_str(dest), mode) |
| 178 |
| 179 Chmod = ActionFactory(chmod_func, chmod_strfunc) |
| 180 |
| 181 def copy_func(dest, src): |
| 182 SCons.Node.FS.invalidate_node_memos(dest) |
| 183 if SCons.Util.is_List(src) and os.path.isdir(dest): |
| 184 for file in src: |
| 185 shutil.copy2(file, dest) |
| 186 return 0 |
| 187 elif os.path.isfile(src): |
| 188 return shutil.copy2(src, dest) |
| 189 else: |
| 190 return shutil.copytree(src, dest, 1) |
| 191 |
| 192 Copy = ActionFactory(copy_func, |
| 193 lambda dest, src: 'Copy("%s", "%s")' % (dest, src), |
| 194 convert=str) |
| 195 |
| 196 def delete_func(dest, must_exist=0): |
| 197 SCons.Node.FS.invalidate_node_memos(dest) |
| 198 if not SCons.Util.is_List(dest): |
| 199 dest = [dest] |
| 200 for entry in dest: |
| 201 entry = str(entry) |
| 202 if not must_exist and not os.path.exists(entry): |
| 203 continue |
| 204 if not os.path.exists(entry) or os.path.isfile(entry): |
| 205 os.unlink(entry) |
| 206 continue |
| 207 else: |
| 208 shutil.rmtree(entry, 1) |
| 209 continue |
| 210 |
| 211 def delete_strfunc(dest, must_exist=0): |
| 212 return 'Delete(%s)' % get_paths_str(dest) |
| 213 |
| 214 Delete = ActionFactory(delete_func, delete_strfunc) |
| 215 |
| 216 def mkdir_func(dest): |
| 217 SCons.Node.FS.invalidate_node_memos(dest) |
| 218 if not SCons.Util.is_List(dest): |
| 219 dest = [dest] |
| 220 for entry in dest: |
| 221 try: |
| 222 os.makedirs(str(entry)) |
| 223 except os.error, e: |
| 224 p = str(entry) |
| 225 if (e.args[0] == errno.EEXIST or |
| 226 (sys.platform=='win32' and e.args[0]==183)) \ |
| 227 and os.path.isdir(str(entry)): |
| 228 pass # not an error if already exists |
| 229 else: |
| 230 raise |
| 231 |
| 232 Mkdir = ActionFactory(mkdir_func, |
| 233 lambda dir: 'Mkdir(%s)' % get_paths_str(dir)) |
| 234 |
| 235 def move_func(dest, src): |
| 236 SCons.Node.FS.invalidate_node_memos(dest) |
| 237 SCons.Node.FS.invalidate_node_memos(src) |
| 238 shutil.move(src, dest) |
| 239 |
| 240 Move = ActionFactory(move_func, |
| 241 lambda dest, src: 'Move("%s", "%s")' % (dest, src), |
| 242 convert=str) |
| 243 |
| 244 def touch_func(dest): |
| 245 SCons.Node.FS.invalidate_node_memos(dest) |
| 246 if not SCons.Util.is_List(dest): |
| 247 dest = [dest] |
| 248 for file in dest: |
| 249 file = str(file) |
| 250 mtime = int(time.time()) |
| 251 if os.path.exists(file): |
| 252 atime = os.path.getatime(file) |
| 253 else: |
| 254 open(file, 'w') |
| 255 atime = mtime |
| 256 os.utime(file, (atime, mtime)) |
| 257 |
| 258 Touch = ActionFactory(touch_func, |
| 259 lambda file: 'Touch(%s)' % get_paths_str(file)) |
| 260 |
| 261 # Internal utility functions |
| 262 |
| 263 def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None): |
| 264 """ |
| 265 Creates a new list from 'list' by first interpolating each element |
| 266 in the list using the 'env' dictionary and then calling f on the |
| 267 list, and finally calling _concat_ixes to concatenate 'prefix' and |
| 268 'suffix' onto each element of the list. |
| 269 """ |
| 270 if not list: |
| 271 return list |
| 272 |
| 273 l = f(SCons.PathList.PathList(list).subst_path(env, target, source)) |
| 274 if l is not None: |
| 275 list = l |
| 276 |
| 277 return _concat_ixes(prefix, list, suffix, env) |
| 278 |
| 279 def _concat_ixes(prefix, list, suffix, env): |
| 280 """ |
| 281 Creates a new list from 'list' by concatenating the 'prefix' and |
| 282 'suffix' arguments onto each element of the list. A trailing space |
| 283 on 'prefix' or leading space on 'suffix' will cause them to be put |
| 284 into separate list elements rather than being concatenated. |
| 285 """ |
| 286 |
| 287 result = [] |
| 288 |
| 289 # ensure that prefix and suffix are strings |
| 290 prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW)) |
| 291 suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW)) |
| 292 |
| 293 for x in list: |
| 294 if isinstance(x, SCons.Node.FS.File): |
| 295 result.append(x) |
| 296 continue |
| 297 x = str(x) |
| 298 if x: |
| 299 |
| 300 if prefix: |
| 301 if prefix[-1] == ' ': |
| 302 result.append(prefix[:-1]) |
| 303 elif x[:len(prefix)] != prefix: |
| 304 x = prefix + x |
| 305 |
| 306 result.append(x) |
| 307 |
| 308 if suffix: |
| 309 if suffix[0] == ' ': |
| 310 result.append(suffix[1:]) |
| 311 elif x[-len(suffix):] != suffix: |
| 312 result[-1] = result[-1]+suffix |
| 313 |
| 314 return result |
| 315 |
| 316 def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None): |
| 317 """ |
| 318 This is a wrapper around _concat()/_concat_ixes() that checks for |
| 319 the existence of prefixes or suffixes on list items and strips them |
| 320 where it finds them. This is used by tools (like the GNU linker) |
| 321 that need to turn something like 'libfoo.a' into '-lfoo'. |
| 322 """ |
| 323 |
| 324 if not itms: |
| 325 return itms |
| 326 |
| 327 if not callable(c): |
| 328 env_c = env['_concat'] |
| 329 if env_c != _concat and callable(env_c): |
| 330 # There's a custom _concat() method in the construction |
| 331 # environment, and we've allowed people to set that in |
| 332 # the past (see test/custom-concat.py), so preserve the |
| 333 # backwards compatibility. |
| 334 c = env_c |
| 335 else: |
| 336 c = _concat_ixes |
| 337 |
| 338 stripprefixes = list(map(env.subst, SCons.Util.flatten(stripprefixes))) |
| 339 stripsuffixes = list(map(env.subst, SCons.Util.flatten(stripsuffixes))) |
| 340 |
| 341 stripped = [] |
| 342 for l in SCons.PathList.PathList(itms).subst_path(env, None, None): |
| 343 if isinstance(l, SCons.Node.FS.File): |
| 344 stripped.append(l) |
| 345 continue |
| 346 |
| 347 if not SCons.Util.is_String(l): |
| 348 l = str(l) |
| 349 |
| 350 for stripprefix in stripprefixes: |
| 351 lsp = len(stripprefix) |
| 352 if l[:lsp] == stripprefix: |
| 353 l = l[lsp:] |
| 354 # Do not strip more than one prefix |
| 355 break |
| 356 |
| 357 for stripsuffix in stripsuffixes: |
| 358 lss = len(stripsuffix) |
| 359 if l[-lss:] == stripsuffix: |
| 360 l = l[:-lss] |
| 361 # Do not strip more than one suffix |
| 362 break |
| 363 |
| 364 stripped.append(l) |
| 365 |
| 366 return c(prefix, stripped, suffix, env) |
| 367 |
| 368 def processDefines(defs): |
| 369 """process defines, resolving strings, lists, dictionaries, into a list of |
| 370 strings |
| 371 """ |
| 372 if SCons.Util.is_List(defs): |
| 373 l = [] |
| 374 for d in defs: |
| 375 if SCons.Util.is_List(d) or isinstance(d, tuple): |
| 376 l.append(str(d[0]) + '=' + str(d[1])) |
| 377 else: |
| 378 l.append(str(d)) |
| 379 elif SCons.Util.is_Dict(defs): |
| 380 # The items in a dictionary are stored in random order, but |
| 381 # if the order of the command-line options changes from |
| 382 # invocation to invocation, then the signature of the command |
| 383 # line will change and we'll get random unnecessary rebuilds. |
| 384 # Consequently, we have to sort the keys to ensure a |
| 385 # consistent order... |
| 386 l = [] |
| 387 for k,v in sorted(defs.items()): |
| 388 if v is None: |
| 389 l.append(str(k)) |
| 390 else: |
| 391 l.append(str(k) + '=' + str(v)) |
| 392 else: |
| 393 l = [str(defs)] |
| 394 return l |
| 395 |
| 396 def _defines(prefix, defs, suffix, env, c=_concat_ixes): |
| 397 """A wrapper around _concat_ixes that turns a list or string |
| 398 into a list of C preprocessor command-line definitions. |
| 399 """ |
| 400 |
| 401 return c(prefix, env.subst_path(processDefines(defs)), suffix, env) |
| 402 |
| 403 class NullCmdGenerator(object): |
| 404 """This is a callable class that can be used in place of other |
| 405 command generators if you don't want them to do anything. |
| 406 |
| 407 The __call__ method for this class simply returns the thing |
| 408 you instantiated it with. |
| 409 |
| 410 Example usage: |
| 411 env["DO_NOTHING"] = NullCmdGenerator |
| 412 env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}" |
| 413 """ |
| 414 |
| 415 def __init__(self, cmd): |
| 416 self.cmd = cmd |
| 417 |
| 418 def __call__(self, target, source, env, for_signature=None): |
| 419 return self.cmd |
| 420 |
| 421 class Variable_Method_Caller(object): |
| 422 """A class for finding a construction variable on the stack and |
| 423 calling one of its methods. |
| 424 |
| 425 We use this to support "construction variables" in our string |
| 426 eval()s that actually stand in for methods--specifically, use |
| 427 of "RDirs" in call to _concat that should actually execute the |
| 428 "TARGET.RDirs" method. (We used to support this by creating a little |
| 429 "build dictionary" that mapped RDirs to the method, but this got in |
| 430 the way of Memoizing construction environments, because we had to |
| 431 create new environment objects to hold the variables.) |
| 432 """ |
| 433 def __init__(self, variable, method): |
| 434 self.variable = variable |
| 435 self.method = method |
| 436 def __call__(self, *args, **kw): |
| 437 try: 1//0 |
| 438 except ZeroDivisionError: |
| 439 # Don't start iterating with the current stack-frame to |
| 440 # prevent creating reference cycles (f_back is safe). |
| 441 frame = sys.exc_info()[2].tb_frame.f_back |
| 442 variable = self.variable |
| 443 while frame: |
| 444 if variable in frame.f_locals: |
| 445 v = frame.f_locals[variable] |
| 446 if v: |
| 447 method = getattr(v, self.method) |
| 448 return method(*args, **kw) |
| 449 frame = frame.f_back |
| 450 return None |
| 451 |
| 452 ConstructionEnvironment = { |
| 453 'BUILDERS' : {}, |
| 454 'SCANNERS' : [], |
| 455 'CONFIGUREDIR' : '#/.sconf_temp', |
| 456 'CONFIGURELOG' : '#/config.log', |
| 457 'CPPSUFFIXES' : SCons.Tool.CSuffixes, |
| 458 'DSUFFIXES' : SCons.Tool.DSuffixes, |
| 459 'ENV' : {}, |
| 460 'IDLSUFFIXES' : SCons.Tool.IDLSuffixes, |
| 461 # 'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes, # moved to the TeX tools genera
te functions |
| 462 '_concat' : _concat, |
| 463 '_defines' : _defines, |
| 464 '_stripixes' : _stripixes, |
| 465 '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', |
| 466 '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__
, RDirs, TARGET, SOURCE)} $)', |
| 467 '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDir
s, TARGET, SOURCE)} $)', |
| 468 '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env_
_)}', |
| 469 'TEMPFILE' : NullCmdGenerator, |
| 470 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), |
| 471 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'), |
| 472 'File' : Variable_Method_Caller('TARGET', 'File'), |
| 473 'RDirs' : Variable_Method_Caller('TARGET', 'RDirs'), |
| 474 } |
| 475 |
| 476 # Local Variables: |
| 477 # tab-width:4 |
| 478 # indent-tabs-mode:nil |
| 479 # End: |
| 480 # vim: set expandtab tabstop=4 shiftwidth=4: |
OLD | NEW |