1"""scons.Node.FS 2 3File system nodes. 4 5These Nodes represent the canonical external objects that people think 6of when they think of building software: files and directories. 7 8This holds a "default_fs" variable that should be initialized with an FS 9that 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, 2009, 2010, 2011 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__revision__="src/engine/SCons/Node/FS.py 5357 2011/09/09 21:31:03 bdeegan" 36 37importfnmatch 38importos 39importre 40importshutil 41importstat 42importsys 43importtime 44importcodecs 45 46importSCons.Action 47fromSCons.DebugimportlogInstanceCreation 48importSCons.Errors 49importSCons.Memoize 50importSCons.Node 51importSCons.Node.Alias 52importSCons.Subst 53importSCons.Util 54importSCons.Warnings 55 56fromSCons.DebugimportTrace 57 58do_store_info=True 59print_duplicate=0 60 61

77 78# The max_drift value: by default, use a cached signature value for 79# any file that's been untouched for more than two days. 80default_max_drift=2*24*60*60 81 82# 83# We stringify these file system Nodes a lot. Turning a file system Node 84# into a string is non-trivial, because the final string representation 85# can depend on a lot of factors: whether it's a derived target or not, 86# whether it's linked to a repository or source directory, and whether 87# there's duplication going on. The normal technique for optimizing 88# calculations like this is to memoize (cache) the string value, so you 89# only have to do the calculation once. 90# 91# A number of the above factors, however, can be set after we've already 92# been asked to return a string for a Node, because a Repository() or 93# VariantDir() call or the like may not occur until later in SConscript 94# files. So this variable controls whether we bother trying to save 95# string values for Nodes. The wrapper interface can set this whenever 96# they're done mucking with Repository and VariantDir and the other stuff, 97# to let this module know it can start returning saved string values 98# for Nodes. 99# 100Save_Strings=None 101

105 106# 107# Avoid unnecessary function calls by recording a Boolean value that 108# tells us whether or not os.path.splitdrive() actually does anything 109# on this system, and therefore whether we need to bother calling it 110# when looking up path names in various methods below. 111# 112 113do_splitdrive=None 114_my_splitdrive=None 115

134else: 135defsplitdrive(p): 136ifp[1:2]==':': 137returnp[:2],p[2:] 138return'',p 139_my_splitdrive=splitdrive 140 141# Keep some commonly used values in global variables to skip to 142# module look-up costs. 143globalOS_SEP 144globalUNC_PREFIX 145globalos_sep_is_slash 146 147OS_SEP=os.sep 148UNC_PREFIX=OS_SEP+OS_SEP 149os_sep_is_slash=OS_SEP=='/' 150 151initialize_do_splitdrive() 152 153# Used to avoid invoking os.path.normpath if not necessary. 154needs_normpath_check=re.compile( 155r''' 156 # We need to renormalize the path if it contains any consecutive 157 # '/' characters. 158 .*// | 159 160 # We need to renormalize the path if it contains a '..' directory. 161 # Note that we check for all the following cases: 162 # 163 # a) The path is a single '..' 164 # b) The path starts with '..'. E.g. '../' or '../moredirs' 165 # but we not match '..abc/'. 166 # c) The path ends with '..'. E.g. '/..' or 'dirs/..' 167 # d) The path contains a '..' in the middle. 168 # E.g. dirs/../moredirs 169 170 (.*/)?\.\.(?:/|$) | 171 172 # We need to renormalize the path if it contains a '.' 173 # directory, but NOT if it is a single '.' '/' characters. We 174 # do not want to match a single '.' because this case is checked 175 # for explicitely since this is common enough case. 176 # 177 # Note that we check for all the following cases: 178 # 179 # a) We don't match a single '.' 180 # b) We match if the path starts with '.'. E.g. './' or 181 # './moredirs' but we not match '.abc/'. 182 # c) We match if the path ends with '.'. E.g. '/.' or 183 # 'dirs/.' 184 # d) We match if the path contains a '.' in the middle. 185 # E.g. dirs/./moredirs 186 187 \./|.*/\.(?:/|$) 188 189 ''', 190re.VERBOSE 191) 192needs_normpath_match=needs_normpath_check.match 193 194# 195# SCons.Action objects for interacting with the outside world. 196# 197# The Node.FS methods in this module should use these actions to 198# create and/or remove files and directories; they should *not* use 199# os.{link,symlink,unlink,mkdir}(), etc., directly. 200# 201# Using these SCons.Action objects ensures that descriptions of these 202# external activities are properly displayed, that the displays are 203# suppressed when the -s (silent) option is used, and (most importantly) 204# the actions are disabled when the the -n option is used, in which case 205# there should be *no* changes to the external file system(s)... 206# 207 208ifhasattr(os,'link'):

210# If the source is a symlink, we can't just hard-link to it 211# because a relative symlink may point somewhere completely 212# different. We must disambiguate the symlink and then 213# hard-link the final destination file. 214whilefs.islink(src): 215link=fs.readlink(src) 216ifnotos.path.isabs(link): 217src=link 218else: 219src=os.path.join(os.path.dirname(src),link) 220fs.link(src,dst)

242# Fill in the Link_Funcs list according to the argument 243# (discarding those not available on the platform). 244 245# Set up the dictionary that maps the argument names to the 246# underlying implementations. We do this inside this function, 247# not in the top-level module code, so that we can remap os.link 248# and os.symlink for testing purposes. 249link_dict={ 250'hard':_hardlink_func, 251'soft':_softlink_func, 252'copy':_copy_func 253} 254 255ifnotduplicateinValid_Duplicates: 256raiseSCons.Errors.InternalError("The argument of set_duplicate " 257"should be in Valid_Duplicates") 258globalLink_Funcs 259Link_Funcs=[] 260forfuncinduplicate.split('-'): 261iflink_dict[func]: 262Link_Funcs.append(link_dict[func])

265# Relative paths cause problems with symbolic links, so 266# we use absolute paths, which may be a problem for people 267# who want to move their soft-linked src-trees around. Those 268# people should use the 'hard-copy' mode, softlinks cannot be 269# used for that; at least I have no idea how ... 270src=source[0].abspath 271dest=target[0].abspath 272dir,file=os.path.split(dest) 273ifdirandnottarget[0].fs.isdir(dir): 274os.makedirs(dir) 275ifnotLink_Funcs: 276# Set a default order of link functions. 277set_duplicate('hard-soft-copy') 278fs=source[0].fs 279# Now link the files with the previously specified order. 280forfuncinLink_Funcs: 281try: 282func(fs,src,dest) 283break 284except(IOError,OSError): 285# An OSError indicates something happened like a permissions 286# problem or an attempt to symlink across file-system 287# boundaries. An IOError indicates something like the file 288# not existing. In either case, keeping trying additional 289# functions in the list and only raise an error if the last 290# one failed. 291iffunc==Link_Funcs[-1]: 292# exception of the last link method (copy) are fatal 293raise 294return0

392result=predicate() 393try: 394# If calling the predicate() cached a None value from stat(), 395# remove it so it doesn't interfere with later attempts to 396# build this Node as we walk the DAG. (This isn't a great way 397# to do this, we're reaching into an interface that doesn't 398# really belong to us, but it's all about performance, so 399# for now we'll just document the dependency...) 400ifnode._memo['stat']isNone: 401delnode._memo['stat'] 402except(AttributeError,KeyError): 403pass 404ifresult: 405raiseTypeError(errorfmt%node.abspath)

546# This is how we implement the "special" attributes 547# such as base, posix, srcdir, etc. 548try: 549attr_function=self.dictSpecialAttrs[name] 550exceptKeyError: 551try: 552attr=SCons.Util.Proxy.__getattr__(self,name) 553exceptAttributeError,e: 554# Raise our own AttributeError subclass with an 555# overridden __str__() method that identifies the 556# name of the entry that caused the exception. 557raiseEntryProxyAttributeError(self,name) 558returnattr 559else: 560returnattr_function(self)

563"""A generic class for file system entries. This class is for 564 when we don't know yet whether the entry being looked up is a file 565 or a directory. Instances of this class can morph into either 566 Dir or File objects by a later, more precise lookup. 567 568 Note: this class does not define __cmp__ and __hash__ for 569 efficiency reasons. SCons does a lot of comparing of 570 Node.FS.{Base,Entry,File,Dir} objects, so those operations must be 571 as fast as possible, which means we want to use Python's built-in 572 object identity comparisons. 573 """ 574 575memoizer_counters=[] 576

619""" 620 This node, which already existed, is being looked up as the 621 specified klass. Raise an exception if it isn't. 622 """ 623ifisinstance(self,klass)orklassisEntry: 624return 625raiseTypeError("Tried to lookup %s '%s' as a %s."%\ 626(self.__class__.__name__,self.path,klass.__name__))

