.EE
.TP
-.RI SConscript( script ", [" exports ", " build_dir ", " duplicate ])
+.RI SConscript( script ", [" exports ", " build_dir ", " src_dir ", " duplicate ])
This tells
.B scons
to execute
resides should actually
be built in
.IR build_dir .
+The optional
+.I src_dir
+argument specifies that the
+source files from which
+the target files should be built
+can be found in
+.IR src_dir .
By default,
.B scons
will link or copy (depending on the platform)
- Added support for the Perforce source code management system.
+ - Fix str(Node.FS) so that it returns a path relative to the calling
+ SConscript file's directory, not the top-level directory.
+
+ - Added support for a separate src_dir argument to SConscript()
+ that allows explicit specification of where the source files
+ for an SConscript file can be found.
+
From Steven Knight:
- Added an INSTALL construction variable that can be set to a function
def get_dir(self):
return None
+ def recurse_get_path(self, dir, path_elems):
+ return path_elems
+
def src_builder(self):
return None
self.name = name
self.fs = fs
+ self.relpath = {}
assert directory, "A directory must be provided"
def __str__(self):
"""A FS node's string representation is its path name."""
if self.duplicate or self.has_builder():
- return self.path
- return self.srcnode().path
+ return self.get_path()
+ return self.srcnode().get_path()
def get_contents(self):
"""Fetch the contents of the entry.
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
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
+ if change_os_dir:
+ os.chdir(dir.abspath)
def Entry(self, name, directory = None, create = 1, klass=None):
"""Lookup or create a generic Entry node with the specified name.
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
assert str(f1) == os.path.normpath('src/test.in'), str(f1)
# Build path does not exist
assert not f1.exists()
- # But source path does
- assert f1.srcnode().exists()
+ # ...but the actual file is not there...
+ assert not os.path.exists(f1.abspath)
# And duplicate=0 should also work just like a Repository
assert f1.rexists()
# rfile() should point to the source path
# Verify the Mkdir and Link actions are called
f9 = fs.File('build/var2/new_dir/test9.out')
+ # Test for an interesting pathological case...we have a source
+ # file in a build path, but not in a source path. This can
+ # happen if you switch from duplicate=1 to duplicate=0, then
+ # delete a source file. At one time, this would cause exists()
+ # to return a 1 but get_contents() to throw.
+ test.write([ 'work', 'build', 'var1', 'asourcefile' ], 'stuff')
+ f10 = fs.File('build/var1/asourcefile')
+ assert f10.exists()
+ assert f10.get_contents() == 'stuff', f10.get_contents()
+
save_Mkdir = SCons.Node.FS.Mkdir
dir_made = []
def mkdir_func(target, source, env, dir_made=dir_made):
fs = SCons.Node.FS.FS()
assert str(fs.getcwd()) == ".", str(fs.getcwd())
fs.chdir(fs.Dir('subdir'))
- assert str(fs.getcwd()) == "subdir", str(fs.getcwd())
+ # The cwd's path is always "."
+ assert str(fs.getcwd()) == ".", str(fs.getcwd())
+ assert fs.getcwd().path == 'subdir', fs.getcwd().path
fs.chdir(fs.Dir('../..'))
- assert str(fs.getcwd()) == test.workdir, str(fs.getcwd())
+ assert fs.getcwd().path == test.workdir, fs.getcwd().path
f1 = fs.File(test.workpath("do_i_exist"))
assert not f1.exists()
path = s.path(env)
test.write('include/fa.cpp', test.read('fa.cpp'))
deps = s(fs.File('#include/fa.cpp'), env, path)
+ fs.chdir(fs.Dir('..'))
deps_match(self, deps, [ 'include/fa.h', 'include/fb.h' ])
test.unlink('include/fa.cpp')
path = s.path(env)
test.write('include/fff4.f', test.read('fff4.f'))
deps = s(fs.File('#include/fff4.f'), env, path)
+ fs.chdir(fs.Dir('..'))
deps_match(self, deps, ['include/f4.f'])
test.unlink('include/fff4.f')
if not src_dir:
src_dir, fname = os.path.split(str(files[0]))
else:
- fname = os.path.split(files[0])[1]
+ if not isinstance(src_dir, SCons.Node.Node):
+ src_dir = SCons.Node.FS.default_fs.Dir(src_dir)
+ fn = files[0]
+ if not isinstance(fn, SCons.Node.Node):
+ fn = SCons.Node.FS.default_fs.File(fn)
+ if fn.is_under(src_dir):
+ # Get path relative to the source directory.
+ fname = fn.get_path(src_dir)
+ else:
+ # Fast way to only get the terminal path component of a Node.
+ fname = fn.get_path(fn.dir)
BuildDir(build_dir, src_dir, duplicate)
files = [os.path.join(str(build_dir), fname)]
else:
f = SCons.Node.FS.default_fs.File(str(fn))
_file_ = None
+ old_dir = SCons.Node.FS.default_fs.getcwd()
+ SCons.Node.FS.default_fs.chdir(SCons.Node.FS.default_fs.Dir('#'),
+ change_os_dir=1)
if f.rexists():
+ # Change directory to top of source tree to make sure
+ # the os's cwd and the cwd of SCons.Node.FS.default_fs
+ # match so we can open the SConscript.
_file_ = open(f.rstr(), "r")
elif f.has_builder():
# The SConscript file apparently exists in a source
s = str(f)
if os.path.exists(s):
_file_ = open(s, "r")
-
if _file_:
- SCons.Node.FS.default_fs.chdir(f.dir)
- if sconscript_chdir:
- old_dir = os.getcwd()
- os.chdir(str(f.dir))
-
+ SCons.Node.FS.default_fs.chdir(f.dir,
+ change_os_dir=sconscript_chdir)
# prepend the SConscript directory to sys.path so
# that Python modules in the SConscript directory can
# be easily imported
- sys.path = [os.path.abspath(str(f.dir))] + sys.path
+ sys.path = [ f.dir.abspath ] + sys.path
# This is the magic line that actually reads up and
# executes the stuff in the SConscript file. We
frame = stack.pop()
SCons.Node.FS.default_fs.chdir(frame.prev_dir)
if old_dir:
- os.chdir(old_dir)
+ SCons.Node.FS.default_fs.chdir(old_dir,
+ change_os_dir=sconscript_chdir)
results.append(frame.retval)
else:
nodes.extend(SCons.Node.arg2nodes(f, SCons.Node.FS.default_fs.Entry))
- s = str(target)
- if clean_targets.has_key(s):
- clean_targets[s].extend(nodes)
- else:
- clean_targets[s] = nodes
+ try:
+ clean_targets[target].extend(nodes)
+ except KeyError:
+ clean_targets[target] = nodes
def AddPreAction(files, action):
nodes = SCons.Node.arg2nodes(files, SCons.Node.FS.default_fs.Entry)
if (self.targets[0].has_builder() or self.targets[0].side_effect) \
and not os.path.isdir(str(self.targets[0])):
display("Removed " + str(self.targets[0]))
- if SCons.Script.SConscript.clean_targets.has_key(str(self.targets[0])):
- files = SCons.Script.SConscript.clean_targets[str(self.targets[0])]
+ if SCons.Script.SConscript.clean_targets.has_key(self.targets[0]):
+ files = SCons.Script.SConscript.clean_targets[self.targets[0]]
for f in files:
SCons.Util.fs_delete(str(f), 0)
else:
if removed:
display("Removed " + str(t))
- if SCons.Script.SConscript.clean_targets.has_key(str(self.targets[0])):
- files = SCons.Script.SConscript.clean_targets[str(self.targets[0])]
+ if SCons.Script.SConscript.clean_targets.has_key(self.targets[0]):
+ files = SCons.Script.SConscript.clean_targets[self.targets[0]]
for f in files:
SCons.Util.fs_delete(str(f))
all6 = test.workpath('build', 'var6', 'all')
all7 = test.workpath('build', 'var7', 'all')
all8 = test.workpath('build', 'var8', 'all')
+all9 = test.workpath('test', 'build', 'var9', 'src', 'all')
test.subdir('test')
var6 = Dir('../build/var6')
var7 = Dir('../build/var7')
var8 = Dir('../build/var8')
+var9 = Dir('../build/var9')
def cat(env, source, target):
target = str(target[0])
SConscript('src/SConscript', build_dir=var7, src_dir=src, duplicate=0)
SConscript('src/SConscript', build_dir='../build/var8', duplicate=0)
+
+# This tests the fact that if you specify a src_dir that is above
+# the dir a SConscript is in, that we do the intuitive thing, i.e.,
+# we set the path of the SConscript accordingly. The below is
+# equivalent to saying:
+#
+# BuildDir('build/var9', '.')
+# SConscript('build/var9/src/SConscript')
+SConscript('src/SConscript', build_dir='build/var9', src_dir='.')
""")
test.subdir(['test', 'src'], ['test', 'alt'])
test.fail_test(test.read(all6) != all_src)
test.fail_test(test.read(all7) != all_src)
test.fail_test(test.read(all8) != all_src)
+test.fail_test(test.read(all9) != all_src)
import os
import stat
#cmd_justlib = "cd %s ; make" % Dir(".")
cmd_generated = "%s $SOURCE" % (sys.executable,)
-cmd_justlib = "%s %s -C %s" % (sys.executable, sys.argv[0], Dir("."))
+cmd_justlib = "%s %s -C ${SOURCE[0].dir}" % (sys.executable, sys.argv[0])
##### Deps appear correct ... but wacky scanning?
# Why?