This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Junk class files disrupt type checking, crash the compiler

11 replies
Christian Szegedy
Joined: 2009-02-08,
User offline. Last seen 42 years 45 weeks ago.
Hi, I have the following problem:

Let us assume the following program:

trait A { def f = 1 }

trait B extends A { def g = f }

object Main extends B {
    def main(args:Array[String]) {
       println(g)  
    }
}

Let us assume that we rename class B to C and move f to C, but forget to
change the use of C (not Main still extends B), that is we get:

trait A { }
trait C extends A{ def f = 2; def g = f }

object Main extends B {
    def main(args:Array[String]) {
       println(g)  
    }
}

Then this programm is obviously buggy, but fsc compiles it without any issues,
but of course it crashes with java.lang.NoSuchMethodError: A.f().

Actually, this example can be made even worse (I did not feel like reducing my
800 LOC instance, but I got an internal exception error in fsc in a similar situation.

However, I am much more concerned about the silent errors that can obscure
bugs (in a more complex programm with several thousand lines, it is much harder
to get detect these type of issues), therefore shattering my trust in the type-checking
overall.

What is your suggestion to solve this issue without removing all class files
before every compilation?


java.lang.RuntimeException: malformed Scala signature of BLessIncr at 3434; reference type X of class A  refers to nonexisting symbol.
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle.errorBadSignature(UnPickler.scala:762)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle.scala$tools$nsc$symtab$classfile$UnPickler$UnPickle$$readSymbol(UnPickler.scala:172)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle$$anonfun$scala$tools$nsc$symtab$classfile$UnPickler$UnPickle$$readSymbolRef$1.apply(UnPickler.scala:714)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle$$anonfun$scala$tools$nsc$symtab$classfile$UnPickler$UnPickle$$readSymbolRef$1.apply(UnPickler.scala:714)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle.scala$tools$nsc$symtab$classfile$UnPickler$UnPickle$$at(UnPickler.scala:139)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle.scala$tools$nsc$symtab$classfile$UnPickler$UnPickle$$readSymbolRef(UnPickler.scala:714)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle.scala$tools$nsc$symtab$classfile$UnPickler$UnPickle$$readType(UnPickler.scala:254)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle$$anonfun$scala$tools$nsc$symtab$classfile$UnPickler$UnPickle$$readTypeRef$1.apply(UnPickler.scala:715)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle$$anonfun$scala$tools$nsc$symtab$classfile$UnPickler$UnPickle$$readTypeRef$1.apply(UnPickler.scala:715)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle.scala$tools$nsc$symtab$classfile$UnPickler$UnPickle$$at(UnPickler.scala:139)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle.scala$tools$nsc$symtab$classfile$UnPickler$UnPickle$$readTypeRef(UnPickler.scala:715)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle$$anonfun$scala$tools$nsc$symtab$classfile$UnPickler$UnPickle$$readType$2.apply(UnPickler.scala:278)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle$$anonfun$scala$tools$nsc$symtab$classfile$UnPickler$UnPickle$$readType$2.apply(UnPickler.scala:278)
        at scala.tools.nsc.symtab.classfile.PickleBuffer.until(PickleBuffer.scala:127)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle.scala$tools$nsc$symtab$classfile$UnPickler$UnPickle$$readType(UnPickler.scala:278)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle$LazyTypeRef$$anonfun$32$$anonfun$apply$4.apply(UnPickler.scala:779)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle$LazyTypeRef$$anonfun$32$$anonfun$apply$4.apply(UnPickler.scala:779)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle.scala$tools$nsc$symtab$classfile$UnPickler$UnPickle$$at(UnPickler.scala:139)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle$LazyTypeRef$$anonfun$32.apply(UnPickler.scala:779)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle$LazyTypeRef$$anonfun$32.apply(UnPickler.scala:779)
        at scala.tools.nsc.Global$$anonfun$6.apply(Global.scala:747)
        at scala.tools.nsc.Global$$anonfun$6.apply(Global.scala:747)
        at scala.tools.nsc.symtab.classfile.UnPickler$UnPickle$LazyTypeRef.complete(UnPickler.scala:779)
        at scala.tools.nsc.symtab.Symbols$Symbol.info(Symbols.scala:556)
        at scala.tools.nsc.transform.AddInterfaces.scala$tools$nsc$transform$AddInterfaces$$isInterfaceMember(AddInterfaces.scala:60)
        at scala.tools.nsc.transform.AddInterfaces$$anonfun$2.apply(AddInterfaces.scala:201)
        at scala.tools.nsc.transform.AddInterfaces$$anonfun$2.apply(AddInterfaces.scala:200)
        at scala.List.filter(List.scala:867)
        at scala.tools.nsc.symtab.Scopes$Scope.filter(Scopes.scala:350)
        at scala.tools.nsc.transform.AddInterfaces.transformMixinInfo(AddInterfaces.scala:200)
        at scala.tools.nsc.transform.Erasure.transformInfo(Erasure.scala:320)
        at scala.tools.nsc.transform.InfoTransform$Phase$$anon$1.transform(InfoTransform.scala:32)
        at scala.tools.nsc.symtab.Symbols$Symbol.rawInfo(Symbols.scala:626)
        at scala.tools.nsc.symtab.Symbols$Symbol.info(Symbols.scala:567)
        at scala.tools.nsc.symtab.Types$TypeRef.thisInfo(Types.scala:1408)
        at scala.tools.nsc.symtab.Types$TypeRef.baseClasses(Types.scala:1551)
        at scala.tools.nsc.symtab.Types$CompoundType.computeBaseClasses$1(Types.scala:1085)
        at scala.tools.nsc.symtab.Types$CompoundType$$anonfun$baseClasses$1.apply(Types.scala:1095)
        at scala.tools.nsc.symtab.Types$CompoundType$$anonfun$baseClasses$1.apply(Types.scala:1095)
        at scala.tools.nsc.symtab.Types$CompoundType.memo(Types.scala:1108)
        at scala.tools.nsc.symtab.Types$CompoundType.baseClasses(Types.scala:1095)
        at scala.tools.nsc.symtab.Symbols$Symbol.mixinClasses(Symbols.scala:923)
        at scala.tools.nsc.transform.AddInterfaces.scala$tools$nsc$transform$AddInterfaces$$addMixinConstructorCalls(AddInterfaces.scala:291)
        at scala.tools.nsc.transform.AddInterfaces$$anon$1.transform(AddInterfaces.scala:316)
        at scala.tools.nsc.ast.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:1478)
        at scala.tools.nsc.ast.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:1476)
        at scala.List$.loop$1(List.scala:293)
        at scala.List$.mapConserve(List.scala:310)
        at scala.tools.nsc.ast.Trees$Transformer.transformStats(Trees.scala:1476)
        at scala.tools.nsc.transform.AddInterfaces$$anon$1.transformStats(AddInterfaces.scala:305)
        at scala.tools.nsc.ast.Trees$Transformer.transform(Trees.scala:1384)
        at scala.tools.nsc.transform.AddInterfaces$$anon$1.transform(AddInterfaces.scala:346)
        at scala.tools.nsc.ast.Trees$Transformer.transformTemplate(Trees.scala:1462)
        at scala.tools.nsc.ast.Trees$Transformer$$anonfun$transform$2.apply(Trees.scala:1356)
        at scala.tools.nsc.ast.Trees$Transformer$$anonfun$transform$2.apply(Trees.scala:1356)
        at scala.tools.nsc.ast.Trees$Transformer.atOwner(Trees.scala:1484)
        at scala.tools.nsc.ast.Trees$Transformer.transform(Trees.scala:1355)
        at scala.tools.nsc.transform.AddInterfaces$$anon$1.transform(AddInterfaces.scala:346)
        at scala.tools.nsc.ast.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:1478)
        at scala.tools.nsc.ast.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:1476)
        at scala.List$.loop$1(List.scala:293)
        at scala.List$.mapConserve(List.scala:310)
        at scala.List$.loop$1(List.scala:297)
        at scala.List$.mapConserve(List.scala:310)
        at scala.List$.loop$1(List.scala:297)
        at scala.List$.mapConserve(List.scala:310)
        at scala.List$.loop$1(List.scala:297)
        at scala.List$.mapConserve(List.scala:310)
        at scala.List$.loop$1(List.scala:297)
        at scala.List$.mapConserve(List.scala:310)
        at scala.tools.nsc.ast.Trees$Transformer.transformStats(Trees.scala:1476)
        at scala.tools.nsc.transform.AddInterfaces$$anon$1.transformStats(AddInterfaces.scala:305)
        at scala.tools.nsc.ast.Trees$Transformer$$anonfun$transform$1.apply(Trees.scala:1352)
        at scala.tools.nsc.ast.Trees$Transformer$$anonfun$transform$1.apply(Trees.scala:1352)
        at scala.tools.nsc.ast.Trees$Transformer.atOwner(Trees.scala:1484)
        at scala.tools.nsc.ast.Trees$Transformer.transform(Trees.scala:1351)
        at scala.tools.nsc.transform.AddInterfaces$$anon$1.transform(AddInterfaces.scala:346)
        at scala.tools.nsc.transform.Erasure$ErasureTransformer$$anonfun$transform$1.apply(Erasure.scala:1072)
        at scala.tools.nsc.transform.Erasure$ErasureTransformer$$anonfun$transform$1.apply(Erasure.scala:1071)
        at scala.tools.nsc.symtab.SymbolTable.atPhase(SymbolTable.scala:106)
        at scala.tools.nsc.transform.Erasure$ErasureTransformer.transform(Erasure.scala:1071)
        at scala.tools.nsc.ast.Trees$Transformer.transformUnit(Trees.scala:1479)
        at scala.tools.nsc.transform.Transform$Phase.apply(Transform.scala:30)
        at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:267)
        at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:246)
        at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:246)
        at scala.Iterator$class.foreach(Iterator.scala:414)
        at scala.collection.mutable.ListBuffer$$anon$1.foreach(ListBuffer.scala:266)
        at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:246)
        at scala.tools.nsc.Global$Run.compileSources(Global.scala:574)
        at scala.tools.nsc.Global$Run.compile(Global.scala:667)
        at scala.tools.nsc.StandardCompileServer.session(CompileServer.scala:119)
        at scala.tools.util.SocketServer$$anonfun$run$1.apply(SocketServer.scala:81)
        at scala.tools.util.SocketServer$$anonfun$run$1.apply(SocketServer.scala:81)
        at scala.util.DynamicVariable.withValue(DynamicVariable.scala:73)
        at scala.Console$.withOut(Console.scala:84)
        at scala.Console$.withOut(Console.scala:102)
        at scala.tools.util.SocketServer.run(SocketServer.scala:80)
        at scala.tools.nsc.StandardCompileServer.main(CompileServer.scala:157)
        at scala.tools.nsc.CompileServer.main(CompileServer.scala)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at scala.tools.nsc.ObjectRunner$$anonfun$run$1.apply(ObjectRunner.scala:75)
        at scala.tools.nsc.ObjectRunner$.withContextClassLoader(ObjectRunner.scala:49)
        at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:74)
        at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:154)
        at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)





















