Bug 542090 - FilerException: Source file already created
Summary: FilerException: Source file already created
Status: ASSIGNED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: APT (show other bugs)
Version: 4.10   Edit
Hardware: PC Windows 10
: P3 critical with 5 votes (vote)
Target Milestone: ---   Edit
Assignee: Generic inbox for the JDT-APT component CLA
QA Contact:
URL:
Whiteboard:
Keywords: helpwanted
Depends on: 534979
Blocks:
  Show dependency tree
 
Reported: 2018-12-04 09:02 EST by Dirk Ziegenbalg CLA
Modified: 2022-07-19 03:58 EDT (History)
9 users (show)

See Also:
register.eclipse: review+
manoj.palat: review+


Attachments
sample workspace (18.14 MB, application/octet-stream)
2018-12-04 09:02 EST, Dirk Ziegenbalg CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Dirk Ziegenbalg CLA 2018-12-04 09:02:45 EST
Created attachment 276811 [details]
sample workspace

I'm using mapstruct as annotation-processor to generate mappers for mapping entities to dtos and vice versa. With M3-Milestone-Release I'm getting a FilerException "Source file already created" when editing a mapper and than saving this file. Now the autobuild is going on and I'm getting the error. When I now call clean projects then the error goes away and the source file is generated.
I'd setup and attach a small workspace where the error occurs.
Comment #18 of bug 367599 (https://bugs.eclipse.org/bugs/show_bug.cgi?id=367599#c18) seems to be the same problem with another anntotation processor.
Comment 1 Dani Megert CLA 2018-12-04 10:14:51 EST
(In reply to Dirk Ziegenbalg from comment #0)
> With M3-Milestone-Release I'm getting a FilerException
Does it work with M2? If not, did it work with 4.9?


 "Source file already created" when editing a mapper and than
> saving this file. Now the autobuild is going on and I'm getting the error.
> When I now call clean projects then the error goes away and the source file
> is generated.
So, you have an ugly workaround?
Comment 2 Dirk Ziegenbalg CLA 2018-12-04 10:21:46 EST
(In reply to Dani Megert from comment #1)
> (In reply to Dirk Ziegenbalg from comment #0)
> > With M3-Milestone-Release I'm getting a FilerException
> Does it work with M2? If not, did it work with 4.9?
No problems with 4.9. M2, I don't know.
 > 
>  "Source file already created" when editing a mapper and than
> > saving this file. Now the autobuild is going on and I'm getting the error.
> > When I now call clean projects then the error goes away and the source file
> > is generated.
> So, you have an ugly workaround?
Yes, there is a workaround but I cannot use it because I have ~ 500 projects in my workspace and everytime cleaning and building all is not possible. And when depending projects where build they also get red.
Comment 3 Dirk Ziegenbalg CLA 2018-12-04 10:31:03 EST
M2 not avaliable for download, tested with M1, same problem here.
Comment 4 Dirk Ziegenbalg CLA 2018-12-04 10:33:42 EST
Exception is raised in org.eclipse.jdt.internal.apt.pluggable.core.filer.IdeFilerImpl.createSourceFile(IdeFilerImpl.java:177)
Comment 5 Till Brychcy CLA 2018-12-04 11:12:31 EST
Thanks, I can reproduce.

I think the whole approach of just checking whether the type exists is broken because of incremental compilation.

My patch from bug Bug 539774 just improved the situation w.r.t. change from Bug 534979, but I think we should just disable this check for 4.10RC2
Comment 6 Till Brychcy CLA 2018-12-04 11:22:36 EST
@Jay, WDYT?
Comment 7 Jay Arthanareeswaran CLA 2018-12-04 11:34:06 EST
I had this typed out before running into collision:

---
I was going to type that the annotation processor should catch these exceptions and should not attempt to create the same file again. But then realized that it's the reconciler we are dealing with and what we already have was created during the previous compilation and not previous round of annotation processing. The batch compiler (and Javac) will get away with it since they start with a clean slate.

The JDK bug [1] does not talk about what to do in case of multiple compilation attempts or incremental build. 

The solution should be to :
1. Somehow identify that the existing file is the compiled output of what we are trying to create during the previous build/save.
2. And if (1) returns true and this is 0th round of annotation processing, 
   i) Don't report 
   ii) Overwrite the existing file silently.

[1] https://bugs.openjdk.java.net/browse/JDK-8193576
Comment 8 Eclipse Genie CLA 2018-12-04 14:02:42 EST
New Gerrit change created: https://git.eclipse.org/r/133478
Comment 9 Jay Arthanareeswaran CLA 2018-12-04 14:10:05 EST
(In reply to Eclipse Genie from comment #8)
> New Gerrit change created: https://git.eclipse.org/r/133478

I am sure this patch has its limitations, but this is all I could muster with the limited time I have. This takes care of the issue reported, though.

When I am more awake tomorrow, will take another look. Meanwhile, if Till or someone can glance through the patch and provide feedback, that will be nice.

In the patch, I am ignoring the .class files that are found in this project's output location (but only if it's the first round of annotation processing).
Comment 10 Dani Megert CLA 2018-12-05 03:18:13 EST
(In reply to Jay Arthanareeswaran from comment #9)
> (In reply to Eclipse Genie from comment #8)
> > New Gerrit change created: https://git.eclipse.org/r/133478
> 
> I am sure this patch has its limitations, but this is all I could muster
> with the limited time I have. This takes care of the issue reported, though.

Remain the other two bugs fixed?
Comment 11 Jay Arthanareeswaran CLA 2018-12-05 04:39:32 EST
I have incorporated Till's suggestion of null check and also added new tests. 


(In reply to Dani Megert from comment #10)
> Remain the other two bugs fixed?

Yes, those fixes are unaffected.
Comment 13 Jay Arthanareeswaran CLA 2018-12-05 06:20:42 EST
Thanks Till and Manoj! The fix is in master now.
Comment 14 Till Brychcy CLA 2018-12-05 07:52:53 EST
@Jay, after thinking about this longer: Wouldn't incremental compilation still fail for classes generated after the first round?
Comment 15 Jay Arthanareeswaran CLA 2018-12-05 11:24:12 EST
(In reply to Till Brychcy from comment #14)
> @Jay, after thinking about this longer: Wouldn't incremental compilation
> still fail for classes generated after the first round?

Yes, but the annotation processor or whoever is creating the file should ensure that such attempt is not made. From the aforementioned JDK bug:

"Any subsequent attempt to create the same file during a run will throw a FilerException"

The CU that contains the annotation and as a result triggers annotation processing is likely to be compiled more than once. I have captured this in the testcase as well. Please see FilerTesterProc#testBug542090a() and testBug542090b().
Comment 16 Till Brychcy CLA 2018-12-05 12:10:41 EST
(In reply to Jay Arthanareeswaran from comment #15)
> (In reply to Till Brychcy from comment #14)
> > @Jay, after thinking about this longer: Wouldn't incremental compilation
> > still fail for classes generated after the first round?
> 
> Yes, but the annotation processor or whoever is creating the file should
> ensure that such attempt is not made. From the aforementioned JDK bug:

I think I was not clear: what about files that are regularly created only in a later round (e.g. because an annotation processor is applied to a generated file from the previous round).
Wouldn't the type for these still exist during incremental compilation and an error be thrown?
Comment 17 Jay Arthanareeswaran CLA 2018-12-06 02:28:53 EST
(In reply to Till Brychcy from comment #16)
> I think I was not clear: what about files that are regularly created only in
> a later round (e.g. because an annotation processor is applied to a
> generated file from the previous round).
> Wouldn't the type for these still exist during incremental compilation and
> an error be thrown?

Yes, agree. Those scenarios still will be affected.

I wonder if I should remove that check at all.
Comment 18 Dirk Ziegenbalg CLA 2018-12-06 03:11:16 EST
Tested with Build id: I20181205-1800

Problem already exists (Exception now raised on another line):

Source file already exists : org.eclipse.annotationprocessing.test.mapper.TestMapperImpl at 
 org.eclipse.jdt.internal.apt.pluggable.core.filer.IdeFilerImpl.createSourceFile(IdeFilerImpl.java:182)
Comment 19 Dirk Ziegenbalg CLA 2018-12-06 03:45:11 EST
Tested with Build id: I20181205-1800

Problem already exists (Exception now raised on another line):

Source file already exists : org.eclipse.annotationprocessing.test.mapper.TestMapperImpl at 
 org.eclipse.jdt.internal.apt.pluggable.core.filer.IdeFilerImpl.createSourceFile(IdeFilerImpl.java:182)
Comment 20 Till Brychcy CLA 2018-12-06 04:58:20 EST
(In reply to Jay Arthanareeswaran from comment #17)
> (In reply to Till Brychcy from comment #16)
> > I think I was not clear: what about files that are regularly created only in
> > a later round (e.g. because an annotation processor is applied to a
> > generated file from the previous round).
> > Wouldn't the type for these still exist during incremental compilation and
> > an error be thrown?
> 
> Yes, agree. Those scenarios still will be affected.
> 
> I wonder if I should remove that check at all.

Maybe doing it only during full builds might be an option?

Still I'd say, let's disable it for 4.10, so we have more time to think about it.
Comment 21 Jay Arthanareeswaran CLA 2018-12-06 05:21:52 EST
(In reply to Jay Arthanareeswaran from comment #17)
> Yes, agree. Those scenarios still will be affected.
> 
> I wonder if I should remove that check at all.

Or the other option is to ignore this error if we are in a reconciler. We have the Phase#RECONCILE, but what we are really dealing with here is the Phase#BUILD, both in case of full build and incremental build.

Looks like this is going to be bit more involved to differentiate incremental compilation. I would like to defer the rest of the effort to post 4.10 if nobody has any objection.
Comment 22 Till Brychcy CLA 2018-12-06 05:28:43 EST
(In reply to Jay Arthanareeswaran from comment #21)
> Looks like this is going to be bit more involved to differentiate
> incremental compilation. I would like to defer the rest of the effort to
> post 4.10 if nobody has any objection.

But with check disabled for 4.10?
Comment 23 Eclipse Genie CLA 2018-12-06 05:43:22 EST
New Gerrit change created: https://git.eclipse.org/r/133587
Comment 24 Jay Arthanareeswaran CLA 2018-12-06 05:47:34 EST
(In reply to Eclipse Genie from comment #23)
> New Gerrit change created: https://git.eclipse.org/r/133587

This rolls back some of the changes made via bug 534979. I have also disabled few tests.

Till, please take a look.
Comment 25 Jay Arthanareeswaran CLA 2018-12-06 06:57:31 EST
(In reply to Eclipse Genie from comment #23)
> New Gerrit change created: https://git.eclipse.org/r/133587

The tests are green. We just missed the last RC2 build. Once Till reviews, I will request for another build.
Comment 27 Jay Arthanareeswaran CLA 2018-12-06 11:37:22 EST
Dirk, can you try out the latest I build and report back? TIA!

   http://download.eclipse.org/eclipse/downloads/drops4/I20181206-0320/
Comment 28 Dirk Ziegenbalg CLA 2018-12-06 13:32:11 EST
(In reply to Jay Arthanareeswaran from comment #27)
> Dirk, can you try out the latest I build and report back? TIA!
> 
>    http://download.eclipse.org/eclipse/downloads/drops4/I20181206-0320/

Same error with this I build (raised on IdeFilerImpl.java:182) because it contains an "old" version of apt.pluggable (org.eclipse.jdt.apt.pluggable.core_1.2.300.v20181205-0900.jar). I wait for a new I build and give it a new try tomorrow. Hopefully the exception should'nt be raised anymore.
Comment 29 Dirk Ziegenbalg CLA 2018-12-06 16:51:39 EST
(In reply to Dirk Ziegenbalg from comment #28)
> (In reply to Jay Arthanareeswaran from comment #27)
> > Dirk, can you try out the latest I build and report back? TIA!
> > 
> >    http://download.eclipse.org/eclipse/downloads/drops4/I20181206-0320/
> 
> Same error with this I build (raised on IdeFilerImpl.java:182) because it
> contains an "old" version of apt.pluggable
> (org.eclipse.jdt.apt.pluggable.core_1.2.300.v20181205-0900.jar). I wait for
> a new I build and give it a new try tomorrow. Hopefully the exception
> should'nt be raised anymore.

Tested with Build id: I20181206-0815 it now works fine. I found another problem with annotation processing. For this I will file a fresh bug. Thanks for the fast response.
Comment 30 Jay Arthanareeswaran CLA 2018-12-06 23:09:29 EST
(In reply to Dirk Ziegenbalg from comment #29)
> Tested with Build id: I20181206-0815 it now works fine. I found another
> problem with annotation processing. For this I will file a fresh bug. Thanks
> for the fast response.

Thanks Dirk, for your quick verification too.

I will keep this in open but move to 4.11.
Comment 31 Dirk Ziegenbalg CLA 2018-12-07 02:29:02 EST
So you moved this bug to 4.11 I don't need to open a new because in my oinion it is caused by the same "source".
With the tested build I now get the same exception raised on another location when I rename the CU and do saving CU:

javax.annotation.processing.FilerException: Source file already created: /Test/.apt_generated/org/eclipse/annotationprocessing/test/mapper/TestMapperImpl.java  	at org.eclipse.jdt.internal.apt.pluggable.core.filer.IdeFilerImpl.createSourceFile(IdeFilerImpl.java:161)  	at org.mapstruct.ap.internal.processor.MapperRenderingProcessor.createSourceFile(MapperRenderingProcessor.java:68)

I've checked with M1 -> same result. With the latest 4.9 release this error doesn't occur.

I think, there is a general problem with incremental compilation detecting source-files generated by apt.
Comment 32 Jay Arthanareeswaran CLA 2018-12-07 04:34:15 EST
(In reply to Dirk Ziegenbalg from comment #31)
> I've checked with M1 -> same result. With the latest 4.9 release this error
> doesn't occur.
> 
> I think, there is a general problem with incremental compilation detecting
> source-files generated by apt.

We need to take a close look at the scenarios you are talking about in light of the below JDK bug and find out what the expected behavior. Simplest way of finding if we have a problem is by comparing the full build with saving just a file or two.

https://bugs.openjdk.java.net/browse/JDK-8193576
Comment 33 Dirk Ziegenbalg CLA 2018-12-07 08:52:35 EST
Jay, you are right. I think you should throw away any generated source when a CU is saved because you don't know what has changed and what was generated before and what will/should now be generated.
So a full annotation-processing-round is required, which could slow down the incremental build significant depending on the annotation processor(s).
Comment 34 Emmanouil Trypakis CLA 2019-01-08 12:46:39 EST
Comment 18 in bug 367599 referenced by Dirk Ziegenbalg in the opening post of this bug is mine. I want to confirm that indeed it was the same problem as yours just with a different annotation processor (Google's AutoValue) and was fixed with the release of 4.10RC2 (and subsequent release 4.10).

As you have already noticed, this affected many annotation processors. Just for reference, apart from AutoValue, I had also noticed a reported issue in Immutables ( https://github.com/immutables/immutables/issues/866 ) probably stemming from this bug and likely fixed too with the fix provided here.
Comment 35 Manoj N Palat CLA 2019-02-11 04:27:51 EST
Bulk move out of 4.11
Comment 36 Jay Arthanareeswaran CLA 2019-05-21 00:31:48 EDT
Sorry, I couldn't provide the required attention during 4.12. I will revisit during 4.13 to assess the situation and take this to completion.
Comment 37 Jay Arthanareeswaran CLA 2019-08-27 00:02:59 EDT
Moving out to 4.14.
Comment 38 Manoj N Palat CLA 2019-11-25 10:49:02 EST
Bulk move out of 4.14
Comment 39 Jay Arthanareeswaran CLA 2020-03-02 04:04:18 EST
Sorry, this didn't get the required attention since I was busy with Java 14 work. Hopefully in the next version!
Comment 40 Jay Arthanareeswaran CLA 2020-05-18 03:50:22 EDT
I don't see myself sparing time for this in near future. Moving out. Anyone willing to investigate, please feel free to take over.
Comment 41 Philippe Hébrais CLA 2020-09-09 19:45:40 EDT
The behavior I see with my annotator is that my Processor's global variables are reset at the end of the round where ProcessingEnvironment.processingOver() returns true (I assume the processor class gets unloaded).  On subsequent rounds, however, eclipse is not giving me a full list of annotated elements to repopulate my global context.  Also, and strangely, it sometimes allows and sometimes forbids updating the source files created on the previous round.

It should be consistent, IMHO.  If you give me PE.processingOver(), allow me to rewrite the source file.  If you unload my processor class, allow me to rescan the annotations.

Manually performing a clean build works though.
Comment 42 Sam B CLA 2021-11-13 19:22:33 EST
Hi,

I believe this issue is related. https://github.com/google/dagger/issues/2987 Can someone confirm that it is? I have a sample repro there also. The gist of the issue is that when files change on disk (due to fetching code or modifying code), eclipse will fail with the error "Could not generate unknown file: Source file already created:"

I am not sure it is related, but I have used the following approach to ensure that annotation processing works when a single file is updated, but an output file takes multiple files as input. https://stackoverflow.com/questions/56133377/how-to-get-all-elements-with-an-annotation-in-an-intellij-incremental-build
Comment 43 Falko Modler CLA 2022-07-06 05:24:49 EDT
I think I'm affected by this as well, also with mapstruct, see: https://github.com/mapstruct/mapstruct/discussions/2920

I tried to compare ECJ and Javac:

Javac has this code (Liberica JDK 17.0.3.1):

private void checkNameAndExistence(ModuleSymbol mod, String typename, boolean allowUnnamedPackageInfo) throws FilerException {
    checkName(typename, allowUnnamedPackageInfo);
    ClassSymbol existing = elementUtils.getTypeElement(typename);
    boolean alreadySeen = aggregateGeneratedSourceNames.contains(Pair.of(mod, typename)) ||
                          aggregateGeneratedClassNames.contains(Pair.of(mod, typename)) ||
                          initialClassNames.contains(typename) ||
                          containedInInitialInputs(typename);
    if (alreadySeen) {
        if (lint)
            log.warning(Warnings.ProcTypeRecreate(typename));
        throw new FilerException("Attempt to recreate a file for type " + typename);
    }
    if (lint && existing != null) {
        log.warning(Warnings.ProcTypeAlreadyExists(typename));
    }
    if (!mod.isUnnamed() && !typename.contains(".")) {
        throw new FilerException("Attempt to create a type in unnamed package of a named module: " + typename);
    }
}

For the source(s) that fail in ECJ, existing is != null but alreadySeen is false.
Because lint is not enabled, there is not even a warning (which I find ok).

So, is javac too lenient or is ECJ too strict? I'd guess the latter.

This is preventing me from using ECJ via Maven in my project(s), which is a pity because ECJ is so much faster as we have many (generated) generics.
Comment 44 Falko Modler CLA 2022-07-19 03:58:50 EDT
(In reply to Falko Modler from comment #43)
> I think I'm affected by this as well, also with mapstruct, see:
> https://github.com/mapstruct/mapstruct/discussions/2920
> 
> ...

FWIW, my problem turned out to be a parameterization issue in plexus-compiler-eclipse (the component that maven-compiler-plugin uses internally to actually call the compiler):
https://github.com/codehaus-plexus/plexus-compiler/issues/232