Multiple directory .h includes in Repositories.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 29 Jul 2002 23:22:50 +0000 (23:22 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 29 Jul 2002 23:22:50 +0000 (23:22 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@422 fdb21ef1-2011-0410-befe-b5e4ea1792b1

13 files changed:
src/engine/SCons/Action.py
src/engine/SCons/ActionTests.py
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/FSTests.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/Scanner/C.py
src/engine/SCons/Scanner/CTests.py
src/engine/SCons/Scanner/Fortran.py
src/engine/SCons/Scanner/FortranTests.py
src/engine/SCons/Script/SConscript.py
src/engine/SCons/Script/__init__.py
src/engine/SCons/Sig/SigTests.py
test/Repository/multi-dir.py [new file with mode: 0644]

index 79fdd2cffb6bbda04af875611836c68e7c8d1410..1ddecdfdcfdd83ef5dbf4812aa8e92ffe04e1e1e 100644 (file)
@@ -271,12 +271,6 @@ class ActionBase:
         else:
             del kw['dir']
 
-        def rstr(x):
-            try:
-                return x.rstr()
-            except AttributeError:
-                return str(x)
-
         if kw.has_key('target'):
             t = kw['target']
             del kw['target']
@@ -286,11 +280,16 @@ class ActionBase:
                 cwd = t[0].cwd
             except (IndexError, AttributeError):
                 pass
-            dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(rstr, t)))
+            dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(str, t)))
             if dict['TARGETS']:
                 dict['TARGET'] = dict['TARGETS'][0]
 
         if kw.has_key('source'):
+            def rstr(x):
+                try:
+                    return x.rstr()
+                except AttributeError:
+                    return str(x)
             s = kw['source']
             del kw['source']
             if not SCons.Util.is_List(s):
index 5fa1b8bd7aec7f44f102eab54f8dd1d7838e194e..16bcd80d952d621b2bf47e40636f7aa60c5ade1e 100644 (file)
@@ -117,13 +117,15 @@ class ActionBaseTestCase(unittest.TestCase):
         class N:
             def __init__(self, name):
                 self.name = name
+            def __str__(self):
+                return self.name
             def rstr(self):
                 return 'rstr-' + self.name
 
         d = a.subst_dict(target = [N('t3'), 't4'], source = ['s3', N('s4')])
         TARGETS = map(lambda x: str(x), d['TARGETS'])
         TARGETS.sort()
-        assert TARGETS == ['rstr-t3', 't4'], d['TARGETS']
+        assert TARGETS == ['t3', 't4'], d['TARGETS']
         SOURCES = map(lambda x: str(x), d['SOURCES'])
         SOURCES.sort()
         assert SOURCES == ['rstr-s4', 's3'], d['SOURCES']
index 0837ed3dd7404abbd5c077ff8b2f918dabeb8716..dfea21b6e50751f36d54852977d1dfc56f853832 100644 (file)
@@ -307,12 +307,12 @@ class FS:
         build_dir.link(src_dir, duplicate)
 
     def Repository(self, *dirs):
-        """Specify repository directories to search."""
+        """Specify Repository directories to search."""
         for d in dirs:
             self.Repositories.append(self.Dir(d))
 
     def Rsearch(self, path, func = exists_path):
-        """Search for something in a repository.  Returns the first
+        """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
@@ -327,10 +327,12 @@ class FS:
         return None
 
     def Rsearchall(self, pathlist, func = exists_path):
-        """Search for a list of somethings in the repository list."""
+        """Search for a list of somethings in the Repository list."""
         ret = []
         if SCons.Util.is_String(pathlist):
             pathlist = string.split(pathlist, os.pathsep)
+        if not SCons.Util.is_List(pathlist):
+            pathlist = [pathlist]
         for path in pathlist:
             if isinstance(path, SCons.Node.Node):
                 ret.append(path)
@@ -339,6 +341,8 @@ class FS:
                 if n:
                     ret.append(n)
                 if not os.path.isabs(path):