ewilligers
Joined: 2008-08-20,
User offline. Last seen 3 years 17 weeks ago.
Re: Junk class files disrupt type checking, crash the compiler

Christian Szegedy wrote:
> Hi, I have the following problem:

...
> However, I am much more concerned about the silent errors that can obscure
> bugs (in a more complex programm with several thousand lines, it is much
> harder
> to get detect these type of issues), therefore shattering my trust in
> the type-checking
> overall.
>
> What is your suggestion to solve this issue without removing all class files
> before every compilation?

This is an issue for me too. We currently remove all the class files,
but build times are a problem.

DRMacIver
Joined: 2008-09-02,
User offline. Last seen 42 years 45 weeks ago.
Re: Junk class files disrupt type checking, crash the compiler
On Sun, Feb 8, 2009 at 6:32 AM, Christian Szegedy <christian.szegedy@gmail.com> wrote:
Hi, I have the following problem:

Let us assume the following program:

trait A { def f = 1 }

trait B extends A { def g = f }

object Main extends B {
    def main(args:Array[String]) {
       println(g)  
    }
}

Let us assume that we rename class B to C and move f to C, but forget to
change the use of C (not Main still extends B), that is we get:

trait A { }
trait C extends A{ def f = 2; def g = f }

object Main extends B {
    def main(args:Array[String]) {
       println(g)  
    }
}

