29

Recently I started to use Eclipse's java compiler, because it is significantly faster than standard javac. I was told that it's faster because it performs incremental compiling. But I'm still a bit unsure about this since I can't find any authoritative documentation about both - eclispse's and sun's - compilers "incremental feature". Is it true that Sun's compiler always compiles every source file and Eclipse's compiler compile only changed files and those that are affected by such a change?

Edit: I'm not using Eclipse autobuild feature but instead I'm setting

-Dbuild.compiler=org.eclipse.jdt.core.JDTCompilerAdapter

for my ant builds.

Paul Sweatte
  • 24,148
  • 7
  • 127
  • 265
calavera.info
  • 1,170
  • 2
  • 15
  • 30
  • 1
    Oracle has made an effort to address this with the `sjavac` tool. Related question: http://stackoverflow.com/questions/26424759/what-is-sjavac-who-is-it-for-and-how-do-i-use-it – aioobe Oct 29 '14 at 14:43
  • Does anyone know if you can install Eclipse's incremental compiler independent of Eclipse? I use Intellij atm. – Alexander Mills Feb 12 '19 at 23:47

4 Answers4

16

Is it true that Sun's compiler always compiles every source file and Eclipse's compiler compile only changed files and those that are affected by such a change?

I believe that you are correct on both counts.

You can of course force Eclipse to recompile everything.

But the other part of the equation is that Java build tools like Ant and Maven are capable of only compiling classes that have changed, and their tree of dependent classes.

EDIT

In Ant, incremental compilation can be done in two ways:

  • By default the <javac> task compares the timestamps of .java and corresponding .class files, and only tells the Java compiler to recompile source (.java) files that are newer than their corresponding target (.class) files, or that don't have a target file at all.

  • The <depend> task also takes into account dependencies between classes, which it determines by reading and analysing the dependency information embedded in the .class files. Having determined which .class files are out of date, the <depend> task deletes them so a following <javac> task will recompile them. However, this is not entirely fool-proof. For example, extensive changes to the source code can lead to the <depend> task may be analysing stale dependencies. Also certain kinds of dependency (e.g. on static constants) are not apparent in the .class file format.

    To understand why Ant <depend> is not fool-proof, read the "Limitations" section of the documentation.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Can you explain how does partial compiling with Ant work? How does Ant force Sun's compiler to compile only changed files and to ignore other files in the source tree? – calavera.info Apr 07 '10 at 11:12
  • I suppose that you answered my original question, but as you provide more information I'm getting more confused. If I understand it right it seems to me that javac task without previous clean is very dangerous operation, because it doesn't compile dependent classes if their timestamp isn't changed. (Funny, I just realized that I propably had this problem yesterday and it looked like a total mystery to me.) – calavera.info Apr 07 '10 at 12:22
  • Why would you need to compile a dependant class if it hasn't changed? – Martin Algesten Dec 04 '10 at 01:13
  • Because type or method signatures that it depends on have changed. Or because compile time constants that it depends on have changed. – Stephen C Dec 04 '10 at 01:44
  • ant can do that, but maven seems can't do ``'s way – J-16 SDiZ Jul 04 '12 at 16:45
  • It's not entirely true that javac rebuilds everything. Sun's javac has an extended option -Xprefer:newer (on by default) which uses the newer file if both source and compiled class are found. This does not result in correct incremental compilation though, but speeds things up in cases where it does work. Here's some code that demonstrates it https://github.com/arienkock/java-incremental-builder/blob/master/builder/test/builder/IncrementalTest.java – Kafkaesque Oct 07 '15 at 10:04
  • What's an object file? You mean a .class file? – Alexander Mills Mar 28 '17 at 02:01
  • Clarified my answer; see above. – Stephen C Mar 28 '17 at 02:36
  • Could you give a specific answer as to why extensive source code changes can cause incremental compilation to fail due to stale classes? I don’t think I fully understand what you are saying. – Jonathan Locke Feb 14 '21 at 21:26
4

