Skip to content

Commit 9df0ced

Browse files
committed
Compute the affected projects of a manifest change and request rebuild
The bnd generated manifest can have some changes that affect other projects so they require a rebuild, this adds a method that computes a delta of the difference in the manifest and request to rebuild these projects.
1 parent c25dc70 commit 9df0ced

File tree

2 files changed

+83
-11
lines changed

2 files changed

+83
-11
lines changed

ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bnd/BndBuilder.java

+79-11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
package org.eclipse.pde.internal.core.bnd;
1515

1616
import java.io.File;
17+
import java.io.IOException;
18+
import java.io.InputStream;
19+
import java.util.ArrayList;
1720
import java.util.List;
1821
import java.util.Map;
1922
import java.util.Objects;
@@ -28,19 +31,30 @@
2831
import org.eclipse.core.resources.IResourceDelta;
2932
import org.eclipse.core.resources.IResourceDeltaVisitor;
3033
import org.eclipse.core.resources.IncrementalProjectBuilder;
34+
import org.eclipse.core.resources.ResourcesPlugin;
3135
import org.eclipse.core.runtime.CoreException;
3236
import org.eclipse.core.runtime.ICoreRunnable;
3337
import org.eclipse.core.runtime.IProgressMonitor;
3438
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
3539
import org.eclipse.core.runtime.jobs.Job;
3640
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
41+
import org.eclipse.osgi.service.resolver.BundleDescription;
42+
import org.eclipse.osgi.service.resolver.BundleSpecification;
43+
import org.eclipse.osgi.service.resolver.ExportPackageDescription;
44+
import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
45+
import org.eclipse.osgi.service.resolver.StateObjectFactory;
46+
import org.eclipse.osgi.util.ManifestElement;
3747
import org.eclipse.osgi.util.NLS;
48+
import org.eclipse.pde.core.plugin.IPluginModelBase;
3849
import org.eclipse.pde.internal.core.ICoreConstants;
3950
import org.eclipse.pde.internal.core.PDECore;
4051
import org.eclipse.pde.internal.core.PDECoreMessages;
52+
import org.eclipse.pde.internal.core.PluginModelManager;
4153
import org.eclipse.pde.internal.core.builders.PDEMarkerFactory;
4254
import org.eclipse.pde.internal.core.natures.BndProject;
4355
import org.eclipse.pde.internal.core.project.PDEProject;
56+
import org.osgi.framework.BundleException;
57+
import org.osgi.framework.FrameworkUtil;
4458