Then this programm is obviously buggy, but fsc compiles it without any issues,
but of course it crashes with java.lang.NoSuchMethodError: A.f().

Actually, this example can be made even worse (I did not feel like reducing my
800 LOC instance, but I got an internal exception error in fsc in a similar situation.

However, I am much more concerned about the silent errors that can obscure
bugs (in a more complex programm with several thousand lines, it is much harder
to get detect these type of issues), therefore shattering my trust in the type-checking
overall.

What is your suggestion to solve this issue without removing all class files
before every compilation?

Hmmm.

You know, I think I can help you with this. :-)

If you're on the scala-tools mailing list, you may have noticed that I am in the process of adding in a compiler feature that causes the compiler to emit a dependency file for tracking conditional recompilation of files.

This file is a ridiculously simple format and contains, among other things, a precise account of which source files emit which classes. Therefore all you need to do (when using a compiler that supports this feature) is run a script which parses this file and then based on the information contained therein delete all class files which either do not have a source file corresponding to them or are older than their corresponding sourcefile.
Mark Harrah
Joined: 2008-12-18,
User offline. Last seen 35 weeks 3 days ago.
Re: Re: Junk class files disrupt type checking, crash the comp

On Sunday 08 February 2009, Eric Willigers wrote:
> Christian Szegedy wrote:
> > Hi, I have the following problem:
>
> ...
>
> > However, I am much more concerned about the silent errors that can
> > obscure bugs (in a more complex programm with several thousand lines, it
> > is much harder
> > to get detect these type of issues), therefore shattering my trust in
> > the type-checking
> > overall.
> >
> > What is your suggestion to solve this issue without removing all class
> > files before every compilation?
>
> This is an issue for me too. We currently remove all the class files,
> but build times are a problem.

