From 7ac476d8744924013ca979781afb7ad96e048226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Tue, 7 May 2024 20:01:42 +0200 Subject: [PATCH] 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. --- .../pde/internal/core/bnd/BndBuilder.java | 81 +++++++++++++++++-- .../pde/internal/core/bnd/ProjectJar.java | 4 + 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bnd/BndBuilder.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bnd/BndBuilder.java index 0cd4f2aecc4..fd4561f9cf4 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bnd/BndBuilder.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bnd/BndBuilder.java @@ -14,6 +14,9 @@ package org.eclipse.pde.internal.core.bnd; import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -34,13 +37,23 @@ import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; +import org.eclipse.osgi.service.resolver.BundleDescription; +import org.eclipse.osgi.service.resolver.BundleSpecification; +import org.eclipse.osgi.service.resolver.ExportPackageDescription; +import org.eclipse.osgi.service.resolver.ImportPackageSpecification; +import org.eclipse.osgi.service.resolver.StateObjectFactory; +import org.eclipse.osgi.util.ManifestElement; import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.core.plugin.IPluginModelBase; import org.eclipse.pde.internal.core.ICoreConstants; import org.eclipse.pde.internal.core.PDECore; import org.eclipse.pde.internal.core.PDECoreMessages; +import org.eclipse.pde.internal.core.PluginModelManager; import org.eclipse.pde.internal.core.builders.PDEMarkerFactory; import org.eclipse.pde.internal.core.natures.BndProject; import org.eclipse.pde.internal.core.project.PDEProject; +import org.osgi.framework.BundleException; +import org.osgi.framework.FrameworkUtil; import aQute.bnd.build.Project; import aQute.bnd.build.ProjectBuilder; @@ -86,7 +99,8 @@ public void done(IJobChangeEvent event) { }); buildJob.schedule(); } else { - buildProjectJar(project, monitor); + List affected = buildProjectJar(project, monitor); + requestProjectsRebuild(affected); } } return new IProject[] { project }; @@ -127,14 +141,14 @@ public void run(IProgressMonitor monitor) { } - private static void buildProjectJar(IProject project, IProgressMonitor monitor) { + private static List buildProjectJar(IProject project, IProgressMonitor monitor) { try { Optional bndProject = BndProjectManager.getBndProject(project); if (bndProject.isEmpty()) { - return; + return List.of(); } if (monitor.isCanceled()) { - return; + return List.of(); } try (Project bnd = bndProject.get(); ProjectBuilder builder = new ProjectBuilder(bnd) { @Override @@ -160,6 +174,7 @@ public void addClasspath(aQute.bnd.osgi.Jar jar) { builder.addBasicPlugin(new MakeJar()); builder.setBase(bnd.getBase()); ProjectJar jar = new ProjectJar(project, CLASS_FILTER); + BundleDescription previousBundleDescription = loadBundleDescription(jar.getManifestFile()); builder.setJar(jar); // build the main jar builder.build(); @@ -188,13 +203,65 @@ public void addClasspath(aQute.bnd.osgi.Jar jar) { } } } - } - if (monitor.isCanceled()) { - return; + jar.getManifestFile().touch(null); + BundleDescription bundleDescription = loadBundleDescription(jar.getManifestFile()); + if (bundleDescription != null) { + // PDECore.getDefault().getModelManager().getState().updateBundleDescription(bundleDescription); + PluginModelManager modelManager = PDECore.getDefault().getModelManager(); + IPluginModelBase[] models = modelManager.getWorkspaceModels(); + List affectedProjects = new ArrayList<>(); + for (IPluginModelBase base : models) { + IResource resource = base.getUnderlyingResource(); + if (resource == null) { + continue; + } + IProject modelProject = resource.getProject(); + if (modelProject.equals(project)) { + continue; + } + if (isModelAffected(base, bundleDescription)) { + affectedProjects.add(modelProject); + } + if (monitor.isCanceled()) { + return List.of(); + } + } + return affectedProjects; + } } } catch (Exception e) { PDECore.log(e); } + return List.of(); + } + + private static boolean isModelAffected(IPluginModelBase base, BundleDescription bundleDescription) { + BundleDescription description = base.getBundleDescription(); + ImportPackageSpecification[] importPackages = description.getImportPackages(); + for (ImportPackageSpecification imp : importPackages) { + for (ExportPackageDescription exp : bundleDescription.getExportPackages()) { + if (imp.isSatisfiedBy(exp)) { + return true; + + } + } + } + for (BundleSpecification req : description.getRequiredBundles()) { + if (req.isSatisfiedBy(bundleDescription)) { + return true; + } + } + return false; + } + + private static BundleDescription loadBundleDescription(IFile file) { + try (InputStream manifest = file.getContents()) { + Map bundleManifest = ManifestElement.parseBundleManifest(manifest); + return StateObjectFactory.defaultFactory.createBundleDescription(null, + FrameworkUtil.asDictionary(bundleManifest), null, System.currentTimeMillis()); + } catch (IOException | CoreException | BundleException e) { + return null; + } } private static boolean requireBuild(IProject project) { diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bnd/ProjectJar.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bnd/ProjectJar.java index d7b3e948811..5179fdf9f54 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bnd/ProjectJar.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bnd/ProjectJar.java @@ -190,4 +190,8 @@ private void cleanup() { } } + public IFile getManifestFile() { + return manifestFile; + } + }