@@ -10,6 +10,9 @@ import tools.nsc.reporters.StoreReporter
10
10
import scalatron .core .Scalatron
11
11
import scala .tools .nsc .util .{BatchSourceFile , Position }
12
12
import akka .actor .Actor
13
+ import scala .util .parsing .input .OffsetPosition
14
+ import java .util .Locale
15
+ import java .io .IOException
13
16
14
17
15
18
/** Each compile actor holds a Scala compiler instance and uses it to process CompileJob messages
@@ -85,6 +88,14 @@ object CompileActor {
85
88
86
89
classPaths
87
90
}
91
+
92
+
93
+ // constants
94
+ object Constants {
95
+ val ScalaCompilerInfo = 0 // see scala.tools.nsc.reporters.Reporter.INFO
96
+ val ScalaCompilerWarning = 1 // see scala.tools.nsc.reporters.Reporter.WARNING
97
+ val ScalaCompilerError = 2 // see scala.tools.nsc.reporters.Reporter.ERROR
98
+ }
88
99
}
89
100
90
101
case class CompileActor (verbose : Boolean ) extends Actor {
@@ -125,12 +136,96 @@ case class CompileActor(verbose: Boolean) extends Actor {
125
136
* @param outputDirectoryPath the output directory path where the class files should go
126
137
*/
127
138
private def compileFromFiles (sourceFilePathList : Iterable [String ], outputDirectoryPath : String ): CompileResult = {
128
- // System.err.println("COMPILE WORKER ACTOR = " + self.toString())
129
- compile(
139
+ // feed all source files to the Scala compiler, including *.java files
140
+ // it will parse the Java files to resolve dependencies, but will not generate code for them
141
+ val scalaCompilationResult = compileScalaCode(
130
142
(run : Global # Run ) => { run.compile(sourceFilePathList.toList) },
131
143
outputDirectoryPath,
132
144
sourceFilePathList.mkString(" , " )
133
145
)
146
+
147
+ // Test: are there *.java sources among the source files?
148
+ val javaFilePathList = sourceFilePathList.filter(_.endsWith(" .java" ))
149
+ if (javaFilePathList.isEmpty) {
150
+ // no *.java files, just *.scala
151
+ scalaCompilationResult
152
+ } else {
153
+ // there are some *.java files => compile them and merge the error messages!
154
+ try {
155
+ import javax .tools ._
156
+
157
+ var javaCompilerErrors = 0
158
+ var javaCompilerWarnings = 0
159
+ val javaCompilerMessageBuilder = Vector .newBuilder[CompilerMessage ]
160
+ val diagnosticListener = new DiagnosticListener [Any ] {
161
+ def report (diagnostic : Diagnostic [_]) {
162
+ val sourceFilePath = diagnostic.getSource match {
163
+ case t : Any => t.toString // TODO
164
+ }
165
+
166
+ import CompileActor .Constants ._
167
+ val severity = diagnostic.getKind match {
168
+ case Diagnostic .Kind .ERROR => javaCompilerErrors += 1 ; ScalaCompilerError
169
+ case Diagnostic .Kind .WARNING => javaCompilerWarnings += 1 ; ScalaCompilerWarning
170
+ case Diagnostic .Kind .MANDATORY_WARNING => javaCompilerWarnings += 1 ; ScalaCompilerWarning
171
+ case _ => ScalaCompilerInfo
172
+ }
173
+
174
+ javaCompilerMessageBuilder +=
175
+ CompilerMessage (
176
+ CompilerMessagePosition (sourceFilePath, diagnostic.getLineNumber.toInt, diagnostic.getColumnNumber.toInt),
177
+ msg = diagnostic.getMessage(Locale .ENGLISH ),
178
+ severity = severity
179
+ )
180
+ }
181
+ }
182
+
183
+ // Prepare the compilation options to be used during Java compilation
184
+ // We are asking the compiler to place the output files under the /out folder.
185
+ val compileOptions = scala.collection.JavaConversions .asJavaIterable(Iterable (" -d" , outputDirectoryPath))
186
+
187
+ val compiler = ToolProvider .getSystemJavaCompiler
188
+ if (compiler== null ) throw new IllegalStateException (" Java Compiler not available (on this platform)" )
189
+
190
+ val fileManager = compiler.getStandardFileManager(diagnosticListener, null , null )
191
+ val fileObjects = fileManager.getJavaFileObjectsFromStrings(scala.collection.JavaConversions .asJavaIterable(javaFilePathList))
192
+ val task = compiler.getTask(null , fileManager, diagnosticListener, compileOptions, null , fileObjects)
193
+ val javaCompilationSuccessful = task.call()
194
+
195
+ try {
196
+ fileManager.close()
197
+ } catch {
198
+ case t : Throwable =>
199
+ System .err.println(" error: while closing Java compiler standard file manager: " + t)
200
+ }
201
+
202
+ val javaCompilationDuration = 0 // TODO: milliseconds
203
+ val javaCompilerMessages = javaCompilerMessageBuilder.result()
204
+ CompileResult (
205
+ javaCompilationSuccessful && scalaCompilationResult.compilationSuccessful,
206
+ javaCompilationDuration + scalaCompilationResult.duration,
207
+ scalaCompilationResult.errorCount,
208
+ scalaCompilationResult.warningCount,
209
+ scalaCompilationResult.compilerMessages ++ javaCompilerMessages
210
+ )
211
+ } catch {
212
+ case t : Throwable =>
213
+ // Uh, something went wrong with the Java compilation - maybe not working on this system?
214
+ System .err.println(" error: exception during attempt to compile Java files: " + t)
215
+ CompileResult (
216
+ false ,
217
+ scalaCompilationResult.duration,
218
+ scalaCompilationResult.errorCount,
219
+ scalaCompilationResult.warningCount,
220
+ scalaCompilationResult.compilerMessages ++
221
+ Iterable (CompilerMessage (
222
+ CompilerMessagePosition (javaFilePathList.head, 0 , 0 ),
223
+ msg = " exception during attempt to compile Java files: " + t,
224
+ severity = CompileActor .Constants .ScalaCompilerError
225
+ ))
226
+ )
227
+ }
228
+ }
134
229
}
135
230
136
231
/** Compiles a given collection of source files residing in memory into a given output directory.
@@ -157,7 +252,7 @@ case class CompileActor(verbose: Boolean) extends Actor {
157
252
at scala.tools.nsc.Global$Run.compileSources(Global.scala:916)
158
253
at scalatron.scalatron.impl.CompileActor$$anonfun$scalatron$scalatron$impl$CompileActor$$compileFromMemory$1.apply(CompileActor.scala:143)
159
254
*/
160
- compile (
255
+ compileScalaCode (
161
256
(run : Global # Run ) => {
162
257
val batchSourceFileList = sourceFiles.map(sf => {
163
258
new BatchSourceFile (sf.filename, sf.code.toCharArray)
@@ -178,7 +273,7 @@ case class CompileActor(verbose: Boolean) extends Actor {
178
273
* @param runDescription a description for verbose output, e.g. a list of filenames
179
274
* @return a CompileResult instance holding any compiler messages
180
275
*/
181
- private def compile (compilerInvocation : (Global # Run ) => Unit , outputDirectoryPath : String , runDescription : String ): CompileResult = {
276
+ private def compileScalaCode (compilerInvocation : (Global # Run ) => Unit , outputDirectoryPath : String , runDescription : String ): CompileResult = {
182
277
compilerGlobalOpt match {
183
278
case None =>
184
279
throw new IllegalStateException (" compiler not initialized" )
@@ -197,7 +292,12 @@ case class CompileActor(verbose: Boolean) extends Actor {
197
292
val elapsed = (endTime - startTime).toInt
198
293
if ( verbose ) println(" ...compilation completed (" + elapsed + " ms)" )
199
294
200
- val errorList = compilerGlobal.reporter.asInstanceOf [StoreReporter ].infos.map(info => CompilerMessage (info.pos, info.msg, info.severity.id))
295
+ val errorList =
296
+ compilerGlobal.reporter.asInstanceOf [StoreReporter ].infos
297
+ .map(info => CompilerMessage (
298
+ CompilerMessagePosition (info.pos.source.path, info.pos.line, info.pos.column),
299
+ info.msg,
300
+ info.severity.id))
201
301
val hasErrors = compilerGlobal.reporter.hasErrors
202
302
val errorCount = compilerGlobal.reporter.ERROR .count
203
303
val warningCount = compilerGlobal.reporter.WARNING .count
@@ -211,7 +311,8 @@ case class CompileActor(verbose: Boolean) extends Actor {
211
311
212
312
213
313
/** Records one error or warning that was generated by the compiler. */
214
- case class CompilerMessage (pos : Position , msg : String , severity : Int )
314
+ case class CompilerMessagePosition (sourceFilePath : String , line : Int , column : Int )
315
+ case class CompilerMessage (pos : CompilerMessagePosition , msg : String , severity : Int )
215
316
216
317
217
318
/** Messages passed to and from the compile actor. */
0 commit comments