772"""Fetch the source code builder for this node. 773 774 If there isn't one, we cache the source code builder specified 775 for the directory (which in turn will cache the value from its 776 parent directory, and so on up to the file system root). 777 """ 778try: 779scb=self.sbuilder 780exceptAttributeError: 781scb=self.dir.src_builder() 782self.sbuilder=scb 783returnscb

804""" 805 806 Generates a target entry that corresponds to this entry (usually 807 a source file) with the specified prefix and suffix. 808 809 Note that this method can be overridden dynamically for generated 810 files that need different behavior. See Tool/swig.py for 811 an example. 812 """ 813returnself.dir.Entry(prefix+splitext(self.name)[0]+suffix)

821""" 822 Return all of the directories for a given path list, including 823 corresponding "backing" directories in any repositories. 824 825 The Node lookups are relative to this Node (typically a 826 directory), so memoizing result saves cycles from looking 827 up the same path for each target in a given directory. 828 """ 829try: 830memo_dict=self._memo['Rfindalldirs'] 831exceptKeyError: 832memo_dict={} 833self._memo['Rfindalldirs']=memo_dict 834else: 835try: 836returnmemo_dict[pathlist] 837exceptKeyError: 838pass 839 840create_dir_relative_to_self=self.Dir 841result=[] 842forpathinpathlist: 843ifisinstance(path,SCons.Node.Node): 844result.append(path) 845else: 846dir=create_dir_relative_to_self(path) 847result.extend(dir.get_all_rdirs()) 848 849memo_dict[pathlist]=result 850 851returnresult

882"""This is the class for generic Node.FS entries--that is, things 883 that could be a File or a Dir, but we're just not sure yet. 884 Consequently, the methods in this class really exist just to 885 transform their associated object into the right class when the 886 time comes, and then call the same-named method in the transformed 887 class.""" 888

893""" 894 """ 895ifself.isdir(): 896self.__class__=Dir 897self._morph() 898elifself.isfile(): 899self.__class__=File 900self._morph() 901self.clear() 902else: 903# There was nothing on-disk at this location, so look in 904# the src directory. 905# 906# We can't just use self.srcnode() straight away because 907# that would create an actual Node for this file in the src 908# directory, and there might not be one. Instead, use the 909# dir_on_disk() method to see if there's something on-disk 910# with that name, in which case we can go ahead and call 911# self.srcnode() to create the right type of entry. 912srcdir=self.dir.srcnode() 913ifsrcdir!=self.dirand \ 914srcdir.entry_exists_on_disk(self.name)and \ 915self.srcnode().isdir(): 916self.__class__=Dir 917self._morph() 918elifmust_exist: 919msg="No such file or directory: '%s'"%self.abspath 920raiseSCons.Errors.UserError(msg) 921else: 922self.__class__=File 923self._morph() 924self.clear() 925returnself

939"""Fetch the contents of the entry. Returns the exact binary 940 contents of the file.""" 941try: 942self=self.disambiguate(must_exist=1) 943exceptSCons.Errors.UserError: 944# There was nothing on disk with which to disambiguate 945# this entry. Leave it as an Entry, but return a null 946# string so calls to get_contents() in emitters and the 947# like (e.g. in qt.py) don't have to disambiguate by hand 948# or catch the exception. 949return'' 950else: 951returnself.get_contents()

954"""Fetch the decoded text contents of a Unicode encoded Entry. 955 956 Since this should return the text contents from the file 957 system, we check to see into what sort of subclass we should 958 morph this Entry.""" 959try: 960self=self.disambiguate(must_exist=1) 961exceptSCons.Errors.UserError: 962# There was nothing on disk with which to disambiguate 963# this entry. Leave it as an Entry, but return a null 964# string so calls to get_text_contents() in emitters and 965# the like (e.g. in qt.py) don't have to disambiguate by 966# hand or catch the exception. 967return'' 968else: 969returnself.get_text_contents()

972"""Called to make sure a Node is a Dir. Since we're an 973 Entry, we can morph into one.""" 974ifself.__class__isnotklass: 975self.__class__=klass 976self._morph() 977self.clear()

