3 Tool-specific initialization for javac.
5 There normally shouldn't be any need to import this module directly.
6 It will usually be imported through the generic SCons.Tool.Tool()
14 # Permission is hereby granted, free of charge, to any person obtaining
15 # a copy of this software and associated documentation files (the
16 # "Software"), to deal in the Software without restriction, including
17 # without limitation the rights to use, copy, modify, merge, publish,
18 # distribute, sublicense, and/or sell copies of the Software, and to
19 # permit persons to whom the Software is furnished to do so, subject to
20 # the following conditions:
22 # The above copyright notice and this permission notice shall be included
23 # in all copies or substantial portions of the Software.
25 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
26 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
27 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
45 # Parse Java files for class names.
47 # This is a really simple and cool parser from Charles Crain
48 # that finds appropriate class names in Java source.
50 _reToken = re.compile(r'[^\\]([\'"])|([\{\}])|' +
51 r'(?:^|[\{\}\s;])((?:class|interface)'+
52 r'\s+[A-Za-z_]\w*)|' +
53 r'(new\s+[A-Za-z_]\w*\s*\([^\)]*\)\s*\{)|' +
54 r'(//[^\r\n]*)|(/\*|\*/)')
60 self.stackBrackets = []
64 def parseToken(self, token):
69 return IgnoreState('*/', self)
71 self.brackets = self.brackets + 1
73 self.brackets = self.brackets - 1
74 if len(self.stackBrackets) and \
75 self.brackets == self.stackBrackets[-1]:
76 self.listOutputs.append(string.join(self.listClasses, '$'))
77 self.listClasses.pop()
78 self.stackBrackets.pop()
80 return IgnoreState('"', self)
82 return IgnoreState("'", self)
83 elif token[:3] == "new":
84 # anonymous inner class
85 if len(self.listClasses) > 0:
86 clazz = self.listClasses[0]
87 self.listOutputs.append('%s$%d' % (clazz, self.nextAnon))
88 self.brackets = self.brackets + 1
89 self.nextAnon = self.nextAnon + 1
90 elif token[:5] == 'class':
91 if len(self.listClasses) == 0:
93 self.listClasses.append(string.join(string.split(token[6:])))
94 self.stackBrackets.append(self.brackets)
95 elif token[:9] == 'interface':
96 if len(self.listClasses) == 0:
98 self.listClasses.append(string.join(string.split(token[10:])))
99 self.stackBrackets.append(self.brackets)
103 def __init__(self, ignore_until, old_state):
104 self.ignore_until = ignore_until
105 self.old_state = old_state
106 def parseToken(self, token):
107 if token == self.ignore_until:
108 return self.old_state
111 def parse_java(file):
112 contents = open(file, 'r').read()
114 # Is there a more efficient way to do this than to split
115 # the contents like this?
117 for line in string.split(contents, "\n"):
118 if line[:7] == 'package':
119 pkg = string.split(line)[1]
122 pkg_dir = apply(os.path.join, string.split(pkg, '.'))
125 initial = OuterState()
127 for matches in _reToken.findall(contents):
128 # The regex produces a bunch of groups, but only one will
129 # have anything in it.
130 token = filter(lambda x: x, matches)[0]
131 currstate = currstate.parseToken(token)
133 return pkg_dir, initial.listOutputs
136 # Don't actually parse Java files for class names.
138 # We might make this a configurable option in the future if
139 # Java-file parsing takes too long (although it shouldn't relative
140 # to how long the Java compiler itself seems to take...).
142 def parse_java(file):
143 """ "Parse" a .java file.
145 This actually just splits the file name, so the assumption here
146 is that the file name matches the public class name, and that
147 the path to the file is the same as the package name.
149 return os.path.split(file)
152 """Add Builders and construction variables for javac to an Environment."""
154 def emit_java_files(target, source, env):
155 """Create and return lists of source java files
156 and their corresponding target class files.
158 env['_JAVACLASSDIR'] = target[0]
159 env['_JAVASRCDIR'] = source[0]
160 java_suffix = env.get('JAVASUFFIX', '.java')
161 class_suffix = env.get('JAVACLASSSUFFIX', '.class')
164 def visit(arg, dirname, names, js=java_suffix):
165 java_files = filter(lambda n, js=js: n[-len(js):] == js, names)
166 java_paths = map(lambda f, d=dirname:
169 arg.extend(java_paths)
170 os.path.walk(source[0], visit, slist)
174 pkg_dir, classes = parse_java(file)
177 tlist.append(os.path.join(target[0],
182 tlist.append(os.path.join(target[0], c + class_suffix))
184 # This is an odd end case: no package and no classes.
185 # Just do our best based on the source file name.
186 tlist.append(os.path.join(target[0],
187 file[:-len(java_suffix)] + class_suffix))
191 JavaBuilder = SCons.Builder.Builder(action = '$JAVACCOM',
192 emitter = emit_java_files,
193 target_factory = SCons.Node.FS.default_fs.File,
194 source_factory = SCons.Node.FS.default_fs.File)
196 env['BUILDERS']['Java'] = JavaBuilder
198 env['JAVAC'] = 'javac'
199 env['JAVACFLAGS'] = ''
200 env['JAVACCOM'] = '$JAVAC $JAVACFLAGS -d $_JAVACLASSDIR -sourcepath $_JAVASRCDIR $SOURCES'
201 env['JAVACLASSSUFFIX'] = '.class'
202 env['JAVASUFFIX'] = '.java'
205 return env.Detect('javac')