__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-import glob
import os.path
+import string
import SCons.Builder
+# Okay, I don't really know what configurability would be good for
+# parsing Java files for package and/or class names, but it was easy, so
+# here it is.
+#
+# Set java_parsing to the following values to enable three different
+# flavors of parsing:
+#
+# 0 The file isn't actually parsed, so this will be quickest. The
+# package + class name is assumed to be the file path name, and we
+# just split the path name. This breaks if a package name will
+# ever be different from the path to the .java file.
+#
+# 1 The file is read to find the package name, after which we stop.
+# This should be pretty darn quick, and allows flexibility in
+# package names, but assumes that the public class name in the
+# file matches the file name. This seems to be a good assumption
+# because, for example, if you try to declare a public class
+# with a different name from the file, javac tells you:
+#
+# class Foo is public, should be declared in a file named Foo.java
+#
+# 2 Full flexibility of class names. We parse for the package name
+# (like level #1) but the output .class file name is assumed to
+# match the declared public class name--and, as a bonus, this will
+# actually support multiple public classes in a single file. My
+# guess is that's illegal Java, though... Or is it an option
+# supported by some compilers?
+#
+java_parsing = 1
+
+if java_parsing == 0:
+ def parse_java(file, suffix):
+ """ "Parse" a .java file.
+
+ This actually just splits the file name, so the assumption here
+ is that the file name matches the public class name, and that
+ the path to the file is the same as the package name.
+ """
+ return os.path.split(file)
+elif java_parsing == 1:
+ def parse_java(file, suffix):
+ """Parse a .java file for a package name.
+
+ This, of course, is not full parsing of Java files, but
+ simple-minded searching for the usual begins-in-column-1
+ "package" string most Java programs use to define their package.
+ """
+ pkg_dir = None
+ classes = []
+ f = open(file, "rb")
+ while 1:
+ line = f.readline()
+ if not line:
+ break
+ if line[:7] == 'package':
+ pkg = string.split(line)[1]
+ if pkg[-1] == ';':
+ pkg = pkg[:-1]
+ pkg_dir = apply(os.path.join, string.split(pkg, '.'))
+ classes = [ os.path.split(file[:-len(suffix)])[1] ]
+ break
+ f.close()
+ return pkg_dir, classes
+
+elif java_parsing == 2:
+ import re
+ pub_re = re.compile('^\s*public(\s+abstract)?\s+class\s+(\S+)')
+ def parse_java(file, suffix):
+ """Parse a .java file for package name and classes.
+
+ This, of course, is not full parsing of Java files, but
+ simple-minded searching for the usual strings most Java programs
+ seem to use for packages and public class names.
+ """
+ pkg_dir = None
+ classes = []
+ f = open(file, "rb")
+ while 1:
+ line = f.readline()
+ if not line:
+ break
+ if line[:7] == 'package':
+ pkg = string.split(line)[1]
+ if pkg[-1] == ';':
+ pkg = pkg[:-1]
+ pkg_dir = apply(os.path.join, string.split(pkg, '.'))
+ elif line[:6] == 'public':
+ c = pub_re.findall(line)
+ try:
+ classes.append(c[0][1])
+ except IndexError:
+ pass
+ f.close()
+ return pkg_dir, classes
+
def generate(env, platform):
"""Add Builders and construction variables for javac to an Environment."""
- try:
- bld = env['BUILDERS']['Java']
- except KeyError:
- def emit_java_files(target, source, env):
- """Create and return lists of source java files
- and their corresponding target class files.
- """
- env['_JAVACLASSDIR'] = target[0]
- env['_JAVASRCDIR'] = source[0]
- 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)
- arg.extend(java_paths)
- os.path.walk(source[0], visit, slist)
- tlist = map(lambda x, t=target[0], cs=class_suffix:
- os.path.join(t, x[:-5] + cs),
- slist)
-
- 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)
-
- env['BUILDERS']['Java'] = JavaBuilder
-
- env['JAVAC'] = 'javac'
- env['JAVACFLAGS'] = ''
- env['JAVACCOM'] = '$JAVAC $JAVACFLAGS -d $_JAVACLASSDIR -sourcepath $_JAVASRCDIR $SOURCES'
- env['JAVACLASSSUFFIX'] = '.class'
- env['JAVASUFFIX'] = '.java'
+ def emit_java_files(target, source, env):
+ """Create and return lists of source java files
+ and their corresponding target class files.
+ """
+ env['_JAVACLASSDIR'] = target[0]
+ env['_JAVASRCDIR'] = source[0]
+ 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)
+ arg.extend(java_paths)
+ os.path.walk(source[0], visit, slist)
+ tlist = []
+ for file in slist:
+ pkg_dir, classes = parse_java(file, java_suffix)
+ if pkg_dir:
+ for c in classes:
+ tlist.append(os.path.join(target[0],
+ pkg_dir,
+ c + class_suffix))
+ else:
+ tlist.append(os.path.join(target[0],
+ file[:-5] + 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)
+
+ env['BUILDERS']['Java'] = JavaBuilder
+
+ env['JAVAC'] = 'javac'
+ env['JAVACFLAGS'] = ''
+ env['JAVACCOM'] = '$JAVAC $JAVACFLAGS -d $_JAVACLASSDIR -sourcepath $_JAVASRCDIR $SOURCES'
+ env['JAVACLASSSUFFIX'] = '.class'
+ env['JAVASUFFIX'] = '.java'
def exists(env):
return env.Detect('javac')
""")
test.write(['com', 'sub', 'foo', 'Example2.java'], """\
-package com.sub.foo;
+package com.other;
public class Example2
{
""")
test.write(['com', 'sub', 'bar', 'Example5.java'], """\
-package com.sub.bar;
+package com.other;
public class Example5
{
test.fail_test(test.read('wrapper.out') != "wrapper.py /usr/local/j2sdk1.3.1/bin/javac -d classes -sourcepath com/sub/bar com/sub/bar/Example4.java com/sub/bar/Example5.java com/sub/bar/Example6.java\n")
test.fail_test(not os.path.exists(test.workpath('classes', 'com', 'sub', 'foo', 'Example1.class')))
-test.fail_test(not os.path.exists(test.workpath('classes', 'com', 'sub', 'foo', 'Example2.class')))
+test.fail_test(not os.path.exists(test.workpath('classes', 'com', 'other', 'Example2.class')))
test.fail_test(not os.path.exists(test.workpath('classes', 'com', 'sub', 'foo', 'Example3.class')))
test.fail_test(not os.path.exists(test.workpath('classes', 'com', 'sub', 'bar', 'Example4.class')))
-test.fail_test(not os.path.exists(test.workpath('classes', 'com', 'sub', 'bar', 'Example5.class')))
+test.fail_test(not os.path.exists(test.workpath('classes', 'com', 'other', 'Example5.class')))
test.fail_test(not os.path.exists(test.workpath('classes', 'com', 'sub', 'bar', 'Example6.class')))
test.up_to_date(arguments = '.')