978 979# The following methods can get called before the Taskmaster has 980# had a chance to call disambiguate() directly to see if this Entry 981# should really be a Dir or a File. We therefore use these to call 982# disambiguate() transparently (from our caller's point of view). 983# 984# Right now, this minimal set of methods has been derived by just 985# looking at some of the methods that will obviously be called early 986# in any of the various Taskmasters' calling sequences, and then 987# empirically figuring out which additional methods are necessary 988# to make various tests pass. 989

10201021ifSCons.Memoize.use_memoizer:1022__metaclass__=SCons.Memoize.Memoized_Metaclass10231024# This class implements an abstraction layer for operations involving1025# a local file system. Essentially, this wraps any function in1026# the os, os.path or shutil modules that we use to actually go do1027# anything with or to the local file system.1028#1029# Note that there's a very good chance we'll refactor this part of1030# the architecture in some way as we really implement the interface(s)1031# for remote file system Nodes. For example, the right architecture1032# might be to have this be a subclass instead of a base class.1033# Nevertheless, we're using this as a first step in that direction.1034#1035# We're not using chdir() yet because the calling subclass method1036# needs to use os.chdir() directly to avoid recursion. Will we1037# really need this one?1038#def chdir(self, path):1039# return os.chdir(path)

1106"""Initialize the Node.FS subsystem.11071108 The supplied path is the top of the source tree, where we1109 expect to find the top-level build file. If no path is1110 supplied, the current directory is the default.11111112 The path argument must be a valid absolute path.1113 """1114if__debug__:logInstanceCreation(self,'Node.FS')11151116self._memo={}11171118self.Root={}1119self.SConstruct_dir=None1120self.max_drift=default_max_drift11211122self.Top=None1123ifpathisNone:1124self.pathTop=os.getcwd()1125else:1126self.pathTop=path1127self.defaultDrive=_my_normcase(_my_splitdrive(self.pathTop)[0])11281129self.Top=self.Dir(self.pathTop)1130self.Top.path='.'1131self.Top.tpath='.'1132self._cwd=self.Top11331134DirNodeInfo.fs=self1135FileNodeInfo.fs=self

1153"""Change the current working directory for lookups.1154 If change_os_dir is true, we will also change the "real" cwd1155 to match.1156 """1157curr=self._cwd1158try:1159ifdirisnotNone:1160self._cwd=dir1161ifchange_os_dir:1162os.chdir(dir.abspath)1163exceptOSError:1164self._cwd=curr1165raise

1168"""1169 Returns the root directory for the specified drive, creating1170 it if necessary.1171 """1172drive=_my_normcase(drive)1173try:1174returnself.Root[drive]1175exceptKeyError:1176root=RootDir(drive,self)1177self.Root[drive]=root1178ifnotdrive:1179self.Root[self.defaultDrive]=root1180elifdrive==self.defaultDrive:1181self.Root['']=root1182returnroot

1185"""1186 The generic entry point for Node lookup with user-supplied data.11871188 This translates arbitrary input into a canonical Node.FS object1189 of the specified fsclass. The general approach for strings is1190 to turn it into a fully normalized absolute path and then call1191 the root directory's lookup_abs() method for the heavy lifting.11921193 If the path name begins with '#', it is unconditionally1194 interpreted relative to the top-level directory of this FS. '#'1195 is treated as a synonym for the top-level SConstruct directory,1196 much like '~' is treated as a synonym for the user's home1197 directory in a UNIX shell. So both '#foo' and '#/foo' refer1198 to the 'foo' subdirectory underneath the top-level SConstruct1199 directory.12001201 If the path name is relative, then the path is looked up relative1202 to the specified directory, or the current directory (self._cwd,1203 typically the SConscript directory) if the specified directory1204 is None.1205 """1206ifisinstance(p,Base):1207# It's already a Node.FS object. Make sure it's the right1208# class and return.1209p.must_be_same(fsclass)1210returnp1211# str(p) in case it's something like a proxy object1212p=str(p)12131214ifnotos_sep_is_slash:1215p=p.replace(OS_SEP,'/')12161217ifp[0:1]=='#':1218# There was an initial '#', so we strip it and override1219# whatever directory they may have specified with the1220# top-level SConstruct directory.1221p=p[1:]1222directory=self.Top12231224# There might be a drive letter following the1225# '#'. Although it is not described in the SCons man page,1226# the regression test suite explicitly tests for that1227# syntax. It seems to mean the following thing:1228#1229# Assuming the the SCons top dir is in C:/xxx/yyy,1230# '#X:/toto' means X:/xxx/yyy/toto.1231#1232# i.e. it assumes that the X: drive has a directory1233# structure similar to the one found on drive C:.1234ifdo_splitdrive:1235drive,p=_my_splitdrive(p)1236ifdrive:1237root=self.get_root(drive)1238else:1239root=directory.root1240else:1241root=directory.root12421243# We can only strip trailing after splitting the drive1244# since the drive might the UNC '//' prefix.1245p=p.strip('/')12461247needs_normpath=needs_normpath_match(p)12481249# The path is relative to the top-level SCons directory.1250ifpin('','.'):1251p=directory.labspath1252else:1253p=directory.labspath+'/'+p1254else:1255ifdo_splitdrive:1256drive,p=_my_splitdrive(p)1257ifdriveandnotp:1258# This causes a naked drive letter to be treated1259# as a synonym for the root directory on that1260# drive.1261p='/'1262else:1263drive=''12641265# We can only strip trailing '/' since the drive might the1266# UNC '//' prefix.1267ifp!='/':1268p=p.rstrip('/')12691270needs_normpath=needs_normpath_match(p)12711272ifp[0:1]=='/':1273# Absolute path1274root=self.get_root(drive)1275else:1276# This is a relative lookup or to the current directory1277# (the path name is not absolute). Add the string to the1278# appropriate directory lookup path, after which the whole1279# thing gets normalized.1280ifdirectory:1281ifnotisinstance(directory,Dir):1282directory=self.Dir(directory)1283else:1284directory=self._cwd12851286ifpin('','.'):1287p=directory.labspath1288else:1289p=directory.labspath+'/'+p12901291ifdrive:1292root=self.get_root(drive)1293else:1294root=directory.root12951296ifneeds_normpathisnotNone:1297# Normalize a pathname. Will return the same result for1298# equivalent paths.1299#1300# We take advantage of the fact that we have an absolute1301# path here for sure. In addition, we know that the1302# components of lookup path are separated by slashes at1303# this point. Because of this, this code is about 2X1304# faster than calling os.path.normpath() followed by1305# replacing os.sep with '/' again.1306ins=p.split('/')[1:]1307outs=[]1308fordinins:1309ifd=='..':1310try:1311outs.pop()1312exceptIndexError:1313pass1314elifdnotin('','.'):1315outs.append(d)1316p='/'+'/'.join(outs)13171318returnroot._lookup_abs(p,fsclass,create)

