RFR: timestamp oddities -sourcepath vs zip/jar
Fredrik Öhrström
oehrstroem at gmail.com
Tue Nov 12 01:54:18 PST 2013
I have now created the bug JDK-8028196 "Javac allows timestamps inside
rt.jar to affect compilation when using -sourcepath." to track the problem.
I also found that Erik Joelsson had already reported JDK-8025702, which has
a good description of the kind of mysterious and difficult to track down
error messages that you get when javac starts preferring rt.jar classes to
your own sources.
//Fredrik
2013/9/26 Martin Buchholz <martinrb at google.com>
> Jonathan,
>
> I think we're in complete agreement.
>
> For jsr166 development, we have a work around. IIRC It used to be that we
> needed to do a "ant clean" after we updated our JDK or else some of our
> sources would fail to be recompiled, but I fiddled with ant/javac for a
> while until the problem went away, but I'm fuzzy on the details (probably
> -Xprefer:source)
>
>
> On Thu, Sep 26, 2013 at 1:42 AM, Jonathan Gibbons <
> jonathan.gibbons at oracle.com> wrote:
>
>> Martin,
>>
>> Your proposal is phrased somewhat better than the earlier one, but
>> realistically, it is too late to do anything in this area for JDK 8.
>>
>> What I disliked about the earlier proposal was the special casing of
>> zip/jar files which was just plain wrong. Here, you are implicitly
>> suggesting a better rule, which is based more on the logical paths involved
>> (i.e. sourcepath, classpath, bootclasspath) than the nature of the file.
>> Effectively, you are saying that in javac's game of rock-paper-scissors,
>> sourcepath beats bootclasspath, no matter what the timestamps say. This
>> behavior could be embodied in a new value for the -Xprefer option, leaving
>> the existing behaviors unchanged.
>>
>> -- Jon
>>
>>
>> On 09/25/2013 01:23 PM, Martin Buchholz wrote:
>>
>> Jonathan,
>>
>> The current behavior is logical, but it is not what users intuitively
>> expect. There is a problem for anyone who tries to build JDK boot classes
>> outside of the normal JDK build process. E.g. we do this with jsr166. We
>> want to build against an unmodified JDK (rt.jar) but to generate new class
>> files for "our" sources.
>>
>> I think that most users in this situation would want the behavior of:
>> - files in the sourcepath get compiled to the destdir
>> - when looking up a type, if it's in the sourcepath, check if it's also
>> in destdir with a newer timestamp; if not, (re)compile it. If in neither,
>> consult rt.jar.
>>
>> (that may be hard to do compatibly and without introducing complexity)
>>
>>
>>
>> On Tue, Sep 24, 2013 at 7:29 AM, Jonathan Gibbons <
>> jonathan.gibbons at oracle.com> wrote:
>>
>>> Fredrik,
>>>
>>> It is a misconfiguration to have classes on the sourcepath, with
>>> corresponding classes in the compiled classes directory, AND in rt.jar. If
>>> you're compiling the core JDK classes, you should make sure that rt.jar is
>>> not on the classpath or bootclasspath. For example, you should put sources
>>> on the source path and then set the bootclasspath to the compiled classes
>>> directory. This is how the "old build" worked, successfully, for many
>>> years.
>>>
>>> Although the situation you are describing is specific to the JDK build,
>>> the general recommendation still applies: don't put a zip file on the class
>>> path containing compiled classes for any sources you are compiling.
>>>
>>> -- Jon
>>>
>>>
>>> On 09/23/2013 10:40 PM, Fredrik Öhrström wrote:
>>>
>>> It is a serious bug that command line javac can behave in two
>>> different ways when given completely static and predictable source code.
>>> Especially since the change in behavior comes from timestamps inside
>>> rt.jar, which a normal user has no control of, nor can easily see.
>>>
>>> The intent of the timestamp compare is to know if the class file is
>>> out of date in respect to the source file.
>>> Thus if the source has been changed then recompile and write the class.
>>> Now the class is up to date.
>>> However a class inside a jar file and >especially< inside rt.jar is
>>> completely outside of this loop and
>>> should only be used when there is no class file in bin, nor any source
>>> file in src. Only the timestamp
>>> of classes in bin, that can actually be affected by recompilation should
>>> matter.
>>>
>>> As I said, there are several ways to fix this. Another way would be to
>>> exclude rt.jar from the comparison.
>>> -Xprefer:source is not good enough because it will recompile everything
>>> indiscriminately on the sourcepath
>>> even though the class has already been compiled and exists in bin.
>>>
>>> Thus preferredFileObject is faulty by design and has managed to stay
>>> that way since
>>> not may people have exercised -sourcepath, especially not when building
>>> classes that
>>> also exist in rt.jar.
>>>
>>> //Fredrik
>>>
>>>
>>>
>>> 2013/9/23 Jonathan Gibbons <jonathan.gibbons at oracle.com>
>>>
>>>> On 09/22/2013 06:41 AM, Fredrik Öhrström wrote:
>>>>
>>>>> The preferredFileObject test in ClassReader.java does cause confusion
>>>>> when timestamps inside zip files are too new.
>>>>>
>>>>> Lets say that you have in src/alfa/A.java
>>>>> public class A { void a() { java.lang.Object.foo = 42; } }
>>>>> and in src/java/lang/Object.java
>>>>> public class Object { public static int foo; }
>>>>>
>>>>> You can now always compile using the following command:
>>>>> javac -d bin src/alfa/A.java src/java/lang/Object.java
>>>>>
>>>>> But you can only >>sometimes<< compile using the following command:
>>>>> javac -d bin -sourcepath src src/alfa/A.java
>>>>>
>>>>> When it fails, javac has picked up java.lang.Object from rt.jar and
>>>>> not from the sourcepath. This happens when the timestamp inside rt.jar
>>>>> is newer than the source file.
>>>>>
>>>>> The net result is that when compiling the OpenJDK with --enable-sjavac
>>>>> everything works fine, then you upgrade your jdk and the compilation
>>>>> fails in very weird ways. And will fail until you touch all your
>>>>> source files.
>>>>>
>>>>> There are several ways to fix this, but all require some thought.
>>>>> However a simple fix, would be to ignore the timestamps of class files
>>>>> inside zip/jars because they probably do not mean anything
>>>>> important....
>>>>>
>>>>> Thus in preferredFileObject, if b is a ZipFileIndexFileObject, then
>>>>> use the source!
>>>>> protected JavaFileObject preferredFileObject(JavaFileObject a,
>>>>> JavaFileObject b) {
>>>>> if (preferSource)
>>>>> return (a.getKind() == JavaFileObject.Kind.SOURCE) ? a : b;
>>>>> else {
>>>>> if (b instanceof
>>>>> com.sun.tools.javac.file.ZipFileIndexArchive.ZipFileIndexFileObject) {
>>>>> return a;
>>>>> }
>>>>> long adate = a.getLastModified();
>>>>> long bdate = b.getLastModified();
>>>>> // 6449326: policy for bad lastModifiedTime in ClassReader
>>>>> //assert adate >= 0 && bdate >= 0;
>>>>> return (adate > bdate) ? a : b;
>>>>> }
>>>>> }
>>>>>
>>>>> What do you think?
>>>>>
>>>>> //Fredrik
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>> Fredrik,
>>>>
>>>> There is no guarantee that items in zip files are presented to javac in
>>>> the class you test for, so the instanceof test is highly suspicious and
>>>> likely to be a source of inconsistent behavior. For example, what happens
>>>> if someone using using javac via the CompilerAPI (JSR 199) and provides
>>>> their own custom file manager?
>>>>
>>>> If you want to make sure you're compiling source code, use
>>>> -Xprefer:source. Or, keep your timestamps straight.
>>>>
>>>> -- Jon
>>>>
>>>
>>>
>>>
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20131112/228ab81f/attachment-0001.html
More information about the compiler-dev
mailing list