Support Java when using Repository and SConscriptChdir(0). (Charles Crain)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 29 Apr 2003 18:44:06 +0000 (18:44 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 29 Apr 2003 18:44:06 +0000 (18:44 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@663 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/RELEASE.txt
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Tool/javac.py
src/engine/SCons/Tool/midl.py
test/Repository/Java.py [new file with mode: 0644]
test/emitter.py

index 7320827c5c52a6063c3a3f31953cad06e5b6ef95..c088d58f5fe299f1dc3f847b7c35650ef77d716d 100644 (file)
@@ -46,6 +46,10 @@ RELEASE 0.14 - XXX
   - Parse the source .java files for class names (including inner class
     names) to figure out the target .class files that will be created.
 
+  - Fix Java support with Repositories and SConscriptChdir(0).
+
+  - Pass Nodes, not strings, to Builder emitter functions.
+
   From Steven Knight:
 
   - Add support for Java (javac and jar).
index d8f2144372c150a2897fb1627033752603492693..db275b7b15fdb4ae0441c9be50125ace17650bc7 100644 (file)
@@ -30,6 +30,10 @@ RELEASE 0.14 - XXX
   - Tool specifications no longer take a "platform" argument.
     XXX
 
+  - Emitter functions in Builders are now passed Node objects, not
+    strings, for all targets and sources.  Your emitter may need to
+    use the str() function to examine the names of specified files.
+
   - The SetBuildSignatureType() and SetContentSignatureType() functions
     have been deprecated.  The new names are TargetSignatures() and
     SourceSignatures().
index aceadbc57030e17fab1a419e1dedc61c05e09497..22b177e973be8659291cd80006e3fa59a3fd2afe 100644 (file)
@@ -289,16 +289,34 @@ class BuilderBase:
         else:
             target = adjustixes(target, pre, suf)
 
-        if self.emitter:
-            # pass the targets and sources to the emitter as strings
-            # rather than nodes since str(node) doesn't work
-            # properly from any directory other than the top directory,
-            # and emitters are called "in" the SConscript directory:
-            target, source = self.emitter(target=target, source=source, env=env)
-
         slist = SCons.Node.arg2nodes(source, self.source_factory)
         tlist = SCons.Node.arg2nodes(target, self.target_factory)
 
+        if self.emitter:
+            # The emitter is going to do str(node), but because we're
+            # being called *from* a builder invocation, the new targets
+            # don't yet have a builder set on them and will look like
+            # source files.  Fool the emitter's str() calls by setting
+            # up a temporary builder on the new targets.
+            new_targets = []
+            for t in tlist:
+                if not t.is_derived():
+                    t.builder = self
+                    new_targets.append(t)
+        
+            target, source = self.emitter(target=tlist, source=slist, env=env)
+
+            # Now delete the temporary builders that we attached to the
+            # new targets, so that _init_nodes() doesn't do weird stuff
+            # to them because it thinks they already have builders.
+            for t in new_targets:
+                t.builder = None
+
+            # Have to call arg2nodes yet again, since it is legal for
+            # emitters to spit out strings as well as Node instances.
+            slist = SCons.Node.arg2nodes(source, self.source_factory)
+            tlist = SCons.Node.arg2nodes(target, self.target_factory)
+
         return tlist, slist
 
     def __call__(self, env, target = None, source = _null, **overrides):
index 190c9e845d452a8196ab582ced10981ce538f139..02c570c178098c46aa0cdc035a929b044d0b775a 100644 (file)
@@ -40,8 +40,6 @@ import TestCmd
 import SCons.Action
 import SCons.Builder
 import SCons.Errors
-import SCons.Node.FS
-import SCons.Warnings
 import SCons.Environment
 
 # Initial setup of the common environment for all tests,
@@ -109,6 +107,27 @@ class Environment:
     
 env = Environment()
 
+class MyNode:
+    def __init__(self, name):
+        self.name = name
+        self.sources = []
+        self.builder = None
+        self.side_effect = 0
+    def __str__(self):
+        return self.name
+    def builder_set(self, builder):
+        self.builder = builder
+    def has_builder(self):
+        return not self.builder is None
+    def env_set(self, env, safe=0):
+        self.env = env
+    def add_source(self, source):
+        self.sources.extend(source)
+    def scanner_key(self):
+        return self.name
+    def is_derived(self):
+        return self.has_builder()
+
 class BuilderTestCase(unittest.TestCase):
 
     def test__nonzero__(self):
@@ -138,28 +157,10 @@ class BuilderTestCase(unittest.TestCase):
     def test__call__(self):
         """Test calling a builder to establish source dependencies
         """
-        class Node:
-            def __init__(self, name):
-                self.name = name
-                self.sources = []
-                self.builder = None
-                self.side_effect = 0
-            def __str__(self):
-                return self.name
-            def builder_set(self, builder):
-                self.builder = builder
-            def has_builder(self):
-                return not self.builder is None
-            def env_set(self, env, safe=0):
-                self.env = env
-            def add_source(self, source):
-                self.sources.extend(source)
-            def scanner_key(self):
-                return self.name
-        builder = SCons.Builder.Builder(action="foo", node_factory=Node)
-
-        n1 = Node("n1");
-        n2 = Node("n2");
+        builder = SCons.Builder.Builder(action="foo", node_factory=MyNode)
+
+        n1 = MyNode("n1");
+        n2 = MyNode("n2");
         builder(env, target = n1, source = n2)
         assert n1.env == env
         assert n1.builder == builder
@@ -582,6 +583,11 @@ class BuilderTestCase(unittest.TestCase):
         def emit(target, source, env):
             foo = env.get('foo', 0)
             bar = env.get('bar', 0)
+            for t in target:
+                assert isinstance(t, MyNode)
+                assert t.has_builder()
+            for s in source:
+                assert isinstance(s, MyNode)
             if foo:
                 target.append("bar%d"%foo)
             if bar:
@@ -589,7 +595,8 @@ class BuilderTestCase(unittest.TestCase):
             return ( target, source )
 
         builder = SCons.Builder.Builder(action='foo',
-                                        emitter=emit)
+                                        emitter=emit,
+                                        node_factory=MyNode)
         tgt = builder(env, target='foo2', source='bar')
         assert str(tgt) == 'foo2', str(tgt)
         assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0])
