Package SCons :: Module Defaults
[hide private]
[frames] | no frames]

Source Code for Module SCons.Defaults

  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 - 2017 The SCons 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 74b2c53bc42290e911b334a6b44f187da698a668 2017/11/14 13:16:53 bdbaddog" 
 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 not 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 DAction = SCons.Action.Action("$DCOM", "$DCOMSTR") 148 ShDAction = SCons.Action.Action("$SHDCOM", "$SHDCOMSTR") 149 150 ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR") 151 ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR") 152 153 LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR") 154 ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR") 155 156 LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR") 157 158 # Common tasks that we allow users to perform in platform-independent 159 # ways by creating ActionFactory instances. 160 ActionFactory = SCons.Action.ActionFactory 161
162 -def get_paths_str(dest):
163 # If dest is a list, we need to manually call str() on each element 164 if SCons.Util.is_List(dest): 165 elem_strs = [] 166 for element in dest: 167 elem_strs.append('"' + str(element) + '"') 168 return '[' + ', '.join(elem_strs) + ']' 169 else: 170 return '"' + str(dest) + '"'
171 172 permission_dic = { 173 'u':{ 174 'r':stat.S_IRUSR, 175 'w':stat.S_IWUSR, 176 'x':stat.S_IXUSR 177 }, 178 'g':{ 179 'r':stat.S_IRGRP, 180 'w':stat.S_IWGRP, 181 'x':stat.S_IXGRP 182 }, 183 'o':{ 184 'r':stat.S_IROTH, 185 'w':stat.S_IWOTH, 186 'x':stat.S_IXOTH 187 } 188 } 189
190 -def chmod_func(dest, mode):
191 import SCons.Util 192 from string import digits 193 SCons.Node.FS.invalidate_node_memos(dest) 194 if not SCons.Util.is_List(dest): 195 dest = [dest] 196 if SCons.Util.is_String(mode) and not 0 in [i in digits for i in mode]: 197 mode = int(mode, 8) 198 if not SCons.Util.is_String(mode): 199 for element in dest: 200 os.chmod(str(element), mode) 201 else: 202 mode = str(mode) 203 for operation in mode.split(","): 204 if "=" in operation: 205 operator = "=" 206 elif "+" in operation: 207 operator = "+" 208 elif "-" in operation: 209 operator = "-" 210 else: 211 raise SyntaxError("Could not find +, - or =") 212 operation_list = operation.split(operator) 213 if len(operation_list) is not 2: 214 raise SyntaxError("More than one operator found") 215 user = operation_list[0].strip().replace("a", "ugo") 216 permission = operation_list[1].strip() 217 new_perm = 0 218 for u in user: 219 for p in permission: 220 try: 221 new_perm = new_perm | permission_dic[u][p] 222 except KeyError: 223 raise SyntaxError("Unrecognized user or permission format") 224 for element in dest: 225 curr_perm = os.stat(str(element)).st_mode 226 if operator == "=": 227 os.chmod(str(element), new_perm) 228 elif operator == "+": 229 os.chmod(str(element), curr_perm | new_perm) 230 elif operator == "-": 231 os.chmod(str(element), curr_perm & ~new_perm)
232
233 -def chmod_strfunc(dest, mode):
234 import SCons.Util 235 if not SCons.Util.is_String(mode): 236 return 'Chmod(%s, 0%o)' % (get_paths_str(dest), mode) 237 else: 238 return 'Chmod(%s, "%s")' % (get_paths_str(dest), str(mode))
239 240 Chmod = ActionFactory(chmod_func, chmod_strfunc) 241
242 -def copy_func(dest, src, symlinks=True):
243 """ 244 If symlinks (is true), then a symbolic link will be 245 shallow copied and recreated as a symbolic link; otherwise, copying 246 a symbolic link will be equivalent to copying the symbolic link's 247 final target regardless of symbolic link depth. 248 """ 249 250 dest = str(dest) 251 src = str(src) 252 253 SCons.Node.FS.invalidate_node_memos(dest) 254 if SCons.Util.is_List(src) and os.path.isdir(dest): 255 for file in src: 256 shutil.copy2(file, dest) 257 return 0 258 elif os.path.islink(src): 259 if symlinks: 260 return os.symlink(os.readlink(src), dest) 261 else: 262 return copy_func(dest, os.path.realpath(src)) 263 elif os.path.isfile(src): 264 shutil.copy2(src, dest) 265 return 0 266 else: 267 shutil.copytree(src, dest, symlinks) 268 # copytree returns None in python2 and destination string in python3 269 # A error is raised in both cases, so we can just return 0 for success 270 return 0
271 272 Copy = ActionFactory( 273 copy_func, 274 lambda dest, src, symlinks=True: 'Copy("%s", "%s")' % (dest, src) 275 ) 276
277 -def delete_func(dest, must_exist=0):
278 SCons.Node.FS.invalidate_node_memos(dest) 279 if not SCons.Util.is_List(dest): 280 dest = [dest] 281 for entry in dest: 282 entry = str(entry) 283 # os.path.exists returns False with broken links that exist 284 entry_exists = os.path.exists(entry) or os.path.islink(entry) 285 if not entry_exists and not must_exist: 286 continue 287 # os.path.isdir returns True when entry is a link to a dir 288 if os.path.isdir(entry) and not os.path.islink(entry): 289 shutil.rmtree(entry, 1) 290 continue 291 os.unlink(entry)
292
293 -def delete_strfunc(dest, must_exist=0):
294 return 'Delete(%s)' % get_paths_str(dest)
295 296 Delete = ActionFactory(delete_func, delete_strfunc) 297
298 -def mkdir_func(dest):
299 SCons.Node.FS.invalidate_node_memos(dest) 300 if not SCons.Util.is_List(dest): 301 dest = [dest] 302 for entry in dest: 303 try: 304 os.makedirs(str(entry)) 305 except os.error as e: 306 p = str(entry) 307 if (e.args[0] == errno.EEXIST or 308 (sys.platform=='win32' and e.args[0]==183)) \ 309 and os.path.isdir(str(entry)): 310 pass # not an error if already exists 311 else: 312 raise
313 314 Mkdir = ActionFactory(mkdir_func, 315 lambda dir: 'Mkdir(%s)' % get_paths_str(dir)) 316
317 -def move_func(dest, src):
318 SCons.Node.FS.invalidate_node_memos(dest) 319 SCons.Node.FS.invalidate_node_memos(src) 320 shutil.move(src, dest)
321 322 Move = ActionFactory(move_func, 323 lambda dest, src: 'Move("%s", "%s")' % (dest, src), 324 convert=str) 325
326 -def touch_func(dest):
327 SCons.Node.FS.invalidate_node_memos(dest) 328 if not SCons.Util.is_List(dest): 329 dest = [dest] 330 for file in dest: 331 file = str(file) 332 mtime = int(time.time()) 333 if os.path.exists(file): 334 atime = os.path.getatime(file) 335 else: 336 open(file, 'w') 337 atime = mtime 338 os.utime(file, (atime, mtime))
339 340 Touch = ActionFactory(touch_func, 341 lambda file: 'Touch(%s)' % get_paths_str(file)) 342 343 # Internal utility functions 344
345 -def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None):
346 """ 347 Creates a new list from 'list' by first interpolating each element 348 in the list using the 'env' dictionary and then calling f on the 349 list, and finally calling _concat_ixes to concatenate 'prefix' and 350 'suffix' onto each element of the list. 351 """ 352 if not list: 353 return list 354 355 l = f(SCons.PathList.PathList(list).subst_path(env, target, source)) 356 if l is not None: 357 list = l 358 359 return _concat_ixes(prefix, list, suffix, env)
360
361 -def _concat_ixes(prefix, list, suffix, env):
362 """ 363 Creates a new list from 'list' by concatenating the 'prefix' and 364 'suffix' arguments onto each element of the list. A trailing space 365 on 'prefix' or leading space on 'suffix' will cause them to be put 366 into separate list elements rather than being concatenated. 367 """ 368 369 result = [] 370 371 # ensure that prefix and suffix are strings 372 prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW)) 373 suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW)) 374 375 for x in list: 376 if isinstance(x, SCons.Node.FS.File): 377 result.append(x) 378 continue 379 x = str(x) 380 if x: 381 382 if prefix: 383 if prefix[-1] == ' ': 384 result.append(prefix[:-1]) 385 elif x[:len(prefix)] != prefix: 386 x = prefix + x 387 388 result.append(x) 389 390 if suffix: 391 if suffix[0] == ' ': 392 result.append(suffix[1:]) 393 elif x[-len(suffix):] != suffix: 394 result[-1] = result[-1]+suffix 395 396 return result
397
398 -def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None):
399 """ 400 This is a wrapper around _concat()/_concat_ixes() that checks for 401 the existence of prefixes or suffixes on list items and strips them 402 where it finds them. This is used by tools (like the GNU linker) 403 that need to turn something like 'libfoo.a' into '-lfoo'. 404 """ 405 406 if not itms: 407 return itms 408 409 if not callable(c): 410 env_c = env['_concat'] 411 if env_c != _concat and callable(env_c): 412 # There's a custom _concat() method in the construction 413 # environment, and we've allowed people to set that in 414 # the past (see test/custom-concat.py), so preserve the 415 # backwards compatibility. 416 c = env_c 417 else: 418 c = _concat_ixes 419 420 stripprefixes = list(map(env.subst, SCons.Util.flatten(stripprefixes))) 421 stripsuffixes = list(map(env.subst, SCons.Util.flatten(stripsuffixes))) 422 423 stripped = [] 424 for l in SCons.PathList.PathList(itms).subst_path(env, None, None): 425 if isinstance(l, SCons.Node.FS.File): 426 stripped.append(l) 427 continue 428 429 if not SCons.Util.is_String(l): 430 l = str(l) 431 432 for stripprefix in stripprefixes: 433 lsp = len(stripprefix) 434 if l[:lsp] == stripprefix: 435 l = l[lsp:] 436 # Do not strip more than one prefix 437 break 438 439 for stripsuffix in stripsuffixes: 440 lss = len(stripsuffix) 441 if l[-lss:] == stripsuffix: 442 l = l[:-lss] 443 # Do not strip more than one suffix 444 break 445 446 stripped.append(l) 447 448 return c(prefix, stripped, suffix, env)
449
450 -def processDefines(defs):
451 """process defines, resolving strings, lists, dictionaries, into a list of 452 strings 453 """ 454 if SCons.Util.is_List(defs): 455 l = [] 456 for d in defs: 457 if d is None: 458 continue 459 elif SCons.Util.is_List(d) or isinstance(d, tuple): 460 if len(d) >= 2: 461 l.append(str(d[0]) + '=' + str(d[1])) 462 else: 463 l.append(str(d[0])) 464 elif SCons.Util.is_Dict(d): 465 for macro,value in d.items(): 466 if value is not None: 467 l.append(str(macro) + '=' + str(value)) 468 else: 469 l.append(str(macro)) 470 elif SCons.Util.is_String(d): 471 l.append(str(d)) 472 else: 473 raise SCons.Errors.UserError("DEFINE %s is not a list, dict, string or None."%repr(d)) 474 elif SCons.Util.is_Dict(defs): 475 # The items in a dictionary are stored in random order, but 476 # if the order of the command-line options changes from 477 # invocation to invocation, then the signature of the command 478 # line will change and we'll get random unnecessary rebuilds. 479 # Consequently, we have to sort the keys to ensure a 480 # consistent order... 481 l = [] 482 for k,v in sorted(defs.items()): 483 if v is None: 484 l.append(str(k)) 485 else: 486 l.append(str(k) + '=' + str(v)) 487 else: 488 l = [str(defs)] 489 return l
490 491
492 -def _defines(prefix, defs, suffix, env, c=_concat_ixes):
493 """A wrapper around _concat_ixes that turns a list or string 494 into a list of C preprocessor command-line definitions. 495 """ 496 497 return c(prefix, env.subst_path(processDefines(defs)), suffix, env)
498 499
500 -class NullCmdGenerator(object):
501 """This is a callable class that can be used in place of other 502 command generators if you don't want them to do anything. 503 504 The __call__ method for this class simply returns the thing 505 you instantiated it with. 506 507 Example usage: 508 env["DO_NOTHING"] = NullCmdGenerator 509 env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}" 510 """ 511
512 - def __init__(self, cmd):
513 self.cmd = cmd
514
515 - def __call__(self, target, source, env, for_signature=None):
516 return self.cmd
517 518
519 -class Variable_Method_Caller(object):
520 """A class for finding a construction variable on the stack and 521 calling one of its methods. 522 523 We use this to support "construction variables" in our string 524 eval()s that actually stand in for methods--specifically, use 525 of "RDirs" in call to _concat that should actually execute the 526 "TARGET.RDirs" method. (We used to support this by creating a little 527 "build dictionary" that mapped RDirs to the method, but this got in 528 the way of Memoizing construction environments, because we had to 529 create new environment objects to hold the variables.) 530 """
531 - def __init__(self, variable, method):
532 self.variable = variable 533 self.method = method
534 - def __call__(self, *args, **kw):
535 try: 1//0 536 except ZeroDivisionError: 537 # Don't start iterating with the current stack-frame to 538 # prevent creating reference cycles (f_back is safe). 539 frame = sys.exc_info()[2].tb_frame.f_back 540 variable = self.variable 541 while frame: 542 if variable in frame.f_locals: 543 v = frame.f_locals[variable] 544 if v: 545 method = getattr(v, self.method) 546 return method(*args, **kw) 547 frame = frame.f_back 548 return None
549 550 # if $version_var is not empty, returns env[flags_var], otherwise returns None
551 -def __libversionflags(env, version_var, flags_var):
552 try: 553 if env.subst('$'+version_var): 554 return env[flags_var] 555 except KeyError: 556 pass 557 return None
558 559 ConstructionEnvironment = { 560 'BUILDERS' : {}, 561 'SCANNERS' : [ SCons.Tool.SourceFileScanner ], 562 'CONFIGUREDIR' : '#/.sconf_temp', 563 'CONFIGURELOG' : '#/config.log', 564 'CPPSUFFIXES' : SCons.Tool.CSuffixes, 565 'DSUFFIXES' : SCons.Tool.DSuffixes, 566 'ENV' : {}, 567 'IDLSUFFIXES' : SCons.Tool.IDLSuffixes, 568 # 'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes, # moved to the TeX tools generate functions 569 '_concat' : _concat, 570 '_defines' : _defines, 571 '_stripixes' : _stripixes, 572 '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', 573 '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', 574 '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', 575 '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', 576 577 '__libversionflags' : __libversionflags, 578 '__SHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}', 579 '__LDMODULEVERSIONFLAGS' : '${__libversionflags(__env__,"LDMODULEVERSION","_LDMODULEVERSIONFLAGS")}', 580 '__DSHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"DSHLIBVERSION","_DSHLIBVERSIONFLAGS")}', 581 582 'TEMPFILE' : NullCmdGenerator, 583 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), 584 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'), 585 'File' : Variable_Method_Caller('TARGET', 'File'), 586 'RDirs' : Variable_Method_Caller('TARGET', 'RDirs'), 587 } 588 589 # Local Variables: 590 # tab-width:4 591 # indent-tabs-mode:nil 592 # End: 593 # vim: set expandtab tabstop=4 shiftwidth=4: 594