Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 365160

Summary: Issues with ProjectClasspathChangeListener
Product: [Modeling] TMF Reporter: Vladimir Piskarev <vpiskarov>
Component: XtextAssignee: Project Inbox <tmf.xtext-inbox>
Status: NEW --- QA Contact:
Severity: enhancement    
Priority: P3    
Version: 2.1.1   
Target Milestone: ---   
Hardware: All   
OS: All   
Whiteboard:

Description Vladimir Piskarev CLA 2011-11-30 02:09:45 EST
Build Identifier: 

There seems to be a couple of issues with ProjectClasspathChangeListener:

- as an eager singleton in SharedModuleWithJdt it is prone to a blindspot 
(i.e. if the user modifies project's classpath before Xtext activation, this change is going to be unnoticed by ProjectClasspathChangeListener)

- the result may depend on when exactly XtextBuilder scheduled to perform FULL_BUILD of the project with changed classpath will run: before workspace (auto-)build (in particular, before JavaBuilder) or after it. In general, it's unpredictable due to inherently concurrent nature.

For example, a Java/Xtext project can be configured to share 'src-gen' source folder between Java APT and Xtext generators. Unfortunately, when doing full build (in particular, in case of classpath change) APT will clean all derived resources under this folder. If JavaBuilder runs before XtextBuilder there are no issues at all: XtextBuilder will re-generate the necessary artifacts. But if XtextBuilder (which ProjectClasspathChangeListener scheduled to perform FULL_BUILD) happens to run before workspace auto-build all the resources it (re-)generates in 'src-gen' folder will be swept away by APT. Another example can be that APT generates some files which XtextBuilder should process, etc. 
In general, the order of builder invocations should match the order of build commands configured on a project (in this case, JavaBuilder should always run before XtextBuilder).

So it seems to me that XtextBuilder could behave like JavaBuilder with regard to classpath changes and get away with ProjectClasspathChangeListener:

/* excerpt from JavaBuilder */
protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {
    //...
    if (kind == FULL_BUILD)
        buildAll();
    else if ((this.lastState = getLastState(this.currentProject)) == null)
        buildAll();
    else if (hasClasspathChanged())
        buildAll();
    else //...

which probably can be generalized with an injectable strategy in XtextBuilder:

protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {
    //...
    if (kind == FULL_BUILD)
        fullBuild(...);
    else if (buildStrategy.isFullBuild(this, kind, args))
        fullBuild(...);
    else //...

Reproducible: Always
Comment 1 Vladimir Piskarev CLA 2011-11-30 02:52:44 EST
For the sake of completeness, one more issue with ProjectClasspathChangeListener:

- if auto-build is turned off, project's classpath is changed, and then incremental build is (manually) invoked, XtextBuilder will miss the change completely, since ProjectClasspathChangeListener is only active when auto-building is on.
Comment 2 Vladimir Piskarev CLA 2011-12-02 05:24:08 EST
It seems that JDT-aware implementation of such a build strategy could be based on a compilation participant. Since XtextBuilder runs after JavaBuilder, the participant-based strategy can detect if JavaBuilder did full build (e.g., in case of classpath change) and tell XtextBuilder do full build, too (by answering #isFullBuild message with 'true').