1321"""Look up or create a generic Entry node with the specified name.1322 If the name is a relative path (begins with ./, ../, or a file1323 name), then it is looked up relative to the supplied directory1324 node, or to the top level directory of the FS (supplied at1325 construction time) if no directory is supplied.1326 """1327returnself._lookup(name,directory,Entry,create)

1330"""Look up or create a File node with the specified name. If1331 the name is a relative path (begins with ./, ../, or a file name),1332 then it is looked up relative to the supplied directory node,1333 or to the top level directory of the FS (supplied at construction1334 time) if no directory is supplied.13351336 This method will raise TypeError if a directory is found at the1337 specified path.1338 """1339returnself._lookup(name,directory,File,create)

1342"""Look up or create a Dir node with the specified name. If1343 the name is a relative path (begins with ./, ../, or a file name),1344 then it is looked up relative to the supplied directory node,1345 or to the top level directory of the FS (supplied at construction1346 time) if no directory is supplied.13471348 This method will raise TypeError if a normal file is found at the1349 specified path.1350 """1351returnself._lookup(name,directory,Dir,create)

1354"""Link the supplied variant directory to the source directory1355 for purposes of building files."""13561357ifnotisinstance(src_dir,SCons.Node.Node):1358src_dir=self.Dir(src_dir)1359ifnotisinstance(variant_dir,SCons.Node.Node):1360variant_dir=self.Dir(variant_dir)1361ifsrc_dir.is_under(variant_dir):1362raiseSCons.Errors.UserError("Source directory cannot be under variant directory.")1363ifvariant_dir.srcdir:1364ifvariant_dir.srcdir==src_dir:1365return# We already did this.1366raiseSCons.Errors.UserError("'%s' already has a source directory: '%s'."%(variant_dir,variant_dir.srcdir))1367variant_dir.link(src_dir,duplicate)

1377"""Create targets in corresponding variant directories13781379 Climb the directory tree, and look up path names1380 relative to any linked variant directories we find.13811382 Even though this loops and walks up the tree, we don't memoize1383 the return value because this is really only used to process1384 the command-line targets.1385 """1386targets=[]1387message=None1388fmt="building associated VariantDir targets: %s"1389start_dir=dir1390whiledir:1391forbdindir.variant_dirs:1392ifstart_dir.is_under(bd):1393# If already in the build-dir location, don't reflect1394return[orig],fmt%str(orig)1395p=os.path.join(bd.path,*tail)1396targets.append(self.Entry(p))1397tail=[dir.name]+tail1398dir=dir.up()1399iftargets:1400message=fmt%' '.join(map(str,targets))1401returntargets,message

1453"""Turn a file system Node (either a freshly initialized directory1454 object or a separate Entry object) into a proper directory object.14551456 Set up this directory's entries and hook it into the file1457 system tree. Specify that directories (this Node) don't use1458 signatures for calculating whether they're current.1459 """14601461self.repositories=[]1462self.srcdir=None14631464self.entries={}1465self.entries['.']=self1466self.entries['..']=self.dir1467self.cwd=self1468self.searched=01469self._sconsign=None1470self.variant_dirs=[]1471self.root=self.dir.root14721473# For directories, we make a difference between the directory1474# 'name' and the directory 'dirname'. The 'name' attribute is1475# used when we need to print the 'name' of the directory or1476# when we it is used as the last part of a path. The 'dirname'1477# is used when the directory is not the last element of the1478# path. The main reason for making that distinction is that1479# for RoorDir's the dirname can not be easily inferred from1480# the name. For example, we have to add a '/' after a drive1481# letter but not after a UNC path prefix ('//').1482self.dirname=self.name+OS_SEP14831484# Don't just reset the executor, replace its action list,1485# because it might have some pre-or post-actions that need to1486# be preserved.1487#1488# But don't reset the executor if there is a non-null executor1489# attached already. The existing executor might have other1490# targets, in which case replacing the action list with a1491# Mkdir action is a big mistake.1492ifnothasattr(self,'executor'):1493self.builder=get_MkdirBuilder()1494self.get_executor().set_action_list(self.builder.action)1495else:1496# Prepend MkdirBuilder action to existing action list1497l=self.get_executor().action_list1498a=get_MkdirBuilder().action1499l.insert(0,a)1500self.get_executor().set_action_list(l)

1507"""Called when we change the repository(ies) for a directory.1508 This clears any cached information that is invalidated by changing1509 the repository."""15101511fornodeinself.entries.values():1512ifnode!=self.dir:1513ifnode!=selfandisinstance(node,Dir):1514node.__clearRepositoryCache(duplicate)1515else:1516node.clear()1517try:1518delnode._srcreps1519exceptAttributeError:1520pass1521ifduplicateisnotNone:1522node.duplicate=duplicate

1550"""Set this directory as the variant directory for the1551 supplied source directory."""1552self.srcdir=srcdir1553self.duplicate=duplicate1554self.__clearRepositoryCache(duplicate)1555srcdir.variant_dirs.append(self)

1603"""Return a path to "other" relative to this directory.1604 """16051606# This complicated and expensive method, which constructs relative1607# paths between arbitrary Node.FS objects, is no longer used1608# by SCons itself. It was introduced to store dependency paths1609# in .sconsign files relative to the target, but that ended up1610# being significantly inefficient.1611#1612# We're continuing to support the method because some SConstruct1613# files out there started using it when it was available, and1614# we're all about backwards compatibility..16151616try:1617memo_dict=self._memo['rel_path']1618exceptKeyError:1619memo_dict={}1620self._memo['rel_path']=memo_dict1621else:1622try:1623returnmemo_dict[other]1624exceptKeyError:1625pass16261627ifselfisother:1628result='.'16291630elifnototherinself.path_elements:1631try:1632other_dir=other.get_dir()1633exceptAttributeError:1634result=str(other)1635else:1636ifother_dirisNone:1637result=other.name1638else:1639dir_rel_path=self.rel_path(other_dir)1640ifdir_rel_path=='.':1641result=other.name1642else:1643result=dir_rel_path+OS_SEP+other.name1644else:1645i=self.path_elements.index(other)+116461647path_elems=['..']*(len(self.path_elements)-i) \ 1648+[n.nameforninother.path_elements[i:]]16491650result=OS_SEP.join(path_elems)16511652memo_dict[other]=result16531654returnresult

