summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Node
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2001-12-15 00:23:46 (GMT)
committerSteven Knight <knight@baldmt.com>2001-12-15 00:23:46 (GMT)
commitaf3987ad4663708a4f9d10b661971fd7b29a9f0d (patch)
tree9de48645f9d229266078ede22a78fcd740f705b6 /src/engine/SCons/Node
parent0d14a2ff1774d42dc855aa9aab94791df3f55157 (diff)
downloadSCons-af3987ad4663708a4f9d10b661971fd7b29a9f0d.zip
SCons-af3987ad4663708a4f9d10b661971fd7b29a9f0d.tar.gz
SCons-af3987ad4663708a4f9d10b661971fd7b29a9f0d.tar.bz2
Add BuildDir(), Export(), and Install() functionality (courtesy Charles Crain).
Diffstat (limited to 'src/engine/SCons/Node')
-rw-r--r--src/engine/SCons/Node/FS.py85
-rw-r--r--src/engine/SCons/Node/FSTests.py54
-rw-r--r--src/engine/SCons/Node/NodeTests.py28
-rw-r--r--src/engine/SCons/Node/__init__.py22
4 files changed, 178 insertions, 11 deletions
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 648e1f9..303db61 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -39,6 +39,18 @@ import types
import SCons.Node
from UserDict import UserDict
import sys
+from SCons.Errors import UserError
+
+try:
+ import os
+ file_link = os.link
+except AttributeError:
+ import shutil
+ import stat
+ def file_link(src, dest):
+ shutil.copyfile(src, dest)
+ st=os.stat(src)
+ os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]))
class PathName:
"""This is a string like object with limited capabilities (i.e.,
@@ -119,6 +131,7 @@ class FS:
self.Root = PathDict()
self.Top = self.__doLookup(Dir, path)
self.Top.path = '.'
+ self.Top.srcpath = '.'
self.Top.path_ = os.path.join('.', '')
self.cwd = self.Top
@@ -242,7 +255,17 @@ class FS:
name, directory = self.__transformPath(name, directory)
return self.__doLookup(Dir, name, directory)
-
+ def BuildDir(self, build_dir, src_dir):
+ """Link the supplied build directory to the source directory
+ for purposes of building files."""
+ dirSrc = self.Dir(src_dir)
+ dirBuild = self.Dir(build_dir)
+ if not dirSrc.is_under(self.Top) or not dirBuild.is_under(self.Top):
+ raise UserError, "Both source and build directories must be under top of build tree."
+ if dirSrc.is_under(dirBuild):
+ raise UserError, "Source directory cannot be under build directory."
+ dirBuild.link(dirSrc)
+
class Entry(SCons.Node.Node):
"""A generic class for file system entries. This class if for
@@ -272,6 +295,19 @@ class Entry(SCons.Node.Node):
self.abspath_ = self.abspath
self.dir = directory
self.use_signature = 1
+ self.__doSrcpath()
+
+ def adjust_srcpath(self):
+ self.__doSrcpath()
+
+ def __doSrcpath(self):
+ if self.dir:
+ if str(self.dir.srcpath) == '.':
+ self.srcpath = self.name
+ else:
+ self.srcpath = os.path.join(self.dir.srcpath, self.name)
+ else:
+ self.srcpath = self.name
def __str__(self):
"""A FS node's string representation is its path name."""
@@ -301,6 +337,13 @@ class Entry(SCons.Node.Node):
return 0
return None
+ def is_under(self, dir):
+ if self is dir:
+ return 1
+ if not self.dir:
+ return 0
+ return self.dir.is_under(dir)
+
# XXX TODO?
@@ -343,6 +386,21 @@ class Dir(Entry):
self.builder = 1
self._sconsign = None
+ def __doReparent(self):
+ for ent in self.entries.values():
+ if not ent is self and not ent is self.dir:
+ ent.adjust_srcpath()
+
+ def adjust_srcpath(self):
+ Entry.adjust_srcpath(self)
+ self.__doReparent()
+
+ def link(self, srcdir):
+ """Set this directory as the build directory for the
+ supplied source directory."""
+ self.srcpath = srcdir.path
+ self.__doReparent()
+
def up(self):
return self.entries['..']
@@ -425,17 +483,21 @@ class Dir(Entry):
class File(Entry):
"""A class for files in a file system.
"""
+ def __init__(self, name, directory = None):
+ Entry.__init__(self, name, directory)
+ self._morph()
+
def _morph(self):
- """Turn a file system node into a File object. Nothing
- to be done, actually, because all of the info we need
- is handled by our base Entry class initialization."""
- pass
+ """Turn a file system node into a File object."""
+ self.created = 0
def root(self):
return self.dir.root()
def get_contents(self):
- return open(self.path, "r").read()
+ if not self.exists():
+ return ''
+ return open(str(self), "r").read()
def get_timestamp(self):
if self.exists():
@@ -472,6 +534,17 @@ class File(Entry):
self.add_implicit(scn.scan(self.path, self.env),
scn)
self.scanned[scn] = 1
+
+ def exists(self):
+ if not self.created:
+ self.created = 1
+ if self.srcpath != self.path and \
+ os.path.exists(self.srcpath):
+ if os.path.exists(self.path):
+ os.unlink(self.path)
+ self.__createDir()
+ file_link(self.srcpath, self.path)
+ return Entry.exists(self)
def __createDir(self):
# ensure that the directories for this node are
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index 45f8e64..0f6b8b7 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -29,7 +29,8 @@ import string
import sys
import unittest
import SCons.Node.FS
-
+from TestCmd import TestCmd
+from SCons.Errors import UserError
built_it = None
@@ -59,6 +60,54 @@ class Environment:
def get_scanner(self, skey):
return self.scanner
+class BuildDirTestCase(unittest.TestCase):
+ def runTest(self):
+ """Test build dir functionality"""
+ fs = SCons.Node.FS.FS()
+ f1 = fs.File('build/test1')
+ fs.BuildDir('build', 'src')
+ f2 = fs.File('build/test2')
+ assert f1.srcpath == 'src/test1', f1.srcpath
+ assert f2.srcpath == 'src/test2', f2.srcpath
+
+ fs = SCons.Node.FS.FS()
+ f1 = fs.File('build/test1')
+ fs.BuildDir('build', '.')
+ f2 = fs.File('build/test2')
+ assert f1.srcpath == 'test1', f1.srcpath
+ assert f2.srcpath == 'test2', f2.srcpath
+
+ fs = SCons.Node.FS.FS()
+ fs.BuildDir('build/var1', 'src')
+ fs.BuildDir('build/var2', 'src')
+ f1 = fs.File('build/var1/test1')
+ f2 = fs.File('build/var2/test1')
+ assert f1.srcpath == 'src/test1', f1.srcpath
+ assert f2.srcpath == 'src/test1', f2.srcpath
+
+ exc_caught = 0
+ try:
+ fs = SCons.Node.FS.FS()
+ fs.BuildDir('/test/foo', '.')
+ except UserError:
+ exc_caught = 1
+ assert exc_caught, "Should have caught a UserError."
+
+ exc_caught = 0
+ try:
+ fs = SCons.Node.FS.FS()
+ fs.BuildDir('build', '/test/foo')
+ except UserError:
+ exc_caught = 1
+ assert exc_caught, "Should have caught a UserError."
+
+ exc_caught = 0
+ try:
+ fs = SCons.Node.FS.FS()
+ fs.BuildDir('build', 'build/src')
+ except UserError:
+ exc_caught = 1
+ assert exc_caught, "Should have caught a UserError."
class FSTestCase(unittest.TestCase):
def runTest(self):
@@ -68,8 +117,6 @@ class FSTestCase(unittest.TestCase):
tests in one environment, so we don't have to set up a
complicated directory structure for each test individually.
"""
- from TestCmd import TestCmd
-
test = TestCmd(workdir = '')
test.subdir('sub', ['sub', 'dir'])
@@ -393,5 +440,6 @@ class FSTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.TestSuite()
suite.addTest(FSTestCase())
+ suite.addTest(BuildDirTestCase())
if not unittest.TextTestRunner().run(suite).wasSuccessful():
sys.exit(1)
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index 673ab25..f55659e 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -415,8 +415,32 @@ class NodeTestCase(unittest.TestCase):
assert n2.children_are_executed()
assert n1.children_are_executed()
-
-
+ def test_rescan(self):
+ """Test that built nodes are rescanned."""
+ class DummyScanner:
+ pass
+
+ class TestNode(SCons.Node.Node):
+ def scan(self):
+ for scn in self.scanners:
+ if not self.scanned.has_key(scn):
+ n=SCons.Node.Node()
+ n.scanner_set(scn)
+ self.add_implicit([ n ], scn)
+ self.scanned[scn] = 1
+ tn=TestNode()
+ tn.builder_set(Builder())
+ tn.env_set(Environment())
+ ds = DummyScanner()
+ tn.scanner_set(ds)
+ tn.scan()
+ map(lambda x: x.scan(), tn.depends)
+ assert tn.scanned[ds]
+ assert len(tn.implicit[ds]) == 1, tn.implicit
+ tn.build()
+ assert len(tn.implicit[ds]) == 2, tn.implicit
+ for dep in tn.implicit[ds]:
+ assert dep.scanned[ds] == 1
if __name__ == "__main__":
suite = unittest.makeSuite(NodeTestCase, 'test_')
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index ddcddfb..0682c7f 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -76,6 +76,28 @@ class Node:
target = self, source = self.sources)
if stat != 0:
raise BuildError(node = self, stat = stat)
+
+ # If we succesfully build a node, then we need to rescan for
+ # implicit dependencies, since it might have changed on us.
+
+ # XXX Modify this so we only rescan using the scanner(s) relevant
+ # to this build.
+ for scn in self.scanners:
+ try:
+ del self.scanned[scn]
+ except KeyError:
+ pass
+
+ self.scan()
+
+ for scn in self.scanners:
+ try:
+ for dep in self.implicit[scn]:
+ w=Walker(dep)
+ while not w.is_done():
+ w.next().scan()
+ except KeyError:
+ pass
return stat
def builder_set(self, builder):