+                    if path[0] == '#':
+                        path = path[1:]
                     for dir in self.Repositories:
                         n = func(os.path.join(dir.path, path))
                         if n:
@@ -422,7 +426,7 @@ class Entry(SCons.Node.Node):
         raise AttributeError
 
     def exists(self):
-        return os.path.exists(self.rstr())
+        return os.path.exists(self.path)
 
     def cached_exists(self):
         try:
@@ -431,6 +435,9 @@ class Entry(SCons.Node.Node):
             self.exists_flag = self.exists()
             return self.exists_flag
 
+    def rexists(self):
+        return os.path.exists(self.rstr())
+
     def get_parents(self):
         parents = SCons.Node.Node.get_parents(self)
         if self.dir and not isinstance(self.dir, ParentOfRoot):
@@ -457,7 +464,6 @@ class Entry(SCons.Node.Node):
 
 # XXX TODO?
 # Annotate with the creator
-# is_under
 # rel_path
 # srcpath / srcdir
 # link / is_linked
@@ -588,12 +594,14 @@ class Dir(Entry):
         # source path exists, we only care about the path.
         return os.path.exists(self.path)
 
+    def rexists(self):
+        # Again, directories are special...we don't care if their
+        # source path exists, we only care about the path.
+        return os.path.exists(self.rstr())
+
 
 
 # XXX TODO?
-# rfile
-# precious
-# rpath
 # rsrcpath
 # source_exists
 # derived_exists
@@ -604,9 +612,7 @@ class Dir(Entry):
 # addsuffix
 # accessible
 # ignore
-# build
 # bind
-# is_under
 # relpath
 
 class File(Entry):
@@ -624,16 +630,44 @@ class File(Entry):
         return self.dir.root()
 
     def get_contents(self):
-        if not self.exists():
+        if not self.rexists():
             return ''
         return open(self.rstr(), "rb").read()
 
     def get_timestamp(self):
-        if self.exists():
+        if self.rexists():
             return os.path.getmtime(self.rstr())
         else:
             return 0
 
+    def calc_signature(self, calc):
+        """
+        Select and calculate the appropriate build signature for a File.
+
+        self - the File node
+        calc - the signature calculation module
+        returns - the signature
+
+        This method does not store the signature in the node or
+        in the .sconsign file.
+        """
+
+        if self.builder:
+            if SCons.Sig.build_signature:
+                if not hasattr(self, 'bsig'):
+                    self.set_bsig(calc.bsig(self.rfile()))
+                return self.get_bsig()
+            else:
+                if not hasattr(self, 'csig'):
+                    self.set_csig(calc.csig(self.rfile()))
+                return self.get_csig()
+        elif not self.rexists():
+            return None
+        else:
+            if not hasattr(self, 'csig'):
+                self.set_csig(calc.csig(self.rfile()))
+            return self.get_csig()
+
     def store_csig(self):
         self.dir.sconsign().set_csig(self.name, self.get_csig())
 
@@ -671,6 +705,17 @@ class File(Entry):
                 file_link(self.srcpath, self.path)
         return Entry.exists(self)
 
+    def rexists(self):
+        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)
+        return Entry.rexists(self)
+
     def scanner_key(self):
         return os.path.splitext(self.name)[1]
 
@@ -705,6 +750,22 @@ class File(Entry):
         else:
             self.__createDir()
 
+    def current(self, calc):
+        bsig = calc.bsig(self)
+        if not self.exists():
+            # The file doesn't exist locally...
+            r = self.rfile()
+            if r != self:
+                # ...but there is one in a Repository...
+                if calc.current(r, bsig):
+                    # ...and it's even up-to-date.
+                    # XXX Future: copy locally if requested
+                    return 1
+            self._rfile = self
+            return None
+        else:
+            return calc.current(self, bsig)
+
     def rfile(self):
         if not hasattr(self, '_rfile'):
             self._rfile = self
