summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2003-04-29 18:44:06 (GMT)
committerSteven Knight <knight@baldmt.com>2003-04-29 18:44:06 (GMT)
commit7ed00239dd9a8e9609063688f2b6287f8fc7372d (patch)
tree41262855c5fbc7dcd007761ea2c7cd9fdf802d0b
parent7cabdf921ae908a3172acd103acbe16f3e75e6b8 (diff)
downloadSCons-7ed00239dd9a8e9609063688f2b6287f8fc7372d.zip
SCons-7ed00239dd9a8e9609063688f2b6287f8fc7372d.tar.gz
SCons-7ed00239dd9a8e9609063688f2b6287f8fc7372d.tar.bz2
Support Java when using Repository and SConscriptChdir(0). (Charles Crain)
-rw-r--r--src/CHANGES.txt4
-rw-r--r--src/RELEASE.txt4
-rw-r--r--src/engine/SCons/Builder.py32
-rw-r--r--src/engine/SCons/BuilderTests.py63
-rw-r--r--src/engine/SCons/Tool/javac.py34
-rw-r--r--src/engine/SCons/Tool/midl.py2
-rw-r--r--test/Repository/Java.py259
-rw-r--r--test/emitter.py17
8 files changed, 359 insertions, 56 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 7320827..c088d58 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -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).
diff --git a/src/RELEASE.txt b/src/RELEASE.txt
index d8f2144..db275b7 100644
--- a/src/RELEASE.txt
+++ b/src/RELEASE.txt
@@ -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().
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index aceadbc..22b177e 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -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):
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index 190c9e8..02c570c 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -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):
diff --git a/src/engine/SCons/Tool/javac.py b/src/engine/SCons/Tool/javac.py
index dbee656..331e183 100644
--- a/src/engine/SCons/Tool/javac.py
+++ b/src/engine/SCons/Tool/javac.py
@@ -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
diff --git a/src/engine/SCons/Tool/midl.py b/src/engine/SCons/Tool/midl.py
index e78f76b..73790fd 100644
--- a/src/engine/SCons/Tool/midl.py
+++ b/src/engine/SCons/Tool/midl.py
@@ -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
index 0000000..fd2986e
--- /dev/null
+++ b/test/Repository/Java.py
@@ -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()
diff --git a/test/emitter.py b/test/emitter.py
index 7fc14f0..70d4c8f 100644
--- a/test/emitter.py
+++ b/test/emitter.py
@@ -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()