1665"""Return this directory's implicit dependencies.16661667 We don't bother caching the results because the scan typically1668 shouldn't be requested more than once (as opposed to scanning1669 .h file contents, which can be requested as many times as the1670 files is #included by other files).1671 """1672ifnotscanner:1673return[]1674# Clear cached info for this Dir. If we already visited this1675# directory on our walk down the tree (because we didn't know at1676# that point it was being used as the source for another Node)1677# then we may have calculated build signature before realizing1678# we had to scan the disk. Now that we have to, though, we need1679# to invalidate the old calculated signature so that any node1680# dependent on our directory structure gets one that includes1681# info about everything on disk.1682self.clear()1683returnscanner(self,env,path)

1703"""Create this directory, silently and without worrying about1704 whether the builder is the default or not."""1705listDirs=[]1706parent=self1707whileparent:1708ifparent.exists():1709break1710listDirs.append(parent)1711p=parent.up()1712ifpisNone:1713# Don't use while: - else: for this condition because1714# if so, then parent is None and has no .path attribute.1715raiseSCons.Errors.StopError(parent.path)1716parent=p1717listDirs.reverse()1718fordirnodeinlistDirs:1719try:1720# Don't call dirnode.build(), call the base Node method1721# directly because we definitely *must* create this1722# directory. The dirnode.build() method will suppress1723# the build if it's the default builder.1724SCons.Node.Node.build(dirnode)1725dirnode.get_executor().nullify()1726# The build() action may or may not have actually1727# created the directory, depending on whether the -n1728# option was used or not. Delete the _exists and1729# _rexists attributes so they can be reevaluated.1730dirnode.clear()1731exceptOSError:1732pass

1753"""Return content signatures and names of all our children1754 separated by new-lines. Ensure that the nodes are sorted."""1755contents=[]1756fornodeinsorted(self.children(),key=lambdat:t.name):1757contents.append('%s %s\n'%(node.get_csig(),node.name))1758return''.join(contents)

1761"""Compute the content signature for Directory nodes. In1762 general, this is not needed and the content signature is not1763 stored in the DirNodeInfo. However, if get_contents on a Dir1764 node is called which has a child directory, the child1765 directory should return the hash of its contents."""1766contents=self.get_contents()1767returnSCons.Util.MD5signature(contents)

1797"""Return the .sconsign file info for this directory,1798 creating it first if necessary."""1799ifnotself._sconsign:1800importSCons.SConsign1801self._sconsign=SCons.SConsign.ForDirectory(self)1802returnself._sconsign

1964"""1965 Walk this directory tree by calling the specified function1966 for each directory in the tree.19671968 This behaves like the os.path.walk() function, but for in-memory1969 Node.FS.Dir objects. The function takes the same arguments as1970 the functions passed to os.path.walk():19711972 func(arg, dirname, fnames)19731974 Except that "dirname" will actually be the directory *Node*,1975 not the string. The '.' and '..' entries are excluded from1976 fnames. The fnames list may be modified in-place to filter the1977 subdirectories visited or otherwise impose a specific order.1978 The "arg" argument is always passed to func() and may be used1979 in any way (or ignored, passing None is common).1980 """1981entries=self.entries1982names=list(entries.keys())1983names.remove('.')1984names.remove('..')1985func(arg,self,names)1986fordirnamein[nforninnamesifisinstance(entries[n],Dir)]:1987entries[dirname].walk(func,arg)

1990"""1991 Returns a list of Nodes (or strings) matching a specified1992 pathname pattern.19931994 Pathname patterns follow UNIX shell semantics: * matches1995 any-length strings of any characters, ? matches any character,1996 and [] can enclose lists or ranges of characters. Matches do1997 not span directory separators.19981999 The matches take into account Repositories, returning local2000 Nodes if a corresponding entry exists in a Repository (either2001 an in-memory Node or something on disk).20022003 By defafult, the glob() function matches entries that exist2004 on-disk, in addition to in-memory Nodes. Setting the "ondisk"2005 argument to False (or some other non-true value) causes the glob()2006 function to only match in-memory Nodes. The default behavior is2007 to return both the on-disk and in-memory Nodes.20082009 The "source" argument, when true, specifies that corresponding2010 source Nodes must be returned if you're globbing in a build2011 directory (initialized with VariantDir()). The default behavior2012 is to return Nodes local to the VariantDir().20132014 The "strings" argument, when true, returns the matches as strings,2015 not Nodes. The strings are path names relative to this directory.20162017 The underlying algorithm is adapted from the glob.glob() function2018 in the Python library (but heavily modified), and uses fnmatch()2019 under the covers.2020 """2021dirname,basename=os.path.split(pathname)2022ifnotdirname:2023returnsorted(self._glob1(basename,ondisk,source,strings),2024key=lambdat:str(t))2025ifhas_glob_magic(dirname):2026list=self.glob(dirname,ondisk,source,strings=False)2027else:2028list=[self.Dir(dirname,create=True)]2029result=[]2030fordirinlist:2031r=dir._glob1(basename,ondisk,source,strings)2032ifstrings:2033r=[os.path.join(str(dir),x)forxinr]2034result.extend(r)2035returnsorted(result,key=lambdaa:str(a))

