summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2002-07-29 23:22:50 (GMT)
committerSteven Knight <knight@baldmt.com>2002-07-29 23:22:50 (GMT)
commit7b142dbd32c3673ae57bda1fea98a8d8e3f7d0e5 (patch)
tree852f6c632d1f018e22d22aa459f2c9753c66ba59
parentcd6d3c9e4f76592845cf32e11cee9a04c1b2f0f6 (diff)
downloadSCons-7b142dbd32c3673ae57bda1fea98a8d8e3f7d0e5.zip
SCons-7b142dbd32c3673ae57bda1fea98a8d8e3f7d0e5.tar.gz
SCons-7b142dbd32c3673ae57bda1fea98a8d8e3f7d0e5.tar.bz2
Multiple directory .h includes in Repositories.
-rw-r--r--src/engine/SCons/Action.py13
-rw-r--r--src/engine/SCons/ActionTests.py4
-rw-r--r--src/engine/SCons/Node/FS.py85
-rw-r--r--src/engine/SCons/Node/FSTests.py59
-rw-r--r--src/engine/SCons/Node/__init__.py3
-rw-r--r--src/engine/SCons/Scanner/C.py4
-rw-r--r--src/engine/SCons/Scanner/CTests.py35
-rw-r--r--src/engine/SCons/Scanner/Fortran.py4
-rw-r--r--src/engine/SCons/Scanner/FortranTests.py25
-rw-r--r--src/engine/SCons/Script/SConscript.py10
-rw-r--r--src/engine/SCons/Script/__init__.py2
-rw-r--r--src/engine/SCons/Sig/SigTests.py3
-rw-r--r--test/Repository/multi-dir.py146
13 files changed, 358 insertions, 35 deletions
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index 79fdd2c..1ddecdf 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -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):
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index 5fa1b8b..16bcd80 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -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']
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 0837ed3..dfea21b 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -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
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index 42a6542..b45cc4b 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -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):
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 45f5bc7..a65310d 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -395,6 +395,9 @@ class Node:
def current(self):
return None
+ def rfile(self):
+ return self
+
def rstr(self):
return str(self)
diff --git a/src/engine/SCons/Scanner/C.py b/src/engine/SCons/Scanner/C.py
index d37bda8..f388b63 100644
--- a/src/engine/SCons/Scanner/C.py
+++ b/src/engine/SCons/Scanner/C.py
@@ -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:
diff --git a/src/engine/SCons/Scanner/CTests.py b/src/engine/SCons/Scanner/CTests.py
index faa4cdd..8a70134 100644
--- a/src/engine/SCons/Scanner/CTests.py
+++ b/src/engine/SCons/Scanner/CTests.py
@@ -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__":
diff --git a/src/engine/SCons/Scanner/Fortran.py b/src/engine/SCons/Scanner/Fortran.py
index 17c9241..e87b885 100644
--- a/src/engine/SCons/Scanner/Fortran.py
+++ b/src/engine/SCons/Scanner/Fortran.py
@@ -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:
diff --git a/src/engine/SCons/Scanner/FortranTests.py b/src/engine/SCons/Scanner/FortranTests.py
index 2883daf..6d2a066 100644
--- a/src/engine/SCons/Scanner/FortranTests.py
+++ b/src/engine/SCons/Scanner/FortranTests.py
@@ -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__":
diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py
index e088c99..38fd080 100644
--- a/src/engine/SCons/Script/SConscript.py
+++ b/src/engine/SCons/Script/SConscript.py
@@ -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:
diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py
index 10794f5..17a1256 100644
--- a/src/engine/SCons/Script/__init__.py
+++ b/src/engine/SCons/Script/__init__.py
@@ -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
diff --git a/src/engine/SCons/Sig/SigTests.py b/src/engine/SCons/Sig/SigTests.py
index 8c9125a..37413aa 100644
--- a/src/engine/SCons/Sig/SigTests.py
+++ b/src/engine/SCons/Sig/SigTests.py
@@ -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
index 0000000..a588814
--- /dev/null
+++ b/test/Repository/multi-dir.py
@@ -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__