I wrote sbt (simple build tool) to handle things like this:

http://code.google.com/p/simple-build-tool

-Mark

Christian Szegedy
Joined: 2009-02-08,
User offline. Last seen 42 years 45 weeks ago.
Re: Junk class files disrupt type checking, crash the compiler
Sounds good, I'd be glad to test that solution. When/where can I get a copy?

However, on the long run, I think the scala distribution needs a fully automatic,
easy to use solution for the very basic task.

Everything else is just a violation of the principle of least surprise.



If you're on the scala-tools mailing list, you may have noticed that I am in the process of adding in a compiler feature that causes the compiler to emit a dependency file for tracking conditional recompilation of files.

This file is a ridiculously simple format and contains, among other things, a precise account of which source files emit which classes. Therefore all you need to do (when using a compiler that supports this feature) is run a script which parses this file and then based on the information contained therein delete all class files which either do not have a source file corresponding to them or are older than their corresponding sourcefile.

ewilligers
Joined: 2008-08-20,
User offline. Last seen 3 years 17 weeks ago.
Re: Junk class files disrupt type checking, crash the compile

David MacIver wrote:
> You know, I think I can help you with this. :-)
>
> If you're on the scala-tools mailing list, you may have noticed that I
> am in the process of adding in a compiler feature that causes the
> compiler to emit a dependency file for tracking conditional
> recompilation of files.
>
> This file is a ridiculously simple format and contains, among other
> things, a precise account of which source files emit which classes.
> Therefore all you need to do (when using a compiler that supports this
> feature) is run a script which parses this file and then based on the
> information contained therein delete all class files which either do not
> have a source file corresponding to them or are older than their
> corresponding sourcefile.

Suppose we have two files:

package A
{
class B { def f = 1 }
}

package A.C
{
class D extends B
}

Then we add a third file:

package A.C
{
class B { def f = 2 }
}

Will your and/or Mark's approach ensure the second file is recompiled to
use the new B?

Christian Szegedy
Joined: 2009-02-08,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Junk class files disrupt type checking, crash the comp
Good suggestion in fact I tried Mark's sbt and it solved my initial issue, but did not solve
the one you proposed.

I have not followed David's suggestion, he could comment on that.

Additionally with sbt I had the other issue that it also wants to compile hidden files, and
emacs creates lock files with names like .#test.scala which confuse sbat.
Mark, is there a simple way to tell sbt to ignore those files?


On Sun, Feb 8, 2009 at 9:24 PM, Eric Willigers <ewilligers@gmail.com> wrote:
David MacIver wrote:
You know, I think I can help you with this. :-)

If you're on the scala-tools mailing list, you may have noticed that I am in the process of adding in a compiler feature that causes the compiler to emit a dependency file for tracking conditional recompilation of files.

This file is a ridiculously simple format and contains, among other things, a precise account of which source files emit which classes. Therefore all you need to do (when using a compiler that supports this feature) is run a script which parses this file and then based on the information contained therein delete all class files which either do not have a source file corresponding to them or are older than their corresponding sourcefile.

Suppose we have two files:

package A
{
 class B { def f = 1 }
}

package A.C
{
 class D extends B
}

Then we add a third file:

package A.C
{
 class B { def f = 2 }
}

Will your and/or Mark's approach ensure the second file is recompiled to use the new B?


DRMacIver
Joined: 2008-09-02,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Junk class files disrupt type checking, crash the comp
On Mon, Feb 9, 2009 at 5:24 AM, Eric Willigers <ewilligers@gmail.com> wrote:
David MacIver wrote:
You know, I think I can help you with this. :-)

If you're on the scala-tools mailing list, you may have noticed that I am in the process of adding in a compiler feature that causes the compiler to emit a dependency file for tracking conditional recompilation of files.

This file is a ridiculously simple format and contains, among other things, a precise account of which source files emit which classes. Therefore all you need to do (when using a compiler that supports this feature) is run a script which parses this file and then based on the information contained therein delete all class files which either do not have a source file corresponding to them or are older than their corresponding sourcefile.

Suppose we have two files:

