Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent SBT from recompiling modified .class files?

In our project, we have a enhance post-process to the .class files generated by compile. This enhance step actually modifies the generated .class files then overrides it.

enhance <<= enhance triggeredBy (compile in Compile)

The problem is that sbt has a mechanism called incremental recompilation. It monitors the generated .class file. Every time the enhancer overrides the generated .class file, sbt recognises these modifications and recompiles related sources in next compile command.

To us, a recompilation is a very time-consuming work. We want to stop sbt from recompiling modified .class file. That may mean making sbt only monitor source changes, not output changes.

I did some searching on this. But I found little things about this. Now I know a trait called Analysis is likely responsible for the mapping from sources to output .class files. So I ask help from you guys.

Ps: we may solve this problem by putting the output of enhance to another folder, but it is not preferred.

like image 816
Chenyu Avatar asked Sep 23 '14 09:09

Chenyu


People also ask

How do I clear my SBT cache?

You can use Pretty Clean to clean the all of dev tools caches including SBT. PrettyClean also cleans the SBT project's target folder.

Does sbt run compile?

Create a directory layout to match what SBT expects, then run sbt compile to compile your project, sbt run to run your project, and sbt package to package your project as a JAR file. Unlike Java, in Scala, the file's package name doesn't have to match the directory name.


2 Answers

sbt strongly discourages mutations to files. You should generate different files instead. By doing so, you will solve your problem, since the incremental compiler of sbt will still be looking at the unaltered .class files. You will have some rewiring to do:

Send the outputs of the compile task somewhere else:

classDirectory in Compile := crossTarget.value / "classes-orig"

Processing these .class files with your tool, and send them to crossTarget.value / "classes" (the original classDirectory:

enhance <<= enhance triggeredBy (compile in Compile)

enhance := {
  val fromDir := (classesDirectory in Compile).value
  val toDir := crossTarget.value / "classes"
  ...
}

Rewire productDirectories to use crossTarget.value / "classes" anyway (otherwise it'll go looking in your modified classDirectory:

productDirectories in Compile := Seq(crossTarget.value / "classes")

Make sure that products depends on your enhance task:

products in Compile <<= (products in Compile) dependsOn enhance

You may need some more rewiring if you have resources (see copyResources). But basically you should be able to get there.

like image 110
sjrd Avatar answered Sep 22 '22 15:09

sjrd


I said about that sbt monitors the output .class file. When a .class file is modified, it recompiles the .class file's source.

After some research, we found that sbt notices file's modification by its last modified time. That is to say, we can fool sbt by rolling back the last modified time after the modification, so that sbt won't notice any changes.

So, our solution is simple but effective:

  1. find all .class files
  2. note down their last modified time
  3. do the enhance
  4. put back the former last modified time

This is a small trick. We still expect more robust solutions.

like image 41
Chenyu Avatar answered Sep 22 '22 15:09

Chenyu