index 42a6542e1a4516b51b79a4737430bde1e85a3bfd..b45cc4ba2afd8be855af37c44737cd2f0a1bb491 100644 (file)
@@ -27,6 +27,7 @@ import os
 import os.path
 import string
 import sys
+import time
 import unittest
 import SCons.Node.FS
 from TestCmd import TestCmd
@@ -501,7 +502,18 @@ class FSTestCase(unittest.TestCase):
         assert c == "", c
         assert e.__class__ == SCons.Node.FS.Dir
 
-        #XXX test get_timestamp()
+        test.write("tstamp", "tstamp\n")
+        # Okay, *this* manipulation accomodates Windows FAT file systems
+        # that only have two-second granularity on their timestamps.
+        # We round down the current time to the nearest even integer
+        # value, subtract two to make sure the timestamp is not "now,"
+        # and then convert it back to a float.
+        tstamp = float(int(time.time() / 2) * 2) - 2
+        os.utime(test.workpath("tstamp"), (tstamp - 2.0, tstamp))
+        f = fs.File("tstamp")
+        t = f.get_timestamp()
+        assert t == tstamp, "expected %f, got %f" % (tstamp, t)
+        test.unlink("tstamp")
 
         #XXX test get_prevsiginfo()
 
@@ -530,6 +542,10 @@ class FSTestCase(unittest.TestCase):
             exc_caught = 1
         assert exc_caught, "Should have caught a TypeError"
 
+        # XXX test calc_signature()
+
+        # XXX test current()
+
 class RepositoryTestCase(unittest.TestCase):
     def runTest(self):
         """Test FS (file system) Repository operations
@@ -585,6 +601,10 @@ class RepositoryTestCase(unittest.TestCase):
         assert fs.Rsearch('f2', os.path.exists)
         assert fs.Rsearch('f3', os.path.exists)
 
+        list = fs.Rsearchall(fs.Dir('d1'))
+        assert len(list) == 1, list
+        assert list[0].path == 'd1', list[0].path
+
         list = fs.Rsearchall([fs.Dir('d1')])
         assert len(list) == 1, list
         assert list[0].path == 'd1', list[0].path
@@ -592,6 +612,9 @@ class RepositoryTestCase(unittest.TestCase):
         list = fs.Rsearchall('d2')
         assert list == [], list
 
+        list = fs.Rsearchall('#d2')
+        assert list == [], list
+
         test.subdir(['work', 'd2'])
         list = fs.Rsearchall('d2')
         assert list == ['d2'], list
@@ -623,6 +646,40 @@ class RepositoryTestCase(unittest.TestCase):
         work_d4 = fs.File(os.path.join('work', 'd4'))
         list = fs.Rsearchall(['d3', work_d4])
         assert list == ['d3', work_d4], list
+        
+        f1 = fs.File(test.workpath("work", "i_do_not_exist"))
+        assert not f1.rexists()
+        
+        test.write(["rep2", "i_exist"], "\n")
+        f1 = fs.File(test.workpath("work", "i_exist"))
+        assert f1.rexists()
+        
+        test.write(["work", "i_exist_too"], "\n")
+        f1 = fs.File(test.workpath("work", "i_exist_too"))
+        assert f1.rexists()
+
+        test.write(["rep2", "tstamp"], "tstamp\n")
+        # Okay, *this* manipulation accomodates Windows FAT file systems
+        # that only have two-second granularity on their timestamps.
+        # We round down the current time to the nearest even integer
+        # value, subtract two to make sure the timestamp is not "now,"
+        # and then convert it back to a float.
+        tstamp = float(int(time.time() / 2) * 2) - 2
+        os.utime(test.workpath("rep2", "tstamp"), (tstamp - 2.0, tstamp))
+        f = fs.File("tstamp")
+        t = f.get_timestamp()
+        assert t == tstamp, "expected %f, got %f" % (tstamp, t)
+        test.unlink(["rep2", "tstamp"])
+
+        # Make sure get_contents() returns the binary contents.
+        test.write(["rep3", "contents"], "Con\x1aTents\n")
+        c = fs.File("contents").get_contents()
+        assert c == "Con\x1aTents\n", "got '%s'" % c
+        test.unlink(["rep3", "contents"])
+
+        # XXX test calc_signature()
+
+        # XXX test current()
 
 class find_fileTestCase(unittest.TestCase):
     def runTest(self):
index 45f5bc7994f053eeadf2c7f397ea82d1da74a651..a65310d8cf22b2e4f2bfd1884749265a4b24b074 100644 (file)
@@ -395,6 +395,9 @@ class Node:
     def current(self):
         return None
 
+    def rfile(self):
+        return self
+
     def rstr(self):
         return str(self)
 
index d37bda8649edf387f738b98e3ebddb69342beb20..f388b6309308fe9db03075f3918a801036146a8b 100644 (file)
@@ -81,14 +81,14 @@ def scan(node, env, target, fs = SCons.Node.FS.default_fs):
     if not hasattr(target, 'cpppath'):
         def Dir(x, dir=target.cwd, fs=fs): return fs.Dir(x,dir)
         try:
-            target.cpppath = tuple(SCons.Node.arg2nodes(env['CPPPATH'],Dir))
+            target.cpppath = tuple(fs.Rsearchall(env['CPPPATH'], Dir))
         except KeyError:
             target.cpppath = ()
 
     cpppath = target.cpppath
 
     if not node.found_includes.has_key(cpppath):
-        if node.exists():
+        if node.rexists():
 
             # cache the includes list in node so we only scan it once:
             if node.includes != None:
index faa4cdd30497d791ae5ab74be2f07b6c535549f6..8a70134ca57dbf4f4ba36c2034f04d96dc0d7fc1 100644 (file)
@@ -113,10 +113,25 @@ int main()
 }
 """)
 
