Package SCons :: Package Node :: Module FS
[hide private]
[frames] | no frames]

Source Code for Module SCons.Node.FS

   1  """scons.Node.FS 
   2   
   3  File system nodes. 
   4   
   5  These Nodes represent the canonical external objects that people think 
   6  of when they think of building software: files and directories. 
   7   
   8  This holds a "default_fs" variable that should be initialized with an FS 
   9  that can be used by scripts or modules looking for the canonical default. 
  10   
  11  """ 
  12   
  13  # 
  14  # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation 
  15  # 
  16  # Permission is hereby granted, free of charge, to any person obtaining 
  17  # a copy of this software and associated documentation files (the 
  18  # "Software"), to deal in the Software without restriction, including 
  19  # without limitation the rights to use, copy, modify, merge, publish, 
  20  # distribute, sublicense, and/or sell copies of the Software, and to 
  21  # permit persons to whom the Software is furnished to do so, subject to 
  22  # the following conditions: 
  23  # 
  24  # The above copyright notice and this permission notice shall be included 
  25  # in all copies or substantial portions of the Software. 
  26  # 
  27  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 
  28  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
  29  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  30  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
  31  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
  32  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
  33  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
  34  # 
  35   
  36  __revision__ = "src/engine/SCons/Node/FS.py 3765 2008/11/04 08:12:16 scons" 
  37   
  38  import fnmatch 
  39  from itertools import izip 
  40  import os 
  41  import os.path 
  42  import re 
  43  import shutil 
  44  import stat 
  45  import string 
  46  import sys 
  47  import time 
  48  import cStringIO 
  49   
  50  import SCons.Action 
  51  from SCons.Debug import logInstanceCreation 
  52  import SCons.Errors 
  53  import SCons.Memoize 
  54  import SCons.Node 
  55  import SCons.Node.Alias 
  56  import SCons.Subst 
  57  import SCons.Util 
  58  import SCons.Warnings 
  59   
  60  from SCons.Debug import Trace 
  61   
  62  do_store_info = True 
  63   
  64  # The max_drift value:  by default, use a cached signature value for 
  65  # any file that's been untouched for more than two days. 
  66  default_max_drift = 2*24*60*60 
  67   
  68  # 
  69  # We stringify these file system Nodes a lot.  Turning a file system Node 
  70  # into a string is non-trivial, because the final string representation 
  71  # can depend on a lot of factors:  whether it's a derived target or not, 
  72  # whether it's linked to a repository or source directory, and whether 
  73  # there's duplication going on.  The normal technique for optimizing 
  74  # calculations like this is to memoize (cache) the string value, so you 
  75  # only have to do the calculation once. 
  76  # 
  77  # A number of the above factors, however, can be set after we've already 
  78  # been asked to return a string for a Node, because a Repository() or 
  79  # VariantDir() call or the like may not occur until later in SConscript 
  80  # files.  So this variable controls whether we bother trying to save 
  81  # string values for Nodes.  The wrapper interface can set this whenever 
  82  # they're done mucking with Repository and VariantDir and the other stuff, 
  83  # to let this module know it can start returning saved string values 
  84  # for Nodes. 
  85  # 
  86  Save_Strings = None 
  87   
