<!ENTITY SCons "<application>SCons</application>">
<!ENTITY scons "<application>scons</application>">
<!ENTITY ScCons "<application>ScCons</application>">
+<!ENTITY swig "<application>swig</application>">
<!ENTITY tar "<application>tar</application>">
<!ENTITY tex "<application>tex</application>">
<!ENTITY touch "<application>touch</application>">
- Fix the --debug=time option when the -j option is specified and all
files are up to date.
+ - Add a $SWIGOUTDIR variable to allow setting the swig -outdir option,
+ and use it to identify files created by the swig -java option.
+
From Leanid Nazdrynau:
- When applying Tool modules after a construction environment has
- Find Java anonymous classes when the next token after the name is
an open parenthesis.
+ From Tilo Prutz:
+
+ - Add support for the file names that Java 1.5 (and 1.6) generates for
+ nested anonymous inner classes, which are different from Java 1.4.
+
From Adam Simpkins:
- Allow worker threads to terminate gracefully when all jobs are
env['CLVar'] = CLVar([])
env.AppendUnique(CLVar = 'bar')
result = env['CLVar']
- if sys.version[0] == '1':
- # Python 1.5.2 has a quirky behavior where CLVar([]) actually
- # matches '' and [] due to different __coerce__() semantics
- # in the UserList implementation. It isn't worth a lot of
- # effort to get this corner case to work identically (support
- # for Python 1.5 support will die soon anyway), so just treat
- # it separately for now.
+ if sys.version[0] == '1' or sys.version[:3] == '2.0':
+ # Python 2.0 and before have a quirky behavior where CLVar([])
+ # actually matches '' and [] due to different __coerce__()
+ # semantics in the UserList implementation. It isn't worth a
+ # lot of effort to get this corner case to work identically
+ # (support for Python 1.5 support will die soon anyway),
+ # so just treat it separately for now.
assert result == 'bar', result
else:
assert isinstance(result, CLVar), repr(result)
env['CLVar'] = CLVar([])
env.PrependUnique(CLVar = 'bar')
result = env['CLVar']
- if sys.version[0] == '1':
- # Python 1.5.2 has a quirky behavior where CLVar([]) actually
- # matches '' and [] due to different __coerce__() semantics
- # in the UserList implementation. It isn't worth a lot of
- # effort to get this corner case to work identically (support
- # for Python 1.5 support will die soon anyway), so just treat
- # it separately for now.
+ if sys.version[0] == '1' or sys.version[:3] == '2.0':
+ # Python 2.0 and before have a quirky behavior where CLVar([])
+ # actually matches '' and [] due to different __coerce__()
+ # semantics in the UserList implementation. It isn't worth a
+ # lot of effort to get this corner case to work identically
+ # (support for Python 1.5 support will die soon anyway),
+ # so just treat it separately for now.
assert result == 'bar', result
else:
assert isinstance(result, CLVar), repr(result)
expect = [
"AttributeError `bar' trying to evaluate `${foo.bar}'",
"AttributeError `Foo instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
+ "AttributeError `'Foo' instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
]
assert str(e) in expect, e
else:
expect = [
"AttributeError `bar' trying to evaluate `${foo.bar}'",
"AttributeError `Foo instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
+ "AttributeError `'Foo' instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
]
assert str(e) in expect, e
else:
java_parsing = 1
+default_java_version = '1.4'
+
if java_parsing:
# Parse Java files for class names.
#
class OuterState:
"""The initial state for parsing a Java file for classes,
interfaces, and anonymous inner classes."""
- def __init__(self):
+ def __init__(self, version=default_java_version):
+
+ if not version in ('1.1', '1.2', '1.3','1.4', '1.5', '1.6'):
+ msg = "Java version %s not supported" % version
+ raise NotImplementedError, msg
+
+ self.version = version
self.listClasses = []
self.listOutputs = []
self.stackBrackets = []
self.brackets = 0
self.nextAnon = 1
+ self.stackAnonClassBrackets = []
+ self.anonStacksStack = [[0]]
self.package = None
def trace(self):
ret = SkipState(1, self)
self.skipState = ret
return ret
+
+ def __getAnonStack(self):
+ return self.anonStacksStack[-1]
def openBracket(self):
self.brackets = self.brackets + 1
self.brackets == self.stackBrackets[-1]:
self.listOutputs.append(string.join(self.listClasses, '$'))
self.listClasses.pop()
+ self.anonStacksStack.pop()
self.stackBrackets.pop()
+ if len(self.stackAnonClassBrackets) and \
+ self.brackets == self.stackAnonClassBrackets[-1]:
+ self.__getAnonStack().pop()
+ self.stackAnonClassBrackets.pop()
def parseToken(self, token):
if token[:2] == '//':
def addAnonClass(self):
"""Add an anonymous inner class"""
- clazz = self.listClasses[0]
- self.listOutputs.append('%s$%d' % (clazz, self.nextAnon))
+ if self.version in ('1.1', '1.2', '1.3', '1.4'):
+ clazz = self.listClasses[0]
+ self.listOutputs.append('%s$%d' % (clazz, self.nextAnon))
+ elif self.version in ('1.5', '1.6'):
+ self.stackAnonClassBrackets.append(self.brackets)
+ className = []
+ className.extend(self.listClasses)
+ self.__getAnonStack()[-1] = self.__getAnonStack()[-1] + 1
+ for anon in self.__getAnonStack():
+ className.append(str(anon))
+ self.listOutputs.append(string.join(className, '$'))
+
self.nextAnon = self.nextAnon + 1
+ self.__getAnonStack().append(0)
def setPackage(self, package):
self.package = package
if token == '\n':
return self
self.outer_state.listClasses.append(token)
+ self.outer_state.anonStacksStack.append([0])
return self.outer_state
class IgnoreState:
self.outer_state.setPackage(token)
return self.outer_state
- def parse_java_file(fn):
- return parse_java(open(fn, 'r').read())
+ def parse_java_file(fn, version=default_java_version):
+ return parse_java(open(fn, 'r').read(), version)
- def parse_java(contents, trace=None):
+ def parse_java(contents, version=default_java_version, trace=None):
"""Parse a .java file and return a double of package directory,
plus a list of .class files that compiling that .java file will
produce"""
package = None
- initial = OuterState()
+ initial = OuterState(version)
currstate = initial
for token in _reToken.findall(contents):
# The regex produces a bunch of groups, but only one will
import SCons.Tool.JavaCommon
-# Adding this trace to any of the calls below to the parse_java() method
-# will cause the parser to spit out trace messages of the tokens it sees
-# and state transitions.
+# Adding trace=trace to any of the parse_jave() calls below will cause
+# the parser to spit out trace messages of the tokens it sees and the
+# attendant transitions.
def trace(token, newstate):
from SCons.Debug import Trace
def test_bare_bones(self):
"""Test a bare-bones class"""
- pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
+ input = """\
package com.sub.bar;
public class Foo
}
}
-""")
+"""
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
assert pkg_dir == os.path.join('com', 'sub', 'bar'), pkg_dir
assert classes == ['Foo'], classes
def test_inner_classes(self):
"""Test parsing various forms of inner classes"""
- pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
+ input = """\
class Empty {
}
};
}
}
-""")
-
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4')
assert pkg_dir is None, pkg_dir
expect = [
'Empty',
]
assert classes == expect, classes
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5')
+ assert pkg_dir is None, pkg_dir
+ expect = [
+ 'Empty',
+ 'Listener',
+ 'Test$Inner$1',
+ 'Test$Inner',
+ 'Test$Inner2',
+ 'Test$Inner3',
+ 'Test$1',
+ 'Test$1$1',
+ 'Test',
+ 'Private$1',
+ 'Private',
+ ]
+ assert classes == expect, (expect, classes)
+
+
def test_comments(self):
"""Test a class with comments"""
- pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
+ input = """\
package com.sub.foo;
import java.rmi.Naming;
}
}
}
-""")
+"""
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
assert pkg_dir == os.path.join('com', 'sub', 'foo'), pkg_dir
assert classes == ['Example1'], classes
def test_arrays(self):
"""Test arrays of class instances"""
- pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
+ input = """\
public class Test {
MyClass abc = new MyClass();
MyClass xyz = new MyClass();
xyz
}
}
-""")
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
assert pkg_dir == None, pkg_dir
assert classes == ['Test'], classes
-# This test comes from bug report #1197470:
-#
-# http://sourceforge.net/tracker/index.php?func=detail&aid=1194740&group_id=30337&atid=398971
-#
-# I've captured it here so that someone with a better grasp of Java syntax
-# and the parse_java() state machine can uncomment it and fix it some day.
-#
-# def test_arrays_in_decls(self):
-# """Test how arrays in method declarations affect class detection"""
-#
-# pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
-#public class A {
-# public class B{
-# public void F(Object[] o) {
-# F(new Object[] {Object[].class});
-# }
-# public void G(Object[] o) {
-# F(new Object[] {});
-# }
-# }
-#}
-#""")
-# assert pkg_dir == None, pkg_dir
-# assert classes == ['A$B', 'A'], classes
-
def test_backslash(self):
"""Test backslash handling"""
- pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
+ input = """\
public class MyTabs
{
private class MyInternal
}
private final static String PATH = "images\\\\";
}
-""")
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
assert pkg_dir == None, pkg_dir
assert classes == ['MyTabs$MyInternal', 'MyTabs'], classes
def test_enum(self):
"""Test the Java 1.5 enum keyword"""
- pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
+ input = """\
package p;
public enum a {}
-""")
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
assert pkg_dir == 'p', pkg_dir
assert classes == ['a'], classes
def test_anon_classes(self):
"""Test anonymous classes"""
- pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
+
+ input = """\
public abstract class TestClass
{
public void completed()
}.start();
}
}
-""")
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
assert pkg_dir == None, pkg_dir
assert classes == ['TestClass$1', 'TestClass$2', 'TestClass'], classes
def test_closing_bracket(self):
"""Test finding a closing bracket instead of an anonymous class"""
- pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
+
+ input = """\
class TestSCons {
public static void main(String[] args) {
Foo[] fooArray = new Foo[] { new Foo() };
}
class Foo { }
-""")
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
assert pkg_dir == None, pkg_dir
assert classes == ['TestSCons', 'Foo'], classes
def test_dot_class_attributes(self):
"""Test handling ".class" attributes"""
- pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
+
+ input = """\
public class Test extends Object
{
static {
Object[] s = new Object[] {};
}
}
-""")
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
assert classes == ['Test'], classes
- pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
+ input = """\
public class A {
public class B {
public void F(Object[] o) {
}
}
}
-""")
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
assert pkg_dir == None, pkg_dir
assert classes == ['A$B', 'A'], classes
def test_anonymous_classes_with_parentheses(self):
"""Test finding anonymous classes marked by parentheses"""
- pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
+
+ input = """\
import java.io.File;
public class Foo {
};
}
}
-""")
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4')
assert classes == ['Foo$1', 'Foo$2', 'Foo'], classes
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5')
+ assert classes == ['Foo$1', 'Foo$1$1', 'Foo'], classes
+
+
+
+ def test_nested_anonymous_inner_classes(self):
+ """Test finding nested anonymous inner classes"""
+
+ input = """\
+// import java.util.*;
+
+public class NestedExample
+{
+ public NestedExample()
+ {
+ Thread t = new Thread() {
+ public void start()
+ {
+ Thread t = new Thread() {
+ public void start()
+ {
+ try {Thread.sleep(200);}
+ catch (Exception e) {}
+ }
+ };
+ while (true)
+ {
+ try {Thread.sleep(200);}
+ catch (Exception e) {}
+ }
+ }
+ };
+ }
+
+
+ public static void main(String argv[])
+ {
+ NestedExample e = new NestedExample();
+ }
+}
+"""
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4')
+ expect = [ 'NestedExample$1', 'NestedExample$2', 'NestedExample' ]
+ assert expect == classes, (expect, classes)
+
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5')
+ expect = [ 'NestedExample$1', 'NestedExample$1$1', 'NestedExample' ]
+ assert expect == classes, (expect, classes)
tlist = []
for f in slist:
- pkg_dir, classes = parse_java_file(f.get_abspath())
+ version = env.get('JAVAVERSION', '1.4')
+ pkg_dir, classes = parse_java_file(f.get_abspath(), version)
if pkg_dir:
for c in classes:
t = target[0].Dir(pkg_dir).File(c+class_suffix)
by default.
</summary>
</cvar>
+
+<cvar name="JAVAVERSION">
+<summary>
+Specifies the Java version being used by the &b-Java; builder.
+This is <emphasis>not</emphasis> currently used to select one
+version of the Java compiler vs. another.
+Instead, you should set this to specify the version of Java
+supported by your &javac; compiler.
+The default is <literal>1.4</literal>.
+
+This is sometimes necessary because
+Java 1.5 changed the file names that are created
+for nested anonymous inner classes,
+which can cause a mismatch with the files
+that &SCons; expects will be generated by the &javac; compiler.
+Setting &cv-JAVAVERSION; to <literal>1.5</literal>
+(or <literal>1.6</literal>, as appropriate)
+can make &SCons; realize that a Java 1.5 or 1.6
+build is actually up to date.
+</summary>
+</cvar>
def _swigEmitter(target, source, env):
for src in source:
src = str(src)
- mname = None
flags = SCons.Util.CLVar(env.subst("$SWIGFLAGS"))
+ mnames = None
if "-python" in flags and "-noproxy" not in flags:
- f = open(src)
- try:
- for l in f.readlines():
- m = _reModule.match(l)
- if m:
- mname = m.group(1)
- finally:
- f.close()
- if mname is not None:
- target.append(mname + ".py")
+ if mnames is None:
+ mnames = _reModule.findall(open(src).read())
+ target.extend(map(lambda m: m + ".py", mnames))
+ if "-java" in flags:
+ if mnames is None:
+ mnames = _reModule.findall(open(src).read())
+ java_files = map(lambda m: [m + ".java", m + "JNI.java"], mnames)
+ java_files = SCons.Util.flatten(java_files)
+ outdir = env.subst('$SWIGOUTDIR')
+ if outdir:
+ java_files = map(lambda j, o=outdir: os.path.join(o, j), java_files)
+ target.extend(java_files)
return (target, source)
def generate(env):
env['SWIGFLAGS'] = SCons.Util.CLVar('')
env['SWIGCFILESUFFIX'] = '_wrap$CFILESUFFIX'
env['SWIGCXXFILESUFFIX'] = '_wrap$CXXFILESUFFIX'
- env['SWIGCOM'] = '$SWIG $SWIGFLAGS -o $TARGET $SOURCES'
+ env['_SWIGOUTDIR'] = '${"-outdir " + SWIGOUTDIR}'
+ env['SWIGCOM'] = '$SWIG -o $TARGET ${_SWIGOUTDIR} $SWIGFLAGS $SOURCES'
env.Append(SCANNERS=Scanner(function=_scanSwig, skeys=[".i"]))
def exists(env):
variable.
</summary>
</cvar>
+
+<cvar name="SWIGOUTDIR">
+<summary>
+Specifies the output directory in which
+the scripting language wrapper and interface generator
+should place generated language-specific files.
+This will be used by SCons to identify
+the files that will be generated by the &swig; call,
+and translated into the
+<literal>swig -outdir</literal> option on the command line.
+</summary>
+</cvar>
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
-Test Java compilation with a live "javac" compiler.
+Test Java compilation with a live Java 1.4 "javac" compiler.
"""
import os
test.write('SConstruct', """
env = Environment(tools = ['javac'],
+ JAVAVERSION = '1.4',
JAVAC = r'%(where_javac)s')
env.Java(target = 'class1', source = 'com/sub/foo')
env.Java(target = 'class2', source = 'com/sub/bar')
test.workpath('class5', 'TestSCons.class'),
]
+failed = None
+
def classes_must_match(dir, expect, got):
if expect != got:
sys.stderr.write("Expected the following class files in '%s':\n" % dir)
sys.stderr.write("Got the following class files in '%s':\n" % dir)
for c in got:
sys.stderr.write(' %s\n' % c)
- test.fail_test()
+ failed = 1
classes_must_match('class1', expect_1, classes_1)
classes_must_match('class2', expect_2, classes_2)
classes_must_match('class3', expect_3, classes_3)
classes_must_match('class4', expect_4, classes_4)
-test.up_to_date(arguments = '.')
+test.fail_test(failed)
+
+test.up_to_date(options='--debug=explain', arguments = '.')
test.pass_test()
--- /dev/null
+#!/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 Java compilation with a live Java 1.5 "javac" compiler.
+"""
+
+import os
+import os.path
+import string
+import sys
+import TestSCons
+
+_python_ = TestSCons._python_
+
+test = TestSCons.TestSCons()
+
+ENV = test.java_ENV()
+ENV['PATH'] = '/usr/lib/jvm/java-1.5.0-sun-1.5.0.11/bin' + os.pathsep + os.environ['PATH']
+
+if test.detect_tool('javac', ENV=ENV):
+ where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
+else:
+ where_javac = test.where_is('javac')
+if not where_javac:
+ test.skip_test("Could not find Java javac, skipping test(s).\n")
+
+
+
+test.write('SConstruct', """
+env = Environment(tools = ['javac'],
+ JAVAVERSION = '1.5',
+ JAVAC = r'%(where_javac)s')
+env.Java(target = 'class1', source = 'com/sub/foo')
+env.Java(target = 'class2', source = 'com/sub/bar')
+env.Java(target = 'class3', source = ['src1', 'src2'])
+env.Java(target = 'class4', source = ['src4'])
+env.Java(target = 'class5', source = ['src5'])
+""" % locals())
+
+test.subdir('com',
+ ['com', 'sub'],
+ ['com', 'sub', 'foo'],
+ ['com', 'sub', 'bar'],
+ 'src1',
+ 'src2',
+ 'src4',
+ 'src5')
+
+test.write(['com', 'sub', 'foo', 'Example1.java'], """\
+package com.sub.foo;
+
+public class Example1
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+test.write(['com', 'sub', 'foo', 'Example2.java'], """\
+package com.other;
+
+public class Example2
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+test.write(['com', 'sub', 'foo', 'Example3.java'], """\
+package com.sub.foo;
+
+public class Example3
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+test.write(['com', 'sub', 'bar', 'Example4.java'], """\
+package com.sub.bar;
+
+public class Example4
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+test.write(['com', 'sub', 'bar', 'Example5.java'], """\
+package com.other;
+
+public class Example5
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+test.write(['com', 'sub', 'bar', 'Example6.java'], """\
+package com.sub.bar;
+
+public class Example6
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+test.write(['src1', 'Example7.java'], """\
+public class Example7
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+# Acid-test file for parsing inner Java classes, courtesy Chad Austin.
+test.write(['src2', 'Test.java'], """\
+class Empty {
+}
+
+interface Listener {
+ public void execute();
+}
+
+public
+class
+Test {
+ class Inner {
+ void go() {
+ use(new Listener() {
+ public void execute() {
+ System.out.println("In Inner");
+ }
+ });
+ }
+ String s1 = "class A";
+ String s2 = "new Listener() { }";
+ /* class B */
+ /* new Listener() { } */
+ }
+
+ public static void main(String[] args) {
+ new Test().run();
+ }
+
+ void run() {
+ use(new Listener() {
+ public void execute() {
+ use(new Listener( ) {
+ public void execute() {
+ System.out.println("Inside execute()");
+ }
+ });
+ }
+ });
+
+ new Inner().go();
+ }
+
+ void use(Listener l) {
+ l.execute();
+ }
+}
+
+class Private {
+ void run() {
+ new Listener() {
+ public void execute() {
+ }
+ };
+ }
+}
+""")
+
+# Testing nested anonymous inner classes, courtesy Brandon Mansfield.
+test.write(['src4', 'NestedExample.java'], """\
+// import java.util.*;
+
+public class NestedExample
+{
+ public NestedExample()
+ {
+ Thread t = new Thread() {
+ public void start()
+ {
+ Thread t = new Thread() {
+ public void start()
+ {
+ try {Thread.sleep(200);}
+ catch (Exception e) {}
+ }
+ };
+ while (true)
+ {
+ try {Thread.sleep(200);}
+ catch (Exception e) {}
+ }
+ }
+ };
+ }
+
+
+ public static void main(String argv[])
+ {
+ NestedExample e = new NestedExample();
+ }
+}
+""")
+
+# Test not finding an anonymous class when the second token after a
+# "new" is a closing brace. This duplicates a test from the unit tests,
+# but lets us make sure that we correctly determine that everything is
+# up-to-date after the build.
+test.write(['src5', 'TestSCons.java'], """\
+class TestSCons {
+ public static void main(String[] args) {
+ Foo[] fooArray = new Foo[] { new Foo() };
+ }
+}
+
+class Foo { }
+""")
+
+test.run(arguments = '.')
+
+def get_class_files(dir):
+ def find_class_files(arg, dirname, fnames):
+ for fname in fnames:
+ if fname[-6:] == '.class':
+ arg.append(os.path.join(dirname, fname))
+ result = []
+ os.path.walk(dir, find_class_files, result)
+ result.sort()
+ return result
+
+classes_1 = get_class_files(test.workpath('class1'))
+classes_2 = get_class_files(test.workpath('class2'))
+classes_3 = get_class_files(test.workpath('class3'))
+classes_4 = get_class_files(test.workpath('class4'))
+classes_5 = get_class_files(test.workpath('class5'))
+
+expect_1 = [
+ test.workpath('class1', 'com', 'other', 'Example2.class'),
+ test.workpath('class1', 'com', 'sub', 'foo', 'Example1.class'),
+ test.workpath('class1', 'com', 'sub', 'foo', 'Example3.class'),
+]
+
+expect_2 = [
+ test.workpath('class2', 'com', 'other', 'Example5.class'),
+ test.workpath('class2', 'com', 'sub', 'bar', 'Example4.class'),
+ test.workpath('class2', 'com', 'sub', 'bar', 'Example6.class'),
+]
+
+expect_3 = [
+ test.workpath('class3', 'Empty.class'),
+ test.workpath('class3', 'Example7.class'),
+ test.workpath('class3', 'Listener.class'),
+ test.workpath('class3', 'Private$1.class'),
+ test.workpath('class3', 'Private.class'),
+ test.workpath('class3', 'Test$1$1.class'),
+ test.workpath('class3', 'Test$1.class'),
+ test.workpath('class3', 'Test$Inner$1.class'),
+ test.workpath('class3', 'Test$Inner.class'),
+ test.workpath('class3', 'Test.class'),
+]
+
+expect_4 = [
+ test.workpath('class4', 'NestedExample$1$1.class'),
+ test.workpath('class4', 'NestedExample$1.class'),
+ test.workpath('class4', 'NestedExample.class'),
+]
+
+expect_5 = [
+ test.workpath('class5', 'Foo.class'),
+ test.workpath('class5', 'TestSCons.class'),
+]
+
+failed = None
+
+def classes_must_match(dir, expect, got):
+ if expect != got:
+ sys.stderr.write("Expected the following class files in '%s':\n" % dir)
+ for c in expect:
+ sys.stderr.write(' %s\n' % c)
+ sys.stderr.write("Got the following class files in '%s':\n" % dir)
+ for c in got:
+ sys.stderr.write(' %s\n' % c)
+ failed = 1
+
+classes_must_match('class1', expect_1, classes_1)
+classes_must_match('class2', expect_2, classes_2)
+classes_must_match('class3', expect_3, classes_3)
+classes_must_match('class4', expect_4, classes_4)
+
+test.fail_test(failed)
+
+test.up_to_date(options='--debug=explain', arguments = '.')
+
+test.pass_test()
--- /dev/null
+#!/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 Java compilation with a live Java 1.6 "javac" compiler.
+"""
+
+import os
+import os.path
+import string
+import sys
+import TestSCons
+
+_python_ = TestSCons._python_
+
+test = TestSCons.TestSCons()
+
+ENV = test.java_ENV()
+ENV['PATH'] = '/usr/lib/jvm/java-6-sun-1.6.0.00/bin' + os.pathsep + os.environ['PATH']
+
+if test.detect_tool('javac', ENV=ENV):
+ where_javac = test.detect('JAVAC', 'javac', ENV=ENV)
+else:
+ where_javac = test.where_is('javac')
+if not where_javac:
+ test.skip_test("Could not find Java javac, skipping test(s).\n")
+
+
+
+test.write('SConstruct', """
+env = Environment(tools = ['javac'],
+ JAVAVERSION = '1.6',
+ JAVAC = r'%(where_javac)s')
+env.Java(target = 'class1', source = 'com/sub/foo')
+env.Java(target = 'class2', source = 'com/sub/bar')
+env.Java(target = 'class3', source = ['src1', 'src2'])
+env.Java(target = 'class4', source = ['src4'])
+env.Java(target = 'class5', source = ['src5'])
+""" % locals())
+
+test.subdir('com',
+ ['com', 'sub'],
+ ['com', 'sub', 'foo'],
+ ['com', 'sub', 'bar'],
+ 'src1',
+ 'src2',
+ 'src4',
+ 'src5')
+
+test.write(['com', 'sub', 'foo', 'Example1.java'], """\
+package com.sub.foo;
+
+public class Example1
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+test.write(['com', 'sub', 'foo', 'Example2.java'], """\
+package com.other;
+
+public class Example2
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+test.write(['com', 'sub', 'foo', 'Example3.java'], """\
+package com.sub.foo;
+
+public class Example3
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+test.write(['com', 'sub', 'bar', 'Example4.java'], """\
+package com.sub.bar;
+
+public class Example4
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+test.write(['com', 'sub', 'bar', 'Example5.java'], """\
+package com.other;
+
+public class Example5
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+test.write(['com', 'sub', 'bar', 'Example6.java'], """\
+package com.sub.bar;
+
+public class Example6
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+test.write(['src1', 'Example7.java'], """\
+public class Example7
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+# Acid-test file for parsing inner Java classes, courtesy Chad Austin.
+test.write(['src2', 'Test.java'], """\
+class Empty {
+}
+
+interface Listener {
+ public void execute();
+}
+
+public
+class
+Test {
+ class Inner {
+ void go() {
+ use(new Listener() {
+ public void execute() {
+ System.out.println("In Inner");
+ }
+ });
+ }
+ String s1 = "class A";
+ String s2 = "new Listener() { }";
+ /* class B */
+ /* new Listener() { } */
+ }
+
+ public static void main(String[] args) {
+ new Test().run();
+ }
+
+ void run() {
+ use(new Listener() {
+ public void execute() {
+ use(new Listener( ) {
+ public void execute() {
+ System.out.println("Inside execute()");
+ }
+ });
+ }
+ });
+
+ new Inner().go();
+ }
+
+ void use(Listener l) {
+ l.execute();
+ }
+}
+
+class Private {
+ void run() {
+ new Listener() {
+ public void execute() {
+ }
+ };
+ }
+}
+""")
+
+# Testing nested anonymous inner classes, courtesy Brandon Mansfield.
+test.write(['src4', 'NestedExample.java'], """\
+// import java.util.*;
+
+public class NestedExample
+{
+ public NestedExample()
+ {
+ Thread t = new Thread() {
+ public void start()
+ {
+ Thread t = new Thread() {
+ public void start()
+ {
+ try {Thread.sleep(200);}
+ catch (Exception e) {}
+ }
+ };
+ while (true)
+ {
+ try {Thread.sleep(200);}
+ catch (Exception e) {}
+ }
+ }
+ };
+ }
+
+
+ public static void main(String argv[])
+ {
+ NestedExample e = new NestedExample();
+ }
+}
+""")
+
+# Test not finding an anonymous class when the second token after a
+# "new" is a closing brace. This duplicates a test from the unit tests,
+# but lets us make sure that we correctly determine that everything is
+# up-to-date after the build.
+test.write(['src5', 'TestSCons.java'], """\
+class TestSCons {
+ public static void main(String[] args) {
+ Foo[] fooArray = new Foo[] { new Foo() };
+ }
+}
+
+class Foo { }
+""")
+
+test.run(arguments = '.')
+
+def get_class_files(dir):
+ def find_class_files(arg, dirname, fnames):
+ for fname in fnames:
+ if fname[-6:] == '.class':
+ arg.append(os.path.join(dirname, fname))
+ result = []
+ os.path.walk(dir, find_class_files, result)
+ result.sort()
+ return result
+
+classes_1 = get_class_files(test.workpath('class1'))
+classes_2 = get_class_files(test.workpath('class2'))
+classes_3 = get_class_files(test.workpath('class3'))
+classes_4 = get_class_files(test.workpath('class4'))
+classes_5 = get_class_files(test.workpath('class5'))
+
+expect_1 = [
+ test.workpath('class1', 'com', 'other', 'Example2.class'),
+ test.workpath('class1', 'com', 'sub', 'foo', 'Example1.class'),
+ test.workpath('class1', 'com', 'sub', 'foo', 'Example3.class'),
+]
+
+expect_2 = [
+ test.workpath('class2', 'com', 'other', 'Example5.class'),
+ test.workpath('class2', 'com', 'sub', 'bar', 'Example4.class'),
+ test.workpath('class2', 'com', 'sub', 'bar', 'Example6.class'),
+]
+
+expect_3 = [
+ test.workpath('class3', 'Empty.class'),
+ test.workpath('class3', 'Example7.class'),
+ test.workpath('class3', 'Listener.class'),
+ test.workpath('class3', 'Private$1.class'),
+ test.workpath('class3', 'Private.class'),
+ test.workpath('class3', 'Test$1$1.class'),
+ test.workpath('class3', 'Test$1.class'),
+ test.workpath('class3', 'Test$Inner$1.class'),
+ test.workpath('class3', 'Test$Inner.class'),
+ test.workpath('class3', 'Test.class'),
+]
+
+expect_4 = [
+ test.workpath('class4', 'NestedExample$1$1.class'),
+ test.workpath('class4', 'NestedExample$1.class'),
+ test.workpath('class4', 'NestedExample.class'),
+]
+
+expect_5 = [
+ test.workpath('class5', 'Foo.class'),
+ test.workpath('class5', 'TestSCons.class'),
+]
+
+failed = None
+
+def classes_must_match(dir, expect, got):
+ if expect != got:
+ sys.stderr.write("Expected the following class files in '%s':\n" % dir)
+ for c in expect:
+ sys.stderr.write(' %s\n' % c)
+ sys.stderr.write("Got the following class files in '%s':\n" % dir)
+ for c in got:
+ sys.stderr.write(' %s\n' % c)
+ failed = 1
+
+classes_must_match('class1', expect_1, classes_1)
+classes_must_match('class2', expect_2, classes_2)
+classes_must_match('class3', expect_3, classes_3)
+classes_must_match('class4', expect_4, classes_4)
+
+test.fail_test(failed)
+
+test.up_to_date(options='--debug=explain', arguments = '.')
+
+test.pass_test()
--- /dev/null
+#!/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__"
+
+"""
+Verify that use of the $SWIGOUTDIR variable causes SCons to recognize
+that Java files are created in the specified output directory.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write(['SConstruct'], """\
+env = Environment(tools = ['default', 'swig'])
+
+Java_foo_interface = env.SharedLibrary(
+ 'Java_foo_interface',
+ 'Java_foo_interface.i',
+ SWIGOUTDIR = 'java/build',
+ SWIGFLAGS = '-c++ -java -Wall',
+ SWIGCXXFILESUFFIX = "_wrap.cpp")
+""" % locals())
+
+test.write('Java_foo_interface.i', """\
+%module foopack
+""")
+
+# SCons should realize that it needs to create the java/build
+# subdirectory to hold the generate .java files.
+test.run(arguments = '.')
+
+# SCons should remove the built .java files.
+test.run(arguments = '-c java/build/foopack.java java/build/foopackJNI.java')
+
+test.must_not_exist('java/build/foopackJNI.java')
+test.must_not_exist('java/build/foopack.java')
+
+# SCons should realize it needs to rebuild the removed .java files.
+test.not_up_to_date(arguments = '.')
+
+
+
+test.pass_test()
python = TestSCons.python
_python_ = TestSCons._python_
-# swig-python expects specific filenames.
-# the platform specific suffix won't necessarily work.
-if sys.platform == 'win32':
- _dll = '.dll'
-else:
- _dll = '.so'
-
test = TestSCons.TestSCons()
swig = test.where_is('swig')
version = sys.version[:3] # see also sys.prefix documentation
-# handle testing on other platforms:
-ldmodule_prefix = '_'
-
-frameworks = ''
-platform_sys_prefix = sys.prefix
-if sys.platform == 'darwin':
- # OS X has a built-in Python but no static libpython
- # so you should link to it using apple's 'framework' scheme.
- # (see top of file for further explanation)
- frameworks = '-framework Python'
- platform_sys_prefix = '/System/Library/Frameworks/Python.framework/Versions/%s/' % version
test.write("dependency.i", """\
%module dependency
""")
test.write('SConstruct', """
-foo = Environment(SWIGFLAGS='-python',
- CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/',
- LDMODULEPREFIX='%(ldmodule_prefix)s',
- LDMODULESUFFIX='%(_dll)s',
- FRAMEWORKSFLAGS='%(frameworks)s',
- )
-
+foo = Environment(SWIGFLAGS='-python')
swig = foo.Dictionary('SWIG')
bar = foo.Clone(SWIG = r'%(_python_)s wrapper.py ' + swig)
foo.CFile(target = 'dependent', source = ['dependent.i'])
FRAMEWORKSFLAGS='%(frameworks)s',
)
+import sys
+if sys.version[0] == '1':
+ # SWIG requires the -classic flag on pre-2.0 Python versions.
+ foo.Append(SWIGFLAGS = ' -classic')
+
swig = foo.Dictionary('SWIG')
bar = foo.Clone(SWIG = r'%(_python_)s wrapper.py ' + swig)
foo.LoadableModule(target = 'foo', source = ['foo.c', 'foo.i'])
FRAMEWORKSFLAGS='%(frameworks)s',
)
+import sys
+if sys.version[0] == '1':
+ # SWIG requires the -classic flag on pre-2.0 Python versions.
+ foo.Append(SWIGFLAGS = ' -classic')
+
foo.LoadableModule(target = 'modulename', source = ['module.i'])
""" % locals())