2038"""2039 Globs for and returns a list of entry names matching a single2040 pattern in this directory.20412042 This searches any repositories and source directories for2043 corresponding entries and returns a Node (or string) relative2044 to the current directory if an entry is found anywhere.20452046 TODO: handle pattern with no wildcard2047 """2048search_dir_list=self.get_all_rdirs()2049forsrcdirinself.srcdir_list():2050search_dir_list.extend(srcdir.get_all_rdirs())20512052selfEntry=self.Entry2053names=[]2054fordirinsearch_dir_list:2055# We use the .name attribute from the Node because the keys of2056# the dir.entries dictionary are normalized (that is, all upper2057# case) on case-insensitive systems like Windows.2058node_names=[v.namefork,vindir.entries.items()2059ifknotin('.','..')]2060names.extend(node_names)2061ifnotstrings:2062# Make sure the working directory (self) actually has2063# entries for all Nodes in repositories or variant dirs.2064fornameinnode_names:selfEntry(name)2065ifondisk:2066try:2067disk_names=os.listdir(dir.abspath)2068exceptos.error:2069continue2070names.extend(disk_names)2071ifnotstrings:2072# We're going to return corresponding Nodes in2073# the local directory, so we need to make sure2074# those Nodes exist. We only want to create2075# Nodes for the entries that will match the2076# specified pattern, though, which means we2077# need to filter the list here, even though2078# the overall list will also be filtered later,2079# after we exit this loop.2080ifpattern[0]!='.':2081#disk_names = [ d for d in disk_names if d[0] != '.' ]2082disk_names=[xforxindisk_namesifx[0]!='.']2083disk_names=fnmatch.filter(disk_names,pattern)2084dirEntry=dir.Entry2085fornameindisk_names:2086# Add './' before disk filename so that '#' at2087# beginning of filename isn't interpreted.2088name='./'+name2089node=dirEntry(name).disambiguate()2090n=selfEntry(name)2091ifn.__class__!=node.__class__:2092n.__class__=node.__class__2093n._morph()20942095names=set(names)2096ifpattern[0]!='.':2097#names = [ n for n in names if n[0] != '.' ]2098names=[xforxinnamesifx[0]!='.']2099names=fnmatch.filter(names,pattern)21002101ifstrings:2102returnnames21032104#return [ self.entries[_my_normcase(n)] for n in names ]2105return[self.entries[_my_normcase(n)]forninnames]

2108"""A class for the root directory of a file system.21092110 This is the same as a Dir class, except that the path separator2111 ('/' or '\\') is actually part of the name, so we don't need to2112 add a separator when creating the path names of entries within2113 this directory.2114 """

2116if__debug__:logInstanceCreation(self,'Node.FS.RootDir')2117# We're going to be our own parent directory (".." entry and .dir2118# attribute) so we have to set up some values so Base.__init__()2119# won't gag won't it calls some of our methods.2120self.abspath=''2121self.labspath=''2122self.path=''2123self.tpath=''2124self.path_elements=[]2125self.duplicate=02126self.root=self21272128# Handle all the types of drives:2129ifdrive=='':2130# No drive, regular UNIX root or Windows default drive.2131name=OS_SEP2132dirname=OS_SEP2133elifdrive=='//':2134# UNC path2135name=UNC_PREFIX2136dirname=UNC_PREFIX2137else:2138# Windows drive letter2139name=drive2140dirname=drive+OS_SEP21412142Base.__init__(self,name,self,fs)21432144# Now set our paths to what we really want them to be. The2145# name should already contain any necessary separators, such2146# as the initial drive letter (the name) plus the directory2147# separator, except for the "lookup abspath," which does not2148# have the drive letter.2149self.abspath=dirname2150self.labspath=''2151self.path=dirname2152self.tpath=dirname2153self._morph()21542155# Must be reset after Dir._morph() is invoked...2156self.dirname=dirname21572158self._lookupDict={}21592160self._lookupDict['']=self2161self._lookupDict['/']=self21622163# The // entry is necessary because os.path.normpath()2164# preserves double slashes at the beginning of a path on Posix2165# platforms.2166ifnothas_unc:2167self._lookupDict['//']=self

2175"""2176 Fast (?) lookup of a *normalized* absolute path.21772178 This method is intended for use by internal lookups with2179 already-normalized path data. For general-purpose lookups,2180 use the FS.Entry(), FS.Dir() or FS.File() methods.21812182 The caller is responsible for making sure we're passed a2183 normalized absolute path; we merely let Python's dictionary look2184 up and return the One True Node.FS object for the path.21852186 If a Node for the specified "p" doesn't already exist, and2187 "create" is specified, the Node may be created after recursive2188 invocation to find or create the parent directory or directories.2189 """2190k=_my_normcase(p)2191try:2192result=self._lookupDict[k]2193exceptKeyError:2194ifnotcreate:2195msg="No such file or directory: '%s' in '%s' (and create is False)"%(p,str(self))2196raiseSCons.Errors.UserError(msg)2197# There is no Node for this path name, and we're allowed2198# to create it.2199# (note: would like to use p.rsplit('/',1) here but2200# that's not in python 2.3)2201# e.g.: dir_name, file_name = p.rsplit('/',1)2202last_slash=p.rindex('/')2203if(last_slash>=0):2204dir_name=p[:last_slash]2205file_name=p[last_slash+1:]2206else:2207dir_name=p# shouldn't happen, just in case2208file_name=''22092210dir_node=self._lookup_abs(dir_name,Dir)2211result=klass(file_name,dir_node,self.fs)22122213# Double-check on disk (as configured) that the Node we2214# created matches whatever is out there in the real world.2215result.diskcheck_match()22162217self._lookupDict[k]=result2218dir_node.entries[_my_normcase(file_name)]=result2219dir_node.implicit=None2220else:2221# There is already a Node for this path name. Allow it to2222# complain if we were looking for an inappropriate type.2223result.must_be_same(klass)2224returnresult

2279"""2280 Converts this FileBuildInfo object for writing to a .sconsign file22812282 This replaces each Node in our various dependency lists with its2283 usual string representation: relative to the top-level SConstruct2284 directory, or an absolute path if it's outside.2285 """2286ifos_sep_is_slash:2287node_to_str=str2288else:2289defnode_to_str(n):2290try:2291s=n.path2292exceptAttributeError:2293s=str(n)2294else:2295s=s.replace(OS_SEP,'/')2296returns

2305"""2306 Converts a newly-read FileBuildInfo object for in-SCons use23072308 For normal up-to-date checking, we don't have any conversion to2309 perform--but we're leaving this method here to make that clear.2310 """2311pass

2313"""2314 Prepares a FileBuildInfo object for explaining what changed23152316 The bsources, bdepends and bimplicit lists have all been2317 stored on disk as paths relative to the top-level SConstruct2318 directory. Convert the strings to actual Nodes (for use by the2319 --debug=explain code and --implicit-cache).2320 """2321attrs=[2322('bsources','bsourcesigs'),2323('bdepends','bdependsigs'),2324('bimplicit','bimplicitsigs'),2325]2326for(nattr,sattr)inattrs:2327try:2328strings=getattr(self,nattr)2329nodeinfos=getattr(self,sattr)2330exceptAttributeError:2331continue2332nodes=[]2333fors,niinzip(strings,nodeinfos):2334ifnotisinstance(s,SCons.Node.Node):2335s=ni.str_to_node(s)2336nodes.append(s)2337setattr(self,nattr,nodes)