@@ -607,7 +614,8 @@ class BuilderTestCase(unittest.TestCase):
 
         env2=Environment(FOO=emit)
         builder2=SCons.Builder.Builder(action='foo',
-                                       emitter="$FOO")
+                                       emitter="$FOO",
+                                       node_factory=MyNode)
 
         tgt = builder2(env2, target='foo5', source='bar')
         assert str(tgt) == 'foo5', str(tgt)
@@ -625,7 +633,8 @@ class BuilderTestCase(unittest.TestCase):
         assert 'bar' in map(str, tgt.sources), map(str, tgt.sources)
 
         builder2a=SCons.Builder.Builder(action='foo',
-                                        emitter="$FOO")
+                                        emitter="$FOO",
+                                        node_factory=MyNode)
         assert builder2 == builder2a, repr(builder2.__dict__) + "\n" + repr(builder2a.__dict__)
 
     def test_no_target(self):
index dbee6562fc5173f4bbe5aaabbcb4a79d4c444e66..331e18395f72066e846ce3259e0d753d2240335d 100644 (file)
@@ -38,6 +38,7 @@ import re
 import string
 
 import SCons.Builder
+from SCons.Node.FS import _my_normcase
 
 java_parsing = 1
 
@@ -232,42 +233,41 @@ def generate(env):
         and their corresponding target class files.
         """
         env['_JAVACLASSDIR'] = target[0]
-        env['_JAVASRCDIR'] = source[0]
+        env['_JAVASRCDIR'] = source[0].rdir()
         java_suffix = env.get('JAVASUFFIX', '.java')
         class_suffix = env.get('JAVACLASSSUFFIX', '.class')
 
         slist = []
-        def visit(arg, dirname, names, js=java_suffix):
-            java_files = filter(lambda n, js=js: n[-len(js):] == js, names)
-            java_paths = map(lambda f, d=dirname:
-                                    os.path.join(d, f),
-                             java_files)
+        js = _my_normcase(java_suffix)
+        def visit(arg, dirname, names, js=js, dirnode=source[0].rdir()):
+            java_files = filter(lambda n, js=js:
+                                       _my_normcase(n[-len(js):]) == js,
+                                names)
+            mydir = dirnode.Dir(dirname)
+            java_paths = map(lambda f, d=mydir: d.File(f), java_files)
             arg.extend(java_paths)
-        os.path.walk(source[0], visit, slist)
-
+        os.path.walk(source[0].rdir().abspath, visit, slist)
+       
         tlist = []
         for file in slist:
-            pkg_dir, classes = parse_java(file)
+            pkg_dir, classes = parse_java(file.abspath)
             if pkg_dir:
                 for c in classes:
-                    tlist.append(os.path.join(target[0],
-                                              pkg_dir,
-                                              c + class_suffix))
+                    tlist.append(target[0].Dir(pkg_dir).File(c+class_suffix))
             elif classes:
                 for c in classes:
-                    tlist.append(os.path.join(target[0], c + class_suffix))
+                    tlist.append(target[0].File(c+class_suffix))
             else:
                 # This is an odd end case:  no package and no classes.
                 # Just do our best based on the source file name.
-                tlist.append(os.path.join(target[0],
-                                          file[:-len(java_suffix)] + class_suffix))
+                tlist.append(target[0].File(str(file)[:-len(java_suffix)] + class_suffix))
 
         return tlist, slist
 
     JavaBuilder = SCons.Builder.Builder(action = '$JAVACCOM',
                         emitter = emit_java_files,
-                        target_factory = SCons.Node.FS.default_fs.File,
-                        source_factory = SCons.Node.FS.default_fs.File)
+                        target_factory = SCons.Node.FS.default_fs.Dir,
+                        source_factory = SCons.Node.FS.default_fs.Dir)
 
     env['BUILDERS']['Java'] = JavaBuilder
 
index e78f76b798a9bb20c1984acf26990a69b6e8d5a4..73790fd129b7c2fffa57b4aa4919a41e38bb0597 100644 (file)
@@ -39,7 +39,7 @@ import os.path
 
 def midl_emitter(target, source, env):
     """Produces a list of outputs from the MIDL compiler"""
-    base, ext = os.path.splitext(source[0])
+    base, ext = os.path.splitext(str(source[0]))
     tlb = base + '.tlb'
     incl = base + '.h'
     interface = base + '_i.c'
diff --git a/test/Repository/Java.py b/test/Repository/Java.py
new file mode 100644 (file)
index 0000000..fd2986e
--- /dev/null
@@ -0,0 +1,259 @@
+#!/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'
+
+if not os.path.exists(javac):
+    print "Could not find Java, skipping test(s)."
+    test.pass_test(1)
+
+###############################################################################
+
+#
+test.subdir('rep1', ['rep1', 'src'],
+            'work1',
+            'work2')
+
+#
+rep1_classes = test.workpath('rep1', 'classes')
+work1_classes = test.workpath('work1', 'classes')
+
+#
+opts = '-Y ' + test.workpath('rep1')
+
+#
+test.write(['rep1', 'SConstruct'], """
+env = Environment(tools = ['javac'],
+                  JAVAC = r'%s')
+env.Java(target = 'classes', source = 'src')
+""" % javac)
+
+test.write(['rep1', 'src', 'Foo1.java'], """\
+public class Foo1
+{
+     public static void main(String[] args)
+     {
+          System.out.println("rep1/src/Foo1.java");
+
+     }
+}
+""")
+
+test.write(['rep1', 'src', 'Foo2.java'], """\
+public class Foo2
+{
+     public static void main(String[] args)
+     {
+          System.out.println("rep1/src/Foo2.java");
+
+     }
+}
+""")
+
+test.write(['rep1', 'src', 'Foo3.java'], """\
+public class Foo3
+{
+     public static void main(String[] args)
+     {
+          System.out.println("rep1/src/Foo3.java");
+
+     }
+}
+""")
+
+# 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 = ".")
+
+test.run(program = java,
+         arguments = "-cp %s Foo1" % work1_classes,
+         stdout = "rep1/src/Foo1.java\n")
+
+test.run(program = java,
+         arguments = "-cp %s Foo2" % work1_classes,
+         stdout = "rep1/src/Foo2.java\n")
+
+test.run(program = java,
+         arguments = "-cp %s Foo3" % work1_classes,
+         stdout = "rep1/src/Foo3.java\n")
+
+test.up_to_date(chdir = 'work1', options = opts, arguments = ".")
+
+#
+test.subdir(['work1', 'src'])
+
+test.write(['work1', 'src', 'Foo1.java'], """\
+public class Foo1
+{
+     public static void main(String[] args)
+     {
+          System.out.println("work1/src/Foo1.java");
+
+     }
+}
+""")
+
+test.write(['work1', 'src', 'Foo2.java'], """\
+public class Foo2
+{
+     public static void main(String[] args)
+     {
+          System.out.println("work1/src/Foo2.java");
+
+     }
+}
+""")
+
+test.write(['work1', 'src', 'Foo3.java'], """\
+public class Foo3
+{
+     public static void main(String[] args)
+     {
+          System.out.println("work1/src/Foo3.java");
+
+     }
+}
+""")
+
+test.run(chdir = 'work1', options = opts, arguments = ".")
+
+test.run(program = java,
+         arguments = "-cp %s Foo1" % work1_classes,
+         stdout = "work1/src/Foo1.java\n")
+
+test.run(program = java,
+         arguments = "-cp %s Foo2" % work1_classes,
+         stdout = "work1/src/Foo2.java\n")
+
+test.run(program = java,
+         arguments = "-cp %s Foo3" % work1_classes,
+         stdout = "work1/src/Foo3.java\n")
+
+test.up_to_date(chdir = 'work1', options = opts, arguments = ".")
+
+#
+test.writable('rep1', 1)
+
+test.run(chdir = 'rep1', options = opts, arguments = ".")
+
+test.run(program = java,
+         arguments = "-cp %s Foo1" % rep1_classes,
+         stdout = "rep1/src/Foo1.java\n")
+
+test.run(program = java,
+         arguments = "-cp %s Foo2" % rep1_classes,
+         stdout = "rep1/src/Foo2.java\n")
+
+test.run(program = java,
+         arguments = "-cp %s Foo3" % rep1_classes,
+         stdout = "rep1/src/Foo3.java\n")
+
+test.up_to_date(chdir = 'rep1', options = opts, arguments = ".")
+
+#
+test.writable('repository', 0)
+
+#
+# If the Java builder were to interact with Repositories like the
+# other builders, then we'd uncomment the following test(s).
+#
+# This tests that, if the .class files are built in the repository,
+# then a local build says that everything is up-to-date.  However,
+# because the destination target is a directory ("classes") not a
+# file, we don't detect that the individual .class files are
+# already there, and think things must be rebuilt.
+#
+#test.up_to_date(chdir = 'work2', options = opts, arguments = ".")
+#
+#test.subdir(['work2', 'src'])
+#
+#test.write(['work2', 'src', 'Foo1.java'], """\
+#public class Foo1
+#{
+#     public static void main(String[] args)
+#     {
+#          System.out.println("work2/src/Foo1.java");
+#
+#     }
+#}
+#""")
+#
+#test.write(['work2', 'src', 'Foo2.java'], """\
+#public class Foo2
+#{
+#     public static void main(String[] args)
+#     {
+#          System.out.println("work2/src/Foo2.java");
+#
+#     }
+#}
+#""")
+#
+#test.write(['work2', 'src', 'Foo3.java'], """\
+#public class Foo3
+#{
+#     public static void main(String[] args)
+#     {
+#          System.out.println("work2/src/Foo3.java");
+#
+#     }
+#}
+#""")
+#
+#test.run(chdir = 'work2', options = opts, arguments = ".")
+#
+#test.run(program = java,
+#         arguments = "-cp %s Foo1" % work2_classes,
+#         stdout = "work2/src/Foo1.java\n")
+#
+#test.run(program = java,
+#         arguments = "-cp %s Foo2" % work2_classes,
+#         stdout = "work2/src/Foo2.java\n")
+#
+#test.run(program = java,
+#         arguments = "-cp %s Foo3" % work2_classes,
+#         stdout = "work2/src/Foo3.java\n")
+#
+#test.up_to_date(chdir = 'work2', options = opts, arguments = ".")
+
+test.pass_test()
index 7fc14f044a0e5f4b8fc1ea1bce88d1d5cefb0536..70d4c8fdf3e4d4868520a8653af9a4c7e9f79059 100644 (file)
@@ -30,6 +30,8 @@ import os.path
 
 test = TestSCons.TestSCons()
 
