Add Java RMIC support.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 10 May 2003 04:44:31 +0000 (04:44 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 10 May 2003 04:44:31 +0000 (04:44 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@677 fdb21ef1-2011-0410-befe-b5e4ea1792b1

bin/files
doc/man/scons.1
src/CHANGES.txt
src/engine/MANIFEST.in
src/engine/SCons/Tool/__init__.py
src/engine/SCons/Tool/rmic.py [new file with mode: 0644]
test/RMIC.py [new file with mode: 0644]
test/Repository/RMIC.py [new file with mode: 0644]

index 9b24f9aad2fce935be0b9ca7bb85b09d76a6955c..dec67505a018a44f5c099645054bb4d08380dffa 100644 (file)
--- a/bin/files
+++ b/bin/files
@@ -66,6 +66,7 @@
 ./SCons/Tool/nasm.py
 ./SCons/Tool/pdflatex.py
 ./SCons/Tool/pdftex.py
+./SCons/Tool/rmic.py
 ./SCons/Tool/sgiar.py
 ./SCons/Tool/sgicc.py
 ./SCons/Tool/sgilink.py
index 2cbf048e2edd028e1fd9888dcf60a796d7718845..99ddfa023b25e3e004a592700be855aa7d5d306f 100644 (file)
@@ -860,8 +860,7 @@ platform name when the Environment is constructed. Changing the PATH
 variable after the Environment is constructed will not cause the tools to
 be redetected.
 
-SCons supports the following tool specifications
-out of the box on all platforms:
+SCons supports the following tool specifications out of the box:
 .ES
 386asm
 ar
@@ -878,6 +877,7 @@ gcc
 gnulink
 jar
 javac
+javah
 latex
 lex
 linkloc
@@ -890,6 +890,7 @@ msvc
 nasm
 pdflatex
 pdftex
+rmic
 sgiar
 sgias
 sgicc
@@ -1368,6 +1369,40 @@ env.JavaH(target = 'export',
           JAVACLASSDIR = 'classes')
 .EE
 
+.IP RMIC
+Builds stub and skeleton class files
+for remote objects
+from Java .class files.
+The target is a directory
+relative to which the stub
+and skeleton class files will be written.
+The source can be the names of .class files,
+or the objects return from the
+.B Java
+builder.
+
+If the construction variable
+.B JAVACLASSDIR
+is set, either in the environment
+or in the call to the
+.B RMIC
+builder itself,
+then the value of the variable
+will be stripped from the
+beginning of any .class file names.
+
+.ES
+classes = env.Java(target = 'classdir', source = 'src')
+env.RMIC(target = 'outdir1', source = classes)
+
+env.RMIC(target = 'outdir2',
+         source = ['package/foo.class', 'package/bar.class'])
+
+env.RMIC(target = 'outdir3',
+         source = ['classes/foo.class', 'classes/bar.class'],
+         JAVACLASSDIR = 'classes')
+.EE
+
 .IP TypeLibrary
 Builds a Windows type library (.tlb) file from and input IDL file
 (.idl).  In addition, it will build the associated inteface stub and
@@ -2734,6 +2769,19 @@ Options that are passed to the $RCS_CO command.
 A function that converts a file name into a list of Dir instances by
 searching the repositories. 
 
+.IP RMIC
+The Java RMI stub compiler.
+
+.IP RMICCOM
+The command line used to compile stub
+and skeleton class files
+from Java classes that contain RMI implementations.
+Any options specified in the $RMICFLAGS construction variable
+are included on this command line.
+
+.IP RMICFLAGS
+General options passed to the Java RMI stub compiler.
+
 .IP SCANNERS
 A list of the available implicit dependency scanners. [CScan] by default.
 
index 1cd8565d678e74ca7123598536df75b173c42873..7717ce645697a362607db7b48c2865ae21a14305 100644 (file)
@@ -57,7 +57,7 @@ RELEASE 0.14 - XXX
 
   From Steven Knight:
 
-  - Add Java support (javac, javah and jar).
+  - Add Java support (javac, javah, jar and rmic).
 
   - Propagate the external SYSTEMROOT environment variable into ENV on
     Win32 systems, so external commands that use sockets will work.
index 8bd3c2fdc0df93e1a23d55057dc46ba84e6dcbda..24277b418ef36f516c0157af151a53aee9aa3e77 100644 (file)
@@ -81,6 +81,7 @@ SCons/Tool/pdftex.py
 SCons/Tool/Perforce.py
 SCons/Tool/PharLapCommon.py
 SCons/Tool/RCS.py
+SCons/Tool/rmic.py
 SCons/Tool/SCCS.py
 SCons/Tool/sgiar.py
 SCons/Tool/sgicc.py
index 25d746223bb9fb4d2f0d0193df43ef74470bb683..908101eaea2fca99dc8425eadf814f2af1ff683b 100644 (file)
@@ -226,7 +226,7 @@ def tool_list(platform, env):
                                 'jar', 'javac', 'javah',
                                 'latex', 'lex', 'midl',
                                 'pdflatex', 'pdftex', 'Perforce',
-                                'RCS', 'SCCS',
+                                'RCS', 'rmic', 'SCCS',
                                 # 'Subversion',
                                 'tar', 'tex', 'yacc', 'zip'],
                                env)
