"""
#
-# Copyright (c) 2001, 2002 Steven Knight
+# __COPYRIGHT__
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-import string
import os
import os.path
-import types
+import shutil
+import stat
+import string
+
+import SCons.Action
+import SCons.Errors
import SCons.Node
-from UserDict import UserDict
-import sys
-from SCons.Errors import UserError
+import SCons.Util
import SCons.Warnings
-try:
- import os
- _link = os.link
-except AttributeError:
- import shutil
- import stat
- def _link(src, dest):
- shutil.copy2(src, dest)
- st=os.stat(src)
- os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
-
-def file_link(src, dest):
+#
+# SCons.Action objects for interacting with the outside world.
+#
+# The Node.FS methods in this module should use these actions to
+# create and/or remove files and directories; they should *not* use
+# os.{link,symlink,unlink,mkdir}(), etc., directly.
+#
+# Using these SCons.Action objects ensures that descriptions of these
+# external activities are properly displayed, that the displays are
+# suppressed when the -s (silent) option is used, and (most importantly)
+# the actions are disabled when the the -n option is used, in which case
+# there should be *no* changes to the external file system(s)...
+#
+
+if hasattr(os, 'symlink'):
+ def _existsp(p):
+ return os.path.exists(p) or os.path.islink(p)
+else:
+ _existsp = os.path.exists
+
+def LinkFunc(target, source, env):
+ src = str(source[0])
+ dest = str(target[0])
dir, file = os.path.split(dest)
if dir and not os.path.isdir(dir):
os.makedirs(dir)
- _link(src, dest)
+ # Now actually link the files. First try to make a hard link. If that
+ # fails, try a symlink. If that fails then just copy it.
+ try :
+ os.link(src, dest)
+ except (AttributeError, OSError):
+ try :
+ os.symlink(src, dest)
+ except (AttributeError, OSError):
+ shutil.copy2(src, dest)
+ st=os.stat(src)
+ os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
+ return 0
+
+Link = SCons.Action.Action(LinkFunc, None)
+
+def LocalString(target, source, env):
+ return 'Local copy of %s from %s' % (target[0], source[0])
+
+LocalCopy = SCons.Action.Action(LinkFunc, LocalString)
+
+def UnlinkFunc(target, source, env):
+ os.unlink(target[0].path)
+ return 0
+
+Unlink = SCons.Action.Action(UnlinkFunc, None)
+
+def MkdirFunc(target, source, env):
+ os.mkdir(target[0].path)
+ return 0
+
+Mkdir = SCons.Action.Action(MkdirFunc, None)
+
+def CacheRetrieveFunc(target, source, env):
+ t = target[0]
+ cachedir, cachefile = t.cachepath()
+ if os.path.exists(cachefile):
+ shutil.copy2(cachefile, t.path)
+ st = os.stat(cachefile)
+ os.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
+ return 0
+ return 1
+
+def CacheRetrieveString(target, source, env):
+ t = target[0]
+ cachedir, cachefile = t.cachepath()
+ if os.path.exists(cachefile):
+ return "Retrieved `%s' from cache" % t.path
+ return None
+
+CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString)
+
+CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None)
+
+def CachePushFunc(target, source, env):
+ t = target[0]
+ cachedir, cachefile = t.cachepath()
+ if os.path.exists(cachefile):
+ # Don't bother copying it if it's already there.
+ return
+
+ if not os.path.isdir(cachedir):
+ os.mkdir(cachedir)
+
+ tempfile = cachefile+'.tmp'
+ try:
+ shutil.copy2(t.path, tempfile)
+ os.rename(tempfile, cachefile)
+ st = os.stat(t.path)
+ os.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
+ except OSError:
+ # It's possible someone else tried writing the file at the same
+ # time we did. Print a warning but don't stop the build, since
+ # it doesn't affect the correctness of the build.
+ SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning,
+ "Unable to copy %s to cache. Cache file is %s"
+ % (str(target), cachefile))
+ return
+
+CachePush = SCons.Action.Action(CachePushFunc, None)
+
+class _Null:
+ pass
+
+_null = _Null()
+
+DefaultSCCSBuilder = None
+DefaultRCSBuilder = None
+
+def get_DefaultSCCSBuilder():
+ global DefaultSCCSBuilder
+ if DefaultSCCSBuilder is None:
+ import SCons.Builder
+ import SCons.Defaults
+ DefaultSCCSBuilder = SCons.Builder.Builder(action = '$SCCSCOM',
+ env = SCons.Defaults._default_env)
+ return DefaultSCCSBuilder
+
+def get_DefaultRCSBuilder():
+ global DefaultRCSBuilder
+ if DefaultRCSBuilder is None:
+ import SCons.Builder
+ import SCons.Defaults
+ DefaultRCSBuilder = SCons.Builder.Builder(action = '$RCS_COCOM',
+ env = SCons.Defaults._default_env)
+ return DefaultRCSBuilder
+#
class ParentOfRoot:
"""
An instance of this class is used as the parent of the root of a
self.name=''
self.duplicate=0
self.srcdir=None
+ self.build_dirs=[]
def is_under(self, dir):
return 0
def get_dir(self):
return None
+ def recurse_get_path(self, dir, path_elems):
+ return path_elems
+
+ def src_builder(self):
+ return _null
+
if os.path.normcase("TeSt") == os.path.normpath("TeSt"):
def _my_normcase(x):
return x
def _my_normcase(x):
return string.upper(x)
+class EntryProxy(SCons.Util.Proxy):
+ def __get_abspath(self):
+ entry = self.get()
+ return SCons.Util.SpecialAttrWrapper(entry.get_abspath(),
+ entry.name + "_abspath")
+
+ def __get_filebase(self):
+ name = self.get().name
+ return SCons.Util.SpecialAttrWrapper(os.path.splitext(name)[0],
+ name + "_filebase")
+
+ def __get_suffix(self):
+ entry.name = self.get().name
+ return SCons.Util.SpecialAttrWrapper(os.path.splitext(name)[1],
+ name + "_suffix")
+
+ def __get_file(self):
+ entry.name = self.get().name
+ return SCons.Util.SpecialAttrWrapper(name, name + "_file")
+
+ def __get_base_path(self):
+ """Return the file's directory and file name, with the
+ suffix stripped."""
+ entry = self.get()
+ return SCons.Util.SpecialAttrWrapper(os.path.splitext(entry.get_path())[0],
+ entry.name + "_base")
+
+ def __get_posix_path(self):
+ """Return the path with / as the path separator, regardless
+ of platform."""
+ if os.sep == '/':
+ return self
+ else:
+ entry = self.get()
+ return SCons.Util.SpecialAttrWrapper(string.replace(entry.get_path(),
+ os.sep, '/'),
+ entry.name + "_posix")
+
+ def __get_srcnode(self):
+ return EntryProxy(self.get().srcnode())
+
+ def __get_srcdir(self):
+ """Returns the directory containing the source node linked to this
+ node via BuildDir(), or the directory of this node if not linked."""
+ return EntryProxy(self.get().srcnode().dir)
+
+ def __get_dir(self):
+ return EntryProxy(self.get().dir)
+
+ dictSpecialAttrs = { "base" : __get_base_path,
+ "posix" : __get_posix_path,
+ "srcpath" : __get_srcnode,
+ "srcdir" : __get_srcdir,
+ "dir" : __get_dir,
+ "abspath" : __get_abspath,
+ "filebase" : __get_filebase,
+ "suffix" : __get_suffix,
+ "file" : __get_file }
+
+ def __getattr__(self, name):
+ # This is how we implement the "special" attributes
+ # such as base, posix, srcdir, etc.
+ try:
+ return self.dictSpecialAttrs[name](self)
+ except KeyError:
+ return SCons.Util.Proxy.__getattr__(self, name)
+
class Entry(SCons.Node.Node):
"""A generic class for file system entries. This class is for
when we don't know yet whether the entry being looked up is a file
self.name = name
self.fs = fs
+ self.relpath = {}
assert directory, "A directory must be provided"
self.cwd = None # will hold the SConscript directory for target nodes
self.duplicate = directory.duplicate
+ def clear(self):
+ """Completely clear an Entry of all its cached state (so that it
+ can be re-evaluated by interfaces that do continuous integration
+ builds).
+ """
+ SCons.Node.Node.clear(self)
+ try:
+ delattr(self, '_exists')
+ except AttributeError:
+ pass
+ try:
+ delattr(self, '_rexists')
+ except AttributeError:
+ pass
+
def get_dir(self):
return self.dir
def __str__(self):
"""A FS node's string representation is its path name."""
- if self.duplicate or self.builder:
- return self.path
- return self.srcnode().path
+ if self.duplicate or self.is_derived():
+ return self.get_path()
+ return self.srcnode().get_path()
def get_contents(self):
"""Fetch the contents of the entry.
try:
return self._exists
except AttributeError:
- self._exists = os.path.exists(self.abspath)
+ self._exists = _existsp(self.abspath)
return self._exists
def rexists(self):
self._srcnode = self
return self._srcnode
+ def recurse_get_path(self, dir, path_elems):
+ """Recursively build a path relative to a supplied directory
+ node."""
+ if self != dir:
+ path_elems.append(self.name)
+ path_elems = self.dir.recurse_get_path(dir, path_elems)
+ return path_elems
+
+ def get_path(self, dir=None):
+ """Return path relative to the current working directory of the
+ FS object that owns us."""
+ if not dir:
+ dir = self.fs.getcwd()
+ try:
+ return self.relpath[dir]
+ except KeyError:
+ if self == dir:
+ # Special case, return "." as the path
+ ret = '.'
+ else:
+ path_elems = self.recurse_get_path(dir, [])
+ path_elems.reverse()
+ ret = string.join(path_elems, os.sep)
+ self.relpath[dir] = ret
+ return ret
+
+ def set_src_builder(self, builder):
+ """Set the source code builder for this node."""
+ self.sbuilder = builder
+
+ def src_builder(self):
+ """Fetch the source code builder for this node.
+
+ If there isn't one, we cache the source code builder specified
+ for the directory (which in turn will cache the value from its
+ parent directory, and so on up to the file system root).
+ """
+ try:
+ scb = self.sbuilder
+ except AttributeError:
+ scb = self.dir.src_builder()
+ self.sbuilder = scb
+ return scb
+
+ def get_abspath(self):
+ """Get the absolute path of the file."""
+ return self.abspath
+
+ def for_signature(self):
+ # Return just our name. Even an absolute path would not work,
+ # because that can change thanks to symlinks or remapped network
+ # paths.
+ return self.name
+
+ def get_subst_proxy(self):
+ try:
+ return self._proxy
+ except AttributeError:
+ ret = EntryProxy(self)
+ self._proxy = ret
+ return ret
+
# This is for later so we can differentiate between Entry the class and Entry
# the method of the FS class.
_classEntry = Entry
self.pathTop = path
self.Root = {}
self.Top = None
+ self.SConstruct_dir = None
+ self.CachePath = None
+ self.cache_force = None
+ self.cache_show = None
def set_toplevel_dir(self, path):
assert not self.Top, "You can only set the top-level path on an FS object that has not had its File, Dir, or Entry methods called yet."
self.pathTop = path
+
+ def set_SConstruct_dir(self, dir):
+ self.SConstruct_dir = dir
def __setTopLevelDir(self):
if not self.Top:
directory = self.Root[drive]
except KeyError:
if not create:
- raise UserError
+ raise SCons.Errors.UserError
dir = Dir(drive, ParentOfRoot(), self)
dir.path = dir.path + os.sep
dir.abspath = dir.abspath + os.sep
Dir)
except KeyError:
if not create:
- raise UserError
+ raise SCons.Errors.UserError
# look at the actual filesystem and make sure there isn't
# a file already there
ret = self.__checkClass(directory.entries[file_name], fsclass)
except KeyError:
if not create:
- raise UserError
+ raise SCons.Errors.UserError
# make sure we don't create File nodes when there is actually
# a directory at that path on the disk, and vice versa
then the same applies.
"""
self.__setTopLevelDir()
- if name[0] == '#':
+ if name and name[0] == '#':
directory = self.Top
name = name[1:]
if name and (name[0] == os.sep or name[0] == '/'):
directory = self._cwd
return (os.path.normpath(name), directory)
- def chdir(self, dir):
+ def chdir(self, dir, change_os_dir=0):
"""Change the current working directory for lookups.
+ If change_os_dir is true, we will also change the "real" cwd
+ to match.
"""
self.__setTopLevelDir()
- if not dir is None:
- self._cwd = dir
+ curr=self._cwd
+ try:
+ if not dir is None:
+ self._cwd = dir
+ if change_os_dir:
+ os.chdir(dir.abspath)
+ except:
+ self._cwd = curr
+ raise
def Entry(self, name, directory = None, create = 1, klass=None):
"""Lookup or create a generic Entry node with the specified name.
if not isinstance(build_dir, SCons.Node.Node):
build_dir = self.Dir(build_dir)
if not src_dir.is_under(self.Top):
- raise UserError, "Source directory must be under top of build tree."
+ raise SCons.Errors.UserError, "Source directory must be under top of build tree."
if src_dir.is_under(build_dir):
- raise UserError, "Source directory cannot be under build directory."
+ raise SCons.Errors.UserError, "Source directory cannot be under build directory."
build_dir.link(src_dir, duplicate)
def Repository(self, *dirs):
n = self.__doLookup(clazz, name, d)
if n.exists():
return n
+ if isinstance(n, Dir):
+ # If n is a Directory that has Repositories directly
+ # attached to it, then any of those is a valid Repository
+ # path. Return the first one that exists.
+ reps = filter(lambda x: x.exists(), n.getRepositories())
+ if len(reps):
+ return reps[0]
d = n.get_dir()
name = n.name
# Search repositories of all directories that this file is under.
# This is usually the case with BuildDir().
# We only want to find pre-existing files.
if rnode.exists() and \
- (isinstance(rnode, Dir) or not rnode.builder):
+ (isinstance(rnode, Dir) or not rnode.is_derived()):
return rnode
except TypeError:
pass # Wrong type of node.
# Go up one directory
d = d.get_dir()
return None
-
def Rsearchall(self, pathlist, must_exist=1, clazz=_classEntry, cwd=None):
"""Search for a list of somethings in the Repository list."""
d = n.get_dir()
name = n.name
- # Search repositories of all directories that this file is under.
+ # Search repositories of all directories that this file
+ # is under.
while d:
for rep in d.getRepositories():
try:
rnode = self.__doLookup(clazz, name, rep)
- # Only find the node if it exists (or must_exist is zero)
- # and it is not a derived file. If for some reason, we
- # are explicitly building a file IN a Repository, we don't
- # want it to show up in the build tree. This is usually the
- # case with BuildDir(). We only want to find pre-existing files.
+ # Only find the node if it exists (or
+ # must_exist is zero) and it is not a
+ # derived file. If for some reason, we
+ # are explicitly building a file IN a
+ # Repository, we don't want it to show up in
+ # the build tree. This is usually the case
+ # with BuildDir(). We only want to find
+ # pre-existing files.
if (not must_exist or rnode.exists()) and \
- (not rnode.builder or isinstance(rnode, Dir)):
+ (not rnode.is_derived() or isinstance(rnode, Dir)):
ret.append(rnode)
except TypeError:
pass # Wrong type of node.
d = d.get_dir()
return ret
+ def CacheDir(self, path):
+ self.CachePath = path
+
+ def build_dir_target_climb(self, dir, tail):
+ """Create targets in corresponding build directories
+
+ Climb the directory tree, and look up path names
+ relative to any linked build directories we find.
+ """
+ targets = []
+ message = None
+ while dir:
+ for bd in dir.build_dirs:
+ p = apply(os.path.join, [bd.path] + tail)
+ targets.append(self.Entry(p))
+ tail = [dir.name] + tail
+ dir = dir.up()
+ if targets:
+ message = "building associated BuildDir targets: %s" % string.join(map(str, targets))
+ return targets, message
+
# XXX TODO?
# Annotate with the creator
# rel_path
self.cwd = self
self.builder = 1
self._sconsign = None
+ self.build_dirs = []
def __clearRepositoryCache(self, duplicate=None):
"""Called when we change the repository(ies) for a directory.
def __resetDuplicate(self, node):
if node != self:
node.duplicate = node.get_dir().duplicate
-
+
+ def Entry(self, name):
+ """Create an entry node named 'name' relative to this directory."""
+ return self.fs.Entry(name, self)
+
def Dir(self, name):
"""Create a directory node named 'name' relative to this directory."""
return self.fs.Dir(name, self)
def File(self, name):
- """Create file node named 'name' relatove to this directory."""
+ """Create a file node named 'name' relative to this directory."""
return self.fs.File(name, self)
-
+
def link(self, srcdir, duplicate):
"""Set this directory as the build directory for the
supplied source directory."""
self.srcdir = srcdir
self.duplicate = duplicate
self.__clearRepositoryCache(duplicate)
+ srcdir.build_dirs.append(self)
def getRepositories(self):
"""Returns a list of repositories for this directory."""
else:
return self.entries['..'].root()
- def all_children(self, scanner):
- #XXX --random: randomize "dependencies?"
+ def all_children(self, scan):
keys = filter(lambda k: k != '.' and k != '..', self.entries.keys())
kids = map(lambda x, s=self: s.entries[x], keys)
def c(one, two):
return 1
return 0
kids.sort(c)
- return kids
+ return kids + SCons.Node.Node.all_children(self, 0)
+
+ def get_actions(self):
+ """A null "builder" for directories."""
+ return []
def build(self):
"""A null "builder" for directories."""
pass
+ def alter_targets(self):
+ """Return any corresponding targets in a build directory.
+ """
+ return self.fs.build_dir_target_climb(self, [])
+
def calc_signature(self, calc):
"""A directory has no signature."""
return None
"""Return a fixed "contents" value of a directory."""
return ''
+ def prepare(self):
+ pass
+
def current(self, calc):
"""If all of our children were up-to-date, then this
directory was up-to-date, too."""
else:
return 0
+ def rdir(self):
+ try:
+ return self._rdir
+ except AttributeError:
+ self._rdir = self
+ if not self.exists():
+ n = self.fs.Rsearch(self.path, clazz=Dir, cwd=self.fs.Top)
+ if n:
+ self._rdir = n
+ return self._rdir
+
def sconsign(self):
"""Return the .sconsign file info for this directory,
creating it first if necessary."""
Entry.__init__(self, name, directory, fs)
self._morph()
+ def Entry(self, name):
+ """Create an entry node named 'name' relative to
+ the SConscript directory of this file."""
+ return self.fs.Entry(name, self.cwd)
def Dir(self, name):
"""Create a directory node named 'name' relative to
return self.fs.Rsearchall(pathlist, clazz=Dir, must_exist=0,
cwd=self.cwd)
- def generate_build_env(self):
- env = SCons.Node.Node.generate_build_env(self)
-
+ def generate_build_env(self, env):
+ """Generate an appropriate Environment to build this File."""
return env.Override({'Dir' : self.Dir,
'File' : self.File,
'RDirs' : self.RDirs})
def _morph(self):
"""Turn a file system node into a File object."""
- self.created = 0
+ self.scanner_paths = {}
+ self.found_includes = {}
if not hasattr(self, '_local'):
self._local = 0
def get_contents(self):
if not self.rexists():
return ''
- return open(self.rstr(), "rb").read()
+ return open(self.rfile().abspath, "rb").read()
def get_timestamp(self):
if self.rexists():
- return os.path.getmtime(self.rstr())
+ return os.path.getmtime(self.rfile().abspath)
else:
return 0
- def calc_signature(self, calc):
+ def calc_signature(self, calc, cache=None):
"""
Select and calculate the appropriate build signature for a File.
self - the File node
calc - the signature calculation module
+ cache - alternate node to use for the signature cache
returns - the signature
-
- This method does not store the signature in the node or
- in the .sconsign file.
"""
- if self.builder:
+ if self.is_derived():
if SCons.Sig.build_signature:
- if not hasattr(self, 'bsig'):
- self.set_bsig(calc.bsig(self.rfile()))
- return self.get_bsig()
+ return calc.bsig(self.rfile(), self)
else:
- if not hasattr(self, 'csig'):
- self.set_csig(calc.csig(self.rfile()))
- return self.get_csig()
+ return calc.csig(self.rfile(), self)
elif not self.rexists():
return None
else:
- if not hasattr(self, 'csig'):
- self.set_csig(calc.csig(self.rfile()))
- return self.get_csig()
-
+ return calc.csig(self.rfile(), self)
+
def store_csig(self):
self.dir.sconsign().set_csig(self.name, self.get_csig())
def get_stored_implicit(self):
return self.dir.sconsign().get_implicit(self.name)
- def get_implicit_deps(self, env, scanner, target):
- if scanner:
- return scanner.scan(self, env, target)
- else:
+ def get_found_includes(self, env, scanner, target):
+ """Return the included implicit dependencies in this file.
+ Cache results so we only scan the file once regardless of
+ how many times this information is requested."""
+ if not scanner:
return []
-
+
+ try:
+ path = target.scanner_paths[scanner]
+ except AttributeError:
+ # The target had no scanner_paths attribute, which means
+ # it's an Alias or some other node that's not actually a
+ # file. In that case, back off and use the path for this
+ # node itself.
+ try:
+ path = self.scanner_paths[scanner]
+ except KeyError:
+ path = scanner.path(env, self.cwd)
+ self.scanner_paths[scanner] = path
+ except KeyError:
+ path = scanner.path(env, target.cwd)
+ target.scanner_paths[scanner] = path
+
+ try:
+ includes = self.found_includes[path]
+ except KeyError:
+ includes = scanner(self, env, path)
+ self.found_includes[path] = includes
+
+ return includes
+
def scanner_key(self):
return os.path.splitext(self.name)[1]
- def __createDir(self):
+ def _createDir(self):
# ensure that the directories for this node are
# created.
if parent.exists():
break
listDirs.append(parent)
- parent = parent.up()
+ p = parent.up()
+ if isinstance(p, ParentOfRoot):
+ raise SCons.Errors.StopError, parent.path
+ parent = p
listDirs.reverse()
for dirnode in listDirs:
try:
- os.mkdir(dirnode.abspath)
- dirnode._exists = 1
+ Mkdir(dirnode, None, None)
+ # The Mkdir() action may or may not have actually
+ # created the directory, depending on whether the -n
+ # option was used or not. Delete the _exists and
+ # _rexists attributes so they can be reevaluated.
+ if hasattr(dirnode, '_exists'):
+ delattr(dirnode, '_exists')
+ if hasattr(dirnode, '_rexists'):
+ delattr(dirnode, '_rexists')
except OSError:
pass
+ def build(self):
+ """Actually build the file.
+
+ This overrides the base class build() method to check for the
+ existence of derived files in a CacheDir before going ahead and
+ building them.
+
+ This method is called from multiple threads in a parallel build,
+ so only do thread safe stuff here. Do thread unsafe stuff in
+ built().
+ """
+ b = self.is_derived()
+ if not b and not self.has_src_builder():
+ return
+ if b and self.fs.CachePath:
+ if self.fs.cache_show:
+ if CacheRetrieveSilent(self, None, None) == 0:
+ def do_print(action, targets, sources, env, self=self):
+ al = action.strfunction(targets, self.sources, env)
+ if not SCons.Util.is_List(al):
+ al = [al]
+ for a in al:
+ action.show(a)
+ self._for_each_action(do_print)
+ return
+ elif CacheRetrieve(self, None, None) == 0:
+ return
+ SCons.Node.Node.build(self)
+
def built(self):
+ """Called just after this node is sucessfully built."""
+ # Push this file out to cache before the superclass Node.built()
+ # method has a chance to clear the build signature, which it
+ # will do if this file has a source scanner.
+ if self.fs.CachePath and os.path.exists(self.path):
+ CachePush(self, None, None)
SCons.Node.Node.built(self)
+ self.found_includes = {}
if hasattr(self, '_exists'):
delattr(self, '_exists')
if hasattr(self, '_rexists'):
delattr(self, '_rexists')
+ def visited(self):
+ if self.fs.CachePath and self.fs.cache_force and os.path.exists(self.path):
+ CachePush(self, None, None)
+
+ def has_src_builder(self):
+ """Return whether this Node has a source builder or not.
+
+ If this Node doesn't have an explicit source code builder, this
+ is where we figure out, on the fly, if there's a transparent
+ source code builder for it.
+
+ Note that if we found a source builder, we also set the
+ self.builder attribute, so that all of the methods that actually
+ *build* this file don't have to do anything different.
+ """
+ try:
+ scb = self.sbuilder
+ except AttributeError:
+ if self.rexists():
+ scb = None
+ else:
+ scb = self.dir.src_builder()
+ if scb is _null:
+ scb = None
+ dir = self.dir.path
+ sccspath = os.path.join('SCCS', 's.' + self.name)
+ if dir != '.':
+ sccspath = os.path.join(dir, sccspath)
+ if os.path.exists(sccspath):
+ scb = get_DefaultSCCSBuilder()
+ else:
+ rcspath = os.path.join('RCS', self.name + ',v')
+ if dir != '.':
+ rcspath = os.path.join(dir, rcspath)
+ if os.path.exists(rcspath):
+ scb = get_DefaultRCSBuilder()
+ self.builder = scb
+ self.sbuilder = scb
+ return not scb is None
+
+ def alter_targets(self):
+ """Return any corresponding targets in a build directory.
+ """
+ if self.is_derived():
+ return [], None
+ return self.fs.build_dir_target_climb(self.dir, [self.name])
+
+ def is_pseudo_derived(self):
+ return self.has_src_builder()
+
def prepare(self):
"""Prepare for this file to be created."""
- if self.exists():
- if self.builder and not self.precious:
- os.unlink(self.path)
- if hasattr(self, '_exists'):
- delattr(self, '_exists')
- else:
- self.__createDir()
+
+ SCons.Node.Node.prepare(self)
+
+ if self.get_state() != SCons.Node.up_to_date:
+ if self.exists():
+ if self.is_derived() and not self.precious:
+ try:
+ Unlink(self, None, None)
+ except OSError, e:
+ raise SCons.Errors.BuildError(node = self,
+ errstr = e.strerror)
+ if hasattr(self, '_exists'):
+ delattr(self, '_exists')
+ else:
+ try:
+ self._createDir()
+ except SCons.Errors.StopError, drive:
+ desc = "No drive `%s' for target `%s'." % (drive, self)
+ raise SCons.Errors.StopError, desc
def remove(self):
"""Remove this file."""
- if os.path.exists(self.path):
+ if _existsp(self.path):
os.unlink(self.path)
return 1
return None
def exists(self):
# Duplicate from source path if we are set up to do this.
- if self.duplicate and not self.builder and not self.created:
+ if self.duplicate and not self.is_derived() and not self.linked:
src=self.srcnode().rfile()
if src.exists() and src.abspath != self.abspath:
+ self._createDir()
try:
- os.unlink(self.abspath)
+ Unlink(self, None, None)
except OSError:
pass
- self.__createDir()
- file_link(src.abspath, self.abspath)
- self.created = 1
-
- # Set our exists cache accordingly
- self._exists=1
- self._rexists=1
- return 1
+ try:
+ Link(self, src, None)
+ except IOError, e:
+ desc = "Cannot duplicate `%s' in `%s': %s." % (src, self.dir, e.strerror)
+ raise SCons.Errors.StopError, desc
+ self.linked = 1
+ # The Link() action may or may not have actually
+ # created the file, depending on whether the -n
+ # option was used or not. Delete the _exists and
+ # _rexists attributes so they can be reevaluated.
+ if hasattr(self, '_exists'):
+ delattr(self, '_exists')
+ if hasattr(self, '_rexists'):
+ delattr(self, '_rexists')
return Entry.exists(self)
def current(self, calc):
# ...and it's even up-to-date...
if self._local:
# ...and they'd like a local copy.
- print "Local copy of %s from %s" % (self.path, r.path)
- file_link(r.path, self.path)
+ LocalCopy(self, r, None)
self.set_bsig(bsig)
self.store_bsig()
return 1
def rstr(self):
return str(self.rfile())
+ def cachepath(self):
+ if self.fs.CachePath:
+ bsig = self.get_bsig()
+ if bsig is None:
+ raise SCons.Errors.InternalError, "cachepath(%s) found a bsig of None" % self.path
+ bsig = str(bsig)
+ subdir = string.upper(bsig[0])
+ dir = os.path.join(self.fs.CachePath, subdir)
+ return dir, os.path.join(dir, bsig)
+ return None, None
default_fs = FS()
try:
node = node_factory(filename, dir)
# Return true of the node exists or is a derived node.
- if node.builder or \
+ if node.is_derived() or \
(isinstance(node, SCons.Node.FS.Entry) and node.exists()):
retval = node
break