FSC recompiles every time
FSC recompiles my .scala files every time even there is no need - I can compile it twice without editing anything between attempts and it recompiles them! For example, I have 2 files
Hello.scala
class Hello{
print("hello")
}
And Tokens.scala:
abstract class Token(val str: String, val start: Int, val end: Int)
{override def toString = getClass.getSimpleName + "(" + "[" + start开发者_运维百科 + "-" + end + "]" + str + ")"}
class InputToken(str: String, start: Int, end: Int)
extends Token(str, start, end)
class ParsedToken(str: String, start: Int, end: Int, val invisible: Boolean)
extends Token(str, start, end)
When I ask ant to compile project from scratch I see following output:
ant compile
init:
[mkdir] Created dir: D:\projects\Test\build\classes
[mkdir] Created dir: D:\projects\Test\build\test\classes
compile:
[fsc] Base directory is `D:\projects\Test`
[fsc] Compiling source files: somepackage\Hello.scala, somepackage\Tokens.scala to D:\projects\Test\build\classes
BUILD SUCCESSFUL
Than I don't edit anything and ask ant compile again:
ant compile
init:
[mkdir] Created dir: D:\projects\Test\build\classes
[mkdir] Created dir: D:\projects\Test\build\test\classes
compile:
[fsc] Base directory is `D:\projects\Test`
[fsc] Compiling source files: somepackage\Tokens.scala to D:\projects\Test\build\classes
BUILD SUCCESSFUL
As you can see, fsc acts smart in case of Hello.scala (no recompilation) and acts dumb in case of Tokens.scala. I suggest that the problem is somehow related with inheritance but that is all.
So what is wrong?
Tokens.scala is recompiled because there isn't a class file matching its basename. That is, it doesn't produce a Tokens.class file. When deciding if a source file should be compiled, fsc looks for a classfile with the same basename and if the class file does not exist or the modification time on the source file is later than that of the class file, the source file will be rebuilt. If you can, I suggest that you look into Simple Build Tool, its continuous compile mode accurately tracks source->classfile mapping and won't recompile Tokens.scala
For extra laughs, think about what the compiler might do if you have a different source file that has class Tokens
in it.
Although scala allows arbitrary public classes/objects in any source file, there's still quite a bit of tooling that assumes you will somewhat follow the java convention and at least have one class/object in the file with the same name as the source file basename.
I don't like much posting stuff written by others, but I think this question merits a more complete answer that what was strictly asked.
So, first of all, fsc
recompiles everything by default, period. It is ant
, not fsc
, which is leaving Hello.scala
out, because the file name matches the class name. It is not leaving Tokens.scala
out because there is no class called Tokens
compiled -- so, in the absence of a Tokens.class
, it recompiled Tokens.scala
.
That is the wrong thing to do with Scala. Scala differs in one fundamental aspect from Java in that, because of technical limitations on JVM, a change in a trait
requires recompilation of every class, object or instantiation that uses it.
Now, one can fix the ant
task to do a smarter thing starting with Scala 2.8. I'm taking this information from blogtrader.net by Caoyuan, of Scala plugin for Netbeans fame. You define the Scala task on the build target like below:
<scalac srcdir="${src.dir}"
destdir="${build.classes.dir}"
classpathref="build.classpath"
force="yes"
addparams="-make:transitive -dependencyfile ${build.dir}/.scala_dependencies"
>
<src path="${basedir}/src1"/>
<!--include name="compile/**/*.scala"/-->
<!--exclude name="forget/**/*.scala"/-->
</scalac>
It tells ant
to recompile everything, as ant
simply isn't smart enough to figure out what needs to be recompiled or not. It also tells Scala to build a file containing compilation dependencies, and use a transitive dependency algorithm to figure out what needs to be recompiled or not.
You also need to change the init target to include the build directory in the build classpath, as Scala will need that to recompile other classes. It should look like this:
<path id="build.classpath">
<pathelement location="${scala-library.jar}"/>
<pathelement location="${scala-compiler.jar}"/>
<pathelement location="${build.classes.dir}"/>
</path>
For more details, please refer to Caoyuan's blog.
精彩评论