88 -def save_strings(val):
89 global Save_Strings 90 Save_Strings = val
91 92 # 93 # Avoid unnecessary function calls by recording a Boolean value that 94 # tells us whether or not os.path.splitdrive() actually does anything 95 # on this system, and therefore whether we need to bother calling it 96 # when looking up path names in various methods below. 97 # 98 99 do_splitdrive = None 100
101 -def initialize_do_splitdrive():
102 global do_splitdrive 103 drive, path = os.path.splitdrive('X:/foo') 104 do_splitdrive = not not drive
105 106 initialize_do_splitdrive() 107 108 # 109 110 needs_normpath_check = None 111
112 -def initialize_normpath_check():
113 """ 114 Initialize the normpath_check regular expression. 115 116 This function is used by the unit tests to re-initialize the pattern 117 when testing for behavior with different values of os.sep. 118 """ 119 global needs_normpath_check 120 if os.sep == '/': 121 pattern = r'.*/|\.$|\.\.$' 122 else: 123 pattern = r'.*[/%s]|\.$|\.\.$' % re.escape(os.sep) 124 needs_normpath_check = re.compile(pattern)
125 126 initialize_normpath_check() 127 128 # 129 # SCons.Action objects for interacting with the outside world. 130 # 131 # The Node.FS methods in this module should use these actions to 132 # create and/or remove files and directories; they should *not* use 133 # os.{link,symlink,unlink,mkdir}(), etc., directly. 134 # 135 # Using these SCons.Action objects ensures that descriptions of these 136 # external activities are properly displayed, that the displays are 137 # suppressed when the -s (silent) option is used, and (most importantly) 138 # the actions are disabled when the the -n option is used, in which case 139 # there should be *no* changes to the external file system(s)... 140 # 141 142 if hasattr(os, 'link'): 155 else: 156 _hardlink_func = None 157 158 if hasattr(os, 'symlink'): 161 else: 162 _softlink_func = None 163
164 -def _copy_func(fs, src, dest):
165 shutil.copy2(src, dest) 166 st = fs.stat(src) 167 fs.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
168 169 170 Valid_Duplicates = ['hard-soft-copy', 'soft-hard-copy', 171 'hard-copy', 'soft-copy', 'copy'] 172 173 Link_Funcs = [] # contains the callables of the specified duplication style 174
175 -def set_duplicate(duplicate):
176 # Fill in the Link_Funcs list according to the argument 177 # (discarding those not available on the platform). 178 179 # Set up the dictionary that maps the argument names to the 180 # underlying implementations. We do this inside this function, 181 # not in the top-level module code, so that we can remap os.link 182 # and os.symlink for testing purposes. 183 link_dict = { 184 'hard' : _hardlink_func, 185 'soft' : _softlink_func, 186 'copy' : _copy_func 187 } 188 189 if not duplicate in Valid_Duplicates: 190 raise SCons.Errors.InternalError, ("The argument of set_duplicate " 191 "should be in Valid_Duplicates") 192 global Link_Funcs 193 Link_Funcs = [] 194 for func in string.split(duplicate,'-'): 195 if link_dict[func]: 196 Link_Funcs.append(link_dict[func])
197
198 -def LinkFunc(target, source, env):
199 # Relative paths cause problems with symbolic links, so 200 # we use absolute paths, which may be a problem for people 201 # who want to move their soft-linked src-trees around. Those 202 # people should use the 'hard-copy' mode, softlinks cannot be 203 # used for that; at least I have no idea how ... 204 src = source[0].abspath 205 dest = target[0].abspath 206 dir, file = os.path.split(dest) 207 if dir and not target[0].fs.isdir(dir): 208 os.makedirs(dir) 209 if not Link_Funcs: 210 # Set a default order of link functions. 211 set_duplicate('hard-soft-copy') 212 fs = source[0].fs 213 # Now link the files with the previously specified order. 214 for func in Link_Funcs: 215 try: 216 func(fs, src, dest) 217 break 218 except (IOError, OSError): 219 # An OSError indicates something happened like a permissions 220 # problem or an attempt to symlink across file-system 221 # boundaries. An IOError indicates something like the file 222 # not existing. In either case, keeping trying additional 223 # functions in the list and only raise an error if the last 224 # one failed. 225 if func == Link_Funcs[-1]: 226 # exception of the last link method (copy) are fatal 227 raise 228 else: 229 pass 230 return 0
231 232 Link = SCons.Action.Action(LinkFunc, None)
233 -def LocalString(target, source, env):
234 return 'Local copy of %s from %s' % (target[0], source[0])
235 236 LocalCopy = SCons.Action.Action(LinkFunc, LocalString) 237
238 -def UnlinkFunc(target, source, env):
239 t = target[0] 240 t.fs.unlink(t.abspath) 241 return 0
242 243 Unlink = SCons.Action.Action(UnlinkFunc, None) 244
245 -def MkdirFunc(target, source, env):
246 t = target[0] 247 if not t.exists(): 248 t.fs.mkdir(t.abspath) 249 return 0
250 251 Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None) 252 253 MkdirBuilder = None 254
255 -def get_MkdirBuilder():
256 global MkdirBuilder 257 if MkdirBuilder is None: 258 import SCons.Builder 259 import SCons.Defaults 260 # "env" will get filled in by Executor.get_build_env() 261 # calling SCons.Defaults.DefaultEnvironment() when necessary. 262 MkdirBuilder = SCons.Builder.Builder(action = Mkdir, 263 env = None, 264 explain = None, 265 is_explicit = None, 266 target_scanner = SCons.Defaults.DirEntryScanner, 267 name = "MkdirBuilder") 268 return MkdirBuilder
269
270 -class _Null:
271 pass
272 273 _null = _Null() 274 275 DefaultSCCSBuilder = None 276 DefaultRCSBuilder = None 277
278 -def get_DefaultSCCSBuilder():
279 global DefaultSCCSBuilder 280 if DefaultSCCSBuilder is None: 281 import SCons.Builder 282 # "env" will get filled in by Executor.get_build_env() 283 # calling SCons.Defaults.DefaultEnvironment() when necessary. 284 act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR') 285 DefaultSCCSBuilder = SCons.Builder.Builder(action = act, 286 env = None, 287 name = "DefaultSCCSBuilder") 288 return DefaultSCCSBuilder
289
290 -def get_DefaultRCSBuilder():
291 global DefaultRCSBuilder 292 if DefaultRCSBuilder is None: 293 import SCons.Builder 294 # "env" will get filled in by Executor.get_build_env() 295 # calling SCons.Defaults.DefaultEnvironment() when necessary. 296 act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR') 297 DefaultRCSBuilder = SCons.Builder.Builder(action = act, 298 env = None, 299 name = "DefaultRCSBuilder") 300 return DefaultRCSBuilder
301 302 # Cygwin's os.path.normcase pretends it's on a case-sensitive filesystem. 303 _is_cygwin = sys.platform == "cygwin" 304 if os.path.normcase("TeSt") == os.path.normpath("TeSt") and not _is_cygwin:
305 - def _my_normcase(x):
306 return x
307 else:
308 - def _my_normcase(x):
309 return string.upper(x)
310 311 312
313 -class DiskChecker:
314 - def __init__(self, type, do, ignore):
315 self.type = type 316 self.do = do 317 self.ignore = ignore 318 self.set_do()
319 - def set_do(self):
320 self.__call__ = self.do
321 - def set_ignore(self):
322 self.__call__ = self.ignore
323 - def set(self, list):
324 if self.type in list: 325 self.set_do() 326 else: 327 self.set_ignore()
328
329 -def do_diskcheck_match(node, predicate, errorfmt):
330 result = predicate() 331 try: 332 # If calling the predicate() cached a None value from stat(), 333 # remove it so it doesn't interfere with later attempts to 334 # build this Node as we walk the DAG. (This isn't a great way 335 # to do this, we're reaching into an interface that doesn't 336 # really belong to us, but it's all about performance, so 337 # for now we'll just document the dependency...) 338 if node._memo['stat'] is None: 339 del node._memo['stat'] 340 except (AttributeError, KeyError): 341 pass 342 if result: 343 raise TypeError, errorfmt % node.abspath
344
345 -