package A
{
 class B { def f = 1 }
}

package A.C
{
 class D extends B
}

Then we add a third file:

package A.C
{
 class B { def f = 2 }
}

Will your and/or Mark's approach ensure the second file is recompiled to use the new B?

Hm. That's an interesting point. In short, no. And to be honest I've no idea how to support that. I'll have to think about it. I'm pretty sure SBT doesn't handle this either. If not, I shall have to steal Mark's solution. :-)
DRMacIver
Joined: 2008-09-02,
User offline. Last seen 42 years 45 weeks ago.
Re: Junk class files disrupt type checking, crash the compiler
On Mon, Feb 9, 2009 at 1:52 AM, Christian Szegedy <christian.szegedy@gmail.com> wrote:
Sounds good, I'd be glad to test that solution. When/where can I get a copy?

Hopefully by checking out trunk in the next couple of days. Or I can send you a patch now if you want.
 

However, on the long run, I think the scala distribution needs a fully automatic,
easy to use solution for the very basic task. Everything else is just a violation of the principle of least surprise.

I can certainly try to arrange bundling such a script with the scala distribution. I think having the compiler randomly delete classfiles might be a little on the surprising side though. For example, if you're building two projects into the same directory you don't really want the compiler deleting files from the other project.

Mark Harrah
Joined: 2008-12-18,
User offline. Last seen 35 weeks 3 days ago.
Re: Re: Junk class files disrupt type checking, crash the comp

On Monday 09 February 2009, Christian Szegedy wrote:
> Good suggestion in fact I tried Mark's sbt and it solved my initial issue,
> but did not solve
> the one you proposed.
>
> I have not followed David's suggestion, he could comment on that.
>
> Additionally with sbt I had the other issue that it also wants to compile
> hidden files, and
> emacs creates lock files with names like .#test.scala which confuse sbat.
> Mark, is there a simple way to tell sbt to ignore those files?

For unix-style hidden files, in your project definition [1]:

override def defaultExcludes = ".*"

I should probably make this the default. Please feel free to use the sbt
mailing list[2] if you have any more problems!

-Mark

[1] http://code.google.com/p/simple-build-tool/wiki/BuildConfiguration
[2] http://groups.google.com/group/simple-build-tool

Mark Harrah
Joined: 2008-12-18,
User offline. Last seen 35 weeks 3 days ago.
Re: Re: Junk class files disrupt type checking, crash the comp

On Monday 09 February 2009, David MacIver wrote:
> On Mon, Feb 9, 2009 at 5:24 AM, Eric Willigers wrote:
> > David MacIver wrote:
> >> You know, I think I can help you with this. :-)
> >>
> >> If you're on the scala-tools mailing list, you may have noticed that I
> >> am in the process of adding in a compiler feature that causes the
> >> compiler to emit a dependency file for tracking conditional
> >> recompilation of files.
> >>
> >> This file is a ridiculously simple format and contains, among other
> >> things, a precise account of which source files emit which classes.
> >> Therefore all you need to do (when using a compiler that supports this
> >> feature) is run a script which parses this file and then based on the
> >> information contained therein delete all class files which either do not
> >> have a source file corresponding to them or are older than their
> >> corresponding sourcefile.
> >
> > Suppose we have two files:
> >
> > package A
> > {
> > class B { def f = 1 }
> > }
> >
> > package A.C
> > {
> > class D extends B
> > }
> >
> > Then we add a third file:
> >
> > package A.C
> > {
> > class B { def f = 2 }
> > }
> >
> > Will your and/or Mark's approach ensure the second file is recompiled to
> > use the new B?
>
> Hm. That's an interesting point. In short, no. And to be honest I've no
> idea how to support that. I'll have to think about it. I'm pretty sure SBT
> doesn't handle this either. If not, I shall have to steal Mark's solution.
>
> :-)

Yeah this is a good one. Is this a problem for the eclipse plugin? I don't
think this can be detected as an issue until after the file is compiled, so I
think any solution would require a recompile.

I'd rather you do the thinking and I do the stealing.

Thanks,
Mark

Jens Alfke
Joined: 2009-01-30,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Junk class files disrupt type checking, crash the comp

On Feb 9, 2009, at 4:11 AM, Mark Harrah wrote:

> For unix-style hidden files, in your project definition [1]:
> override def defaultExcludes = ".*"
> I should probably make this the default.

Please do — Mac OS X has an infamous tendency to create "._"-prefixed
side-files on foreign filesystems (NFS, SMB, etc.) to store metadata
about files. These have the same name endings as the originals, so
tools that consider extensions but not "."-prefixes get confused by
them.

Thanks,

—Jens

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland