Bug 43114 - package-info.java repeatedly compiled
Summary: package-info.java repeatedly compiled
Status: RESOLVED FIXED
Alias: None
Product: Ant
Classification: Unclassified
Component: Core tasks (show other bugs)
Version: 1.7.0
Hardware: All All
: P3 major with 4 votes (vote)
Target Milestone: 1.8.0
Assignee: Ant Notifications List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2007-08-13 15:03 UTC by Jesse Glick
Modified: 2014-02-17 13:58 UTC (History)
6 users (show)



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jesse Glick 2007-08-13 15:03:07 UTC
If you have a file package-info.java in a source tree and run <javac> on the
tree, all the regular sources will be compiled into class files, but not
package-info.java - which is correct. However, if (after making no source
changes) you rerun the same task for an incremental build, Ant will report one
file being recompiled. This is confusing, and also probably slows down
incremental builds a bit.

It is not a good idea to simply exclude **/package-info.java from the source
fileset; Javadoc needs them, and javac might as well (when using annotation
processors anyway). Better to just have the up-to-date check in <javac> know to
expect package-info.class to not exist.
Comment 1 Peter Reilly 2007-08-13 15:45:50 UTC
I have been thinking about this - it is very
annoying!

Where would we store the time that
that the last compilation of the package-info.java
took place at?

Could the up-to-date check could be based
on the directory time i.e. if we see
a package-info.java, we would check the
last changed time of the directory the
compiled class would have gone into, if
it older than package-info.java, include the package-info.java
in the list of java files to be compiled,
and on successfull compilation,
touch the directory if the modified time is older that the package-info.java
file.

Comment 2 Jesse Glick 2007-08-13 15:49:06 UTC
I think it would be simpler to behave as follows: run javac only if some *.java
other than package-info.java are missing their .class, in which case also
include every package-info.java in the fileset; else do nothing.
Comment 3 Peter Reilly 2007-08-13 15:52:06 UTC
A thing to note is that package level runtime annotations would
be compiled into .class files - this is the way JAXB works.
It is common (at least for me) to mess up the package-info.java
file and need to change it without changing other java files.
Comment 4 Jesse Glick 2007-08-13 16:16:44 UTC
Ah, that does complicate things. Actually, in my tests, any annotation on the
package (even SOURCE retention) causes a package-info.class to be generated.

Perhaps the following behavior would work, on the assumption that compiling
_only_ a package-info.java is useless: collect two lists, outOfDate and
piOutOfDate. For any regular *.java which is either missing its *.class or whose
*.class is newer, add to outOfDate. For any package-info.java which is missing
package-info.class, add to piOutOfDate. For any package-info.java which has a
present but newer package-info.class, add to outOfDate. Now, if outOfDate is
nonempty, run javac on outOfDate + piOutOfDate.
Comment 5 Peter Reilly 2007-08-22 01:28:21 UTC
I have gone with a combination of both approaches, please
test the nightly build.
From the javac manual page:
    <ol>
      <li>
        If a <code>package-info.class</code> file exists and is older than
        the <code>package-info.java</code> file.
      </li>
      <li>
        If the directory for the 
        <code>package-info.class</code> file does not exist.
      </li>
      <li>
        If the directory for the
        <code>package-info.class</code> file exists, and has an older
        modification time than the
        the <code>package-info.java</code> file. In this case
        &lt;javac&gt; will touch the corresponding .class directory
        on successful compilation.
      </li>
  