2396"""Turn a file system node into a File object."""2397self.scanner_paths={}2398ifnothasattr(self,'_local'):2399self._local=024002401# If there was already a Builder set on this entry, then2402# we need to make sure we call the target-decider function,2403# not the source-decider. Reaching in and doing this by hand2404# is a little bogus. We'd prefer to handle this by adding2405# an Entry.builder_set() method that disambiguates like the2406# other methods, but that starts running into problems with the2407# fragile way we initialize Dir Nodes with their Mkdir builders,2408# yet still allow them to be overridden by the user. Since it's2409# not clear right now how to fix that, stick with what works2410# until it becomes clear...2411ifself.has_builder():2412self.changed_since_last_build=self.decide_target

2433contents=self.get_contents()2434# The behavior of various decode() methods and functions2435# w.r.t. the initial BOM bytes is different for different2436# encodings and/or Python versions. ('utf-8' does not strip2437# them, but has a 'utf-8-sig' which does; 'utf-16' seems to2438# strip them; etc.) Just sidestep all the complication by2439# explicitly stripping the BOM before we decode().2440ifcontents.startswith(codecs.BOM_UTF8):2441returncontents[len(codecs.BOM_UTF8):].decode('utf-8')2442ifcontents.startswith(codecs.BOM_UTF16_LE):2443returncontents[len(codecs.BOM_UTF16_LE):].decode('utf-16-le')2444ifcontents.startswith(codecs.BOM_UTF16_BE):2445returncontents[len(codecs.BOM_UTF16_BE):].decode('utf-16-be')2446returncontents

2500# Merge our build information into the already-stored entry.2501# This accomodates "chained builds" where a file that's a target2502# in one build (SConstruct file) is a source in a different build.2503# See test/chained-build.py for the use case.2504ifdo_store_info:2505self.dir.sconsign().store_info(self.name,self)

2524# Convert a .sconsign entry from before the Big Signature2525# Refactoring, doing what we can to convert its information2526# to the new .sconsign entry format.2527#2528# The old format looked essentially like this:2529#2530# BuildInfo2531# .ninfo (NodeInfo)2532# .bsig2533# .csig2534# .timestamp2535# .size2536# .bsources2537# .bsourcesigs ("signature" list)2538# .bdepends2539# .bdependsigs ("signature" list)2540# .bimplicit2541# .bimplicitsigs ("signature" list)2542# .bact2543# .bactsig2544#2545# The new format looks like this:2546#2547# .ninfo (NodeInfo)2548# .bsig2549# .csig2550# .timestamp2551# .size2552# .binfo (BuildInfo)2553# .bsources2554# .bsourcesigs (NodeInfo list)2555# .bsig2556# .csig2557# .timestamp2558# .size2559# .bdepends2560# .bdependsigs (NodeInfo list)2561# .bsig2562# .csig2563# .timestamp2564# .size2565# .bimplicit2566# .bimplicitsigs (NodeInfo list)2567# .bsig2568# .csig2569# .timestamp2570# .size2571# .bact2572# .bactsig2573#2574# The basic idea of the new structure is that a NodeInfo always2575# holds all available information about the state of a given Node2576# at a certain point in time. The various .b*sigs lists can just2577# be a list of pointers to the .ninfo attributes of the different2578# dependent nodes, without any copying of information until it's2579# time to pickle it for writing out to a .sconsign file.2580#2581# The complicating issue is that the *old* format only stored one2582# "signature" per dependency, based on however the *last* build2583# was configured. We don't know from just looking at it whether2584# it was a build signature, a content signature, or a timestamp2585# "signature". Since we no longer use build signatures, the2586# best we can do is look at the length and if it's thirty two,2587# assume that it was (or might have been) a content signature.2588# If it was actually a build signature, then it will cause a2589# rebuild anyway when it doesn't match the new content signature,2590# but that's probably the best we can do.2591importSCons.SConsign2592new_entry=SCons.SConsign.SConsignEntry()2593new_entry.binfo=self.new_binfo()2594binfo=new_entry.binfo2595forattrinself.convert_copy_attrs:2596try:2597value=getattr(old_entry,attr)2598exceptAttributeError:2599continue2600setattr(binfo,attr,value)2601delattr(old_entry,attr)2602forattrinself.convert_sig_attrs:2603try:2604sig_list=getattr(old_entry,attr)2605exceptAttributeError:2606continue2607value=[]2608forsiginsig_list:2609ninfo=self.new_ninfo()2610iflen(sig)==32:2611ninfo.csig=sig2612else:2613ninfo.timestamp=sig2614value.append(ninfo)2615setattr(binfo,attr,value)2616delattr(old_entry,attr)2617returnnew_entry

2622try:2623returnself._memo['get_stored_info']2624exceptKeyError:2625pass26262627try:2628sconsign_entry=self.dir.sconsign().get_entry(self.name)2629except(KeyError,EnvironmentError):2630importSCons.SConsign2631sconsign_entry=SCons.SConsign.SConsignEntry()2632sconsign_entry.binfo=self.new_binfo()2633sconsign_entry.ninfo=self.new_ninfo()2634else:2635ifisinstance(sconsign_entry,FileBuildInfo):2636# This is a .sconsign file from before the Big Signature2637# Refactoring; convert it as best we can.2638sconsign_entry=self.convert_old_entry(sconsign_entry)2639try:2640delattr(sconsign_entry.ninfo,'bsig')2641exceptAttributeError:2642pass26432644self._memo['get_stored_info']=sconsign_entry26452646returnsconsign_entry

2663"""Return the included implicit dependencies in this file.2664 Cache results so we only scan the file once per path2665 regardless of how many times this information is requested.2666 """2667memo_key=(id(env),id(scanner),path)2668try:2669memo_dict=self._memo['get_found_includes']2670exceptKeyError:2671memo_dict={}2672self._memo['get_found_includes']=memo_dict2673else:2674try:2675returnmemo_dict[memo_key]2676exceptKeyError:2677pass26782679ifscanner:2680# result = [n.disambiguate() for n in scanner(self, env, path)]2681result=scanner(self,env,path)2682result=[N.disambiguate()forNinresult]2683else:2684result=[]26852686memo_dict[memo_key]=result26872688returnresult

