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 - 2015 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 rel_2.4.0:3365:9259ea1c13d7 2015/09/21 14:03:43 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 -def chmod_func(dest, mode):
173 SCons.Node.FS.invalidate_node_memos(dest) 174 if not SCons.Util.is_List(dest): 175 dest = [dest] 176 for element in dest: 177 os.chmod(str(element), mode)
178
179 -def chmod_strfunc(dest, mode):
180 return 'Chmod(%s, 0%o)' % (get_paths_str(dest), mode)
181 182 Chmod = ActionFactory(chmod_func, chmod_strfunc) 183
184 -def copy_func(dest, src, symlinks=True):
185 """ 186 If symlinks (is true), then a symbolic link will be 187 shallow copied and recreated as a symbolic link; otherwise, copying 188 a symbolic link will be equivalent to copying the symbolic link's 189 final target regardless of symbolic link depth. 190 """ 191 192 dest = str(dest) 193 src = str(src) 194 195 SCons.Node.FS.invalidate_node_memos(dest) 196 if SCons.Util.is_List(src) and os.path.isdir(dest): 197 for file in src: 198 shutil.copy2(file, dest) 199 return 0 200 elif os.path.islink(src): 201 if symlinks: 202 return os.symlink(os.readlink(src), dest) 203 else: 204 return copy_func(dest, os.path.realpath(src)) 205 elif os.path.isfile(src): 206 return shutil.copy2(src, dest) 207 else: 208 return shutil.copytree(src, dest, symlinks)
209 210 Copy = ActionFactory( 211 copy_func, 212 lambda dest, src, symlinks=True: 'Copy("%s", "%s")' % (dest, src) 213 ) 214
215 -def delete_func(dest, must_exist=0):
216 SCons.Node.FS.invalidate_node_memos(dest) 217 if not SCons.Util.is_List(dest): 218 dest = [dest] 219 for entry in dest: 220 entry = str(entry) 221 # os.path.exists returns False with broken links that exist 222 entry_exists = os.path.exists(entry) or os.path.islink(entry) 223 if not entry_exists and not must_exist: 224 continue 225 # os.path.isdir returns True when entry is a link to a dir 226 if os.path.isdir(entry) and not os.path.islink(entry): 227 shutil.rmtree(entry, 1) 228 continue 229 os.unlink(entry)
230
231 -def delete_strfunc(dest, must_exist=0):
232 return 'Delete(%s)' % get_paths_str(dest)
233 234 Delete = ActionFactory(delete_func, delete_strfunc) 235
236 -def mkdir_func(dest):
237 SCons.Node.FS.invalidate_node_memos(dest) 238 if not SCons.Util.is_List(dest): 239 dest = [dest] 240 for entry in dest: 241 try: 242 os.makedirs(str(entry)) 243 except os.error, e: 244 p = str(entry) 245 if (e.args[0] == errno.EEXIST or 246 (sys.platform=='win32' and e.args[0]==183)) \ 247 and os.path.isdir(str(entry)): 248 pass # not an error if already exists 249 else: 250 raise
251 252 Mkdir = ActionFactory(mkdir_func, 253 lambda dir: 'Mkdir(%s)' % get_paths_str(dir)) 254
255 -def move_func(dest, src):
256 SCons.Node.FS.invalidate_node_memos(dest) 257 SCons.Node.FS.invalidate_node_memos(src) 258 shutil.move(src, dest)
259 260 Move = ActionFactory(move_func, 261 lambda dest, src: 'Move("%s", "%s")' % (dest, src), 262 convert=str) 263
264 -def touch_func(dest):
265 SCons.Node.FS.invalidate_node_memos(dest) 266 if not SCons.Util.is_List(dest): 267 dest = [dest] 268 for file in dest: 269 file = str(file) 270 mtime = int(time.time()) 271 if os.path.exists(file): 272 atime = os.path.getatime(file) 273 else: 274 open(file, 'w') 275 atime = mtime 276 os.utime(file, (atime, mtime))
277 278 Touch = ActionFactory(touch_func, 279 lambda file: 'Touch(%s)' % get_paths_str(file)) 280 281 # Internal utility functions 282
283 -def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None):
284 """ 285 Creates a new list from 'list' by first interpolating each element 286 in the list using the 'env' dictionary and then calling f on the 287 list, and finally calling _concat_ixes to concatenate 'prefix' and 288 'suffix' onto each element of the list. 289 """ 290 if not list: 291 return list 292 293 l = f(SCons.PathList.PathList(list).subst_path(env, target, source)) 294 if l is not None: 295 list = l 296 297 return _concat_ixes(prefix, list, suffix, env)
298
299 -def _concat_ixes(prefix, list, suffix, env):
300 """ 301 Creates a new list from 'list' by concatenating the 'prefix' and 302 'suffix' arguments onto each element of the list. A trailing space 303 on 'prefix' or leading space on 'suffix' will cause them to be put 304 into separate list elements rather than being concatenated. 305 """ 306 307 result = [] 308 309 # ensure that prefix and suffix are strings 310 prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW)) 311 suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW)) 312 313 for x in list: 314 if isinstance(x, SCons.Node.FS.File): 315 result.append(x) 316 continue 317 x = str(x) 318 if x: 319 320 if prefix: 321 if prefix[-1] == ' ': 322 result.append(prefix[:-1]) 323 elif x[:len(prefix)] != prefix: 324 x = prefix + x 325 326 result.append(x) 327 328 if suffix: 329 if suffix[0] == ' ': 330 result.append(suffix[1:]) 331 elif x[-len(suffix):] != suffix: 332 result[-1] = result[-1]+suffix 333 334 return result
335
336 -def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None):
337 """ 338 This is a wrapper around _concat()/_concat_ixes() that checks for 339 the existence of prefixes or suffixes on list items and strips them 340 where it finds them. This is used by tools (like the GNU linker) 341 that need to turn something like 'libfoo.a' into '-lfoo'. 342 """ 343 344 if not itms: 345 return itms 346 347 if not callable(c): 348 env_c = env['_concat'] 349 if env_c != _concat and callable(env_c): 350 # There's a custom _concat() method in the construction 351 # environment, and we've allowed people to set that in 352 # the past (see test/custom-concat.py), so preserve the 353 # backwards compatibility. 354 c = env_c 355 else: 356 c = _concat_ixes 357 358 stripprefixes = list(map(env.subst, SCons.Util.flatten(stripprefixes))) 359 stripsuffixes = list(map(env.subst, SCons.Util.flatten(stripsuffixes))) 360 361 stripped = [] 362 for l in SCons.PathList.PathList(itms).subst_path(env, None, None): 363 if isinstance(l, SCons.Node.FS.File): 364 stripped.append(l) 365 continue 366 367 if not SCons.Util.is_String(l): 368 l = str(l) 369 370 for stripprefix in stripprefixes: 371 lsp = len(stripprefix) 372 if l[:lsp] == stripprefix: 373 l = l[lsp:] 374 # Do not strip more than one prefix 375 break 376 377 for stripsuffix in stripsuffixes: 378 lss = len(stripsuffix) 379 if l[-lss:] == stripsuffix: 380 l = l[:-lss] 381 # Do not strip more than one suffix 382 break 383 384 stripped.append(l) 385 386 return c(prefix, stripped, suffix, env)
387
388 -def processDefines(defs):
389 """process defines, resolving strings, lists, dictionaries, into a list of 390 strings 391 """ 392 if SCons.Util.is_List(defs): 393 l = [] 394 for d in defs: 395 if d is None: 396 continue 397 elif SCons.Util.is_List(d) or isinstance(d, tuple): 398 if len(d) >= 2: 399 l.append(str(d[0]) + '=' + str(d[1])) 400 else: 401 l.append(str(d[0])) 402 elif SCons.Util.is_Dict(d): 403 for macro,value in d.iteritems(): 404 if value is not None: 405 l.append(str(macro) + '=' + str(value)) 406 else: 407 l.append(str(macro)) 408 elif SCons.Util.is_String(d): 409 l.append(str(d)) 410 else: 411 raise SCons.Errors.UserError("DEFINE %s is not a list, dict, string or None."%repr(d)) 412 elif SCons.Util.is_Dict(defs): 413 # The items in a dictionary are stored in random order, but 414 # if the order of the command-line options changes from 415 # invocation to invocation, then the signature of the command 416 # line will change and we'll get random unnecessary rebuilds. 417 # Consequently, we have to sort the keys to ensure a 418 # consistent order... 419 l = [] 420 for k,v in sorted(defs.items()): 421 if v is None: 422 l.append(str(k)) 423 else: 424 l.append(str(k) + '=' + str(v)) 425 else: 426 l = [str(defs)] 427 return l
428
429 -def _defines(prefix, defs, suffix, env, c=_concat_ixes):
430 """A wrapper around _concat_ixes that turns a list or string 431 into a list of C preprocessor command-line definitions. 432 """ 433 434 return c(prefix, env.subst_path(processDefines(defs)), suffix, env)
435
436 -class NullCmdGenerator(object):
437 """This is a callable class that can be used in place of other 438 command generators if you don't want them to do anything. 439 440 The __call__ method for this class simply returns the thing 441 you instantiated it with. 442 443 Example usage: 444 env["DO_NOTHING"] = NullCmdGenerator 445 env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}" 446 """ 447
448 - def __init__(self, cmd):
449 self.cmd = cmd
450
451 - def __call__(self, target, source, env, for_signature=None):
452 return self.cmd
453
454 -class Variable_Method_Caller(object):
455 """A class for finding a construction variable on the stack and 456 calling one of its methods. 457 458 We use this to support "construction variables" in our string 459 eval()s that actually stand in for methods--specifically, use 460 of "RDirs" in call to _concat that should actually execute the 461 "TARGET.RDirs" method. (We used to support this by creating a little 462 "build dictionary" that mapped RDirs to the method, but this got in 463 the way of Memoizing construction environments, because we had to 464 create new environment objects to hold the variables.) 465 """
466 - def __init__(self, variable, method):
467 self.variable = variable 468 self.method = method
469 - def __call__(self, *args, **kw):
470 try: 1//0 471 except ZeroDivisionError: 472 # Don't start iterating with the current stack-frame to 473 # prevent creating reference cycles (f_back is safe). 474 frame = sys.exc_info()[2].tb_frame.f_back 475 variable = self.variable 476 while frame: 477 if variable in frame.f_locals: 478 v = frame.f_locals[variable] 479 if v: 480 method = getattr(v, self.method) 481 return method(*args, **kw) 482 frame = frame.f_back 483 return None
484 485 ConstructionEnvironment = { 486 'BUILDERS' : {}, 487 'SCANNERS' : [], 488 'CONFIGUREDIR' : '#/.sconf_temp', 489 'CONFIGURELOG' : '#/config.log', 490 'CPPSUFFIXES' : SCons.Tool.CSuffixes, 491 'DSUFFIXES' : SCons.Tool.DSuffixes, 492 'ENV' : {}, 493 'IDLSUFFIXES' : SCons.Tool.IDLSuffixes, 494 # 'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes, # moved to the TeX tools generate functions 495 '_concat' : _concat, 496 '_defines' : _defines, 497 '_stripixes' : _stripixes, 498 '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', 499 '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', 500 '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', 501 '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', 502 'TEMPFILE' : NullCmdGenerator, 503 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), 504 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'), 505 'File' : Variable_Method_Caller('TARGET', 'File'), 506 'RDirs' : Variable_Method_Caller('TARGET', 'RDirs'), 507 } 508 509 # Local Variables: 510 # tab-width:4 511 # indent-tabs-mode:nil 512 # End: 513 # vim: set expandtab tabstop=4 shiftwidth=4: 514