diff options
author | Steven Knight <knight@baldmt.com> | 2001-12-15 00:23:46 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2001-12-15 00:23:46 (GMT) |
commit | af3987ad4663708a4f9d10b661971fd7b29a9f0d (patch) | |
tree | 9de48645f9d229266078ede22a78fcd740f705b6 /src/engine/SCons/Node | |
parent | 0d14a2ff1774d42dc855aa9aab94791df3f55157 (diff) | |
download | SCons-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.py | 85 | ||||
-rw-r--r-- | src/engine/SCons/Node/FSTests.py | 54 | ||||
-rw-r--r-- | src/engine/SCons/Node/NodeTests.py | 28 | ||||
-rw-r--r-- | src/engine/SCons/Node/__init__.py | 22 |
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): |