From bd91e792df09aebec78f7972e17eab873dc71246 Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Wed, 16 Oct 2002 04:13:14 +0000 Subject: Refactor Repository and BuildDir. (Charles Crain) --- rpm/scons.spec | 2 + src/CHANGES.txt | 6 +- src/engine/SCons/EnvironmentTests.py | 50 ++-- src/engine/SCons/Node/FS.py | 548 ++++++++++++++++++----------------- src/engine/SCons/Node/FSTests.py | 213 +++++++++----- src/engine/SCons/Scanner/C.py | 6 +- src/engine/SCons/Scanner/Fortran.py | 6 +- src/engine/SCons/Script/__init__.py | 5 +- src/engine/SCons/Util.py | 34 +++ test/BuildDir.py | 88 ++++-- test/CPPPATH.py | 4 +- test/Repository/BuildDir.py | 42 +-- test/Repository/LIBPATH.py | 121 ++++++++ test/Repository/variants.py | 186 ++++++++++++ test/option--U.py | 2 +- test/option--implicit-cache.py | 2 +- 16 files changed, 890 insertions(+), 425 deletions(-) create mode 100644 test/Repository/LIBPATH.py create mode 100644 test/Repository/variants.py diff --git a/rpm/scons.spec b/rpm/scons.spec index a4635d9..8c1cf73 100644 --- a/rpm/scons.spec +++ b/rpm/scons.spec @@ -68,6 +68,8 @@ rm -rf $RPM_BUILD_ROOT /usr/lib/scons/SCons/Node/FS.pyc /usr/lib/scons/SCons/Node/__init__.py /usr/lib/scons/SCons/Node/__init__.pyc +/usr/lib/scons/SCons/Options.py +/usr/lib/scons/SCons/Options.pyc /usr/lib/scons/SCons/Platform/cygwin.py /usr/lib/scons/SCons/Platform/cygwin.pyc /usr/lib/scons/SCons/Platform/os2.py diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 42ce7e8..d542c0d 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -15,9 +15,11 @@ RELEASE 0.09 - - Add a Prepend() method to Environments, to append values to the beginning of construction variables. - From Steven Knight: + From Charles Crain and Steven Knight: + + - Add Repository() functionality. - - Add Repository() functionality. + From Steven Knight: - Fix auto-deduction of target names so that deduced targets end up in the same subdirectory as the source. diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index 45b6901..2211bf8 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -485,26 +485,16 @@ class EnvironmentTestCase(unittest.TestCase): def test_autogenerate(dict): """Test autogenerating variables in a dictionary.""" - def Dir(name): - dir = SCons.Node.FS.default_fs.Dir('/xx') - return SCons.Node.FS.default_fs.Dir(name, dir) - - def File(name): - dir = SCons.Node.FS.default_fs.Dir('/xx') - return SCons.Node.FS.default_fs.File(name, dir) - - def RDirs(pathlist, Dir=Dir): - def path_dirs(rep, path, Dir=Dir): - if rep: - path = os.path.join(rep, path) - return Dir(path) - - return SCons.Node.FS.default_fs.Rsearchall(pathlist, path_dirs) + def RDirs(pathlist): + return SCons.Node.FS.default_fs.Rsearchall(pathlist, + clazz=SCons.Node.FS.Dir, + must_exist=0, + cwd=SCons.Node.FS.default_fs.Dir('xx')) env = Environment(LIBS = [ 'foo', 'bar', 'baz' ], LIBLINKPREFIX = 'foo', LIBLINKSUFFIX = 'bar', - Dir=Dir, File=File, RDirs=RDirs) + RDirs=RDirs) flags = env.subst_list('$_LIBFLAGS', 1)[0] assert len(flags) == 3, flags assert flags[0] == 'foofoobar', \ @@ -520,18 +510,18 @@ class EnvironmentTestCase(unittest.TestCase): INCPREFIX = 'foo ', INCSUFFIX = 'bar', FOO = 'baz', - Dir=Dir, File=File, RDirs=RDirs) + RDirs=RDirs) flags = env.subst_list('$_CPPINCFLAGS', 1)[0] assert len(flags) == 8, flags assert flags[0] == '$(', \ flags[0] assert flags[1] == os.path.normpath('foo'), \ flags[1] - assert flags[2] == os.path.normpath('/xx/foobar'), \ + assert flags[2] == os.path.normpath('xx/foobar'), \ flags[2] assert flags[3] == os.path.normpath('foo'), \ flags[3] - assert flags[4] == os.path.normpath('/xx/baz/barbar'), \ + assert flags[4] == os.path.normpath('xx/baz/barbar'), \ flags[4] assert flags[5] == os.path.normpath('foo'), \ flags[5] @@ -544,18 +534,18 @@ class EnvironmentTestCase(unittest.TestCase): INCPREFIX = 'foo ', INCSUFFIX = 'bar', FOO = 'baz', - Dir=Dir, File=File, RDirs=RDirs) + RDirs=RDirs) flags = env.subst_list('$_F77INCFLAGS', 1)[0] assert len(flags) == 8, flags assert flags[0] == '$(', \ flags[0] assert flags[1] == os.path.normpath('foo'), \ flags[1] - assert flags[2] == os.path.normpath('/xx/foobar'), \ + assert flags[2] == os.path.normpath('xx/foobar'), \ flags[2] assert flags[3] == os.path.normpath('foo'), \ flags[3] - assert flags[4] == os.path.normpath('/xx/baz/barbar'), \ + assert flags[4] == os.path.normpath('xx/baz/barbar'), \ flags[4] assert flags[5] == os.path.normpath('foo'), \ flags[5] @@ -565,7 +555,7 @@ class EnvironmentTestCase(unittest.TestCase): flags[7] env = Environment(CPPPATH = '', F77PATH = '', LIBPATH = '', - Dir=Dir, File=File, RDirs=RDirs) + RDirs=RDirs) assert len(env.subst_list('$_CPPINCFLAGS')[0]) == 0 assert len(env.subst_list('$_F77INCFLAGS')[0]) == 0 assert len(env.subst_list('$_LIBDIRFLAGS')[0]) == 0 @@ -576,21 +566,21 @@ class EnvironmentTestCase(unittest.TestCase): INCPREFIX = '-I ', INCSUFFIX = 'XXX', FOO = 'baz', - Dir=Dir, File=File, RDirs=RDirs) + RDirs=RDirs) flags = env.subst_list('$_CPPINCFLAGS', 1)[0] assert flags[0] == '$(', \ flags[0] assert flags[1] == '-I', \ flags[1] - assert flags[2] == os.path.normpath('/xx/fooXXX'), \ + assert flags[2] == os.path.normpath('xx/fooXXX'), \ flags[2] assert flags[3] == '-I', \ flags[3] - assert flags[4] == os.path.normpath('/rep1/fooXXX'), \ + assert flags[4] == os.path.normpath('/rep1/xx/fooXXX'), \ flags[4] assert flags[5] == '-I', \ flags[5] - assert flags[6] == os.path.normpath('/rep2/fooXXX'), \ + assert flags[6] == os.path.normpath('/rep2/xx/fooXXX'), \ flags[6] assert flags[7] == '-I', \ flags[7] @@ -598,15 +588,15 @@ class EnvironmentTestCase(unittest.TestCase): flags[8] assert flags[9] == '-I', \ flags[9] - assert flags[10] == os.path.normpath('/xx/baz/barXXX'), \ + assert flags[10] == os.path.normpath('xx/baz/barXXX'), \ flags[10] assert flags[11] == '-I', \ flags[11] - assert flags[12] == os.path.normpath('/rep1/baz/barXXX'), \ + assert flags[12] == os.path.normpath('/rep1/xx/baz/barXXX'), \ flags[12] assert flags[13] == '-I', \ flags[13] - assert flags[14] == os.path.normpath('/rep2/baz/barXXX'), \ + assert flags[14] == os.path.normpath('/rep2/xx/baz/barXXX'), \ flags[14] assert flags[15] == '-I', \ flags[15] diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 1ac7566..aa6482d 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -41,6 +41,7 @@ import SCons.Node from UserDict import UserDict import sys from SCons.Errors import UserError +import SCons.Warnings try: import os @@ -62,20 +63,26 @@ class ParentOfRoot: This class is an instance of the Null object pattern. """ def __init__(self): - self.duplicate = 1 self.abspath = '' self.path = '' - self.srcpath = '' self.abspath_ = '' self.path_ = '' - self.srcpath_ = '' - + self.name='' + self.duplicate=0 + self.srcdir=None + def is_under(self, dir): return 0 def up(self): return None + def getRepositories(self): + return [] + + def get_dir(self): + return None + if os.path.normcase("TeSt") == os.path.normpath("TeSt"): def _my_normcase(x): return x @@ -83,15 +90,128 @@ else: def _my_normcase(x): return string.upper(x) +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 + or a directory. Instances of this class can morph into either + Dir or File objects by a later, more precise lookup. + + Note: this class does not define __cmp__ and __hash__ for efficiency + reasons. SCons does a lot of comparing of Entry objects, and so that + operation must be as fast as possible, which means we want to use + Python's built-in object identity comparison. + """ + + def __init__(self, name, directory, fs): + """Initialize a generic file system Entry. + + Call the superclass initialization, take care of setting up + our relative and absolute paths, identify our parent + directory, and indicate that this node should use + signatures.""" + SCons.Node.Node.__init__(self) + + self.name = name + self.fs = fs + + assert directory, "A directory must be provided" + + self.abspath = directory.abspath_ + name + if directory.path == '.': + self.path = name + else: + self.path = directory.path_ + name + + self.path_ = self.path + self.abspath_ = self.abspath + self.dir = directory + self.cwd = None # will hold the SConscript directory for target nodes + self.duplicate = directory.duplicate + + 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 + + def get_contents(self): + """Fetch the contents of the entry. + + Since this should return the real contents from the file + system, we check to see into what sort of subclass we should + morph this Entry.""" + if os.path.isfile(self.abspath): + self.__class__ = File + self._morph() + return File.get_contents(self) + if os.path.isdir(self.abspath): + self.__class__ = Dir + self._morph() + return Dir.get_contents(self) + raise AttributeError + + def exists(self): + try: + return self._exists + except AttributeError: + self._exists = os.path.exists(self.abspath) + return self._exists -def exists_path(rep, path): - """Return a path if it's already a Node or it exists in the - real filesystem.""" - if rep: - path = os.path.join(rep, path) - if os.path.exists(path): - return path - return None + def rexists(self): + if not hasattr(self, '_rexists'): + self._rexists = self.rfile().exists() + return self._rexists + + def get_parents(self): + parents = SCons.Node.Node.get_parents(self) + if self.dir and not isinstance(self.dir, ParentOfRoot): + parents.append(self.dir) + return parents + + def current(self, calc): + """If the underlying path doesn't exist, we know the node is + not current without even checking the signature, so return 0. + Otherwise, return None to indicate that signature calculation + should proceed as normal to find out if the node is current.""" + bsig = calc.bsig(self) + if not self.exists(): + return 0 + return calc.current(self, bsig) + + def is_under(self, dir): + if self is dir: + return 1 + else: + return self.dir.is_under(dir) + + def set_local(self): + self._local = 1 + + def srcnode(self): + """If this node is in a build path, return the node + corresponding to its source file. Otherwise, return + ourself.""" + try: + return self._srcnode + except AttributeError: + dir=self.dir + name=self.name + while dir: + if dir.srcdir: + self._srcnode = self.fs.Entry(name, dir.srcdir, + klass=self.__class__) + return self._srcnode + name = dir.name + os.sep + name + dir=dir.get_dir() + self._srcnode = self + return self._srcnode + +# This is for later so we can differentiate between Entry the class and Entry +# the method of the FS class. +_classEntry = Entry class FS: @@ -110,7 +230,6 @@ class FS: self.pathTop = path self.Root = {} self.Top = None - self.Repositories = [] 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." @@ -120,7 +239,6 @@ class FS: if not self.Top: self.Top = self.__doLookup(Dir, os.path.normpath(self.pathTop)) self.Top.path = '.' - self.Top.srcpath = '.' self.Top.path_ = '.' + os.sep self._cwd = self.Top @@ -137,7 +255,7 @@ class FS: return node if not isinstance(node, klass): raise TypeError, "Tried to lookup %s '%s' as a %s." % \ - (node.__class__.__name__, str(node), klass.__name__) + (node.__class__.__name__, node.path, klass.__name__) return node def __doLookup(self, fsclass, name, directory = None, create = 1): @@ -173,7 +291,6 @@ class FS: dir = Dir(drive, ParentOfRoot(), self) dir.path = dir.path + os.sep dir.abspath = dir.abspath + os.sep - dir.srcpath = dir.srcpath + os.sep self.Root[drive] = dir directory = dir path_comp = path_comp[1:] @@ -192,7 +309,7 @@ class FS: # look at the actual filesystem and make sure there isn't # a file already there - path = os.path.join(str(directory), path_name) + path = directory.path_ + path_name if os.path.isfile(path): raise TypeError, \ "File %s found where directory expected." % path @@ -210,7 +327,7 @@ class FS: # make sure we don't create File nodes when there is actually # a directory at that path on the disk, and vice versa - path = os.path.join(str(directory), path_comp[-1]) + path = directory.path_ + path_comp[-1] if fsclass == File: if os.path.isdir(path): raise TypeError, \ @@ -301,12 +418,12 @@ class FS: def BuildDir(self, build_dir, src_dir, duplicate=1): """Link the supplied build directory to the source directory for purposes of building files.""" + self.__setTopLevelDir() if not isinstance(src_dir, SCons.Node.Node): src_dir = self.Dir(src_dir) if not isinstance(build_dir, SCons.Node.Node): build_dir = self.Dir(build_dir) - build_dir.duplicate = duplicate if not src_dir.is_under(self.Top): raise UserError, "Source directory must be under top of build tree." if src_dir.is_under(build_dir): @@ -316,24 +433,47 @@ class FS: def Repository(self, *dirs): """Specify Repository directories to search.""" for d in dirs: - self.Repositories.append(self.Dir(d)) + if not isinstance(d, SCons.Node.Node): + d = self.Dir(d) + self.__setTopLevelDir() + self.Top.addRepository(d) - def Rsearch(self, path, func = exists_path): + def Rsearch(self, path, clazz=_classEntry, cwd=None): """Search for something in a Repository. Returns the first one found in the list, or None if there isn't one.""" if isinstance(path, SCons.Node.Node): return path else: - n = func(None, path) - if n: + name, d = self.__transformPath(path, cwd) + n = self.__doLookup(clazz, name, d) + if n.exists(): return n - for rep in self.Repositories: - n = func(rep.path, path) - if n: - return n + d = n.get_dir() + name = n.name + # 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 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 rnode.exists() and \ + (isinstance(rnode, Dir) or not rnode.builder): + return rnode + except TypeError: + pass # Wrong type of node. + # Prepend directory name + name = d.name + os.sep + name + # Go up one directory + d = d.get_dir() return None + - def Rsearchall(self, pathlist, func = exists_path): + def Rsearchall(self, pathlist, must_exist=1, clazz=_classEntry, cwd=None): """Search for a list of somethings in the Repository list.""" ret = [] if SCons.Util.is_String(pathlist): @@ -344,133 +484,39 @@ class FS: if isinstance(path, SCons.Node.Node): ret.append(path) else: - n = func(None, path) - if n: + name, d = self.__transformPath(path, cwd) + n = self.__doLookup(clazz, name, d) + if not must_exist or n.exists(): ret.append(n) - if not os.path.isabs(path): - if path[0] == '#': - path = path[1:] - for rep in self.Repositories: - n = func(rep.path, path) - if n: - ret.append(n) + if isinstance(n, Dir): + # If this node is a directory, then any repositories + # attached to this node can be repository paths. + ret.extend(filter(lambda x, me=must_exist, clazz=clazz: isinstance(x, clazz) and (not me or x.exists()), + n.getRepositories())) + + d = n.get_dir() + name = n.name + # 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. + if (not must_exist or rnode.exists()) and \ + (not rnode.builder or isinstance(rnode, Dir)): + ret.append(rnode) + except TypeError: + pass # Wrong type of node. + # Prepend directory name + name = d.name + os.sep + name + # Go up one directory + d = d.get_dir() return ret - -class Entry(SCons.Node.Node): - """A generic class for file system entries. This class if for - when we don't know yet whether the entry being looked up is a file - or a directory. Instances of this class can morph into either - Dir or File objects by a later, more precise lookup. - - Note: this class does not define __cmp__ and __hash__ for efficiency - reasons. SCons does a lot of comparing of Entry objects, and so that - operation must be as fast as possible, which means we want to use - Python's built-in object identity comparison. - """ - - def __init__(self, name, directory, fs): - """Initialize a generic file system Entry. - - Call the superclass initialization, take care of setting up - our relative and absolute paths, identify our parent - directory, and indicate that this node should use - signatures.""" - SCons.Node.Node.__init__(self) - - self.name = name - - assert directory, "A directory must be provided" - - self.duplicate = directory.duplicate - self.abspath = directory.abspath_ + name - if str(directory.path) == '.': - self.path = name - else: - self.path = directory.path_ + name - - self.path_ = self.path - self.abspath_ = self.abspath - self.dir = directory - self.__doSrcpath(self.duplicate) - self.srcpath_ = self.srcpath - self.cwd = None # will hold the SConscript directory for target nodes - self._local = None - self.fs = fs # The filesystem that this entry is part of - - def get_dir(self): - return self.dir - - def adjust_srcpath(self, duplicate): - self.__doSrcpath(duplicate) - - def __doSrcpath(self, duplicate): - self.duplicate = duplicate - if str(self.dir.srcpath) == '.': - self.srcpath = self.name - else: - self.srcpath = self.dir.srcpath_ + self.name - - def __str__(self): - """A FS node's string representation is its path name.""" - if self.duplicate or self.builder: - return self.path - else: - return self.srcpath - - def get_contents(self): - """Fetch the contents of the entry. - - Since this should return the real contents from the file - system, we check to see into what sort of subclass we should - morph this Entry.""" - if os.path.isfile(self.abspath): - self.__class__ = File - self._morph() - return File.get_contents(self) - if os.path.isdir(self.abspath): - self.__class__ = Dir - self._morph() - return Dir.get_contents(self) - raise AttributeError - - def exists(self): - if not hasattr(self, '_exists'): - self._exists = os.path.exists(str(self)) - return self._exists - - def rexists(self): - if not hasattr(self, '_rexists'): - self._rexists = os.path.exists(self.rstr()) - return self._rexists - - def get_parents(self): - parents = SCons.Node.Node.get_parents(self) - if self.dir and not isinstance(self.dir, ParentOfRoot): - parents.append(self.dir) - return parents - - def current(self, calc): - """If the underlying path doesn't exist, we know the node is - not current without even checking the signature, so return 0. - Otherwise, return None to indicate that signature calculation - should proceed as normal to find out if the node is current.""" - bsig = calc.bsig(self) - if not self.exists(): - return 0 - return calc.current(self, bsig) - - def is_under(self, dir): - if self is dir: - return 1 - else: - return self.dir.is_under(dir) - - def set_local(self): - self._local = 1 - - - # XXX TODO? # Annotate with the creator # rel_path @@ -497,15 +543,53 @@ class Dir(Entry): self.path_ = self.path + os.sep self.abspath_ = self.abspath + os.sep - self.srcpath_ = self.srcpath + os.sep - + self.repositories = [] + self.srcdir = None + self.entries = {} self.entries['.'] = self self.entries['..'] = self.dir self.cwd = self self.builder = 1 self._sconsign = None - + + def __clearRepositoryCache(self, duplicate=None): + """Called when we change the repository(ies) for a directory. + This clears any cached information that is invalidated by changing + the repository.""" + + for node in self.entries.values(): + if node != self.dir: + if node != self and isinstance(node, Dir): + node.__clearRepositoryCache(duplicate) + else: + try: + del node._srcreps + except AttributeError: + pass + try: + del node._rfile + except AttributeError: + pass + try: + del node._rexists + except AttributeError: + pass + try: + del node._exists + except AttributeError: + pass + try: + del node._srcnode + except AttributeError: + pass + if duplicate != None: + node.duplicate=duplicate + + def __resetDuplicate(self, node): + if node != self: + node.duplicate = node.get_dir().duplicate + def Dir(self, name): """Create a directory node named 'name' relative to this directory.""" return self.fs.Dir(name, self) @@ -513,23 +597,32 @@ class Dir(Entry): def File(self, name): """Create file node named 'name' relatove to this directory.""" return self.fs.File(name, self) - - def __doReparent(self, duplicate): - for ent in self.entries.values(): - if not ent is self and not ent is self.dir: - ent.adjust_srcpath(duplicate) - - def adjust_srcpath(self, duplicate): - Entry.adjust_srcpath(self, duplicate) - self.srcpath_ = self.srcpath + os.sep - self.__doReparent(duplicate) def link(self, srcdir, duplicate): """Set this directory as the build directory for the supplied source directory.""" - self.srcpath = srcdir.path - self.srcpath_ = srcdir.path_ - self.__doReparent(duplicate) + self.srcdir = srcdir + self.duplicate = duplicate + self.__clearRepositoryCache(duplicate) + + def getRepositories(self): + """Returns a list of repositories for this directory.""" + if self.srcdir and not self.duplicate: + try: + return self._srcreps + except AttributeError: + self._srcreps = self.fs.Rsearchall(self.srcdir.path, + clazz=Dir, + must_exist=0, + cwd=self.fs.Top) \ + + self.repositories + return self._srcreps + return self.repositories + + def addRepository(self, dir): + if not dir in self.repositories and dir != self: + self.repositories.append(dir) + self.__clearRepositoryCache() def up(self): return self.entries['..'] @@ -595,32 +688,6 @@ class Dir(Entry): self._sconsign = SCons.Sig.SConsignFile(self) return self._sconsign - def __str__(self): - # Reimplemented from Entry since, unlike for - # Entry and File, we want to return the source - # path *even if* the builder is non-zero - # (which it always is for a directory) - if self.duplicate: - return self.path - else: - return self.srcpath - - def exists(self): - # Again, directories are special...we don't care if their - # source path exists, we only care about the path. - if not hasattr(self, '_exists'): - self._exists = os.path.exists(self.path) - return self._exists - - def rexists(self): - # Again, directories are special...we don't care if their - # source path exists, we only care about the path. - if not hasattr(self, '_rexists'): - self._rexists = os.path.exists(self.rstr()) - return self._rexists - - - # XXX TODO? # base_suf # suffix @@ -648,12 +715,8 @@ class File(Entry): def RDirs(self, pathlist): """Search for a list of directories in the Repository list.""" - def path_dirs(rep, path, Dir=self.Dir): - if rep: - path = os.path.join(rep, path) - return Dir(path) - - return self.fs.Rsearchall(pathlist, path_dirs) + 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) @@ -665,6 +728,7 @@ class File(Entry): def _morph(self): """Turn a file system node into a File object.""" self.created = 0 + self._local = 0 def root(self): return self.dir.root() @@ -733,56 +797,7 @@ class File(Entry): return scanner.scan(self, env, target) else: return [] - - def exists(self): - if not hasattr(self, '_exists'): - if self.duplicate and 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) - self._exists = os.path.exists(str(self)) - return self._exists - - def rexists(self): - if not hasattr(self, '_rexists'): - if self.path != self.srcpath: - if os.path.exists(self.srcpath): - if self.duplicate and not self.created: - self.created = 1 - if os.path.exists(self.path): - os.unlink(self.path) - self.__createDir() - file_link(self.srcpath, self.path) - self._rexists = 1 - return self._rexists - for rep in self.fs.Repositories: - if not os.path.isabs(self.path): - f = os.path.join(rep.path, self.path) - if os.path.exists(f): - self._rexists = 1 - return self._rexists - f = os.path.join(rep.path, self.srcpath) - if os.path.exists(f): - if self.duplicate and not self.created: - self.created = 1 - if os.path.exists(self.path): - os.unlink(self.path) - self.__createDir() - file_link(f, self.path) - else: - self.srcpath = f - self.srcpath_ = f + os.sep - self._rexists = 1 - return self._rexists - self._rexists = None - else: - self._rexists = Entry.rexists(self) - return self._rexists - + def scanner_key(self): return os.path.splitext(self.name)[1] @@ -829,6 +844,26 @@ class File(Entry): 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: + src=self.srcnode().rfile() + if src.exists() and src.abspath != self.abspath: + try: + os.unlink(self.abspath) + 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 + return Entry.exists(self) + def current(self, calc): bsig = calc.bsig(self) if not self.exists(): @@ -853,20 +888,15 @@ class File(Entry): def rfile(self): if not hasattr(self, '_rfile'): self._rfile = self - if not os.path.isabs(self.path) and not os.path.isfile(self.path): - def file_node(dir, path, fs=self.fs): - if dir: - path = os.path.join(dir, path) - if os.path.isfile(path): - return fs.File(path) - return None - n = self.fs.Rsearch(self.path, file_node) + if not self.exists(): + n = self.fs.Rsearch(self.path, clazz=File, + cwd=self.fs.Top) if n: self._rfile = n return self._rfile def rstr(self): - return os.path.normpath(str(self.rfile())) + return str(self.rfile()) default_fs = FS() diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 51d4a6f..0c7bd69 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -82,23 +82,23 @@ class BuildDirTestCase(unittest.TestCase): f1 = fs.File('build/test1') fs.BuildDir('build', 'src') f2 = fs.File('build/test2') - assert f1.srcpath == os.path.normpath('src/test1'), f1.srcpath - assert f2.srcpath == os.path.normpath('src/test2'), f2.srcpath + assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path + assert f2.srcnode().path == os.path.normpath('src/test2'), f2.srcnode().path 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 + assert f1.srcnode().path == 'test1', f1.srcnode().path + assert f2.srcnode().path == 'test2', f2.srcnode().path 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 == os.path.normpath('src/test1'), f1.srcpath - assert f2.srcpath == os.path.normpath('src/test1'), f2.srcpath + assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path + assert f2.srcnode().path == os.path.normpath('src/test1'), f2.srcnode().path fs = SCons.Node.FS.FS() fs.BuildDir('../var1', 'src') @@ -106,56 +106,136 @@ class BuildDirTestCase(unittest.TestCase): f1 = fs.File('../var1/test1') f2 = fs.File('../var2/test1') assert hasattr(f1, 'overrides') - assert f1.srcpath == os.path.normpath('src/test1'), f1.srcpath - assert f2.srcpath == os.path.normpath('src/test1'), f2.srcpath + assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path + assert f2.srcnode().path == os.path.normpath('src/test1'), f2.srcnode().path + + # Set up some files + test.subdir('work', ['work', 'src']) + test.subdir(['work', 'build'], ['work', 'build', 'var1']) + test.subdir(['work', 'build', 'var2']) + test.subdir('rep1', ['rep1', 'src']) + test.subdir(['rep1', 'build'], ['rep1', 'build', 'var1']) + test.subdir(['rep1', 'build', 'var2']) + + # A source file in the source directory + test.write([ 'work', 'src', 'test.in' ], 'test.in') + + # A source file in the repository + test.write([ 'rep1', 'src', 'test2.in' ], 'test2.in') + + # Some source files in the build directory + test.write([ 'work', 'build', 'var2', 'test.in' ], 'test.old') + test.write([ 'work', 'build', 'var2', 'test2.in' ], 'test2.old') - fs = SCons.Node.FS.FS() + # An old derived file in the build directories + test.write([ 'work', 'build', 'var1', 'test.out' ], 'test.old') + test.write([ 'work', 'build', 'var2', 'test.out' ], 'test.old') + + # And just in case we are weird, a derived file in the source + # dir. + test.write([ 'work', 'src', 'test.out' ], 'test.out.src') + + # A derived file in the repository + test.write([ 'rep1', 'build', 'var1', 'test2.out' ], 'test2.out_rep') + test.write([ 'rep1', 'build', 'var2', 'test2.out' ], 'test2.out_rep') + + fs = SCons.Node.FS.FS(test.workpath('work')) fs.BuildDir('build/var1', 'src', duplicate=0) fs.BuildDir('build/var2', 'src') - f1 = fs.File('build/var1/test') + f1 = fs.File('build/var1/test.in') f1out = fs.File('build/var1/test.out') f1out.builder = 1 - f2 = fs.File('build/var2/test') + f1out_2 = fs.File('build/var1/test2.out') + f1out_2.builder = 1 + f2 = fs.File('build/var2/test.in') f2out = fs.File('build/var2/test.out') f2out.builder = 1 - - assert f1.srcpath == os.path.normpath('src/test'), f1.srcpath - assert f1out.srcpath == os.path.normpath('src/test.out'), f1out.srcpath - assert str(f1) == os.path.normpath('src/test'), str(f1) - assert str(f1out) == os.path.normpath('build/var1/test.out'), str(f1out) - assert f2.srcpath == os.path.normpath('src/test'), f2.srcpath - assert f2out.srcpath == os.path.normpath('src/test.out'), f2out.srcpath - assert str(f2) == os.path.normpath('build/var2/test'), str(f2) - assert str(f2out) == os.path.normpath('build/var2/test.out'), str(f2out) - - assert not f1.exists() - assert not f1out.exists() - assert not f2.exists() - assert not f2out.exists() - - test.subdir('src') - test.write(['src', 'test'], "src/test\n") - test.write(['src', 'test'], "src/test.out\n") - + f2out_2 = fs.File('build/var2/test2.out') + f2out_2.builder = 1 + fs.Repository(test.workpath('rep1')) + + assert f1.srcnode().path == os.path.normpath('src/test.in'),\ + f1.srcnode().path + # str(node) returns source path for duplicate = 0 + assert str(f1) == os.path.normpath('src/test.in'), str(f1) + # Build path does not exist assert not f1.exists() - assert not f1out.exists() - assert not f2.exists() - assert not f2out.exists() - - f1.built() - f2.built() - - assert f1.exists() - assert not f1out.exists() - assert not f2.exists() - assert not f2out.exists() - - d1 = fs.Dir('build/var1') - d2 = fs.Dir('build/var2') - - assert str(d1) == 'src', str(d1) - assert str(d2) == os.path.normpath('build/var2'), str(d2) + # But source path does + assert f1.srcnode().exists() + # And duplicate=0 should also work just like a Repository + assert f1.rexists() + # rfile() should point to the source path + assert f1.rfile().path == os.path.normpath('src/test.in'),\ + f1.rfile().path + + assert f2.srcnode().path == os.path.normpath('src/test.in'),\ + f2.srcnode().path + # str(node) returns build path for duplicate = 1 + assert str(f2) == os.path.normpath('build/var2/test.in'), str(f2) + # Build path exists + assert f2.exists() + # ...and should copy the file from src to build path + assert test.read(['work', 'build', 'var2', 'test.in']) == 'test.in',\ + test.read(['work', 'build', 'var2', 'test.in']) + # Since exists() is true, so should rexists() be + assert f2.rexists() + f3 = fs.File('build/var1/test2.in') + f4 = fs.File('build/var2/test2.in') + + assert f3.srcnode().path == os.path.normpath('src/test2.in'),\ + f3.srcnode().path + # str(node) returns source path for duplicate = 0 + assert str(f3) == os.path.normpath('src/test2.in'), str(f3) + # Build path does not exist + assert not f3.exists() + # Source path does not either + assert not f3.srcnode().exists() + # But we do have a file in the Repository + assert f3.rexists() + # rfile() should point to the source path + assert f3.rfile().path == test.workpath('rep1/src/test2.in'),\ + f3.rfile().path + + assert f4.srcnode().path == os.path.normpath('src/test2.in'),\ + f4.srcnode().path + # str(node) returns build path for duplicate = 1 + assert str(f4) == os.path.normpath('build/var2/test2.in'), str(f4) + # Build path should exist + assert f4.exists() + # ...and copy over the file into the local build path + assert test.read(['work', 'build', 'var2', 'test2.in']) == 'test2.in' + # should exist in repository, since exists() is true + assert f4.rexists() + # rfile() should point to ourselves + assert f4.rfile().path == os.path.normpath('build/var2/test2.in'),\ + f4.rfile().path + + f5 = fs.File('build/var1/test.out') + f6 = fs.File('build/var2/test.out') + + assert f5.exists() + # We should not copy the file from the source dir, since this is + # a derived file. + assert test.read(['work', 'build', 'var1', 'test.out']) == 'test.old' + + assert f6.exists() + # We should not copy the file from the source dir, since this is + # a derived file. + assert test.read(['work', 'build', 'var2', 'test.out']) == 'test.old' + f7 = fs.File('build/var1/test2.out') + f8 = fs.File('build/var2/test2.out') + + assert not f7.exists() + assert f7.rexists() + assert f7.rfile().path == test.workpath('rep1/build/var1/test2.out'),\ + f7.rfile().path + + assert not f8.exists() + assert f8.rexists() + assert f8.rfile().path == test.workpath('rep1/build/var2/test2.out'),\ + f8.rfile().path + # Test to see if file_link() works... test.subdir('src','build') test.write('src/foo', 'foo\n') @@ -664,8 +744,9 @@ class RepositoryTestCase(unittest.TestCase): fs.Repository(os.path.join('bar', 'foo')) fs.Repository('bar') - assert len(fs.Repositories) == 4, fs.Repositories - r = map(lambda x, np=os.path.normpath: np(str(x)), fs.Repositories) + rep = fs.Dir('#').getRepositories() + assert len(rep) == 4, map(str, rep) + r = map(lambda x, np=os.path.normpath: np(str(x)), rep) assert r == ['foo', os.path.join('foo', 'bar'), os.path.join('bar', 'foo'), @@ -703,15 +784,6 @@ class RepositoryTestCase(unittest.TestCase): assert fs.Rsearch('f2') assert fs.Rsearch(f3) is f3 - def my_exists(rep, path): - if rep: - path = os.path.join(rep, path) - return os.path.exists(path) - - assert not fs.Rsearch('f1', my_exists) - assert fs.Rsearch('f2', my_exists) - assert fs.Rsearch('f3', my_exists) - list = fs.Rsearchall(fs.Dir('d1')) assert len(list) == 1, list assert list[0].path == 'd1', list[0].path @@ -727,36 +799,41 @@ class RepositoryTestCase(unittest.TestCase): assert list == [], list test.subdir(['work', 'd2']) + fs.File('d2').built() # Clear exists cache list = fs.Rsearchall('d2') - assert list == ['d2'], list + assert map(str, list) == ['d2'], list test.subdir(['rep2', 'd2']) + fs.File('../rep2/d2').built() # Clear exists cache list = fs.Rsearchall('d2') - assert list == ['d2', test.workpath('rep2', 'd2')], list + assert map(str, list) == ['d2', test.workpath('rep2', 'd2')], list test.subdir(['rep1', 'd2']) + fs.File('../rep1/d2').built() # Clear exists cache list = fs.Rsearchall('d2') - assert list == ['d2', - test.workpath('rep1', 'd2'), - test.workpath('rep2', 'd2')], list + assert map(str, list) == ['d2', + test.workpath('rep1', 'd2'), + test.workpath('rep2', 'd2')], list list = fs.Rsearchall(['d3', 'd4']) assert list == [], list test.subdir(['work', 'd3']) - list = fs.Rsearchall(['d3', 'd4']) + fs.File('d3').built() # Clear exists cache + list = map(str, fs.Rsearchall(['d3', 'd4'])) assert list == ['d3'], list test.subdir(['rep3', 'd4']) - list = fs.Rsearchall(['d3', 'd4']) + fs.File('../rep3/d4').built() # Clear exists cache + list = map(str, fs.Rsearchall(['d3', 'd4'])) assert list == ['d3', test.workpath('rep3', 'd4')], list - list = fs.Rsearchall(string.join(['d3', 'd4'], os.pathsep)) + list = map(str, fs.Rsearchall(string.join(['d3', 'd4'], os.pathsep))) assert list == ['d3', test.workpath('rep3', 'd4')], list work_d4 = fs.File(os.path.join('work', 'd4')) - list = fs.Rsearchall(['d3', work_d4]) - assert list == ['d3', work_d4], list + list = map(str, fs.Rsearchall(['d3', work_d4])) + assert list == ['d3', str(work_d4)], list fs.BuildDir('build', '.') diff --git a/src/engine/SCons/Scanner/C.py b/src/engine/SCons/Scanner/C.py index d12fd45..47b6ea7 100644 --- a/src/engine/SCons/Scanner/C.py +++ b/src/engine/SCons/Scanner/C.py @@ -79,12 +79,8 @@ def scan(node, env, target, fs = SCons.Node.FS.default_fs): # node.includes - the result of include_re.findall() if not hasattr(target, 'cpppath'): - def Dir(rep, path, dir=target.cwd, fs=fs): - if rep: - path = os.path.join(rep, path) - return fs.Dir(path, dir) try: - target.cpppath = tuple(fs.Rsearchall(env['CPPPATH'], Dir)) + target.cpppath = tuple(fs.Rsearchall(SCons.Util.mapPaths(env['CPPPATH'], target.cwd), clazz=SCons.Node.FS.Dir)) except KeyError: target.cpppath = () diff --git a/src/engine/SCons/Scanner/Fortran.py b/src/engine/SCons/Scanner/Fortran.py index 5e0d6d3..fd8c971 100644 --- a/src/engine/SCons/Scanner/Fortran.py +++ b/src/engine/SCons/Scanner/Fortran.py @@ -77,12 +77,8 @@ def scan(node, env, target, fs = SCons.Node.FS.default_fs): # node.includes - the result of include_re.findall() if not hasattr(target, 'f77path'): - def Dir(rep, path, dir=target.cwd, fs=fs): - if rep: - path = os.path.join(rep, path) - return fs.Dir(path, dir) try: - target.f77path = tuple(fs.Rsearchall(env['F77PATH'], Dir)) + target.f77path = tuple(fs.Rsearchall(SCons.Util.mapPaths(env['F77PATH'], target.cwd), clazz=SCons.Node.FS.Dir)) except KeyError: target.f77path = () diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index 65d900c..a0bab35 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -997,8 +997,9 @@ def _main(): # -U with default targets default_targets = SCons.Script.SConscript.default_targets def check_dir(x): - cwd = SCons.Node.FS.default_fs.Dir(x.cwd.srcpath) - return cwd == target_top + reps = SCons.Node.FS.default_fs.Rsearchall(str(x.cwd), must_exist=0, + clazz=SCons.Node.FS.Dir) + return target_top in reps default_targets = filter(check_dir, default_targets) SCons.Script.SConscript.default_targets = default_targets target_top = None diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index 63c992e..3a39c4e 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -370,6 +370,40 @@ def Split(arg): else: return [arg] +def mapPaths(paths, dir): + """Takes a single node or string, or a list of nodes and/or + strings. We leave the nodes untouched, but we put the strings + under the supplied directory node dir, if they are not an absolute + path. + + For instance, the following: + + n = SCons.Node.FS.default_fs.File('foo') + mapPaths([ n, 'foo', '/bar' ], + SCons.Node.FS.default_fs.Dir('baz')) + + ...would return: + + [ n, 'baz/foo', '/bar' ] + """ + + def mapPathFunc(path, dir=dir): + if dir and is_String(path): + if not path: + return str(dir) + if os.path.isabs(path) or path[0] == '#': + return path + return dir.path_ + path + return path + + if not is_List(paths): + paths = [ paths ] + ret = map(mapPathFunc, paths) + if len(ret) == 1: + ret = ret[0] + return ret + + if hasattr(types, 'UnicodeType'): def is_String(e): return type(e) is types.StringType \ diff --git a/test/BuildDir.py b/test/BuildDir.py index b8e073b..349ed51 100644 --- a/test/BuildDir.py +++ b/test/BuildDir.py @@ -76,13 +76,15 @@ var2 = Dir('build/var2') var3 = Dir('build/var3') var4 = Dir('build/var4') var5 = Dir('../build/var5') +var6 = Dir('../build/var6') BuildDir('build/var1', src) -BuildDir(var2, src) -BuildDir(var3, src, duplicate=0) +BuildDir(var2, src, duplicate=0) +BuildDir(var3, src) BuildDir(var4, src, duplicate=0) -BuildDir(var5, src, duplicate=0) +BuildDir(var5, src) +BuildDir(var6, src, duplicate=0) env = Environment(CPPPATH='#src', F77PATH='#src') SConscript('build/var1/SConscript', "env") @@ -94,6 +96,7 @@ SConscript(File('SConscript', var4), "env") env = Environment(CPPPATH='.', F77PATH='.') SConscript('../build/var5/SConscript', "env") +SConscript('../build/var6/SConscript', "env") """) test.subdir(['test', 'src']) @@ -114,6 +117,13 @@ Import("env") env.Command(target='f2.c', source='f2.in', action=buildIt) env.Program(target='foo2', source='f2.c') env.Program(target='foo1', source='f1.c') +env.Command(target='f3.h', source='f3h.in', action=buildIt) +env.Command(target='f4.h', source='f4h.in', action=buildIt) +env.Command(target='f4.c', source='f4.in', action=buildIt) + +env2=env.Copy(CPPPATH='.') +env2.Program(target='foo3', source='f3.c') +env2.Program(target='foo4', source='f4.c') try: f77 = env['F77'] @@ -122,8 +132,8 @@ except: if f77 and WhereIs(env['F77']): env.Command(target='b2.f', source='b2.in', action=buildIt) - env.Copy(LIBS = 'g2c').Program(target='bar2', source='b2.f') - env.Copy(LIBS = 'g2c').Program(target='bar1', source='b1.f') + env.Copy(LIBS = ['g2c']).Program(target='bar2', source='b2.f') + env.Copy(LIBS = ['g2c']).Program(target='bar1', source='b1.f') """) test.write('test/src/f1.c', r""" @@ -150,6 +160,30 @@ main(int argc, char *argv[]) } """) +test.write('test/src/f3.c', r""" +#include "f3.h" + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf(F3_STR); + exit (0); +} +""") + +test.write('test/src/f4.in', r""" +#include "f4.h" + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf(F4_STR); + exit (0); +} +""") + test.write('test/src/f1.h', r""" #define F1_STR "f1.c\n" """) @@ -158,6 +192,14 @@ test.write('test/src/f2.h', r""" #define F2_STR "f2.c\n" """) +test.write('test/src/f3h.in', r""" +#define F3_STR "f3.c\n" +""") + +test.write('test/src/f4h.in', r""" +#define F4_STR "f4.c\n" +""") + test.write(['test', 'src', 'b1.f'], r""" PROGRAM FOO INCLUDE 'b1.for' @@ -214,37 +256,25 @@ def equal_stats(x,y): return (stat.S_IMODE(x[stat.ST_MODE]) == stat.S_IMODE(y[stat.ST_MODE]) and x[stat.ST_MTIME] == y[stat.ST_MTIME]) -# Make sure we did duplicate the source files in build/var2, and that their stats are the same: -test.fail_test(not os.path.exists(test.workpath('test', 'build', 'var2', 'f1.c'))) -test.fail_test(not os.path.exists(test.workpath('test', 'build', 'var2', 'f2.in'))) -test.fail_test(not equal_stats(test.workpath('test', 'build', 'var2', 'f1.c'), test.workpath('test', 'src', 'f1.c'))) -test.fail_test(not equal_stats(test.workpath('test', 'build', 'var2', 'f2.in'), test.workpath('test', 'src', 'f2.in'))) - -# Make sure we didn't duplicate the source files in build/var3. -test.fail_test(os.path.exists(test.workpath('test', 'build', 'var3', 'f1.c'))) -test.fail_test(os.path.exists(test.workpath('test', 'build', 'var3', 'f2.in'))) -test.fail_test(os.path.exists(test.workpath('test', 'build', 'var3', 'b1.f'))) -test.fail_test(os.path.exists(test.workpath('test', 'build', 'var3', 'b2.in'))) - -# Make sure we didn't duplicate the source files in build/var4. -test.fail_test(os.path.exists(test.workpath('test', 'build', 'var4', 'f1.c'))) -test.fail_test(os.path.exists(test.workpath('test', 'build', 'var4', 'f2.in'))) -test.fail_test(os.path.exists(test.workpath('test', 'build', 'var4', 'b1.f'))) -test.fail_test(os.path.exists(test.workpath('test', 'build', 'var4', 'b2.in'))) - -# Make sure we didn't duplicate the source files in build/var5. -test.fail_test(os.path.exists(test.workpath('build', 'var5', 'f1.c'))) -test.fail_test(os.path.exists(test.workpath('build', 'var5', 'f2.in'))) -test.fail_test(os.path.exists(test.workpath('build', 'var5', 'b1.f'))) -test.fail_test(os.path.exists(test.workpath('build', 'var5', 'b2.in'))) - # verify that header files in the source directory are scanned properly: test.write(['test', 'src', 'f1.h'], r""" #define F1_STR "f1.c 2\n" """) +test.write(['test', 'src', 'f3h.in'], r""" +#define F3_STR "f3.c 2\n" +""") + +test.write(['test', 'src', 'f4h.in'], r""" +#define F4_STR "f4.c 2\n" +""") + test.run(chdir='test', arguments = '../build/var5') test.run(program = foo51, stdout = "f1.c 2\n") +test.run(program = test.workpath('build', 'var5', 'foo3' + _exe), + stdout = "f3.c 2\n") +test.run(program = test.workpath('build', 'var5', 'foo4' + _exe), + stdout = "f4.c 2\n") test.pass_test() diff --git a/test/CPPPATH.py b/test/CPPPATH.py index a2cae10..45216c8 100644 --- a/test/CPPPATH.py +++ b/test/CPPPATH.py @@ -49,7 +49,7 @@ obj = env.Object(target='foobar/prog', source='subdir/prog.c') env.Program(target='prog', source=obj) SConscript('subdir/SConscript', "env") -BuildDir('variant', 'subdir', 0) +BuildDir('variant', 'subdir', duplicate=0) include = Dir('include') env = Environment(CPPPATH=[include]) SConscript('variant/SConscript', "env") @@ -168,7 +168,7 @@ obj = env.Object(target='foobar/prog', source='subdir/prog.c') env.Program(target='prog', source=obj) SConscript('subdir/SConscript', "env") -BuildDir('variant', 'subdir', 0) +BuildDir('variant', 'subdir') include = Dir('include') env = Environment(CPPPATH=['inc2', include]) SConscript('variant/SConscript', "env") diff --git a/test/Repository/BuildDir.py b/test/Repository/BuildDir.py index 2d75590..905c618 100644 --- a/test/Repository/BuildDir.py +++ b/test/Repository/BuildDir.py @@ -38,8 +38,8 @@ opts = "-Y " + test.workpath('repository') # test.write(['repository', 'SConstruct'], r""" -BuildDir('build0', 'src', duplicate=0) -BuildDir('build1', 'src', duplicate=1) +BuildDir('build0', 'src') +BuildDir('build1', 'src', duplicate=0) SConscript('build0/SConscript') SConscript('build1/SConscript') """) @@ -78,9 +78,9 @@ repository/src/bbb.in repository/src/ccc.in """) -test.fail_test(os.path.exists('work1/build0/aaa.in')) -test.fail_test(os.path.exists('work1/build0/bbb.in')) -test.fail_test(os.path.exists('work1/build0/ccc.in')) +test.fail_test(not os.path.exists('work1/build0/aaa.in')) +test.fail_test(not os.path.exists('work1/build0/bbb.in')) +test.fail_test(not os.path.exists('work1/build0/ccc.in')) test.fail_test(not os.path.exists('work1/build0/aaa.mid')) test.fail_test(not os.path.exists('work1/build0/bbb.mid')) test.fail_test(not os.path.exists('work1/build0/ccc.mid')) @@ -91,9 +91,9 @@ repository/src/bbb.in repository/src/ccc.in """) -test.fail_test(not os.path.exists('work1/build1/aaa.in')) -test.fail_test(not os.path.exists('work1/build1/bbb.in')) -test.fail_test(not os.path.exists('work1/build1/ccc.in')) +test.fail_test(os.path.exists('work1/build1/aaa.in')) +test.fail_test(os.path.exists('work1/build1/bbb.in')) +test.fail_test(os.path.exists('work1/build1/ccc.in')) test.fail_test(not os.path.exists('work1/build1/aaa.mid')) test.fail_test(not os.path.exists('work1/build1/bbb.mid')) test.fail_test(not os.path.exists('work1/build1/ccc.mid')) @@ -111,9 +111,9 @@ work1/src/bbb.in repository/src/ccc.in """) -test.fail_test(os.path.exists('work1/build0/aaa.in')) -test.fail_test(os.path.exists('work1/build0/bbb.in')) -test.fail_test(os.path.exists('work1/build0/ccc.in')) +test.fail_test(not os.path.exists('work1/build0/aaa.in')) +test.fail_test(not os.path.exists('work1/build0/bbb.in')) +test.fail_test(not os.path.exists('work1/build0/ccc.in')) test.fail_test(not os.path.exists('work1/build0/aaa.mid')) test.fail_test(not os.path.exists('work1/build0/bbb.mid')) test.fail_test(not os.path.exists('work1/build0/ccc.mid')) @@ -124,9 +124,9 @@ work1/src/bbb.in repository/src/ccc.in """) -test.fail_test(not os.path.exists('work1/build1/aaa.in')) -test.fail_test(not os.path.exists('work1/build1/bbb.in')) -test.fail_test(not os.path.exists('work1/build1/ccc.in')) +test.fail_test(os.path.exists('work1/build1/aaa.in')) +test.fail_test(os.path.exists('work1/build1/bbb.in')) +test.fail_test(os.path.exists('work1/build1/ccc.in')) test.fail_test(not os.path.exists('work1/build1/aaa.mid')) test.fail_test(not os.path.exists('work1/build1/bbb.mid')) test.fail_test(not os.path.exists('work1/build1/ccc.mid')) @@ -144,9 +144,9 @@ test.writable('repository', 0) # test.run(chdir = 'work2', options = opts, arguments = '.') -test.fail_test(os.path.exists('work2/build0/aaa.in')) -test.fail_test(os.path.exists('work2/build0/bbb.in')) -test.fail_test(os.path.exists('work2/build0/ccc.in')) +test.fail_test(not os.path.exists('work2/build0/aaa.in')) +test.fail_test(not os.path.exists('work2/build0/bbb.in')) +test.fail_test(not os.path.exists('work2/build0/ccc.in')) test.fail_test(os.path.exists('work2/build0/aaa.mid')) test.fail_test(os.path.exists('work2/build0/bbb.mid')) test.fail_test(os.path.exists('work2/build0/ccc.mid')) @@ -173,9 +173,9 @@ work2/src/bbb.in repository/src/ccc.in """) -test.fail_test(os.path.exists('work2/build0/aaa.in')) -test.fail_test(os.path.exists('work2/build0/bbb.in')) -test.fail_test(os.path.exists('work2/build0/ccc.in')) +test.fail_test(not os.path.exists('work2/build0/aaa.in')) +test.fail_test(not os.path.exists('work2/build0/bbb.in')) +test.fail_test(not os.path.exists('work2/build0/ccc.in')) test.fail_test(os.path.exists('work2/build0/aaa.mid')) test.fail_test(not os.path.exists('work2/build0/bbb.mid')) test.fail_test(os.path.exists('work2/build0/ccc.mid')) @@ -187,7 +187,7 @@ repository/src/ccc.in """) test.fail_test(os.path.exists('work2/build1/aaa.in')) -test.fail_test(not os.path.exists('work2/build1/bbb.in')) +test.fail_test(os.path.exists('work2/build1/bbb.in')) test.fail_test(os.path.exists('work2/build1/ccc.in')) test.fail_test(os.path.exists('work2/build1/aaa.mid')) test.fail_test(not os.path.exists('work2/build1/bbb.mid')) diff --git a/test/Repository/LIBPATH.py b/test/Repository/LIBPATH.py new file mode 100644 index 0000000..a385410 --- /dev/null +++ b/test/Repository/LIBPATH.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# +# Copyright (c) 2001, 2002 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import string +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('foo', ['foo', 'zzz'], 'bar', ['bar', 'yyy'], 'work') + +workpath_foo = test.workpath('foo') +workpath_foo_yyy = test.workpath('foo', 'yyy') +workpath_foo_zzz = test.workpath('foo', 'zzz') +workpath_bar = test.workpath('bar') +workpath_bar_yyy = test.workpath('bar', 'yyy') +workpath_bar_zzz = test.workpath('bar', 'zzz') +workpath_work = test.workpath('work') + +test.write(['work', 'SConstruct'], r""" +import string +env_zzz = Environment(LIBPATH = ['.', 'zzz']) +env_yyy = Environment(LIBPATH = ['yyy', '.']) +aaa_exe = env_zzz.Program('aaa', 'aaa.c') +bbb_exe = env_yyy.Program('bbb', 'bbb.c') +def write_LIBDIRFLAGS(env, target, source): + pre = env.subst('$LIBDIRPREFIX') + suf = env.subst('$LIBDIRSUFFIX') + f = open(str(target[0]), 'wb') + for arg in string.split(env.subst('$_LIBDIRFLAGS')): + if arg[:len(pre)] == pre: + arg = arg[len(pre):] + if arg[-len(suf):] == suf: + arg = arg[:-len(pre)] + f.write(arg + '\n') + f.close() + return 0 +env_zzz.Command('zzz.out', aaa_exe, write_LIBDIRFLAGS) +env_yyy.Command('yyy.out', bbb_exe, write_LIBDIRFLAGS) +""") + +test.write(['work', 'aaa.c'], r""" +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("work/aaa.c\n"); + exit (0); +} +""") + +test.write(['work', 'bbb.c'], r""" +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("work/bbb.c\n"); + exit (0); +} +""") + +# +opts = "-Y %s -Y %s -Y %s" % (workpath_foo, workpath_work, workpath_bar) +test.run(chdir = 'work', options = opts, arguments = ".") + +#dirs = ['.', workpath_foo, workpath_bar, workpath_foo_zzz] +dirs = ['.', workpath_foo, workpath_bar, + 'zzz', workpath_foo_zzz, workpath_bar_zzz] +test.fail_test(test.read(['work', 'zzz.out']) != + string.join(dirs, '\n') + '\n') + +#dirs = [workpath_bar_yyy, '.', workpath_foo, workpath_bar] +dirs = ['yyy', workpath_foo_yyy, workpath_bar_yyy, + '.', workpath_foo, workpath_bar] +test.fail_test(test.read(['work', 'yyy.out']) != + string.join(dirs, '\n') + '\n') + +# +test.run(chdir = 'work', options = '-c', arguments = ".") + +test.subdir(['work', 'zzz'], ['work', 'yyy']) + +# +test.run(chdir = 'work', options = opts, arguments = ".") + +#dirs = ['.', workpath_foo, workpath_bar, 'zzz', workpath_foo_zzz] +dirs = ['.', workpath_foo, workpath_bar, + 'zzz', workpath_foo_zzz, workpath_bar_zzz] +test.fail_test(test.read(['work', 'zzz.out']) != + string.join(dirs, '\n') + '\n') + +#dirs = ['yyy', workpath_bar_yyy, '.', workpath_foo, workpath_bar] +dirs = ['yyy', workpath_foo_yyy, workpath_bar_yyy, + '.', workpath_foo, workpath_bar] +test.fail_test(test.read(['work', 'yyy.out']) != + string.join(dirs, '\n') + '\n') + +# +test.pass_test() diff --git a/test/Repository/variants.py b/test/Repository/variants.py new file mode 100644 index 0000000..d91235f --- /dev/null +++ b/test/Repository/variants.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python +# +# Copyright (c) 2001, 2002 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os.path +import time +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('repository', ['repository', 'src'], + 'work1', ['work1', 'src'], + 'work2', ['work2', 'src']) + +repository_build_foo_xxx = test.workpath('repository', 'build', 'foo', 'xxx') +work1_build_foo_xxx = test.workpath('work1', 'build', 'foo', 'xxx') +work1_build_bar_xxx = test.workpath('work1', 'build', 'bar', 'xxx') + +opts = "-Y " + test.workpath('repository') + +# +test.write(['repository', 'SConstruct'], r""" +OS = ARGUMENTS['OS'] +build_os = "#build/" + OS +ccflags = { + 'foo' : '-DFOO', + 'bar' : '-DBAR', +} +env = Environment(CCFLAGS = ccflags[OS], + CPPPATH = build_os) +BuildDir(build_os, 'src') +SConscript(build_os + '/SConscript', "env") +""") + +test.write(['repository', 'src', 'SConscript'], r""" +Import("env") +env.Program('xxx', ['aaa.c', 'bbb.c', 'main.c']) +""") + +test.write(['repository', 'src', 'iii.h'], r""" +#ifdef FOO +#define STRING "REPOSITORY_FOO" +#endif +#ifdef BAR +#define STRING "REPOSITORY_BAR" +#endif +""") + +test.write(['repository', 'src', 'aaa.c'], r""" +#include +void +aaa(void) +{ + printf("repository/src/aaa.c: %s\n", STRING); +} +""") + +test.write(['repository', 'src', 'bbb.c'], r""" +#include +void +bbb(void) +{ + printf("repository/src/bbb.c: %s\n", STRING); +} +""") + +test.write(['repository', 'src', 'main.c'], r""" +#include +extern void aaa(void); +extern void bbb(void); +int +main(int argc, char *argv[]) +{ +#ifdef BAR + printf("Only when -DBAR.\n"); +#endif + aaa(); + bbb(); + printf("repository/src/main.c: %s\n", STRING); + exit (0); +} +""") + +# +test.run(chdir = 'repository', options = opts + " OS=foo", arguments = '.') + +test.run(program = repository_build_foo_xxx, stdout = +"""repository/src/aaa.c: REPOSITORY_FOO +repository/src/bbb.c: REPOSITORY_FOO +repository/src/main.c: REPOSITORY_FOO +""") + +test.fail_test(os.path.exists(test.workpath('repository', 'src', '.sconsign'))) +test.fail_test(os.path.exists(test.workpath('src', '.sconsign'))) + +# Make the entire repository non-writable, so we'll detect +# if we try to write into it accidentally. +test.writable('repository', 0) + +# +test.up_to_date(chdir = 'work1', options = opts + " OS=foo", arguments = '.') + +test.fail_test(os.path.exists(test.workpath('work1', 'build', 'foo', 'aaa.o'))) +test.fail_test(os.path.exists(test.workpath('work1', 'build', 'foo', 'bbb.o'))) +test.fail_test(os.path.exists(test.workpath('work1', 'build', 'foo', 'main.o'))) +test.fail_test(os.path.exists(test.workpath('work1', 'build', 'foo', 'xxx'))) + +# +test.run(chdir = 'work1', options = opts, arguments = 'OS=bar .') + +test.run(program = work1_build_bar_xxx, stdout = +"""Only when -DBAR. +repository/src/aaa.c: REPOSITORY_BAR +repository/src/bbb.c: REPOSITORY_BAR +repository/src/main.c: REPOSITORY_BAR +""") + +test.fail_test(os.path.exists(test.workpath('repository', 'src', '.sconsign'))) +test.fail_test(os.path.exists(test.workpath('src', '.sconsign'))) + +test.up_to_date(chdir = 'work1', options = opts + " OS=bar", arguments = '.') + +# +time.sleep(2) +test.write(['work1', 'src', 'iii.h'], r""" +#ifdef FOO +#define STRING "WORK_FOO" +#endif +#ifdef BAR +#define STRING "WORK_BAR" +#endif +""") + +# +test.run(chdir = 'work1', options = opts + " OS=bar", arguments = '.') + +test.run(program = work1_build_bar_xxx, stdout = +"""Only when -DBAR. +repository/src/aaa.c: WORK_BAR +repository/src/bbb.c: WORK_BAR +repository/src/main.c: WORK_BAR +""") + +test.fail_test(os.path.exists(test.workpath('repository', 'src', '.sconsign'))) +test.fail_test(os.path.exists(test.workpath('src', '.sconsign'))) + +test.up_to_date(chdir = 'work1', options = opts + " OS=bar", arguments = '.') + +# +test.run(chdir = 'work1', options = opts + " OS=foo", arguments = '.') + +test.run(program = work1_build_foo_xxx, stdout = +"""repository/src/aaa.c: WORK_FOO +repository/src/bbb.c: WORK_FOO +repository/src/main.c: WORK_FOO +""") + +test.fail_test(os.path.exists(test.workpath('repository', 'src', '.sconsign'))) +test.fail_test(os.path.exists(test.workpath('src', '.sconsign'))) + +test.up_to_date(chdir = 'work1', options = opts + " OS=foo", arguments = '.') + +# +test.pass_test() diff --git a/test/option--U.py b/test/option--U.py index 23e82b1..0160015 100644 --- a/test/option--U.py +++ b/test/option--U.py @@ -52,7 +52,7 @@ Default(env.B(target = 'sub1/foo.out', source = 'sub1/foo.in')) Export('env') SConscript('sub2/SConscript') Default(env.B(target = 'sub3/baz.out', source = 'sub3/baz.in')) -BuildDir('sub2b', 'sub2') +BuildDir('sub2b', 'sub2', duplicate=0) SConscript('sub2b/SConscript') Default(env.B(target = 'sub2/xxx.out', source = 'xxx.in')) SConscript('SConscript') diff --git a/test/option--implicit-cache.py b/test/option--implicit-cache.py index 4e5cf1e..186b637 100644 --- a/test/option--implicit-cache.py +++ b/test/option--implicit-cache.py @@ -52,7 +52,7 @@ obj = env.Object(target='prog', source='subdir/prog.c') env.Program(target='prog', source=obj) SConscript('subdir/SConscript', "env") -BuildDir('variant', 'subdir', 0) +BuildDir('variant', 'subdir') include = Dir('include') env = Environment(CPPPATH=['inc2', include]) SConscript('variant/SConscript', "env") -- cgit v0.12