Split the Java parser into a JavaCommon.py module.
[scons.git] / src / engine / SCons / Tool / javac.py
index e2d4ecb262d7a3675793e9bf551e6b68d5059411..3d4df107fb7ee7f4b338e6434dfa1f290fa7d67e 100644 (file)
@@ -33,147 +33,75 @@ selection method.
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+import os
 import os.path
+import re
 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
+from SCons.Node.FS import _my_normcase
+from SCons.Tool.JavaCommon import parse_java_file
+
+def classname(path):
+    """Turn a string (path name) into a Java class name."""
+    return string.replace(os.path.normpath(path), os.sep, '.')
+
+def emit_java_classes(target, source, env):
+    """Create and return lists of source java files
+    and their corresponding target class files.
+    """
+    java_suffix = env.get('JAVASUFFIX', '.java')
+    class_suffix = env.get('JAVACLASSSUFFIX', '.class')
+
+    slist = []
+    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].rdir().get_abspath(), visit, slist)
+
+    tlist = []
+    for file in slist:
+        pkg_dir, classes = parse_java_file(file.get_abspath())
+        if pkg_dir:
+            for c in classes:
+                t = target[0].Dir(pkg_dir).File(c+class_suffix)
+                t.attributes.java_classdir = target[0]
+                t.attributes.java_classname = classname(pkg_dir + os.sep + c)
+                tlist.append(t)
+        elif classes:
+            for c in classes:
+                t = target[0].File(c+class_suffix)
+                t.attributes.java_classdir = target[0]
+                t.attributes.java_classname = classname(c)
+                tlist.append(t)
+        else:
+            # This is an odd end case:  no package and no classes.
+            # Just do our best based on the source file name.
+            base = str(file)[:-len(java_suffix)]
+            t = target[0].File(base + class_suffix)
+            t.attributes.java_classdir = target[0]
+            t.attributes.java_classname = classname(base)
+            tlist.append(t)
+
+    return tlist, slist
+
+JavaBuilder = SCons.Builder.Builder(action = '$JAVACCOM',
+                    emitter = emit_java_classes,
+                    target_factory = SCons.Node.FS.default_fs.Dir,
+                    source_factory = SCons.Node.FS.default_fs.Dir)
 
 def generate(env):
     """Add Builders and construction variables for javac to an Environment."""
-    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['JAVACCOM']         = '$JAVAC $JAVACFLAGS -d ${TARGET.attributes.java_classdir} -sourcepath ${SOURCE.dir.rdir()} $SOURCES'
     env['JAVACLASSSUFFIX']  = '.class'
     env['JAVASUFFIX']       = '.java'