Skip to content

Commit

Permalink
Add more bundles for PDE classpath resolving (#2673)
Browse files Browse the repository at this point in the history
The fix #2671 removed a bit more code as needed to fix slow PDE
classpath resolving of the PDE project classpath, resulting in "The
following classes needed for SpotBugs analysis on project XYZ were
missing" warnings reported for few plugin projects.

This is a corner case, but not nice.

After a bit more evaluation, turned out, we do not need to manually
(recursively) resolve plugins classpath looking in each dependent
*workspace* PDE bundle (which caused repetitive classpath resolving and
huge overhead), but we still need to add all libraries from all *bundle
dependencies* (recursive), even if they do not contribute to the project
runtime classpath (OSGI does that behind the scenes, so PDE doesn't
include them by default).

So this patch restores some of that PDE classpath extension logic
removed via #2671 (but still solves original issue reported in #2671).

See #2671
  • Loading branch information
iloveeclipse committed Nov 2, 2023
1 parent 65c8c37 commit 2541a96
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Currently the versioning policy of this project follows [Semantic Versioning v2.
- Fixed false positive RV_EXCEPTION_NOT_THROWN when asserting to exception throws ([[#2628](https://github.com/spotbugs/spotbugs/issues/2628)])
- Fix false positive CT_CONSTRUCTOR_THROW when supertype has final finalize ([[#2665](https://github.com/spotbugs/spotbugs/issues/2665)])
- Lowered the priority of `PA_PUBLIC_MUTABLE_OBJECT_ATTRIBUTE` bug ([[#2652](https://github.com/spotbugs/spotbugs/issues/2652)])
- Eclipse: fixed startup overhead (on computing classpath) for PDE projects ([[#2671](https://github.com/spotbugs/spotbugs/pull/2671)])

### Build
- Fix deprecated GHA on '::set-output' by using GITHUB_OUTPUT ([[#2651](https://github.com/spotbugs/spotbugs/pull/2651)])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@
*/
package de.tobject.findbugs.builder;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.runtime.CoreException;
Expand All @@ -32,6 +36,11 @@
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.launching.JREContainer;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.PluginRegistry;
import org.eclipse.pde.internal.build.site.PDEState;
import org.eclipse.pde.internal.core.ClasspathUtilCore;

import de.tobject.findbugs.FindbugsPlugin;

Expand All @@ -50,10 +59,17 @@ public class PDEClassPathGenerator {
*/
public static String[] computeClassPath(IJavaProject javaProject) {
Collection<String> classPath = Collections.EMPTY_SET;

// try to get default java classpath
classPath = createJavaClasspath(javaProject);

try {
// first try to check and resolve plugin project. It can fail if
// there is no
// PDE plugins installed in the current Eclipse instance (PDE is
// optional)
classPath = createPluginClassPath(javaProject);
} catch (NoClassDefFoundError ce) {
// ok, we do not have PDE installed, now try to get default java
// classpath
classPath = createJavaClasspath(javaProject);
}
return classPath.toArray(new String[classPath.size()]);
}

Expand Down Expand Up @@ -107,4 +123,85 @@ private static boolean isValidPath(IPath path) {
return path != null && path.segmentCount() > 1 && path.toFile().exists();
}

private static Collection<String> createPluginClassPath(IJavaProject javaProject) {
Set<String> javaClassPath = createJavaClasspath(javaProject);
IPluginModelBase model = PluginRegistry.findModel(javaProject.getProject());
if (model == null || model.getPluginBase().getId() == null) {
return javaClassPath;
}
BundleDescription target = model.getBundleDescription();

// target is null if plugin uses non OSGI format
if (target == null) {
return javaClassPath;
}
List<String> pdeClassPath = new ArrayList<>(javaClassPath);
Set<BundleDescription> bundles = new HashSet<>();
addDependentBundles(target, bundles);
for (BundleDescription bd : bundles) {
appendBundleToClasspath(bd, pdeClassPath);
}
return pdeClassPath;
}

private static void appendBundleToClasspath(BundleDescription bd, List<String> pdeClassPath) {
IPluginModelBase model = PluginRegistry.findModel(bd);
if (model == null) {
return;
}
ArrayList<IClasspathEntry> classpathEntries = new ArrayList<>();
ClasspathUtilCore.addLibraries(model, classpathEntries);

for (IClasspathEntry cpe : classpathEntries) {
IPath location = null;
if (cpe.getEntryKind() != IClasspathEntry.CPE_SOURCE) {
location = cpe.getPath();
}
if (location == null) {
continue;
}
String locationStr = location.toOSString();
if (pdeClassPath.contains(locationStr)) {
continue;
}
// extra cleanup for some directories on classpath
String bundleLocation = bd.getLocation();
if (bundleLocation != null && !"jar".equals(location.getFileExtension()) &&
new File(bundleLocation).isDirectory()) {
if (bd.getSymbolicName().equals(location.lastSegment())) {
// ignore badly resolved plugin directories inside workspace
// ("." as classpath is resolved as plugin root directory)
// which is, if under workspace, NOT a part of the classpath
continue;
}
}
if (!location.isAbsolute()) {
location = ResourceUtils.relativeToAbsolute(location);
}
if (!isValidPath(location)) {
continue;
}
locationStr = location.toOSString();
if (!pdeClassPath.contains(locationStr)) {
pdeClassPath.add(locationStr);
}
}
}

private static void addDependentBundles(BundleDescription bd, Set<BundleDescription> bundles) {
// TODO for some reasons, this does not add "native" fragments for the
// platform. See also: ContributedClasspathEntriesEntry, RequiredPluginsClasspathContainer
// BundleDescription[] requires = PDEState.getDependentBundles(target);
BundleDescription[] bundles2 = PDEState.getDependentBundlesWithFragments(bd);
for (BundleDescription bundleDescription : bundles2) {
if (bundleDescription == null) {
continue;
}
if (!bundles.contains(bundleDescription)) {
bundles.add(bundleDescription);
addDependentBundles(bundleDescription, bundles);
}
}
}

}

0 comments on commit 2541a96

Please sign in to comment.