-test.write('include/fa.h', "\n")
-test.write('include/fb.h', "\n")
-test.write('subdir/include/fa.h', "\n")
-test.write('subdir/include/fb.h', "\n")
+test.write(['include', 'fa.h'], "\n")
+test.write(['include', 'fb.h'], "\n")
+test.write(['subdir', 'include', 'fa.h'], "\n")
+test.write(['subdir', 'include', 'fb.h'], "\n")
+
+
+test.subdir('repository', ['repository', 'include'])
+test.subdir('work', ['work', 'src'])
+
+test.write(['repository', 'include', 'iii.h'], "\n")
+
+test.write(['work', 'src', 'fff.c'], """
+#include <iii.h>
+
+int main()
+{
+    return 0;
+}
+""")
 
 # define some helpers:
 
@@ -267,6 +282,17 @@ class CScannerTestCase10(unittest.TestCase):
         deps_match(self, deps, [ 'include/fa.h', 'include/fb.h' ])
         test.unlink('include/fa.cpp')
 
+class CScannerTestCase11(unittest.TestCase):
+    def runTest(self):
+        os.chdir(test.workpath('work'))
+        fs = SCons.Node.FS.FS(test.workpath('work'))
+        fs.Repository(test.workpath('repository'))
+        s = SCons.Scanner.C.CScan(fs=fs)
+        env = DummyEnvironment(['include'])
+        deps = s.scan(fs.File('src/fff.c'), env, DummyTarget())
+        deps_match(self, deps, [test.workpath('repository/include/iii.h')])
+        os.chdir(test.workpath(''))
+
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(CScannerTestCase1())
@@ -278,6 +304,7 @@ def suite():
     suite.addTest(CScannerTestCase8())
     suite.addTest(CScannerTestCase9())
     suite.addTest(CScannerTestCase10())
+    suite.addTest(CScannerTestCase11())
     return suite
 
 if __name__ == "__main__":
index 17c924143a294e24fb63106620ef678cbc60e4b6..e87b885d19cbbe7854816c5ef2c27624774bafeb 100644 (file)
@@ -79,7 +79,7 @@ def scan(node, env, target, fs = SCons.Node.FS.default_fs):
     if not hasattr(target, 'f77path'):
         def Dir(x, dir=target.cwd, fs=fs): return fs.Dir(x,dir)
         try:
-            target.f77path = tuple(SCons.Node.arg2nodes(env['F77PATH'],Dir))
+            target.f77path = tuple(fs.Rsearchall(env['F77PATH'], Dir))
         except KeyError:
             target.f77path = ()
 
@@ -90,7 +90,7 @@ def scan(node, env, target, fs = SCons.Node.FS.default_fs):
     try:
         nodes = node.found_includes[f77path]
     except KeyError:
-        if node.exists():
+        if node.rexists():
 
             # cache the includes list in node so we only scan it once:
             if node.includes != None:
index 2883daf9863d2db61e1b81d03eb4bacf9cdc7981..6d2a0664255965881de8d2793c36f22ec9e48909 100644 (file)
@@ -92,6 +92,19 @@ test.write('fff4.f',"""
 test.write('include/f4.f', "\n")
 test.write('subdir/include/f4.f', "\n")
 
+
+test.subdir('repository', ['repository', 'include'])
+test.subdir('work', ['work', 'src'])
+
+test.write(['repository', 'include', 'iii.f'], "\n")
+
+test.write(['work', 'src', 'fff.f'], """
+      PROGRAM FOO
+      INCLUDE 'iii.f'
+      STOP
+      END
+""")
+
 # define some helpers:
 
 class DummyTarget:
@@ -278,6 +291,17 @@ class FortranScannerTestCase12(unittest.TestCase):
         deps_match(self, deps, ['include/f4.f'])
         test.unlink('include/fff4.f')
 
+class FortranScannerTestCase13(unittest.TestCase):
+    def runTest(self):
+        os.chdir(test.workpath('work'))
+        fs = SCons.Node.FS.FS(test.workpath('work'))
+        fs.Repository(test.workpath('repository'))
+        s = SCons.Scanner.Fortran.FortranScan(fs=fs)
+        env = DummyEnvironment(['include'])
+        deps = s.scan(fs.File('src/fff.f'), env, DummyTarget())
+        deps_match(self, deps, [test.workpath('repository/include/iii.f')])
+        os.chdir(test.workpath(''))
+
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(FortranScannerTestCase1())
@@ -292,6 +316,7 @@ def suite():
     suite.addTest(FortranScannerTestCase10())
     suite.addTest(FortranScannerTestCase11())
     suite.addTest(FortranScannerTestCase12())
+    suite.addTest(FortranScannerTestCase13())
     return suite
 
 if __name__ == "__main__":
index e088c991dec2bd58969e27a4a59b9b7aac354248..38fd080e13d63c5bf70d6876ec1618b36d345eb9 100644 (file)
@@ -162,11 +162,11 @@ def SConscript(*ls, **kw):
             if fn == "-":
                 exec sys.stdin in stack[-1].globals
             else:
-               if isinstance(fn, SCons.Node.Node):
-                   f = fn
-               else:
-                   f = SCons.Node.FS.default_fs.File(str(fn))
-                if f.exists():
+                if isinstance(fn, SCons.Node.Node):
+                    f = fn
+                else:
+                    f = SCons.Node.FS.default_fs.File(str(fn))
+                if f.rexists():
                     file = open(f.rstr(), "r")
                     SCons.Node.FS.default_fs.chdir(f.dir)
                     if sconscript_chdir:
index 10794f5103871d9e700ee627813d08b2ea09da9b..17a12562e4b9e000b46b31c50f1809bdb295c918 100644 (file)
@@ -835,7 +835,7 @@ def _SConstruct_exists(dirname=''):
         sfile = os.path.join(dirname, file)
         if os.path.isfile(sfile):
             return sfile
-        if not os.path.isabs(file):
+        if not os.path.isabs(sfile):
             for rep in repositories:
                 if os.path.isfile(os.path.join(rep, sfile)):
                     return sfile
index 8c9125a94e5e73caeee55a66d137ac6ad906f9ba..37413aa176bd88d360cbb3df52102d70dd876b6f 100644 (file)
@@ -81,6 +81,9 @@ class DummyNode:
             self.exists_cache = self.exists()
             return self.exists_cache
 
+    def rexists(self):
+        return not self.file.contents is None
+
     def children(self):
         return filter(lambda x, i=self.ignore: x not in i,
                       self.sources + self.depends)
diff --git a/test/Repository/multi-dir.py b/test/Repository/multi-dir.py
new file mode 100644 (file)
index 0000000..a588814
--- /dev/null
@@ -0,0 +1,146 @@
+#!/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 sys
+import TestSCons
+
+if sys.platform == 'win32':
+    _exe = '.exe'
+else:
+    _exe = ''
+
+
+
+test = TestSCons.TestSCons()
+
+#
+test.subdir('work',
+            ['work', 'src'],
+            ['work', 'include'],
+            'repository',
+            ['repository', 'src'],
+            ['repository', 'include'])
+
+#
+workpath_repository = test.workpath('repository')
+work_include_my_string_h = test.workpath('work', 'include', 'my_string.h')
+work_src_xxx = test.workpath('work', 'src', 'xxx')
+repository_src_xxx = test.workpath('repository', 'src', 'xxx')
+
+opts = "-Y " + workpath_repository
+
+#
+test.write(['repository', 'SConstruct'], """
+env = Environment(CPPPATH = ['#src', '#include'])
+SConscript('src/SConscript', "env")
+""")
+
+test.write(['repository', 'src', 'SConscript'], """
+Import("env")
+env.Program(target = 'xxx', source = 'main.c')
+""")
+
+test.write(['repository', 'include', 'my_string.h'], r"""
+#define        MY_STRING       "repository/include/my_string.h"
+""")
+
+test.write(['repository', 'src', 'include.h'], r"""
+#include <my_string.h>
+#define        LOCAL_STRING    "repository/src/include.h"
+""")
+
+test.write(['repository', 'src', 'main.c'], r"""
+#include <include.h>
+int
+main(int argc, char *argv[])
+{
+       argv[argc++] = "--";
+       printf("%s\n", MY_STRING);
+       printf("%s\n", LOCAL_STRING);
+       printf("repository/src/main.c\n");
+       exit (0);
+}
+""")
+
+#
+test.run(chdir = 'repository', arguments = ".")
+
+test.run(program = repository_src_xxx, stdout =
+"""repository/include/my_string.h
+repository/src/include.h
+repository/src/main.c
+""")
+
+# Double-check that the Repository is up-to-date.
+#test.up_to_date(chdir = 'repository', arguments = ".")
+
+# Make the repository non-writable,
+# so we'll detect if we try to write into it accidentally.
+test.writable('repository', 0)
+
+# Because the Repository is completely up-to-date,
+# a build in an empty work directory should also be up-to-date.
+test.up_to_date(chdir = 'work', options = opts, arguments = ".")
+
+test.write(['work', 'include', 'my_string.h'], r"""
+#define        MY_STRING       "work/include/my_string.h"
+""")
+
+test.run(chdir = 'work', options = opts, arguments = ".")
+
+test.run(program = work_src_xxx, stdout =
+"""work/include/my_string.h
+repository/src/include.h
+repository/src/main.c
+""")
+
+test.write(['work', 'src', 'include.h'], r"""
+#include <my_string.h>
+#define        LOCAL_STRING    "work/src/include.h"
+""")
+
+test.run(chdir = 'work', options = opts, arguments = ".")
+
+test.run(program = work_src_xxx, stdout =
+"""work/include/my_string.h
+work/src/include.h
+repository/src/main.c
+""")
+
+#
+test.unlink(work_include_my_string_h)
+
+test.run(chdir = 'work', options = opts, arguments = ".")
+
+test.run(program = work_src_xxx, stdout =
+"""repository/include/my_string.h
+work/src/include.h
+repository/src/main.c
+""")
+
+#
+test.pass_test()
+__END__