4559
import aQute.bnd.build.Project;
4660
import aQute.bnd.build.ProjectBuilder;
@@ -51,10 +65,6 @@ public class BndBuilder extends IncrementalProjectBuilder {
5165

5266
private static final String CLASS_EXTENSION = ".class"; //$NON-NLS-1$
5367

54-
// This is currently disabled as it sometimes lead to jar not generated as
55-
// JDT is clearing the outputfolder while the build is running, need to
56-
// investigate if we can avoid this and it actually has benefits to build
57-
// everything async.
5868
private static final boolean USE_JOB = false;
5969

6070
private static final Predicate<IResource> CLASS_FILTER = resource -> {
@@ -76,6 +86,9 @@ protected IProject[] build(int kind, Map<String, String> args, IProgressMonitor
7686
Job buildJob = buildJobMap.compute(project, (p, oldJob) -> {
7787
Job job = Job.create(NLS.bind(PDECoreMessages.BundleBuilder_building, project.getName()),
7888
new BndBuild(p, oldJob));
89+
// The job is given a workspace lock to prevent other
90+
// buildjobs to run concurrently
91+
job.setRule(ResourcesPlugin.getWorkspace().getRoot());
7992
job.addJobChangeListener(new JobChangeAdapter() {
8093
@Override
8194
public void done(IJobChangeEvent event) {
@@ -86,7 +99,8 @@ public void done(IJobChangeEvent event) {
8699
});
87100
buildJob.schedule();
88101
} else {
89-
buildProjectJar(project, monitor);
102+
List<IProject> affected = buildProjectJar(project, monitor);
103+
requestProjectsRebuild(affected);
90104
}
91105
}
92106
return new IProject[] { project };
@@ -127,14 +141,14 @@ public void run(IProgressMonitor monitor) {
127141

128142
}
129143

130-
private static void buildProjectJar(IProject project, IProgressMonitor monitor) {
144+
private static List<IProject> buildProjectJar(IProject project, IProgressMonitor monitor) {
131145
try {
132146
Optional<Project> bndProject = BndProjectManager.getBndProject(project);
133147
if (bndProject.isEmpty()) {
134-
return;
148+
return List.of();
135149
}
136150
if (monitor.isCanceled()) {
137-
return;
151+
return List.of();
138152
}
139153
try (Project bnd = bndProject.get(); ProjectBuilder builder = new ProjectBuilder(bnd) {
140154
@Override
@@ -160,6 +174,7 @@ public void addClasspath(aQute.bnd.osgi.Jar jar) {
160174
builder.addBasicPlugin(new MakeJar());
161175
builder.setBase(bnd.getBase());
162176
ProjectJar jar = new ProjectJar(project, CLASS_FILTER);
177+
BundleDescription previousBundleDescription = loadBundleDescription(jar.getManifestFile());
163178
builder.setJar(jar);
164179
// build the main jar
165180
builder.build();
@@ -188,13 +203,66 @@ public void addClasspath(aQute.bnd.osgi.Jar jar) {
188203
}
189204
}
190205
}
191-
}
192-
if (monitor.isCanceled()) {
193-
return;
206+
jar.getManifestFile().touch(null);
207+
BundleDescription bundleDescription = loadBundleDescription(jar.getManifestFile());
208+
if (bundleDescription != null) {
209+
// TODO compare with previous description and also consider
210+
// BND project dependencies!
211+
PluginModelManager modelManager = PDECore.getDefault().getModelManager();
212+
IPluginModelBase[] models = modelManager.getWorkspaceModels();
213+
List<IProject> affectedProjects = new ArrayList<>();
214+
for (IPluginModelBase base : models) {
215+
IResource resource = base.getUnderlyingResource();
216+
if (resource == null) {
217+
continue;
218+
}
219+
IProject modelProject = resource.getProject();
220+
if (modelProject.equals(project)) {
221+
continue;
222+
}
223+
if (isModelAffected(base, bundleDescription)) {
224+
affectedProjects.add(modelProject);
225+
}
226+
if (monitor.isCanceled()) {
227+
return List.of();
228+
}
229+
}
230+
return affectedProjects;
231+
}
194232
}
195233
} catch (Exception e) {
196234
PDECore.log(e);
197235
}
236+
return List.of();
237+
}
238+
239+
private static boolean isModelAffected(IPluginModelBase base, BundleDescription bundleDescription) {
240+
BundleDescription description = base.getBundleDescription();
241+
ImportPackageSpecification[] importPackages = description.getImportPackages();
242+
for (ImportPackageSpecification imp : importPackages) {
243+
for (ExportPackageDescription exp : bundleDescription.getExportPackages()) {
244+
if (imp.isSatisfiedBy(exp)) {
245+
return true;
246+
247+
}
248+
}
249+
}
250+
for (BundleSpecification req : description.getRequiredBundles()) {
251+
if (req.isSatisfiedBy(bundleDescription)) {
252+
return true;
253+
}
254+
}
255+
return false;
256+
}
257+
258+
private static BundleDescription loadBundleDescription(IFile file) {
259+
try (InputStream manifest = file.getContents()) {
260+
Map<String, String> bundleManifest = ManifestElement.parseBundleManifest(manifest);
261+
return StateObjectFactory.defaultFactory.createBundleDescription(null,
262+
FrameworkUtil.asDictionary(bundleManifest), null, System.currentTimeMillis());
263+
} catch (IOException | CoreException | BundleException e) {
264+
return null;
265+
}
198266
}
199267

200268
private static boolean requireBuild(IProject project) {

ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bnd/ProjectJar.java

+4
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,8 @@ private void cleanup() {
190190
}
191191
}
192192

193+
public IFile getManifestFile() {
194+
return manifestFile;
195+
}
196+
193197
}

0 commit comments

Comments
 (0)