2696"""Try to push the node into a cache2697 """2698# This should get called before the Nodes' .built() method is2699# called, which would clear the build signature if the file has2700# a source scanner.2701#2702# We have to clear the local memoized values *before* we push2703# the node to cache so that the memoization of the self.exists()2704# return value doesn't interfere.2705ifself.nocache:2706return2707self.clear_memoized_values()2708ifself.exists():2709self.get_build_env().get_CacheDir().push(self)

2712"""Try to retrieve the node's content from a cache27132714 This method is called from multiple threads in a parallel build,2715 so only do thread safe stuff here. Do thread unsafe stuff in2716 built().27172718 Returns true iff the node was successfully retrieved.2719 """2720ifself.nocache:2721returnNone2722ifnotself.is_derived():2723returnNone2724returnself.get_build_env().get_CacheDir().retrieve(self)

2770"""Return whether this Node has a source builder or not.27712772 If this Node doesn't have an explicit source code builder, this2773 is where we figure out, on the fly, if there's a transparent2774 source code builder for it.27752776 Note that if we found a source builder, we also set the2777 self.builder attribute, so that all of the methods that actually2778 *build* this file don't have to do anything different.2779 """2780try:2781scb=self.sbuilder2782exceptAttributeError:2783scb=self.sbuilder=self.find_src_builder()2784returnscbisnotNone

2836self._createDir()2837ifprint_duplicate:2838print"dup: relinking variant '%s' from '%s'"%(self,src)2839Unlink(self,None,None)2840e=Link(self,src,None)2841ifisinstance(e,SCons.Errors.BuildError):2842desc="Cannot duplicate `%s' in `%s': %s."%(src.path,self.dir.path,e.errstr)2843raiseSCons.Errors.StopError(desc)2844self.linked=12845# The Link() action may or may not have actually2846# created the file, depending on whether the -n2847# option was used or not. Delete the _exists and2848# _rexists attributes so they can be reevaluated.2849self.clear()

2854try:2855returnself._memo['exists']2856exceptKeyError:2857pass2858# Duplicate from source path if we are set up to do this.2859ifself.duplicateandnotself.is_derived()andnotself.linked:2860src=self.srcnode()2861ifsrcisnotself:2862# At this point, src is meant to be copied in a variant directory.2863src=src.rfile()2864ifsrc.abspath!=self.abspath:2865ifsrc.exists():2866self.do_duplicate(src)2867# Can't return 1 here because the duplication might2868# not actually occur if the -n option is being used.2869else:2870# The source file does not exist. Make sure no old2871# copy remains in the variant directory.2872ifprint_duplicate:2873print"dup: no src for %s, unlinking old variant copy"%self2874ifBase.exists(self)orself.islink():2875self.fs.unlink(self.path)2876# Return None explicitly because the Base.exists() call2877# above will have cached its value if the file existed.2878self._memo['exists']=None2879returnNone2880result=Base.exists(self)2881self._memo['exists']=result2882returnresult

2889"""2890 Returns the content signature currently stored for this node2891 if it's been unmodified longer than the max_drift value, or the2892 max_drift value is 0. Returns None otherwise.2893 """2894old=self.get_stored_info()2895mtime=self.get_timestamp()28962897max_drift=self.fs.max_drift2898ifmax_drift>0:2899if(time.time()-mtime)>max_drift:2900try:2901n=old.ninfo2902ifn.timestampandn.csigandn.timestamp==mtime:2903returnn.csig2904exceptAttributeError:2905pass2906elifmax_drift==0:2907try:2908returnold.ninfo.csig2909exceptAttributeError:2910pass29112912returnNone

2915"""2916 Generate a node's content signature, the digested signature2917 of its content.29182919 node - the node2920 cache - alternate node to use for the signature cache2921 returns - the content signature2922 """2923ninfo=self.get_ninfo()2924try:2925returnninfo.csig2926exceptAttributeError:2927pass29282929csig=self.get_max_drift_csig()2930ifcsigisNone:29312932try:2933ifself.get_size()<SCons.Node.FS.File.md5_chunksize:2934contents=self.get_contents()2935else:2936csig=self.get_content_hash()2937exceptIOError:2938# This can happen if there's actually a directory on-disk,2939# which can be the case if they've disabled disk checks,2940# or if an action with a File target actually happens to2941# create a same-named directory by mistake.2942csig=''2943else:2944ifnotcsig:2945csig=SCons.Util.MD5signature(contents)29462947ninfo.csig=csig29482949returncsig

3001T=03002ifT:Trace('is_up_to_date(%s):'%self)3003ifnotself.exists():3004ifT:Trace(' not self.exists():')3005# The file doesn't exist locally...3006r=self.rfile()3007ifr!=self:3008# ...but there is one in a Repository...3009ifnotself.changed(r):3010ifT:Trace(' changed(%s):'%r)3011# ...and it's even up-to-date...3012ifself._local:3013# ...and they'd like a local copy.3014e=LocalCopy(self,r,None)3015ifisinstance(e,SCons.Errors.BuildError):3016raise3017self.store_info()3018ifT:Trace(' 1\n')3019return13020self.changed()3021ifT:Trace(' None\n')3022returnNone3023else:3024r=self.changed()3025ifT:Trace(' self.exists(): %s\n'%r)3026returnnotr

3031try:3032returnself._memo['rfile']3033exceptKeyError:3034pass3035result=self3036ifnotself.exists():3037norm_name=_my_normcase(self.name)3038fordirinself.dir.get_all_rdirs():3039try:node=dir.entries[norm_name]3040exceptKeyError:node=dir.file_on_disk(self.name)3041ifnodeandnode.exists()and \ 3042(isinstance(node,File)orisinstance(node,Entry) \ 3043ornotnode.is_derived()):3044result=node3045# Copy over our local attributes to the repository3046# Node so we identify shared object files in the3047# repository and don't assume they're static.3048#3049# This isn't perfect; the attribute would ideally3050# be attached to the object in the repository in3051# case it was built statically in the repository3052# and we changed it to shared locally, but that's3053# rarely the case and would only occur if you3054# intentionally used the same suffix for both3055# shared and static objects anyway. So this3056# should work well in practice.3057result.attributes=self.attributes3058break3059self._memo['rfile']=result3060returnresult

3066"""3067 Fetch a Node's content signature for purposes of computing3068 another Node's cachesig.30693070 This is a wrapper around the normal get_csig() method that handles3071 the somewhat obscure case of using CacheDir with the -n option.3072 Any files that don't exist would normally be "built" by fetching3073 them from the cache, but the normal get_csig() method will try3074 to open up the local file, which doesn't exist because the -n3075 option