/* * Contributions to FindBugs * Copyright (C) 2008, Andrei Loskutov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package de.tobject.findbugs.builder; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IClasspathContainer; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.internal.launching.JREContainer; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jface.preference.IPreferenceStore; 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; import de.tobject.findbugs.preferences.FindBugsConstants; /** * Helper class to resolve full classpath for Eclipse plugin projects. Plugin * projects are very special for Eclipse and they differ from "usual" java * projects... * * @author Andrei */ public class PDEClassPathGenerator { /** * @param javaProject non null * @return never null (may be empty array) */ public static String[] computeClassPath(IJavaProject javaProject) { String[] classPath = new String[0]; 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); } catch (CoreException e) { FindbugsPlugin.getDefault().logException(e, "Could not compute aux. classpath for project " + javaProject); return classPath; } return classPath; } private static String[] createJavaClasspath(IJavaProject javaProject) { LinkedHashSet<String> classPath = new LinkedHashSet<String>(); try { // doesn't return jre libraries String[] defaultClassPath = JavaRuntime.computeDefaultRuntimeClassPath(javaProject); for (String classpathEntry : defaultClassPath) { IPath path = new Path(classpathEntry); if(isValidPath(path)) { classPath.add(path.toOSString()); } } IPreferenceStore store = FindbugsPlugin.getPluginPreferences(javaProject.getProject()); boolean shortClassPath = store.getBoolean(FindBugsConstants.KEY_SHORT_CLASSPATH); if(shortClassPath){ return classPath.toArray(new String[classPath.size()]); } // add CPE_CONTAINER classpathes IClasspathEntry[] rawClasspath = javaProject.getRawClasspath(); for (IClasspathEntry entry : rawClasspath) { if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { IClasspathContainer classpathContainer = JavaCore.getClasspathContainer(entry.getPath(), javaProject); if (classpathContainer != null && !(classpathContainer instanceof JREContainer)) { IClasspathEntry[] classpathEntries = classpathContainer.getClasspathEntries(); for (IClasspathEntry iClasspathEntry : classpathEntries) { IPath path = iClasspathEntry.getPath(); if (isValidPath(path)) { classPath.add(path.toOSString()); } } } } } } catch (CoreException e) { FindbugsPlugin.getDefault().logException(e, "Could not compute aux. classpath for project " + javaProject); } return classPath.toArray(new String[classPath.size()]); } /** * @param path may be null * @return true if the path is considered as valid for the classpath */ private static boolean isValidPath(IPath path) { // segmentCount() > 1 is workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=281189 // classpath contains unlikely one of root directories like /lib/ "as is" return path != null && path.segmentCount() > 1 && path.toFile().exists(); } private static String[] createPluginClassPath(IJavaProject javaProject) throws CoreException { String[] javaClassPath = createJavaClasspath(javaProject); IPluginModelBase model = PluginRegistry.findModel(javaProject.getProject()); IPreferenceStore store = FindbugsPlugin.getPluginPreferences(javaProject.getProject()); boolean shortClassPath = store.getBoolean(FindBugsConstants.KEY_SHORT_CLASSPATH); if (shortClassPath || model == null || model.getPluginBase().getId() == null) { return javaClassPath; } List<String> pdeClassPath = new ArrayList<String>(); pdeClassPath.addAll(Arrays.asList(javaClassPath)); BundleDescription target = model.getBundleDescription(); Set<BundleDescription> bundles = new HashSet<BundleDescription>(); // target is null if plugin uses non OSGI format if (target != null) { addDependentBundles(target, bundles); } // convert default location (relative to wsp) to absolute path IPath defaultOutputLocation = ResourceUtils.relativeToAbsolute(javaProject.getOutputLocation()); for (BundleDescription bd : bundles) { appendBundleToClasspath(bd, pdeClassPath, defaultOutputLocation); } if(defaultOutputLocation != null) { String defaultOutput = defaultOutputLocation.toOSString(); if(pdeClassPath.indexOf(defaultOutput) > 0) { pdeClassPath.remove(defaultOutput); pdeClassPath.add(0, defaultOutput); } } return pdeClassPath.toArray(new String[pdeClassPath.size()]); } private static void appendBundleToClasspath(BundleDescription bd, List<String> pdeClassPath, IPath defaultOutputLocation) { IPluginModelBase model = PluginRegistry.findModel(bd); if(model == null) { return; } ArrayList<IClasspathEntry> classpathEntries = new ArrayList<IClasspathEntry>(); ClasspathUtilCore.addLibraries(model, classpathEntries); for (IClasspathEntry cpe : classpathEntries) { IPath location; if (cpe.getEntryKind() == IClasspathEntry.CPE_SOURCE) { location = ResourceUtils.getOutputLocation(cpe, defaultOutputLocation); } else { 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); } } } }