Javac only compiles source files that are either named on the command line or are dependencies and are out of date. Eclipse may have a finer-grained way of deciding what that means.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Can you link some documents that support this answer? I think that 1) dependencies are not compiled by plain javac and 2) Eclipse has it's own, completely different compiler, not just finer-grained way of deciding. I can't prove it either but that's why I asked:)... – calavera.info Dec 08 '10 at 20:37
  • http://download.oracle.com/javase/6/docs/technotes/tools/windows/javac.html#searching. Of course you can prove it: just set up the conditions I described and you will see for yourself. – user207421 Dec 09 '10 at 07:00
  • This is correct: javac compiles the files you tell it to compile, not "every source file." It does not decide on its own which files need to be compiled, so it is not incremental in the sense the OP was asking about. Further (as EJP said) when javac is compiling class A and finds other .java files that A depends on, it (depending on a number of factors) compiles them too. This is not the same as looking for files that might be affected by changes to A.java, and I think that's where people get confused. – gatkin Mar 26 '12 at 23:43
  • @gatkin You have contradicted yourself. To clarify, or restate, javac compiles every file you tell it to *and* any out of date dependencies it can detect, which it *does* 'decide about' – user207421 Mar 26 '12 at 23:45
  • @EJP I posted the comment to explain why I was upvoting you. We're both saying the same thing about what javac does. What it doesn't do is look for out of date dependents, like Eclipse does, which is what the OP was asking about. Even though I think your answer is right, and said so, I thought that last point was worth mentioning. – gatkin Mar 27 '12 at 10:31
  • @gatkin I do not understand. I don't know what the difference is between 'finds other .java files that A depends on' and 'looking for files that might be affected by changes to A' or 'look for out of date dependents'. What I said, and what you claim you are agreeing with, is that the *technique* for determining what is out of date may vary as between `javac` and Eclipse. Which is *not* to say that 'what it doesn't do is look for out of date dependents'. That is *exactly* what it does, and what my answer states. That you claim you're agreeing with. – user207421 Dec 11 '15 at 09:14
2

Restating what I've heard here and phrasing it for lazy folks like me:

You can achieve incremental builds with the javac task in ant, but you should use the depend task to clear out .class files for your modified .java AND you must not leave the includes statement unspecified in the javac task. (Specifying just src path in the javac task and leaving includes unspecified causes javac recompile all sources it finds.)

Here are my depends and javac tasks. With the standard Oracle java compiler, only .java files I modify are compiled. Hope this helps!

<depend srcdir="JavaSource" destdir="${target.classes}" cache="${dependencies.dir}" closure="yes">
    <classpath refid="compiler.classpath" />
    <include name="**/*.java"/>
</depend>

<javac destdir="${target.classes}" debug="true" debuglevel="${debug.features}" optimize="${optimize.flag}" fork="yes" deprecation="no" source="1.6" target="1.6" encoding="UTF-8" includeantruntime="no">
    <classpath refid="compiler.classpath"/>
    <src path="JavaSource"/>
    <include name="**/*.java" />   <!-- This enables the incremental build -->
</javac>
DWoldrich
  • 3,817
  • 1
  • 21
  • 19
  • 2
    When you say "only .java files I modify are compiled", I see a problem here. It's only going to be OK if you changed an implementation detail, but is incorrect if your changes are externally-visible, which in Java means changing a non-private method signature, a constant value, or a default value for an argument ... Correct build systems have always taken implicit deps into account, and the fact that Java does not separate interface from implementation in separate files means we need tools like the one you're using. The lack of tooling from Sun/Oracle after 25 years is unacceptable joke. – Johan Boulé Apr 12 '20 at 10:22
2

Eclipse certainly does this. Also it does it at save time if you have that option turned on (and it is by default). It looks like sun also doesn't do this (it is very easy to test, just make a small project where A is the main class that uses class B, but B doesn't use class A. Then change A and compile the project again, see if the timestamp for b.class has changed.

This is the way many compilers work (also gcc for instance). You can use tools like ant and make to compile only the part the project that has changed. Also note that these tools aren't perfect, sometimes eclipse just loses track of the changes and you'll need to do a full rebuild.

Thirler
  • 20,239
  • 14
  • 63
  • 92
  • How does Eclipse do this? Does Eclipse have it's own version of javac? Seems unlikely but dunno. – Alexander Mills Feb 12 '19 at 23:47
  • @AlexanderMills Eclipse uses its own compiler, that is independent from Sun/Oracle's javac. Historically, IBM had its own java compiler, called Jikes, as early as 1997, just one year after Sun's release. – Johan Boulé Mar 31 '20 at 21:19