Bug 576736 - Run API analysis in a job
- added "Run API analysis builder as job" option for PDE
- this option is disabled by default
- enabling it would run API analysis builder tasks parallel to the
running Java build
- for full workspace build this avoids the problem that Java builder of
next projects to build have to wait for API build of the current project
Change-Id: Icac0487ba3dc14efc340afed43afd9407a2c7d1d
Reviewed-on: https://git.eclipse.org/r/c/pde/eclipse.pde.ui/+/186655
Tested-by: PDE Bot <pde-bot@eclipse.org>
Tested-by: Vikas Chandra <Vikas.Chandra@in.ibm.com>
Reviewed-by: Vikas Chandra <Vikas.Chandra@in.ibm.com>
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiAnalysisApplication.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiAnalysisApplication.java
index beee248..287dadb 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiAnalysisApplication.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiAnalysisApplication.java
@@ -113,6 +113,7 @@
desc.setAutoBuilding(false);
ResourcesPlugin.getWorkspace().setDescription(desc);
PDECore.getDefault().getPreferencesManager().setValue(ICoreConstants.DISABLE_API_ANALYSIS_BUILDER, false);
+ PDECore.getDefault().getPreferencesManager().setValue(ICoreConstants.RUN_API_ANALYSIS_AS_JOB, false);
Request args = Request
.readFromArgs((String[]) context.getArguments().get(IApplicationContext.APPLICATION_ARGS));
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiDescription.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiDescription.java
index fdd0a4e..84a3f78 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiDescription.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiDescription.java
@@ -69,7 +69,7 @@
/**
* Whether this description needs saving
*/
- private boolean fModified = false;
+ private volatile boolean fModified;
/**
* A comparator for {@link ManifestNode}s. Used while visiting child nodes
@@ -634,7 +634,7 @@
/**
* Marks the description as modified
*/
- protected synchronized void modified() {
+ protected void modified() {
fModified = true;
}
@@ -643,7 +643,7 @@
*
* @return
*/
- protected synchronized boolean isModified() {
+ protected boolean isModified() {
return fModified;
}
@@ -653,7 +653,7 @@
* @param mod
* @return
*/
- protected synchronized void setModified(boolean mod) {
+ protected void setModified(boolean mod) {
fModified = mod;
}
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ProjectApiDescription.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ProjectApiDescription.java
index e8c48d2..3b5475f 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ProjectApiDescription.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ProjectApiDescription.java
@@ -77,7 +77,7 @@
/**
* Whether a package refresh is in progress
*/
- private boolean fRefreshingInProgress = false;
+ private volatile boolean fRefreshingInProgress;
/**
* Associated manifest file
@@ -90,7 +90,7 @@
* traversing the cached nodes, rather than traversing the java model
* elements (effectively building the cache).
*/
- private boolean fInSynch = false;
+ private volatile boolean fInSynch;
/**
* A node for a package.
@@ -168,7 +168,7 @@
long fTimeStamp = -1L;
long fBuildStamp = -1L;
- private boolean fRefreshing = false;
+ private volatile boolean fRefreshing;
IType fType;
@@ -191,114 +191,124 @@
}
@Override
- protected synchronized ManifestNode refresh() {
+ protected ManifestNode refresh() {
if (fRefreshing) {
- if (ApiPlugin.DEBUG_API_DESCRIPTION) {
- StringBuilder buffer = new StringBuilder();
- buffer.append("Refreshing manifest node: "); //$NON-NLS-1$
- buffer.append(this);
- buffer.append(" aborted because a refresh is already in progress"); //$NON-NLS-1$
- System.out.println(buffer.toString());
- }
+ logRefreshing();
return this;
}
- try {
- fRefreshing = true;
- int parentVis = resolveVisibility(parent);
- if (VisibilityModifiers.isAPI(parentVis)) {
- ICompilationUnit unit = fType.getCompilationUnit();
- if (unit != null) {
- IResource resource = null;
- try {
- resource = unit.getUnderlyingResource();
- } catch (JavaModelException e) {
- if (ApiPlugin.DEBUG_API_DESCRIPTION) {
- StringBuilder buffer = new StringBuilder();
- buffer.append("Failed to get underlying resource for compilation unit: "); //$NON-NLS-1$
- buffer.append(unit);
- System.out.println(buffer.toString());
- }
- // exception if the resource does not exist
- if (!e.getJavaModelStatus().isDoesNotExist()) {
- ApiPlugin.log(e.getStatus());
- return this;
- }
- }
- if (resource != null && resource.exists()) {
- long stamp = resource.getModificationStamp();
- if (stamp != fTimeStamp) {
- // compute current CRC
- CRCVisitor visitor = new CRCVisitor();
- visitType(this, visitor);
- long crc = visitor.getValue();
+ synchronized (this) {
+ if (fRefreshing) {
+ logRefreshing();
+ return this;
+ }
+ try {
+ fRefreshing = true;
+ int parentVis = resolveVisibility(parent);
+ if (VisibilityModifiers.isAPI(parentVis)) {
+ ICompilationUnit unit = fType.getCompilationUnit();
+ if (unit != null) {
+ IResource resource = null;
+ try {
+ resource = unit.getUnderlyingResource();
+ } catch (JavaModelException e) {
if (ApiPlugin.DEBUG_API_DESCRIPTION) {
StringBuilder buffer = new StringBuilder();
- buffer.append("Resource has changed for type manifest node: "); //$NON-NLS-1$
- buffer.append(this);
- buffer.append(" tag scanning the new type"); //$NON-NLS-1$
- buffer.append(" (CRC "); //$NON-NLS-1$
- buffer.append(crc);
- buffer.append(')');
+ buffer.append("Failed to get underlying resource for compilation unit: "); //$NON-NLS-1$
+ buffer.append(unit);
System.out.println(buffer.toString());
}
- modified();
- children.clear();
- restrictions = RestrictionModifiers.NO_RESTRICTIONS;
- fTimeStamp = resource.getModificationStamp();
- try {
- TagScanner.newScanner().scan(unit, ProjectApiDescription.this, getApiTypeContainer((IPackageFragmentRoot) fType.getPackageFragment().getParent()), null);
- } catch (CoreException e) {
+ // exception if the resource does not exist
+ if (!e.getJavaModelStatus().isDoesNotExist()) {
ApiPlugin.log(e.getStatus());
+ return this;
}
- // see if the description changed
- visitor = new CRCVisitor();
- visitType(this, visitor);
- long crc2 = visitor.getValue();
- if (crc != crc2) {
- // update relative build time stamp
- fBuildStamp = BuildStamps.getBuildStamp(resource.getProject());
+ }
+ if (resource != null && resource.exists()) {
+ long stamp = resource.getModificationStamp();
+ if (stamp != fTimeStamp) {
+ // compute current CRC
+ CRCVisitor visitor = new CRCVisitor();
+ visitType(this, visitor);
+ long crc = visitor.getValue();
if (ApiPlugin.DEBUG_API_DESCRIPTION) {
StringBuilder buffer = new StringBuilder();
- buffer.append("CRC changed for type manifest node: "); //$NON-NLS-1$
+ buffer.append("Resource has changed for type manifest node: "); //$NON-NLS-1$
buffer.append(this);
+ buffer.append(" tag scanning the new type"); //$NON-NLS-1$
buffer.append(" (CRC "); //$NON-NLS-1$
- buffer.append(crc2);
+ buffer.append(crc);
buffer.append(')');
System.out.println(buffer.toString());
}
+ modified();
+ children.clear();
+ restrictions = RestrictionModifiers.NO_RESTRICTIONS;
+ fTimeStamp = resource.getModificationStamp();
+ try {
+ TagScanner.newScanner().scan(unit, ProjectApiDescription.this, getApiTypeContainer((IPackageFragmentRoot) fType.getPackageFragment().getParent()), null);
+ } catch (CoreException e) {
+ ApiPlugin.log(e.getStatus());
+ }
+ // see if the description changed
+ visitor = new CRCVisitor();
+ visitType(this, visitor);
+ long crc2 = visitor.getValue();
+ if (crc != crc2) {
+ // update relative build time stamp
+ fBuildStamp = BuildStamps.getBuildStamp(resource.getProject());
+ if (ApiPlugin.DEBUG_API_DESCRIPTION) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("CRC changed for type manifest node: "); //$NON-NLS-1$
+ buffer.append(this);
+ buffer.append(" (CRC "); //$NON-NLS-1$
+ buffer.append(crc2);
+ buffer.append(')');
+ System.out.println(buffer.toString());
+ }
+ }
}
+ } else {
+ if (ApiPlugin.DEBUG_API_DESCRIPTION) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("Underlying resource for the type manifest node: "); //$NON-NLS-1$
+ buffer.append(this);
+ buffer.append(" does not exist or is null"); //$NON-NLS-1$
+ System.out.println(buffer.toString());
+ }
+ // element has been removed
+ modified();
+ parent.children.remove(element);
+ return null;
}
} else {
if (ApiPlugin.DEBUG_API_DESCRIPTION) {
StringBuilder buffer = new StringBuilder();
- buffer.append("Underlying resource for the type manifest node: "); //$NON-NLS-1$
+ buffer.append("Failed to look up compilation unit for "); //$NON-NLS-1$
+ buffer.append(fType);
+ buffer.append(" refreshing type manifest node: "); //$NON-NLS-1$
buffer.append(this);
- buffer.append(" does not exist or is null"); //$NON-NLS-1$
System.out.println(buffer.toString());
}
- // element has been removed
- modified();
- parent.children.remove(element);
- return null;
+ // TODO: binary type
}
} else {
- if (ApiPlugin.DEBUG_API_DESCRIPTION) {
- StringBuilder buffer = new StringBuilder();
- buffer.append("Failed to look up compilation unit for "); //$NON-NLS-1$
- buffer.append(fType);
- buffer.append(" refreshing type manifest node: "); //$NON-NLS-1$
- buffer.append(this);
- System.out.println(buffer.toString());
- }
- // TODO: binary type
+ // don't scan internal types
}
- } else {
- // don't scan internal types
+ } finally {
+ fRefreshing = false;
}
- } finally {
- fRefreshing = false;
+ return this;
}
- return this;
+ }
+
+ private void logRefreshing() {
+ if (ApiPlugin.DEBUG_API_DESCRIPTION) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("Refreshing manifest node: "); //$NON-NLS-1$
+ buffer.append(this);
+ buffer.append(" aborted because a refresh is already in progress"); //$NON-NLS-1$
+ System.out.println(buffer.toString());
+ }
}
@Override
@@ -555,47 +565,57 @@
/**
* Refreshes package nodes if required.
*/
- synchronized void refreshPackages() {
+ void refreshPackages() {
if (fRefreshingInProgress) {
- if (ApiPlugin.DEBUG_API_DESCRIPTION) {
- StringBuilder buffer = new StringBuilder();
- buffer.append("Refreshing manifest node: "); //$NON-NLS-1$
- buffer.append(this);
- buffer.append(" aborted because a refresh is already in progress"); //$NON-NLS-1$
- System.out.println(buffer.toString());
- }
+ logPackafesRefresh();
return;
}
- // check if in synch
- if (fManifestFile == null || (fManifestFile.getModificationStamp() != fPackageTimeStamp)) {
- try {
- modified();
- fRefreshingInProgress = true;
- // set all existing packages to PRIVATE (could clear
- // the map, but it would be less efficient)
- Iterator<ManifestNode> iterator = fPackageMap.values().iterator();
- while (iterator.hasNext()) {
- PackageNode node = (PackageNode) iterator.next();
- node.visibility = VisibilityModifiers.PRIVATE;
- }
- fManifestFile = getJavaProject().getProject().getFile(JarFile.MANIFEST_NAME);
- if (fManifestFile.exists()) {
- try {
- IPackageFragment[] fragments = getLocalPackageFragments();
- Set<String> names = new HashSet<>();
- for (IPackageFragment fragment : fragments) {
- names.add(fragment.getElementName());
- }
- ProjectComponent component = getApiComponent();
- BundleComponent.initializeApiDescription(this, component.getBundleDescription(), names);
- fPackageTimeStamp = fManifestFile.getModificationStamp();
- } catch (CoreException e) {
- ApiPlugin.log(e.getStatus());
- }
- }
- } finally {
- fRefreshingInProgress = false;
+ synchronized (this) {
+ if (fRefreshingInProgress) {
+ logPackafesRefresh();
+ return;
}
+ // check if in synch
+ if (fManifestFile == null || (fManifestFile.getModificationStamp() != fPackageTimeStamp)) {
+ try {
+ modified();
+ fRefreshingInProgress = true;
+ // set all existing packages to PRIVATE (could clear
+ // the map, but it would be less efficient)
+ Iterator<ManifestNode> iterator = fPackageMap.values().iterator();
+ while (iterator.hasNext()) {
+ PackageNode node = (PackageNode) iterator.next();
+ node.visibility = VisibilityModifiers.PRIVATE;
+ }
+ fManifestFile = getJavaProject().getProject().getFile(JarFile.MANIFEST_NAME);
+ if (fManifestFile.exists()) {
+ try {
+ IPackageFragment[] fragments = getLocalPackageFragments();
+ Set<String> names = new HashSet<>();
+ for (IPackageFragment fragment : fragments) {
+ names.add(fragment.getElementName());
+ }
+ ProjectComponent component = getApiComponent();
+ BundleComponent.initializeApiDescription(this, component.getBundleDescription(), names);
+ fPackageTimeStamp = fManifestFile.getModificationStamp();
+ } catch (CoreException e) {
+ ApiPlugin.log(e.getStatus());
+ }
+ }
+ } finally {
+ fRefreshingInProgress = false;
+ }
+ }
+ }
+ }
+
+ private void logPackafesRefresh() {
+ if (ApiPlugin.DEBUG_API_DESCRIPTION) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("Refreshing manifest node: "); //$NON-NLS-1$
+ buffer.append(this);
+ buffer.append(" aborted because a refresh is already in progress"); //$NON-NLS-1$
+ System.out.println(buffer.toString());
}
}
@@ -704,7 +724,7 @@
* Notes that the underlying project has changed in some way and that the
* description cache is no longer in synch with the project.
*/
- public synchronized void projectChanged() {
+ public void projectChanged() {
fInSynch = false;
}
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/builder/ApiAnalysisBuilder.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/builder/ApiAnalysisBuilder.java
index 95b4511..7d4f32e 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/builder/ApiAnalysisBuilder.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/builder/ApiAnalysisBuilder.java
@@ -26,6 +26,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.jar.JarFile;
import org.eclipse.core.resources.IFile;
@@ -36,6 +37,7 @@
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
@@ -44,6 +46,8 @@
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
@@ -178,6 +182,8 @@
*/
private BuildState buildstate = null;
+ private ConcurrentLinkedQueue<Runnable> markersQueue = new ConcurrentLinkedQueue<>();
+
/**
* Bug 549838: In case auto-building on a API tools settings change is not desired,
* specify VM property: {@code -Dorg.eclipse.disableAutoBuildOnSettingsChange=true}
@@ -190,6 +196,20 @@
* @param resource
*/
void cleanupMarkers(IResource resource) {
+ if (isRunningAsJob()) {
+ ApiAnalysisMarkersJob job = new ApiAnalysisMarkersJob(() -> cleanupMarkersInternally(resource));
+ job.schedule();
+ } else {
+ cleanupMarkersInternally(resource);
+ }
+ }
+
+ /**
+ * Cleans up markers associated with API Tools on the given resource.
+ *
+ * @param resource
+ */
+ void cleanupMarkersInternally(IResource resource) {
cleanUnusedFilterMarkers(resource);
cleanupUsageMarkers(resource);
cleanupCompatibilityMarkers(resource);
@@ -342,6 +362,13 @@
}
@Override
+ public ISchedulingRule getRule(int kind, Map<String, String> args) {
+ // TODO probably we don't need even this and can return null if we are running as job
+ // if(isRunningAsJob()) return null;
+ return currentproject;
+ }
+
+ @Override
protected IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
PDEPreferencesManager prefs = PDECore.getDefault().getPreferencesManager();
boolean disableAPIAnalysisBuilder = prefs.getBoolean(ICoreConstants.DISABLE_API_ANALYSIS_BUILDER);
@@ -357,7 +384,6 @@
if (ApiPlugin.DEBUG_BUILDER) {
System.out.println("\nApiAnalysisBuilder: Starting build of " + this.currentproject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$
}
- SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_builder, 8);
IApiBaseline wbaseline = ApiPlugin.getDefault().getApiBaselineManager().getWorkspaceBaseline();
if (wbaseline == null) {
if (ApiPlugin.DEBUG_BUILDER) {
@@ -366,85 +392,93 @@
return NO_PROJECTS;
}
final IProject[] projects = getRequiredProjects(true);
+ if (kind != FULL_BUILD && kind != AUTO_BUILD && kind != INCREMENTAL_BUILD) {
+ return projects;
+ }
+ boolean fullBuild = kind == FULL_BUILD;
+ boolean runAsJob = prefs.getBoolean(ICoreConstants.RUN_API_ANALYSIS_AS_JOB);
+ if (runAsJob) {
+ ApiAnalysisJob job = new ApiAnalysisJob(BuilderMessages.api_analysis_builder, currentproject, fullBuild,
+ wbaseline, projects);
+ job.cancelSimilarJobs(fullBuild);
+ job.schedule(100);
+ } else {
+ work(fullBuild, wbaseline, projects, monitor);
+ }
+ return projects;
+ }
+
+ protected void work(final boolean fullBuild, IApiBaseline wbaseline, IProject[] projects, IProgressMonitor monitor)
+ throws CoreException {
+ SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_builder, 8);
+
IApiBaseline baseline = ApiPlugin.getDefault().getApiBaselineManager().getDefaultApiBaseline();
try {
SubMonitor switchMonitor = localMonitor.split(4);
- switch (kind) {
- case FULL_BUILD: {
- if (ApiPlugin.DEBUG_BUILDER) {
- System.out.println("ApiAnalysisBuilder: Performing full build as requested"); //$NON-NLS-1$
- }
- buildAll(baseline, wbaseline, switchMonitor);
- break;
+ if (fullBuild) {
+ if (ApiPlugin.DEBUG_BUILDER) {
+ System.out.println("ApiAnalysisBuilder: Performing full build as requested"); //$NON-NLS-1$
}
- case AUTO_BUILD:
- case INCREMENTAL_BUILD: {
- this.buildstate = BuildState.getLastBuiltState(currentproject);
- if (this.buildstate == null) {
+ buildAll(baseline, wbaseline, switchMonitor);
+ } else {
+ this.buildstate = BuildState.getLastBuiltState(currentproject);
+ if (this.buildstate == null) {
+ buildAll(baseline, wbaseline, switchMonitor);
+ } else if (worthDoingFullBuild(projects)) {
+ buildAll(baseline, wbaseline, switchMonitor);
+ } else {
+ IResourceDelta[] deltas = getDeltas(projects);
+ if (deltas.length < 1) {
buildAll(baseline, wbaseline, switchMonitor);
- break;
- } else if (worthDoingFullBuild(projects)) {
- buildAll(baseline, wbaseline, switchMonitor);
- break;
} else {
- IResourceDelta[] deltas = getDeltas(projects);
- if (deltas.length < 1) {
- buildAll(baseline, wbaseline, switchMonitor);
- } else {
- IResourceDelta filters = null;
- boolean full = false;
- for (IResourceDelta delta : deltas) {
- full = shouldFullBuild(delta);
- if (full) {
- break;
- }
- filters = delta.findMember(FILTER_PATH);
- if (filters != null) {
- switch (filters.getKind()) {
- case IResourceDelta.ADDED:
- case IResourceDelta.REMOVED: {
- full = true;
- break;
- }
- case IResourceDelta.CHANGED: {
- full = (filters.getFlags() & (IResourceDelta.REPLACED | IResourceDelta.CONTENT)) > 0;
- break;
- }
- default: {
- break;
- }
+ IResourceDelta filters = null;
+ boolean full = false;
+ for (IResourceDelta delta : deltas) {
+ full = shouldFullBuild(delta);
+ if (full) {
+ break;
+ }
+ filters = delta.findMember(FILTER_PATH);
+ if (filters != null) {
+ switch (filters.getKind()) {
+ case IResourceDelta.ADDED:
+ case IResourceDelta.REMOVED: {
+ full = true;
+ break;
}
- if (full) {
+ case IResourceDelta.CHANGED: {
+ full = (filters.getFlags() & (IResourceDelta.REPLACED | IResourceDelta.CONTENT)) > 0;
+ break;
+ }
+ default: {
break;
}
}
- }
- if (full) {
- if (ApiPlugin.DEBUG_BUILDER) {
- System.out.println("ApiAnalysisBuilder: Performing full build since MANIFEST.MF or .api_filters was modified"); //$NON-NLS-1$
- }
- buildAll(baseline, wbaseline, switchMonitor);
- } else {
- switchMonitor.setWorkRemaining(2);
- State state = (State) JavaModelManager.getJavaModelManager().getLastBuiltState(this.currentproject, switchMonitor.split(1));
- if (state == null) {
- buildAll(baseline, wbaseline, switchMonitor.split(1));
+ if (full) {
break;
}
+ }
+ }
+ if (full) {
+ if (ApiPlugin.DEBUG_BUILDER) {
+ System.out.println("ApiAnalysisBuilder: Performing full build since MANIFEST.MF or .api_filters was modified"); //$NON-NLS-1$
+ }
+ buildAll(baseline, wbaseline, switchMonitor);
+ } else {
+ switchMonitor.setWorkRemaining(2);
+ State state = (State) JavaModelManager.getJavaModelManager().getLastBuiltState(this.currentproject, switchMonitor.split(1));
+ if (state == null) {
+ buildAll(baseline, wbaseline, switchMonitor.split(1));
+ } else {
BuildState.setLastBuiltState(this.currentproject, null);
IncrementalApiBuilder builder = new IncrementalApiBuilder(this);
builder.build(baseline, wbaseline, deltas, state, this.buildstate, switchMonitor.split(1));
}
}
}
- break;
- }
- default: {
- break;
}
}
localMonitor.split(1);
-
} catch (OperationCanceledException oce) {
// do nothing, but don't forward it
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=304315
@@ -469,9 +503,10 @@
// be built do not close
// the baselines yet, they might be re-read by another build
// cycle
- if (baseline != null) {
- baseline.close();
- }
+ // TODO: this seem to be not needed anymore.
+ // if (baseline != null) {
+ // baseline.close();
+ // }
}
localMonitor.split(1);
if (this.buildstate != null) {
@@ -517,7 +552,50 @@
if (ApiPlugin.DEBUG_BUILDER) {
System.out.println("ApiAnalysisBuilder: Finished build of " + this.currentproject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$
}
- return projects;
+ }
+
+ class ApiAnalysisJob extends Job {
+
+ private boolean fullBuild;
+ private IApiBaseline wbaseline;
+ private IProject[] projects;
+ private IProject project;
+
+ public ApiAnalysisJob(String name, IProject project, boolean fullBuild, IApiBaseline wbaseline,
+ IProject[] projects) {
+ super(name);
+ this.project = project;
+ this.fullBuild = fullBuild;
+ this.wbaseline = wbaseline;
+ this.projects = projects;
+ // Intentionally no rule set to allow run in parallel with build locking entire workspace
+ // setRule(project);
+ }
+
+ @Override
+ public IStatus run(IProgressMonitor monitor) {
+ try {
+ work(fullBuild, wbaseline, projects, monitor);
+ } catch (CoreException e) {
+ return e.getStatus();
+ }
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public boolean belongsTo(Object family) {
+ return super.belongsTo(family) || ApiAnalysisJob.class == family;
+ }
+
+ void cancelSimilarJobs(boolean fullBuild) {
+ Job[] jobs = Job.getJobManager().find(ApiAnalysisJob.class);
+ for (Job job : jobs) {
+ ApiAnalysisJob ajob = (ApiAnalysisJob) job;
+ if (fullBuild == ajob.fullBuild && project.equals(ajob.project)) {
+ job.cancel();
+ }
+ }
+ }
}
/**
@@ -721,14 +799,30 @@
}
}
+ Runnable task;
if (hasFatalProblem) {
- cleanupMarkers(project);
- IApiProblem problem = ApiProblemFactory.newFatalProblem(Path.EMPTY.toString(), new String[] { project.getName() }, IApiProblem.FATAL_JDT_BUILDPATH_PROBLEM);
- createMarkerForProblem(IApiProblem.CATEGORY_FATAL_PROBLEM, IApiMarkerConstants.FATAL_PROBLEM_MARKER, problem);
- return true;
+ task = () -> {
+ cleanupMarkers(project);
+ IApiProblem problem = ApiProblemFactory.newFatalProblem(Path.EMPTY.toString(), new String[] { project.getName() }, IApiProblem.FATAL_JDT_BUILDPATH_PROBLEM);
+ createMarkerForProblem(IApiProblem.CATEGORY_FATAL_PROBLEM, IApiMarkerConstants.FATAL_PROBLEM_MARKER, problem);
+ };
+ } else {
+ task = () -> cleanupFatalMarkers(project);
}
- cleanupFatalMarkers(project);
- return false;
+
+ boolean runAsJob = isRunningAsJob();
+ if (runAsJob) {
+ new ApiAnalysisMarkersJob(task).schedule();
+ } else {
+ task.run();
+ }
+ return hasFatalProblem;
+ }
+
+ private static boolean isRunningAsJob() {
+ PDEPreferencesManager prefs = PDECore.getDefault().getPreferencesManager();
+ boolean runAsJob = prefs.getBoolean(ICoreConstants.RUN_API_ANALYSIS_AS_JOB);
+ return runAsJob;
}
/**
@@ -810,11 +904,56 @@
}
/**
+ * Creates or removes markers, uses the current project rule.
+ * The tasks to do are maintained by markersQueue and executed in the submission order
+ */
+ class ApiAnalysisMarkersJob extends WorkspaceJob {
+
+ public ApiAnalysisMarkersJob(Runnable task) {
+ super("Creating markers on " + currentproject.getName()); //$NON-NLS-1$
+ markersQueue.add(task);
+ setRule(currentproject);
+ setSystem(true);
+ }
+
+ @Override
+ public boolean belongsTo(Object family) {
+ return super.belongsTo(family) || ApiAnalysisMarkersJob.class == family;
+ }
+
+ @Override
+ public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
+ while (!markersQueue.isEmpty()) {
+ Runnable task = markersQueue.poll();
+ task.run();
+ }
+ return Status.OK_STATUS;
+ }
+
+ }
+
+ /**
* Creates new markers are for the listing of problems added to this
* reporter. If no problems have been added to this reporter, or we are not
* running in the framework, no work is done.
*/
protected void createMarkers() {
+ IApiProblem[] problems = getAnalyzer().getProblems();
+ if (isRunningAsJob()) {
+ new ApiAnalysisMarkersJob(() -> createMarkersInternally(problems)).schedule();
+ } else {
+ createMarkersInternally(problems);
+ }
+ }
+
+ /**
+ * Creates new markers are for the listing of problems added to this reporter.
+ * If no problems have been added to this reporter, or we are not running in the
+ * framework, no work is done.
+ *
+ * @param problems
+ */
+ protected void createMarkersInternally(IApiProblem[] problems) {
try {
IResource manifest = Util.getManifestFile(this.currentproject);
if (manifest != null) {
@@ -825,7 +964,6 @@
} catch (CoreException e) {
ApiPlugin.log(e);
}
- IApiProblem[] problems = getAnalyzer().getProblems();
String type = null;
for (IApiProblem problem : problems) {
int category = problem.getCategory();
diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/ICoreConstants.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/ICoreConstants.java
index a8db535..e28d771 100644
--- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/ICoreConstants.java
+++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/ICoreConstants.java
@@ -347,6 +347,11 @@
*/
String DISABLE_API_ANALYSIS_BUILDER = "Preferences.MainPage.disableAPIAnalysisBuilder";//$NON-NLS-1$
/**
+ * Boolean preference whether API analysis should run asynchronous to the
+ * build as background job
+ */
+ String RUN_API_ANALYSIS_AS_JOB = "Preferences.MainPage.runAPIAnalysisAsJob";//$NON-NLS-1$
+ /**
* Boolean preference whether add
* '-Dorg.eclipse.swt.graphics.Resource.reportNonDisposed=true' to VM
* arguments when creating a new launch configuration
diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PreferenceInitializer.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PreferenceInitializer.java
index 8f931fa..103ab6a 100644
--- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PreferenceInitializer.java
+++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PreferenceInitializer.java
@@ -75,6 +75,7 @@
PDEPreferencesManager corePrefs = PDECore.getDefault().getPreferencesManager();
corePrefs.setDefault(ICoreConstants.WORKSPACE_PLUGINS_OVERRIDE_TARGET, true);
corePrefs.setDefault(ICoreConstants.DISABLE_API_ANALYSIS_BUILDER, false);
+ corePrefs.setDefault(ICoreConstants.RUN_API_ANALYSIS_AS_JOB, false);
corePrefs.setDefault(ICoreConstants.ADD_SWT_NON_DISPOSAL_REPORTING, true);
corePrefs.setDefault(ICoreConstants.TEST_PLUGIN_PATTERN, ICoreConstants.TEST_PLUGIN_PATTERN_DEFAULTVALUE);
}
diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java
index cf95eba..2a79503 100644
--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java
+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java
@@ -561,6 +561,7 @@
public static String MainPreferencePage_WorkspacePluginsOverrideTarget;
public static String MainPreferencePage_WorkspacePluginsOverrideTargetTooltip;
public static String MainPreferencePage_DisableAPIAnalysisBuilder;
+ public static String MainPreferencePage_RunAPIAnalysisBuilderAsJob;
public static String MainPreferencePage_test_plugin_pattern_group;
public static String MainPreferencePage_test_plugin_pattern_label;
diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties
index c8e5f9b..4eac9b7 100644
--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties
+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties
@@ -376,6 +376,7 @@
MainPreferencePage_updateStale=&Update stale manifest files prior to launching
MainPreferencePage_WorkspacePluginsOverrideTarget=Workspace p&lug-ins override target platform plug-ins with the same id
MainPreferencePage_DisableAPIAnalysisBuilder=&Disable API analysis builder
+MainPreferencePage_RunAPIAnalysisBuilderAsJob=&Run API analysis builder as job
MainPreferencePage_WorkspacePluginsOverrideTargetTooltip=When disabled, all plug-in versions from workspace and target platform are being used.
MainPreferencePage_test_plugin_pattern_group=Test plug-in detection:
MainPreferencePage_test_plugin_pattern_description=Source folders in test plug-ins are marked to contain test sources.
diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/preferences/MainPreferencePage.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/preferences/MainPreferencePage.java
index 2a17ee0..81f5306 100644
--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/preferences/MainPreferencePage.java
+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/preferences/MainPreferencePage.java
@@ -140,6 +140,7 @@
private Button fShowTargetStatus;
private Button fAlwaysPreferWorkspace;
private Button fDisableAPIAnalysisBuilder;
+ private Button fRunAPIAnalysisBuilderAsJob;
private Button fAddSwtNonDisposalReporting;
private Text fRuntimeWorkspaceLocation;
@@ -199,6 +200,11 @@
fDisableAPIAnalysisBuilder.setText(PDEUIMessages.MainPreferencePage_DisableAPIAnalysisBuilder);
fDisableAPIAnalysisBuilder.setSelection(store.getBoolean(IPreferenceConstants.DISABLE_API_ANALYSIS_BUILDER));
+ fRunAPIAnalysisBuilderAsJob = new Button(optionComp, SWT.CHECK);
+ fRunAPIAnalysisBuilderAsJob.setText(PDEUIMessages.MainPreferencePage_RunAPIAnalysisBuilderAsJob);
+ fRunAPIAnalysisBuilderAsJob.setSelection(
+ PDECore.getDefault().getPreferencesManager().getBoolean(ICoreConstants.RUN_API_ANALYSIS_AS_JOB));
+
fAddSwtNonDisposalReporting = new Button(optionComp, SWT.CHECK);
fAddSwtNonDisposalReporting.setText(PDEUIMessages.MainPreferencePage_AddSwtNonDisposedToVMArguments);
fAddSwtNonDisposalReporting
@@ -381,13 +387,20 @@
}
+ boolean runAPIAnalysisAsJob = fRunAPIAnalysisBuilderAsJob.getSelection();
+ if (PDECore.getDefault().getPreferencesManager()
+ .getBoolean(ICoreConstants.RUN_API_ANALYSIS_AS_JOB) != runAPIAnalysisAsJob) {
+ PDEPreferencesManager prefs = PDECore.getDefault().getPreferencesManager();
+ prefs.setValue(ICoreConstants.RUN_API_ANALYSIS_AS_JOB, runAPIAnalysisAsJob);
+ }
+
boolean addSwtNonDisposalReporting = fAddSwtNonDisposalReporting.getSelection();
if (store.getBoolean(IPreferenceConstants.ADD_SWT_NON_DISPOSAL_REPORTING) != addSwtNonDisposalReporting) {
store.setValue(IPreferenceConstants.ADD_SWT_NON_DISPOSAL_REPORTING, addSwtNonDisposalReporting);
PDEPreferencesManager prefs = PDECore.getDefault().getPreferencesManager();
prefs.setValue(ICoreConstants.ADD_SWT_NON_DISPOSAL_REPORTING, addSwtNonDisposalReporting);
}
-
+ PDECore.getDefault().getPreferencesManager().savePluginPreferences();
PDEPlugin.getDefault().getPreferenceManager().savePluginPreferences();
PDEPreferencesManager launchingStore = PDELaunchingPlugin.getDefault().getPreferenceManager();