+test.subdir('src')
+
 test.write('SConstruct',"""
 BuildDir('var1', 'src', duplicate=0)
 BuildDir('var2', 'src', duplicate=1)
@@ -38,10 +40,6 @@ SConscript('var1/SConscript')
 SConscript('var2/SConscript')
 """)
 
-test.subdir('src')
-
-test.write('src/f.in', 'f.in')
-
 test.write('src/SConscript',"""
 def build(target, source, env):
     for t in target:
@@ -55,8 +53,12 @@ b = Builder(action=build, emitter=emitter)
 
 env=Environment(BUILDERS={ 'foo': b })
 env.foo('f.out', 'f.in')
+env.foo(File('g.out'), 'g.in')
 """)
 
+test.write(['src', 'f.in'], 'f.in')
+test.write(['src', 'g.in'], 'g.in')
+
 test.run(arguments='.')
 
 test.fail_test(not os.path.exists(test.workpath('src', 'f.out')))
@@ -66,4 +68,11 @@ test.fail_test(not os.path.exists(test.workpath('var1', 'f.out.foo')))
 test.fail_test(not os.path.exists(test.workpath('var2', 'f.out')))
 test.fail_test(not os.path.exists(test.workpath('var2', 'f.out.foo')))
 
+test.fail_test(not os.path.exists(test.workpath('src', 'g.out')))
+test.fail_test(not os.path.exists(test.workpath('src', 'g.out.foo')))
+test.fail_test(not os.path.exists(test.workpath('var1', 'g.out')))
+test.fail_test(not os.path.exists(test.workpath('var1', 'g.out.foo')))
+test.fail_test(not os.path.exists(test.workpath('var2', 'g.out')))
+test.fail_test(not os.path.exists(test.workpath('var2', 'g.out.foo')))
+
 test.pass_test()