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 - 2019 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 3a41ed6b288cee8d085373ad7fa02894e1903864 2019-01-23 17:30:35 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 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
346 -def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None):
347 """ 348 Creates a new list from 'list' by first interpolating each element 349 in the list using the 'env' dictionary and then calling f on the 350 list, and finally calling _concat_ixes to concatenate 'prefix' and 351 'suffix' onto each element of the list. 352 """ 353 if not list: 354 return list 355 356 l = f(SCons.PathList.PathList(list).subst_path(env, target, source)) 357 if l is not None: 358 list = l 359 360 return _concat_ixes(prefix, list, suffix, env)
361 362
363 -def _concat_ixes(prefix, list, suffix, env):
364 """ 365 Creates a new list from 'list' by concatenating the 'prefix' and 366 'suffix' arguments onto each element of the list. A trailing space 367 on 'prefix' or leading space on 'suffix' will cause them to be put 368 into separate list elements rather than being concatenated. 369 """ 370 371 result = [] 372 373 # ensure that prefix and suffix are strings 374 prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW)) 375 suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW)) 376 377 for x in list: 378 if isinstance(x, SCons.Node.FS.File): 379 result.append(x) 380 continue 381 x = str(x) 382 if x: 383 384 if prefix: 385 if prefix[-1] == ' ': 386 result.append(prefix[:-1]) 387 elif x[:len(prefix)] != prefix: 388 x = prefix + x 389 390 result.append(x) 391 392 if suffix: 393 if suffix[0] == ' ': 394 result.append(suffix[1:]) 395 elif x[-len(suffix):] != suffix: 396 result[-1] = result[-1]+suffix 397 398 return result
399 400
401 -def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None):
402 """ 403 This is a wrapper around _concat()/_concat_ixes() that checks for 404 the existence of prefixes or suffixes on list items and strips them 405 where it finds them. This is used by tools (like the GNU linker) 406 that need to turn something like 'libfoo.a' into '-lfoo'. 407 """ 408 409 if not itms: 410 return itms 411 412 if not callable(c): 413 env_c = env['_concat'] 414 if env_c != _concat and callable(env_c): 415 # There's a custom _concat() method in the construction 416 # environment, and we've allowed people to set that in 417 # the past (see test/custom-concat.py), so preserve the 418 # backwards compatibility. 419 c = env_c 420 else: 421 c = _concat_ixes 422 423 stripprefixes = list(map(env.subst, SCons.Util.flatten(stripprefixes))) 424 stripsuffixes = list(map(env.subst, SCons.Util.flatten(stripsuffixes))) 425 426 stripped = [] 427 for l in SCons.PathList.PathList(itms).subst_path(env, None, None): 428 if isinstance(l, SCons.Node.FS.File): 429 stripped.append(l) 430 continue 431 432 if not SCons.Util.is_String(l): 433 l = str(l) 434 435 for stripprefix in stripprefixes: 436 lsp = len(stripprefix) 437 if l[:lsp] == stripprefix: 438 l = l[lsp:] 439 # Do not strip more than one prefix 440 break 441 442 for stripsuffix in stripsuffixes: 443 lss = len(stripsuffix) 444 if l[-lss:] == stripsuffix: 445 l = l[:-lss] 446 # Do not strip more than one suffix 447 break 448 449 stripped.append(l) 450 451 return c(prefix, stripped, suffix, env)
452
453 -def processDefines(defs):
454 """process defines, resolving strings, lists, dictionaries, into a list of 455 strings 456 """ 457 if SCons.Util.is_List(defs): 458 l = [] 459 for d in defs: 460 if d is None: 461 continue 462 elif SCons.Util.is_List(d) or isinstance(d, tuple): 463 if len(d) >= 2: 464 l.append(str(d[0]) + '=' + str(d[1])) 465 else: 466 l.append(str(d[0])) 467 elif SCons.Util.is_Dict(d): 468 for macro,value in d.items(): 469 if value is not None: 470 l.append(str(macro) + '=' + str(value)) 471 else: 472 l.append(str(macro)) 473 elif SCons.Util.is_String(d): 474 l.append(str(d)) 475 else: 476 raise SCons.Errors.UserError("DEFINE %s is not a list, dict, string or None."%repr(d)) 477 elif SCons.Util.is_Dict(defs): 478 # The items in a dictionary are stored in random order, but 479 # if the order of the command-line options changes from 480 # invocation to invocation, then the signature of the command 481 # line will change and we'll get random unnecessary rebuilds. 482 # Consequently, we have to sort the keys to ensure a 483 # consistent order... 484 l = [] 485 for k,v in sorted(defs.items()): 486 if v is None: 487 l.append(str(k)) 488 else: 489 l.append(str(k) + '=' + str(v)) 490 else: 491 l = [str(defs)] 492 return l
493 494
495 -def _defines(prefix, defs, suffix, env, c=_concat_ixes):
496 """A wrapper around _concat_ixes that turns a list or string 497 into a list of C preprocessor command-line definitions. 498 """ 499 500 return c(prefix, env.subst_path(processDefines(defs)), suffix, env)
501 502
503 -class NullCmdGenerator(object):
504 """This is a callable class that can be used in place of other 505 command generators if you don't want them to do anything. 506 507 The __call__ method for this class simply returns the thing 508 you instantiated it with. 509 510 Example usage: 511 env["DO_NOTHING"] = NullCmdGenerator 512 env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}" 513 """ 514
515 - def __init__(self, cmd):
516 self.cmd = cmd
517
518 - def __call__(self, target, source, env, for_signature=None):
519 return self.cmd
520 521
522 -class Variable_Method_Caller(object):
523 """A class for finding a construction variable on the stack and 524 calling one of its methods. 525 526 We use this to support "construction variables" in our string 527 eval()s that actually stand in for methods--specifically, use 528 of "RDirs" in call to _concat that should actually execute the 529 "TARGET.RDirs" method. (We used to support this by creating a little 530 "build dictionary" that mapped RDirs to the method, but this got in 531 the way of Memoizing construction environments, because we had to 532 create new environment objects to hold the variables.) 533 """
534 - def __init__(self, variable, method):
535 self.variable = variable 536 self.method = method
537 - def __call__(self, *args, **kw):
538 try: 1//0 539 except ZeroDivisionError: 540 # Don't start iterating with the current stack-frame to 541 # prevent creating reference cycles (f_back is safe). 542 frame = sys.exc_info()[2].tb_frame.f_back 543 variable = self.variable 544 while frame: 545 if variable in frame.f_locals: 546 v = frame.f_locals[variable] 547 if v: 548 method = getattr(v, self.method) 549 return method(*args, **kw) 550 frame = frame.f_back 551 return None
552 553 # if $version_var is not empty, returns env[flags_var], otherwise returns None
554 -def __libversionflags(env, version_var, flags_var):
555 try: 556 if env.subst('$'+version_var): 557 return env[flags_var] 558 except KeyError: 559 pass 560 return None
561 562 ConstructionEnvironment = { 563 'BUILDERS' : {}, 564 'SCANNERS' : [ SCons.Tool.SourceFileScanner ], 565 'CONFIGUREDIR' : '#/.sconf_temp', 566 'CONFIGURELOG' : '#/config.log', 567 'CPPSUFFIXES' : SCons.Tool.CSuffixes, 568 'DSUFFIXES' : SCons.Tool.DSuffixes, 569 'ENV' : {}, 570 'IDLSUFFIXES' : SCons.Tool.IDLSuffixes, 571 '_concat' : _concat, 572 '_defines' : _defines, 573 '_stripixes' : _stripixes, 574 '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', 575 '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', 576 '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', 577 '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', 578 579 '__libversionflags' : __libversionflags, 580 '__SHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}', 581 '__LDMODULEVERSIONFLAGS' : '${__libversionflags(__env__,"LDMODULEVERSION","_LDMODULEVERSIONFLAGS")}', 582 '__DSHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"DSHLIBVERSION","_DSHLIBVERSIONFLAGS")}', 583 584 'TEMPFILE' : NullCmdGenerator, 585 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), 586 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'), 587 'File' : Variable_Method_Caller('TARGET', 'File'), 588 'RDirs' : Variable_Method_Caller('TARGET', 'RDirs'), 589 } 590 591 # Local Variables: 592 # tab-width:4 593 # indent-tabs-mode:nil 594 # End: 595 # vim: set expandtab tabstop=4 shiftwidth=4: 596