diff --git a/src/engine/SCons/Tool/rmic.py b/src/engine/SCons/Tool/rmic.py
new file mode 100644 (file)
index 0000000..7043566
--- /dev/null
@@ -0,0 +1,109 @@
+"""SCons.Tool.rmic
+
+Tool-specific initialization for rmic.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# 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 re
+import string
+
+import SCons.Builder
+import SCons.Node.FS
+
+def emit_rmic_classes(target, source, env):
+    """Create and return lists of Java RMI stub and skeleton
+    class files to be created from a set of class files.
+    """
+    class_suffix = env.get('JAVACLASSSUFFIX', '.class')
+    classdir = env.get('JAVACLASSDIR')
+
+    if not classdir:
+        try:
+            s = source[0]
+        except IndexError:
+            classdir = '.'
+        else:
+            try:
+                classdir = s.attributes.java_classdir
+            except:
+                classdir = '.'
+    classdir = SCons.Node.FS.default_fs.Dir(classdir).rdir()
+    if str(classdir) == '.':
+        c_ = None
+    else:
+        c_ = str(classdir) + os.sep
+
+    slist = []
+    for src in source:
+        try:
+            classname = src.attributes.java_classname
+        except AttributeError:
+            classname = str(src)
+            if c_ and classname[:len(c_)] == c_:
+                classname = classname[len(c_):]
+            if class_suffix and classname[:-len(class_suffix)] == class_suffix:
+                classname = classname[-len(class_suffix):]
+        s = src.rfile()
+        s.attributes.java_classdir = classdir
+        s.attributes.java_classname = classname
+        slist.append(s)
+
+    File = SCons.Node.FS.default_fs.File
+    tlist = []
+    for s in source:
+        for suff in ['_Skel', '_Stub']:
+            fname = string.replace(s.attributes.java_classname, '.', os.sep) + \
+                    suff + class_suffix
+            t = target[0].File(fname)
+            t.attributes.java_lookupdir = target[0]
+            tlist.append(t)
+
+    return tlist, source
+
+RMICBuilder = SCons.Builder.Builder(action = '$RMICCOM',
+                     emitter = emit_rmic_classes,
+                     src_suffix = '$JAVACLASSSUFFIX',
+                     target_factory = SCons.Node.FS.default_fs.Dir,
+                     source_factory = SCons.Node.FS.default_fs.File)
+
+def generate(env):
+    """Add Builders and construction variables for rmic to an Environment."""
+    env['BUILDERS']['RMIC'] = RMICBuilder
+
+    env['RMIC']            = 'rmic'
+    env['RMICFLAGS']       = ''
+    env['RMICCOM']         = '$RMIC $RMICFLAGS -d ${TARGET.attributes.java_lookupdir} -classpath ${SOURCE.attributes.java_classdir} ${SOURCES.attributes.java_classname}'
+    env['JAVACLASSSUFFIX']  = '.class'
+
+def exists(env):
+    return env.Detect('rmic')
diff --git a/test/RMIC.py b/test/RMIC.py
new file mode 100644 (file)
index 0000000..abad6cd
--- /dev/null
@@ -0,0 +1,325 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# 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
+import string
+import sys
+import TestSCons
+
+python = TestSCons.python
+
+test = TestSCons.TestSCons()
+
+test.write('myrmic.py', r"""
+import os.path
+import sys
+args = sys.argv[1:]
+while args:
+    a = args[0]
+    if a == '-d':
+        outdir = args[1]
+        args = args[1:]
+    elif a == '-classpath':
+        args = args[1:]
+    elif a == '-sourcepath':
+        args = args[1:]
+    else:
+        break
+    args = args[1:]
+for file in args:
+    infile = open(file, 'rb')
+    outfile = open(os.path.join(outdir, file[:-5] + '.class'), 'wb')
+    for l in infile.readlines():
+        if l[:8] != '/*rmic*/':
+            outfile.write(l)
+sys.exit(0)
+""")
+
+test.write('SConstruct', """
+env = Environment(tools = ['rmic'],
+                  RMIC = r'%s myrmic.py')
+env.RMIC(target = 'outdir', source = 'test1.java')
+""" % (python))
+
+test.write('test1.java', """\
+test1.java
+/*rmic*/
+line 3
+""")
+
+test.run(arguments = '.', stderr = None)
+
+test.fail_test(test.read(['outdir', 'test1.class']) != "test1.java\nline 3\n")
+
+if os.path.normcase('.java') == os.path.normcase('.JAVA'):
+
+    test.write('SConstruct', """\
+env = Environment(tools = ['rmic'],
+                  RMIC = r'%s myrmic.py')
+env.Java(target = 'outdir', source = 'test2.JAVA')
+""" % python)
+
+    test.write('test2.JAVA', """\
+test2.JAVA
+/*rmic*/
+line 3
+""")
+
+    test.run(arguments = '.', stderr = None)
+
+    test.fail_test(test.read('test2.class') != "test2.JAVA\nline 3\n")
+
+
+if not os.path.exists('/usr/local/j2sdk1.3.1/bin/rmic'):
+    print "Could not find Java, skipping test(s)."
+    test.pass_test(1)
+
+
+
+test.write("wrapper.py", """\
+import os
+import string
+import sys
+open('%s', 'ab').write("wrapper.py %%s\\n" %% string.join(sys.argv[1:]))
+os.system(string.join(sys.argv[1:], " "))
+""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\'))
+
+test.write('SConstruct', """
+import string
+foo = Environment(tools = ['javac', 'rmic'],
+                  JAVAC = '/usr/local/j2sdk1.3.1/bin/javac',
+                  RMIC = '/usr/local/j2sdk1.3.1/bin/rmic')
+foo.Java(target = 'class1', source = 'com/sub/foo')
+foo.RMIC(target = 'outdir1',
+          source = ['class1/com/sub/foo/Example1.class',
+                    'class1/com/sub/foo/Example2'],
+          JAVACLASSDIR = 'class1')
+
+rmic = foo.Dictionary('RMIC')
+bar = foo.Copy(RMIC = r'%s wrapper.py ' + rmic)
+bar_classes = bar.Java(target = 'class2', source = 'com/sub/bar')
+# XXX This is kind of a Python brute-force way to do what Ant
+# does with its "excludes" attribute.  We should probably find
+# a similar friendlier way to do this.
+bar_classes = filter(lambda c: string.find(str(c), 'Hello') == -1, bar_classes)
+bar.RMIC(target = Dir('outdir2'), source = bar_classes)
+""" % python)
+
+test.subdir('com',
+            ['com', 'other'],
+            ['com', 'sub'],
+            ['com', 'sub', 'foo'],
+            ['com', 'sub', 'bar'],
+            'src3a',
+            'src3b')
+
+test.write(['com', 'sub', 'foo', 'Hello.java'], """\
+package com.sub.foo;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface Hello extends Remote {
+    String sayHello() throws RemoteException;
+}
+""")
+
+test.write(['com', 'sub', 'foo', 'Example1.java'], """\
+package com.sub.foo;
+
+import java.rmi.Naming;
+import java.rmi.RemoteException;
+import java.rmi.RMISecurityManager;
+import java.rmi.server.UnicastRemoteObject;
+
+public class Example1 extends UnicastRemoteObject implements Hello {
+
+    public Example1() throws RemoteException {
+        super();
+    }
+
+    public String sayHello() {
+        return "Hello World!";
+    }
+
+    public static void main(String args[]) {
+        if (System.getSecurityManager() == null) {
+            System.setSecurityManager(new RMISecurityManager());
+        }
+
+        try {
+            Example1 obj = new Example1();
+
+            Naming.rebind("//myhost/HelloServer", obj);
+
+            System.out.println("HelloServer bound in registry");
+        } catch (Exception e) {
+            System.out.println("Example1 err: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
+""")
+
+test.write(['com', 'sub', 'foo', 'Example2.java'], """\
+package com.sub.foo;
+
+import java.rmi.Naming;
+import java.rmi.RemoteException;
+import java.rmi.RMISecurityManager;
+import java.rmi.server.UnicastRemoteObject;
+
+public class Example2 extends UnicastRemoteObject implements Hello {
+
+    public Example2() throws RemoteException {
+        super();
+    }
+
+    public String sayHello() {
+        return "Hello World!";
+    }
+
+    public static void main(String args[]) {
+        if (System.getSecurityManager() == null) {
+            System.setSecurityManager(new RMISecurityManager());
+        }
+
+        try {
+            Example2 obj = new Example2();
+
+            Naming.rebind("//myhost/HelloServer", obj);
+
+            System.out.println("HelloServer bound in registry");
+        } catch (Exception e) {
+            System.out.println("Example2 err: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
+""")
+
+test.write(['com', 'sub', 'bar', 'Hello.java'], """\
+package com.sub.bar;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface Hello extends Remote {
+    String sayHello() throws RemoteException;
+}
+""")
+
+test.write(['com', 'sub', 'bar', 'Example3.java'], """\
+package com.sub.bar;
+
+import java.rmi.Naming;
+import java.rmi.RemoteException;
+import java.rmi.RMISecurityManager;
+import java.rmi.server.UnicastRemoteObject;
+
+public class Example3 extends UnicastRemoteObject implements Hello {
+
+    public Example3() throws RemoteException {
+        super();
+    }
+
+    public String sayHello() {
+        return "Hello World!";
+    }
+
+    public static void main(String args[]) {
+        if (System.getSecurityManager() == null) {
+            System.setSecurityManager(new RMISecurityManager());
+        }
+
+        try {
+            Example3 obj = new Example3();
+
+            Naming.rebind("//myhost/HelloServer", obj);
+
+            System.out.println("HelloServer bound in registry");
+        } catch (Exception e) {
+            System.out.println("Example3 err: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
+""")
+
+test.write(['com', 'sub', 'bar', 'Example4.java'], """\
+package com.sub.bar;
+
+import java.rmi.Naming;
+import java.rmi.RemoteException;
+import java.rmi.RMISecurityManager;
+import java.rmi.server.UnicastRemoteObject;
+
+public class Example4 extends UnicastRemoteObject implements Hello {
+
+    public Example4() throws RemoteException {
+        super();
+    }
+
+    public String sayHello() {
+        return "Hello World!";
+    }
+
+    public static void main(String args[]) {
+        if (System.getSecurityManager() == null) {
+            System.setSecurityManager(new RMISecurityManager());
+        }
+
+        try {
+            Example4 obj = new Example4();
+
+            Naming.rebind("//myhost/HelloServer", obj);
+
+            System.out.println("HelloServer bound in registry");
+        } catch (Exception e) {
+            System.out.println("Example4 err: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
+""")
+
+test.run(arguments = '.')
+
+test.fail_test(test.read('wrapper.out') != "wrapper.py /usr/local/j2sdk1.3.1/bin/rmic -d outdir2 -classpath class2 com.sub.bar.Example3 com.sub.bar.Example4\n")
+
+test.fail_test(not os.path.exists(test.workpath('outdir1', 'com', 'sub', 'foo', 'Example1_Skel.class')))
+test.fail_test(not os.path.exists(test.workpath('outdir1', 'com', 'sub', 'foo', 'Example1_Stub.class')))
+test.fail_test(not os.path.exists(test.workpath('outdir1', 'com', 'sub', 'foo', 'Example2_Skel.class')))
+test.fail_test(not os.path.exists(test.workpath('outdir1', 'com', 'sub', 'foo', 'Example2_Stub.class')))
+
+test.fail_test(not os.path.exists(test.workpath('outdir2', 'com', 'sub', 'bar', 'Example3_Skel.class')))
+test.fail_test(not os.path.exists(test.workpath('outdir2', 'com', 'sub', 'bar', 'Example3_Stub.class')))
+test.fail_test(not os.path.exists(test.workpath('outdir2', 'com', 'sub', 'bar', 'Example4_Skel.class')))
+test.fail_test(not os.path.exists(test.workpath('outdir2', 'com', 'sub', 'bar', 'Example4_Stub.class')))
+
+test.up_to_date(arguments = '.')
+
+test.pass_test()
diff --git a/test/Repository/RMIC.py b/test/Repository/RMIC.py
new file mode 100644 (file)
index 0000000..9282835
--- /dev/null
@@ -0,0 +1,343 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# 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__"
+
+"""
+Test building Java applications when using Repositories.
+"""
+
+import os
+import string
+import sys
+import TestSCons
+
+python = TestSCons.python
+
+test = TestSCons.TestSCons()
+
+java = '/usr/local/j2sdk1.3.1/bin/java'
+javac = '/usr/local/j2sdk1.3.1/bin/javac'
+rmic = '/usr/local/j2sdk1.3.1/bin/rmic'
+
+if not os.path.exists(javac):
+    print "Could not find Java (javac), skipping test(s)."
+    test.pass_test(1)
+
+if not os.path.exists(rmic):
+    print "Could not find Java (rmic), skipping test(s)."
+    test.pass_test(1)
+
+###############################################################################
+
+#
+test.subdir('rep1', ['rep1', 'src'],
+            'work1',
+            'work2',
+            'work3')
+
+#
+rep1_classes = test.workpath('rep1', 'classes')
+work1_classes = test.workpath('work1', 'classes')
+work3_classes = test.workpath('work3', 'classes')
+
+#
+opts = '-Y ' + test.workpath('rep1')
+
+#
+test.write(['rep1', 'SConstruct'], """
+import string
+env = Environment(tools = ['javac', 'rmic'],
+                  JAVAC = r'%s',
+                  RMIC = r'%s')
+classes = env.Java(target = 'classes', source = 'src')
+# Brute-force removal of the "Hello" class.
+classes = filter(lambda c: string.find(str(c), 'Hello') == -1, classes)
+env.RMIC(target = 'outdir', source = classes)
+""" % (javac, rmic))
+
+test.write(['rep1', 'src', 'Hello.java'], """\
+package com.sub.foo;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface Hello extends Remote {
+    String sayHello() throws RemoteException;
+}
+""")
+
+test.write(['rep1', 'src', 'Foo1.java'], """\
+package com.sub.foo;
+
+import java.rmi.Naming;
+import java.rmi.RemoteException;
+import java.rmi.RMISecurityManager;
+import java.rmi.server.UnicastRemoteObject;
+
+public class Foo1 extends UnicastRemoteObject implements Hello {
+
+    public Foo1() throws RemoteException {
+        super();
+    }
+
+    public String sayHello() {
+        return "rep1/src/Foo1.java";
+    }
+
+    public static void main(String args[]) {
+        if (System.getSecurityManager() == null) {
+            System.setSecurityManager(new RMISecurityManager());
+        }
+
+        try {
+            Foo1 obj = new Foo1();
+
+            Naming.rebind("//myhost/HelloServer", obj);
+
+            System.out.println("HelloServer bound in registry");
+        } catch (Exception e) {
+            System.out.println("Foo1 err: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
+""")
+
+test.write(['rep1', 'src', 'Foo2.java'], """\
+package com.sub.foo;
+
+import java.rmi.Naming;
+import java.rmi.RemoteException;
+import java.rmi.RMISecurityManager;
+import java.rmi.server.UnicastRemoteObject;
+
+public class Foo2 extends UnicastRemoteObject implements Hello {
+
+    public Foo2() throws RemoteException {
+        super();
+    }
+
+    public String sayHello() {
+        return "rep1/src/Foo2.java";
+    }
+
+    public static void main(String args[]) {
+        if (System.getSecurityManager() == null) {
+            System.setSecurityManager(new RMISecurityManager());
+        }
+
+        try {
+            Foo2 obj = new Foo2();
+
+            Naming.rebind("//myhost/HelloServer", obj);
+
+            System.out.println("HelloServer bound in registry");
+        } catch (Exception e) {
+            System.out.println("Foo2 err: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
+""")
+
+# Make the repository non-writable,
+# so we'll detect if we try to write into it accidentally.
+test.writable('repository', 0)
+
+#
+test.run(chdir = 'work1', options = opts, arguments = ".")
+
+# XXX I'd rather run the resulting class files through the JVM here to
+# see that they were built from the proper rep1 sources, but I don't
+# know how to do that with RMI, so punt for now.
+
+test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class')))
+test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class')))
+test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class')))
+test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class')))
+
+test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class')))
+test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class')))
+test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class')))
+test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class')))
+
+test.up_to_date(chdir = 'work1', options = opts, arguments = ".")
+
+#
+test.subdir(['work1', 'src'])
+
+test.write(['work1', 'src', 'Hello.java'], """\
+package com.sub.foo;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface Hello extends Remote {
+    String sayHello() throws RemoteException;
+}
+""")
+
+test.write(['work1', 'src', 'Foo1.java'], """\
+package com.sub.foo;
+
+import java.rmi.Naming;
+import java.rmi.RemoteException;
+import java.rmi.RMISecurityManager;
+import java.rmi.server.UnicastRemoteObject;
+
+public class Foo1 extends UnicastRemoteObject implements Hello {
+
+    public Foo1() throws RemoteException {
+        super();
+    }
+
+    public String sayHello() {
+        return "work1/src/Foo1.java";
+    }
+
+    public static void main(String args[]) {
+        if (System.getSecurityManager() == null) {
+            System.setSecurityManager(new RMISecurityManager());
+        }
+
+        try {
+            Foo1 obj = new Foo1();
+
+            Naming.rebind("//myhost/HelloServer", obj);
+
+            System.out.println("HelloServer bound in registry");
+        } catch (Exception e) {
+            System.out.println("Foo1 err: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
+""")
+
+test.write(['work1', 'src', 'Foo2.java'], """\
+package com.sub.foo;
+
+import java.rmi.Naming;
+import java.rmi.RemoteException;
+import java.rmi.RMISecurityManager;
+import java.rmi.server.UnicastRemoteObject;
+
+public class Foo2 extends UnicastRemoteObject implements Hello {
+
+    public Foo2() throws RemoteException {
+        super();
+    }
+
+    public String sayHello() {
+        return "work1/src/Foo2.java";
+    }
+
+    public static void main(String args[]) {
+        if (System.getSecurityManager() == null) {
+            System.setSecurityManager(new RMISecurityManager());
+        }
+
+        try {
+            Foo2 obj = new Foo2();
+
+            Naming.rebind("//myhost/HelloServer", obj);
+
+            System.out.println("HelloServer bound in registry");
+        } catch (Exception e) {
+            System.out.println("Foo2 err: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
+""")
+
+test.run(chdir = 'work1', options = opts, arguments = ".")
+
+test.fail_test(string.find(test.stdout(), ' src/Foo1.java src/Foo2.java') == -1)
+test.fail_test(string.find(test.stdout(), ' com.sub.foo.Foo1 com.sub.foo.Foo2') == -1)
+
+# XXX I'd rather run the resulting class files through the JVM here to
+# see that they were built from the proper work1 sources, but I don't
+# know how to do that with RMI, so punt for now.
+
+test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class')))
+test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class')))
+test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class')))
+test.fail_test(os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class')))
+
+test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class')))
+test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class')))
+test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class')))
+test.fail_test(not os.path.exists(test.workpath('work1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class')))
+
+test.up_to_date(chdir = 'work1', options = opts, arguments = ".")
+
+#
+test.writable('rep1', 1)
+
+test.run(chdir = 'rep1', options = opts, arguments = ".")
+
+# XXX I'd rather run the resulting class files through the JVM here to
+# see that they were built from the proper work1 sources, but I don't
+# know how to do that with RMI, so punt for now.
+
+test.fail_test(not os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class')))
+test.fail_test(not os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class')))
+test.fail_test(not os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class')))
+test.fail_test(not os.path.exists(test.workpath('rep1', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class')))
+
+test.up_to_date(chdir = 'rep1', options = opts, arguments = ".")
+
+#
+test.writable('repository', 0)
+
+#
+test.up_to_date(chdir = 'work2', options = opts, arguments = ".")
+
+#
+test.write(['work3', 'SConstruct'], """
+import string
+env = Environment(tools = ['javac', 'rmic'],
+                  JAVAC = r'%s',
+                  RMIC = r'%s')
+classes = env.Java(target = 'classes', source = 'src')
+# Brute-force removal of the "Hello" class.
+classes = filter(lambda c: string.find(str(c), 'Hello') == -1, classes)
+rmi_classes = env.RMIC(target = 'outdir', source = classes)
+Local(rmi_classes)
+""" % (javac, rmic))
+
+test.run(chdir = 'work3', options = opts, arguments = ".")
+
+test.fail_test(os.path.exists(test.workpath('work3', 'classes', 'com', 'sub', 'foo', 'Hello.class')))
+test.fail_test(os.path.exists(test.workpath('work3', 'classes', 'com', 'sub', 'foo', 'Foo1.class')))
+test.fail_test(os.path.exists(test.workpath('work3', 'classes', 'com', 'sub', 'foo', 'Foo2.class')))
+
+test.fail_test(not os.path.exists(test.workpath('work3', 'outdir', 'com', 'sub', 'foo', 'Foo1_Skel.class')))
+test.fail_test(not os.path.exists(test.workpath('work3', 'outdir', 'com', 'sub', 'foo', 'Foo1_Stub.class')))
+test.fail_test(not os.path.exists(test.workpath('work3', 'outdir', 'com', 'sub', 'foo', 'Foo2_Skel.class')))
+test.fail_test(not os.path.exists(test.workpath('work3', 'outdir', 'com', 'sub', 'foo', 'Foo2_Stub.class')))
+
+test.pass_test()