Comment 6 Lachlan O'Dea 2008-04-17 20:11:43 UTC
(In reply to comment #5)
> I have gone with a combination of both approaches, please
> test the nightly build.

Ok, I understand why you've decided on this, but there's a potential problem if the package-info.java does contain annotations with CLASS or RUNTIME retention, and you somehow get in the state where the package-info.class file has been deleted, but the containing directory has not. In such a state, javac will refuse to recreate the package-info.class file, until the containing directory is deleted.

This is happening to me because I'm using a dependset to manually manage the dependencies of a custom annotation processor. There's currently no way to make dependset delete target directories (only files), so I end up in the state described above.

I'm not sure of the best way to deal with it. If it is felt that this scenario is generally unlikely, and dependset is a special case, then adding an option to dependset to also delete target directories would do the trick. Otherwise, adding a javac option to control the behavior is a possibility.

Actually, I'm not sure if checking the target directory timestamp is a good idea in general. Even if your package-info.java is updated, if any previous step in the build has touched its target directory in some way, it won't be compiled. This is a potential problem for SOURCE retention as well...
Comment 7 Peter Reilly 2008-04-18 04:39:43 UTC
(in reply to #8)
Your points are correct (well perhaps
not about the SOURCE level annotation - according to
Jesse a .class is also generated in this case).

The fix I put in would cause a serious regression from 1.7.0 -> 1.7.1

I will reopen this, it needs to be resolved for 1.7.1.
Comment 8 normann 2008-07-30 04:07:55 UTC
Our build scenario is as follows:

1. Copy the complete source tree to a temporary location.
2. Compile everything in the temporary location.

After upgrading from 1.7.0 to 1.7.1, our package-info.java files don't get compiled any more, and this breaks our JAXB code. The reason for this may be because the copy preserves the file timestamps but not the folder timestamps, the latter are therefore more recent. Due to this change in behavior, we cannot use 1.7.1 at this point.

With all this said, I personally don't understand how this change went into 1.7.1. Peter Reilly stated on April 18 that the change committed introduced serious regression and that this had to be resolved before releasing 1.7.1. I suspect this has not been done because the bug is still open.

I strongly urge you to reconsider the changes for 1.7.2.
Comment 9 Jesse Glick 2008-07-30 06:32:57 UTC
As a workaround, can you at least do

<touch>
  <fileset dir="tmp-src" include="**/package-info.java"/>
</>

before <javac>?
Comment 10 Jerry Andrews 2008-09-02 06:44:47 UTC
(In reply to comment #9)
> As a workaround, can you at least do
> 
> <touch>
>   <fileset dir="tmp-src" include="**/package-info.java"/>
> </>
> 
> before <javac>?

That doesn't work if the target package directory doesn't already exist and the package contains other classes or packages which fall alphabetically above package-info.java.  In our case, if I do a clean and build, the package-info.java file is never compiled, because the target package is created by other .java files compiled in the package before the compiler gets to package-info.java, then the rules above prevent its being built, because the directory is newer than the file.  The only workaround I have is to build package-info.java specifically and first, then the rest of the .java files in a second call to javac.

It would be very nice if we could simply configure javac so that it follows the rules above (which seem pretty good, overall), but override them explicitly if we know that package-info.java is going to have annotations. Alternately, ensure package-info sorts to the top of the javac compile list for a given package and associated sub-packages.  Such a sort would make sure the package directory was created by the package-info compile (thereby alleviating the problem with "clean" builds).
Comment 11 Marek Novotny 2008-09-09 00:29:46 UTC
(In reply to comment #8)
> Our build scenario is as follows:
> 
> 1. Copy the complete source tree to a temporary location.
> 2. Compile everything in the temporary location.
> 
> After upgrading from 1.7.0 to 1.7.1, our package-info.java files don't get
> compiled any more, and this breaks our JAXB code. The reason for this may be
> because the copy preserves the file timestamps but not the folder timestamps,
> the latter are therefore more recent. Due to this change in behavior, we cannot
> use 1.7.1 at this point.
> 
> With all this said, I personally don't understand how this change went into
> 1.7.1. Peter Reilly stated on April 18 that the change committed introduced
> serious regression and that this had to be resolved before releasing 1.7.1. I
> suspect this has not been done because the bug is still open.
> 
> I strongly urge you to reconsider the changes for 1.7.2.
> 

I have the similar issue, I tried build with ant 1.7.1 and some package-info.java files weren't compiled at all even if in debug log ant says that this file will be compiled. Concrete compiled class isn't there, I mean in output dir for compiled classes. Ant 1.7.0 works fine. I can't believe that this change is only marked as minor, because for me it is blocker for using 1.7.1 version.
Comment 12 Jerry Andrews 2008-09-09 06:46:07 UTC
Our work-around for this problem is to <javac... **/package-info.java ...</javac> as the first part of our compile target.  This guarantees the files will be compiled regardless of the on-disk situation given the 1.7.1 rules.
Comment 13 Jesse Glick 2008-11-03 17:43:09 UTC
One possibility to consider: when running javac and passing a package-info.java source file to it, if no package-info.class is created, make one yourself. (It would no annotations and thus essentially be empty except for the package name. The format should be simple to write directly from Java code.) This would ensure that up-to-date checks always worked sanely, as if every p-i.java had at least one class- (or runtime-) retention annotation.

You could also try to do this only if you detect an annotation in the file (just tokenize it, strip out comments, and look for '@').

The empty p-i.class files should be harmless at runtime, but it would perhaps be possible to exclude them from JARs by default.
Comment 14 Adam Batkin 2009-01-07 06:05:10 UTC
Since it seems that there is no resolution on this, can I request that for the next release, all special handling of package-info files is removed? (i.e. this change is reverted) If someone comes up with a better idea/fix that works for everyone, that can always be added at a later date.

My rationale is that Ant is currently broken for some people with this change in, and special-casing package-info.java (besides adding 30 or so extra lines of code and the extra complexity associated with conditionally excluding this single file) adds almost no benefit.

While the original reporter said that ant reporting a single file always being recompiled was confusing, I find it FAR more confusing that my code works when compiled by hand (or Eclipse) but breaks when compiled with Ant.
Comment 15 Jerry Andrews 2009-01-07 06:27:32 UTC
(In reply to comment #14)
> Since it seems that there is no resolution on this, can I request that for the
> next release, all special handling of package-info files is removed? 

I concur with this request.

The initial requester could resolve his complaint by placing a "touch package-info.class" command immediately after his javac to ensure that a package-info.class file (empty, in this case) exists.
Comment 16 Adam Batkin 2009-02-05 05:09:42 UTC
Seriously, any news on this? How do we get this fixed and a new release of Ant pushed?
Comment 17 Jesse Glick 2009-02-12 14:43:41 UTC
I have tried to do a better fix in revision 743910.

The real problem was that while javac would create a package-info.class whenever package-info.java had any annotations (even RetentionPolicy.SOURCE!), packages with no annotations at all - i.e just a replacement for package.html when creating Javadoc - did not result in package-info.class files. This confused up-to-date checks.

With the new code, if package-info.java is included in the list of source files passed to the compiler and javac generates package-info.class, then nothing further is done. But if it is missing (or out of date), a placeholder package-info.class is created that is equivalent to that which (JDK 5) javac would create for a package-info.java with only SOURCE annotations. This is effectively a no-op for the JVM but makes <javac> work predictably: *.java is compiled during an incremental build iff the corresponding *.class is missing or old, and this works the same for package-info.java as for any other *.java.
Comment 18 Abram Catalano 2009-02-24 10:46:31 UTC
first, thanks for fixing.

second, I've blown an entire day due to this bug. Seems like the ant project should pull 1.7.1 from download section, recommend 1.7.0 as the stable version, or put a big fat warning on the 1.7.1 download page warning about use with JAXB.
Comment 19 Jerry Andrews 2009-02-25 13:13:41 UTC
sounds like the right fix.  Thanks for persevering.
Comment 20 Marek Novotny 2009-07-01 03:36:33 UTC
(In reply to comment #17)
> I have tried to do a better fix in revision 743910.
> 
> The real problem was that while javac would create a package-info.class
> whenever package-info.java had any annotations (even RetentionPolicy.SOURCE!),
> packages with no annotations at all - i.e just a replacement for package.html
> when creating Javadoc - did not result in package-info.class files. This
> confused up-to-date checks.
> 
> With the new code, if package-info.java is included in the list of source files
> passed to the compiler and javac generates package-info.class, then nothing
> further is done. But if it is missing (or out of date), a placeholder
> package-info.class is created that is equivalent to that which (JDK 5) javac
> would create for a package-info.java with only SOURCE annotations. This is
> effectively a no-op for the JVM but makes <javac> work predictably: *.java is
> compiled during an incremental build iff the corresponding *.class is missing
> or old, and this works the same for package-info.java as for any other *.java.

When is 1.8.0 planned? I can't find out the date.
Comment 21 Jesse Glick 2009-07-01 07:12:48 UTC
I am not aware of any plan for a 1.8.0 release but please ask on the dev list rather than here.
Comment 22 Stefan Bodewig 2009-07-13 04:52:29 UTC
svn revision 793562 contains AntUnit tests for the original bug as well as the regression introduced with Ant 1.7.1
Comment 23 Sultan Tezadov 2009-11-23 09:52:01 UTC
This "feature" introduced in 1.7.1 was the worst bug I ever encountered in Ant. I lost almost a week because of this extremely annoying issue with JAX-WS not working intermittently. By pure coincidence I spotted package-info.java not getting compiled. Otherwise I don't know how longer I would continue digging around SOAP messages, namespaces, XJC generated classes, etc.
Now that 1.7.1 leaked into IDEs (at least NetBeans) it got even worse -- one has to modify IDE-generated build scripts to work around this issue.
Please, please, please, remove this feature and release 1.7.2 as soon as possible!
Comment 24 Jesse Glick 2009-11-23 12:02:12 UTC
The new fix will be in 1.8.0 when it is released. I have no personal objection to a 1.7.2 update release but it would need to be proposed on the dev mailing list and a release manager found.
Comment 25 Jason Pyeron 2011-01-20 13:24:09 UTC
work around for older versions of ant:

add any package level annotation to the package line of the package-info.java file.

Ex:
@Generated(value={})
package x.y.z;

import javax.annotation.Generated;


Tested with
$ ant -version
Apache Ant version 1.6.5 compiled on June 2 2005
$ javac -version
javac 1.6.0_20