From 43df91757f158fdfda66f48a10ffa6624249e353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Sun, 12 Nov 2023 19:32:15 +0100 Subject: [PATCH] Add support for specify test requirements of a bundle Currently there is only limited support for "test only" dependencies of a bundle, the usual way is to add an (optional) import/require bundle to the test but this is not very suitable when one wants to have tests in the bundle itself, also some requirements can't be expressed as a package/bundle without binding to a particular implementation and even if, these are still added as regular dependencies and not with test scope. This ads a way to specify a list of 'test.requirements' in the build.properties to account for this. --- ui/org.eclipse.pde.core/META-INF/MANIFEST.MF | 7 +- .../eclipse/pde/core/build/IBuildEntry.java | 22 ++- .../internal/core/PDEClasspathContainer.java | 41 ++-- .../pde/internal/core/PDECoreMessages.java | 1 + .../RequiredPluginsClasspathContainer.java | 186 +++++++++++------- .../core/bnd/PDEPluginsContainer.java | 11 ++ .../core/builders/BuildErrorReporter.java | 35 ++++ .../pde/internal/core/pderesources.properties | 1 + .../sourcelookup/PDESourceLookupDirector.java | 2 +- 9 files changed, 215 insertions(+), 91 deletions(-) create mode 100644 ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bnd/PDEPluginsContainer.java diff --git a/ui/org.eclipse.pde.core/META-INF/MANIFEST.MF b/ui/org.eclipse.pde.core/META-INF/MANIFEST.MF index 58917c063b..9d51e2d534 100644 --- a/ui/org.eclipse.pde.core/META-INF/MANIFEST.MF +++ b/ui/org.eclipse.pde.core/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %name Bundle-SymbolicName: org.eclipse.pde.core; singleton:=true -Bundle-Version: 3.17.200.qualifier +Bundle-Version: 3.18.0.qualifier Bundle-Activator: org.eclipse.pde.internal.core.PDECore Bundle-Vendor: %provider-name Bundle-Localization: plugin @@ -89,11 +89,14 @@ Import-Package: aQute.bnd.build;version="[4.4.0,5.0.0)", aQute.bnd.service;version="[4.7.0,5.0.0)", aQute.bnd.version;version="[2.2.0,3.0.0)", aQute.service.reporter;version="[1.2.0,2.0.0)", + biz.aQute.resolve;version="[9.0.0,10.0.0)", org.eclipse.equinox.internal.p2.publisher.eclipse, org.eclipse.equinox.p2.publisher, org.eclipse.equinox.p2.publisher.eclipse, org.osgi.service.repository;version="[1.1.0,2.0.0)", - org.osgi.util.promise;version="[1.3.0,2.0.0)" + org.osgi.util.promise;version="[1.3.0,2.0.0)", + org.osgi.service.resolver, + org.osgi.resource Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.29.0,4.0.0)", org.eclipse.e4.core.contexts;bundle-version="[1.12.300,2.0.0)", diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/build/IBuildEntry.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/build/IBuildEntry.java index 0d7da2ef76..935373c1f1 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/build/IBuildEntry.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/build/IBuildEntry.java @@ -27,39 +27,47 @@ public interface IBuildEntry extends IWritable { /** * A property name for changes to the 'name' field. */ - public static final String P_NAME = "name"; //$NON-NLS-1$ + String P_NAME = "name"; //$NON-NLS-1$ /** * The prefix for any key denoting the source folders that * should be compiled into a JAR. The suffix will * be the name of the JAR. */ - public static final String JAR_PREFIX = "source."; //$NON-NLS-1$ + String JAR_PREFIX = "source."; //$NON-NLS-1$ /** * The prefix for any key denoting output folders for a particular * JAR. The suffix will be the name of the JAR. */ - public static final String OUTPUT_PREFIX = "output."; //$NON-NLS-1$ + String OUTPUT_PREFIX = "output."; //$NON-NLS-1$ /** * The name of the key that lists all the folders and files * to be included in the binary build. */ - public static final String BIN_INCLUDES = "bin.includes"; //$NON-NLS-1$ + String BIN_INCLUDES = "bin.includes"; //$NON-NLS-1$ /** * The name of the key that lists all the folders and files * to be included in the source build. */ - public static final String SRC_INCLUDES = "src.includes"; //$NON-NLS-1$ + String SRC_INCLUDES = "src.includes"; //$NON-NLS-1$ /** * The name of the key that declares extra library entries to be added * to the class path at build time only.. */ - public static final String JARS_EXTRA_CLASSPATH = "jars.extra.classpath"; //$NON-NLS-1$ + String JARS_EXTRA_CLASSPATH = "jars.extra.classpath"; //$NON-NLS-1$ /** * The name of the key that declares additional plug-in dependencies to augment development classpath * * @since 3.2 */ - public static final String SECONDARY_DEPENDENCIES = "additional.bundles"; //$NON-NLS-1$ + String SECONDARY_DEPENDENCIES = "additional.bundles"; //$NON-NLS-1$ + + /** + * The name of the key that declares additional plug-in requirements for + * tests + * + * @since 3.18 + */ + String TEST_REQUIREMENTS = "test.requirements"; //$NON-NLS-1$ /** * Adds the token to the list of token for this entry. diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEClasspathContainer.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEClasspathContainer.java index 9f24538f74..f8fa545f3e 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEClasspathContainer.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEClasspathContainer.java @@ -49,10 +49,11 @@ public String toString() { private static final IAccessRule EXCLUDE_ALL_RULE = JavaCore.newAccessRule(IPath.fromOSString("**/*"), IAccessRule.K_NON_ACCESSIBLE | IAccessRule.IGNORE_IF_BETTER); //$NON-NLS-1$ protected void addProjectEntry(IProject project, List rules, boolean exportsExternalAnnotations, - List entries) throws CoreException { + List entries, boolean test) throws CoreException { if (project.hasNature(JavaCore.NATURE_ID)) { IAccessRule[] accessRules = rules != null ? getAccessRules(rules) : null; - IClasspathAttribute[] extraAttribs = getClasspathAttributesForProject(project, exportsExternalAnnotations); + IClasspathAttribute[] extraAttribs = getClasspathAttributesForProject(project, exportsExternalAnnotations, + test); IClasspathEntry entry = JavaCore.newProjectEntry(project.getFullPath(), accessRules, true, extraAttribs, false); if (!entries.contains(entry)) { entries.add(entry); @@ -60,36 +61,42 @@ protected void addProjectEntry(IProject project, List rules, boolean expor } } - private IClasspathAttribute[] getClasspathAttributesForProject(IProject project, boolean exportsExternalAnnotations) - throws JavaModelException { + private IClasspathAttribute[] getClasspathAttributesForProject(IProject project, boolean exportsExternalAnnotations, + boolean test) throws JavaModelException { + List attributes = new ArrayList<>(); + if (test) { + attributes.add(newClasspathAttribute(IClasspathAttribute.TEST, "true")); //$NON-NLS-1$ + } if (exportsExternalAnnotations) { String annotationPath = JavaCore.create(project).getOutputLocation().toString(); - return new IClasspathAttribute[] { - JavaCore.newClasspathAttribute(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, annotationPath) }; + attributes + .add(JavaCore.newClasspathAttribute(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, annotationPath)); } - return new IClasspathAttribute[0]; + return attributes.toArray(IClasspathAttribute[]::new); } - public static IClasspathEntry[] getExternalEntries(IPluginModelBase model) { + public static IClasspathEntry[] getExternalEntries(IPluginModelBase model, boolean test) { List entries = new ArrayList<>(); - addExternalPlugin(model, List.of(), entries); + addExternalPlugin(model, List.of(), entries, test); return entries.toArray(new IClasspathEntry[entries.size()]); } - protected static void addExternalPlugin(IPluginModelBase model, List rules, List entries) { + protected static void addExternalPlugin(IPluginModelBase model, List rules, List entries, + boolean test) { boolean isJarShape = new File(model.getInstallLocation()).isFile(); if (isJarShape) { IPath srcPath = ClasspathUtilCore.getSourceAnnotation(model, ".", isJarShape); //$NON-NLS-1$ if (srcPath == null) { srcPath = IPath.fromOSString(model.getInstallLocation()); } - addLibraryEntry(IPath.fromOSString(model.getInstallLocation()), srcPath, rules, getClasspathAttributes(model), entries); + addLibraryEntry(IPath.fromOSString(model.getInstallLocation()), srcPath, rules, + getClasspathAttributes(model, test), entries); // If the jarred plugin contains any jarred libraries they must be extracted as the compiler can't handle nested jar files File[] extractedLibraries = PDECore.getDefault().getModelManager().getExternalModelManager().getExtractedLibraries(model); for (File libraryFile : extractedLibraries) { IPath path = IPath.fromOSString(libraryFile.getAbsolutePath()); - addLibraryEntry(path, path, rules, getClasspathAttributes(model), entries); + addLibraryEntry(path, path, rules, getClasspathAttributes(model, test), entries); } } else { IPluginLibrary[] libraries = model.getPluginBase().getLibraries(); @@ -99,7 +106,8 @@ protected static void addExternalPlugin(IPluginModelBase model, List rules if (srcPath == null) { srcPath = IPath.fromOSString(model.getInstallLocation()); } - addLibraryEntry(IPath.fromOSString(model.getInstallLocation()), srcPath, rules, getClasspathAttributes(model), entries); + addLibraryEntry(IPath.fromOSString(model.getInstallLocation()), srcPath, rules, + getClasspathAttributes(model, test), entries); } else { for (IPluginLibrary library : libraries) { if (IPluginLibrary.RESOURCE.equals(library.getType())) { @@ -117,7 +125,7 @@ protected static void addExternalPlugin(IPluginModelBase model, List rules } if (path != null) { addLibraryEntry(path, ClasspathUtilCore.getSourceAnnotation(model, expandedName, isJarShape), - rules, getClasspathAttributes(model), entries); + rules, getClasspathAttributes(model, test), entries); } } } @@ -150,7 +158,7 @@ protected static IAccessRule[] getAccessRules(List rules) { return accessRules; } - private static IClasspathAttribute[] getClasspathAttributes(IPluginModelBase model) { + private static IClasspathAttribute[] getClasspathAttributes(IPluginModelBase model, boolean test) { List attributes = new ArrayList<>(); JavadocLocationManager manager = PDECore.getDefault().getJavadocLocationManager(); String location = manager.getJavadocLocation(model); @@ -161,6 +169,9 @@ private static IClasspathAttribute[] getClasspathAttributes(IPluginModelBase mod String installLocation = model.getInstallLocation(); attributes.add(newClasspathAttribute(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, installLocation)); } + if (test) { + attributes.add(newClasspathAttribute(IClasspathAttribute.TEST, "true")); //$NON-NLS-1$ + } return attributes.toArray(IClasspathAttribute[]::new); } diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDECoreMessages.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDECoreMessages.java index d9a04792ca..f7df550331 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDECoreMessages.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDECoreMessages.java @@ -23,6 +23,7 @@ public class PDECoreMessages extends NLS { public static String BaseExportTask_pdeExport; public static String BuildErrorReporter_cannotFindBundle; + public static String BuildErrorReporter_cannotRequirement; public static String BuildErrorReporter_cannotFindJar; diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/RequiredPluginsClasspathContainer.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/RequiredPluginsClasspathContainer.java index ccce3be142..cdb06d4fea 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/RequiredPluginsClasspathContainer.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/RequiredPluginsClasspathContainer.java @@ -49,6 +49,7 @@ import org.eclipse.osgi.service.resolver.HostSpecification; import org.eclipse.osgi.service.resolver.ImportPackageSpecification; import org.eclipse.osgi.service.resolver.StateHelper; +import org.eclipse.osgi.service.resolver.VersionRange; import org.eclipse.pde.core.IClasspathContributor; import org.eclipse.pde.core.build.IBuild; import org.eclipse.pde.core.build.IBuildEntry; @@ -56,10 +57,19 @@ import org.eclipse.pde.core.plugin.PluginRegistry; import org.eclipse.pde.internal.build.IBuildPropertiesConstants; import org.eclipse.pde.internal.core.bnd.BndProjectManager; +import org.eclipse.pde.internal.core.bnd.TargetRepository; import org.eclipse.pde.internal.core.ibundle.IBundlePluginModelBase; +import org.osgi.resource.Capability; +import org.osgi.resource.Requirement; +import org.osgi.service.repository.Repository; import aQute.bnd.build.Container; import aQute.bnd.build.Project; +import aQute.bnd.header.Parameters; +import aQute.bnd.osgi.resource.CapReqBuilder; +import aQute.bnd.osgi.resource.ResourceUtils; +import aQute.bnd.osgi.resource.ResourceUtils.BundleCap; +import aQute.bnd.version.Version; public class RequiredPluginsClasspathContainer extends PDEClasspathContainer implements IClasspathContainer { @@ -81,6 +91,7 @@ public class RequiredPluginsClasspathContainer extends PDEClasspathContainer imp /** * Cached list of {@link IClasspathContributor} from plug-in extensions + * * @see #getClasspathContributors() */ private static List fClasspathContributors = null; @@ -119,7 +130,7 @@ public String getDescription() { public IClasspathEntry[] getClasspathEntries() { if (fEntries == null) { if (fModel == null) { - fEntries = computePluginEntriesByProject(); + fEntries = new IClasspathEntry[0]; } else { fEntries = computePluginEntriesByModel().toArray(IClasspathEntry[]::new); } @@ -133,31 +144,6 @@ public IClasspathEntry[] getClasspathEntries() { return fEntries; } - private IClasspathEntry[] computePluginEntriesByProject() { - try { - Optional bndProject = BndProjectManager.getBndProject(project); - if (bndProject.isPresent()) { - try (Project bnd = bndProject.get()) { - IClasspathEntry[] entries = BndProjectManager - .getClasspathEntries(bnd, project.getWorkspace().getRoot()).toArray(IClasspathEntry[]::new); - for (String err : bnd.getErrors()) { - System.out.println("ERR: " + err); //$NON-NLS-1$ - } - for (String warn : bnd.getWarnings()) { - System.out.println("WARN: " + warn); //$NON-NLS-1$ - } - return entries; - } - } - } catch (Exception e) { - PDECore.getDefault().getLog().error("Can't compute classpath!", e); //$NON-NLS-1$ - } - if (PDECore.DEBUG_CLASSPATH) { - System.out.println("********Returned an empty container"); //$NON-NLS-1$ - } - return new IClasspathEntry[0]; - } - private List computePluginEntriesByModel() { List entries = new ArrayList<>(); try { @@ -168,7 +154,8 @@ private List computePluginEntriesByModel() { Map> map = retrieveVisiblePackagesFromState(desc); - // Add any library entries contributed via classpath contributor extension (Bug 363733) + // Add any library entries contributed via classpath contributor + // extension (Bug 363733) for (IClasspathContributor cc : getClasspathContributors()) { List classpathEntries = cc.getInitialEntries(desc); if (classpathEntries != null && !classpathEntries.isEmpty()) { @@ -183,12 +170,12 @@ private List computePluginEntriesByModel() { HostSpecification host = desc.getHost(); if (host != null) { - addHostPlugin(host, added, map, entries); + addHostPlugin(host, added, map, entries, false); } else if ("true".equals(System.getProperty("pde.allowCycles"))) { //$NON-NLS-1$ //$NON-NLS-2$ BundleDescription[] fragments = desc.getFragments(); for (BundleDescription fragment : fragments) { if (fragment.isResolved()) { - addPlugin(fragment, false, map, entries); + addPlugin(fragment, false, map, entries, false); } } } @@ -196,7 +183,7 @@ private List computePluginEntriesByModel() { // add dependencies BundleSpecification[] required = desc.getRequiredBundles(); for (BundleSpecification element : required) { - addDependency((BundleDescription) element.getSupplier(), added, map, entries); + addDependency((BundleDescription) element.getSupplier(), added, map, entries, false); } if (fBuild == null) { @@ -204,6 +191,7 @@ private List computePluginEntriesByModel() { } if (fBuild != null) { addSecondaryDependencies(desc, added, entries); + addTestDependencies(desc, added, entries); } addBndClasspath(desc, added, entries); @@ -216,7 +204,7 @@ private List computePluginEntriesByModel() { for (BundleDescription bundle : sortedMap.values()) { IPluginModelBase model = PluginRegistry.findModel(bundle); if (model != null && model.isEnabled()) { - addDependencyViaImportPackage(model.getBundleDescription(), added, map, entries); + addDependencyViaImportPackage(model.getBundleDescription(), added, map, entries, false); } } @@ -231,15 +219,46 @@ private List computePluginEntriesByModel() { return entries; } + private void addTestDependencies(BundleDescription desc, Set added, + List entries) throws CoreException { + IBuildEntry entry = fBuild.getEntry(IBuildEntry.TEST_REQUIREMENTS); + if (entry != null) { + List requirements = Arrays.stream(entry.getTokens()).flatMap(token -> { + Parameters parameters = new Parameters(token); + return CapReqBuilder.getRequirementsFrom(parameters).stream(); + }).toList(); + if (PDECore.DEBUG_CLASSPATH) { + for (Requirement requirement : requirements) { + System.out.println("Requires: " + requirement); //$NON-NLS-1$ + } + } + Repository repository = TargetRepository.getTargetRepository(); + Map> providers = repository.findProviders(requirements); + List testBundles = providers.values().stream().flatMap(Collection::stream) + .map(cap -> cap.getResource()).distinct().map(res -> ResourceUtils.getBundleCapability(res)) + .filter(Objects::nonNull).toList(); + for (BundleCap bundle : testBundles) { + if (PDECore.DEBUG_CLASSPATH) { + System.out.println( + "Providing bundles: " + bundle.osgi_wiring_bundle() + " @ " + bundle.bundle_version()); //$NON-NLS-1$ //$NON-NLS-2$ + } + addExtraModel(desc, added, entries, bundle.osgi_wiring_bundle(), + toVersionRange(bundle.bundle_version()), true); + } + } + } + private void addBndClasspath(BundleDescription desc, Set added, List entries) { try { Optional bndProject = BndProjectManager.getBndProject(project); if (bndProject.isPresent()) { for (Container container : bndProject.get().getBuildpath()) { - addExtraModel(desc, added, entries, container.getBundleSymbolicName()); + addExtraModel(desc, added, entries, container.getBundleSymbolicName(), + toVersionRange(container.getVersion()), false); } for (Container container : bndProject.get().getTestpath()) { - addExtraModel(desc, added, entries, container.getBundleSymbolicName()); + addExtraModel(desc, added, entries, container.getBundleSymbolicName(), + toVersionRange(container.getVersion()), true); } } } catch (Exception e) { @@ -247,16 +266,37 @@ private void addBndClasspath(BundleDescription desc, Set adde } } + private VersionRange toVersionRange(Version version) { + if (version != null) { + return toVersionRange(version.toString()); + } + return null; + } + + private VersionRange toVersionRange(String version) { + if (version != null) { + try { + return new VersionRange(String.format("[%s,%s]", version, version)); //$NON-NLS-1$ + } catch (IllegalArgumentException iea) { + // can'T use that version range... + } + } + return null; + } + /** * Return the list of {@link IClasspathContributor}s provided by the - * org.eclipse.pde.core.pluginClasspathContributors extension point. + * org.eclipse.pde.core.pluginClasspathContributors extension + * point. + * * @return list of classpath contributors from the extension point */ private static synchronized List getClasspathContributors() { if (fClasspathContributors == null) { fClasspathContributors = new ArrayList<>(); IExtensionRegistry registry = Platform.getExtensionRegistry(); - IConfigurationElement[] elements = registry.getConfigurationElementsFor("org.eclipse.pde.core.pluginClasspathContributors"); //$NON-NLS-1$ + IConfigurationElement[] elements = registry + .getConfigurationElementsFor("org.eclipse.pde.core.pluginClasspathContributors"); //$NON-NLS-1$ for (IConfigurationElement element : elements) { try { fClasspathContributors.add((IClasspathContributor) element.createExecutableExtension("class")); //$NON-NLS-1$ @@ -305,30 +345,30 @@ private Rule getRule(StateHelper helper, BundleDescription desc, ExportPackageDe } protected void addDependencyViaImportPackage(BundleDescription desc, Set added, - Map> map, List entries) throws CoreException { + Map> map, List entries, boolean test) throws CoreException { if (desc == null || !added.add(desc)) { return; } - addPlugin(desc, true, map, entries); + addPlugin(desc, true, map, entries, test); if (hasExtensibleAPI(desc) && desc.getContainingState() != null) { BundleDescription[] fragments = desc.getFragments(); for (BundleDescription fragment : fragments) { if (fragment.isResolved()) { - addDependencyViaImportPackage(fragment, added, map, entries); + addDependencyViaImportPackage(fragment, added, map, entries, test); } } } } private void addDependency(BundleDescription desc, Set added, - Map> map, List entries) throws CoreException { - addDependency(desc, added, map, entries, true); + Map> map, List entries, boolean test) throws CoreException { + addDependency(desc, added, map, entries, true, test); } private void addDependency(BundleDescription desc, Set added, - Map> map, List entries, boolean useInclusion) + Map> map, List entries, boolean useInclusion, boolean test) throws CoreException { if (desc == null || !added.add(desc)) { return; @@ -339,23 +379,23 @@ private void addDependency(BundleDescription desc, Set added, // add fragment patches before host for (BundleDescription fragment : fragments) { if (fragment.isResolved() && ClasspathUtilCore.isPatchFragment(fragment)) { - addDependency(fragment, added, map, entries, useInclusion); + addDependency(fragment, added, map, entries, useInclusion, test); } } - addPlugin(desc, useInclusion, map, entries); + addPlugin(desc, useInclusion, map, entries, test); // add fragments that are not patches after the host for (int i = 0; i < fragments.length; i++) { if (fragments[i].isResolved() && !ClasspathUtilCore.isPatchFragment(fragments[i])) { - addDependency(fragments[i], added, map, entries, useInclusion); + addDependency(fragments[i], added, map, entries, useInclusion, test); } } BundleSpecification[] required = desc.getRequiredBundles(); for (BundleSpecification element : required) { if (element.isExported()) { - addDependency((BundleDescription) element.getSupplier(), added, map, entries, useInclusion); + addDependency((BundleDescription) element.getSupplier(), added, map, entries, useInclusion, test); } } @@ -365,14 +405,14 @@ private void addDependency(BundleDescription desc, Set added, for (ImportPackageSpecification importSpec : imports) { BaseDescription supplier = importSpec.getSupplier(); if (supplier instanceof ExportPackageDescription exportPackageDescription) { - addDependencyViaImportPackage(exportPackageDescription.getExporter(), added, map, entries); + addDependencyViaImportPackage(exportPackageDescription.getExporter(), added, map, entries, test); } } } } private boolean addPlugin(BundleDescription desc, boolean useInclusions, Map> map, - List entries) throws CoreException { + List entries, boolean test) throws CoreException { IPluginModelBase model = PluginRegistry.findModel(desc); if (model == null || !model.isEnabled()) { return false; @@ -386,7 +426,8 @@ private boolean addPlugin(BundleDescription desc, boolean useInclusions, Map classpathEntries = cc.getEntriesForDependency(hostBundle, desc); if (classpathEntries == null || classpathEntries.isEmpty()) { @@ -396,9 +437,10 @@ private boolean addPlugin(BundleDescription desc, boolean useInclusions, Map getInclusions(Map> map, IPlugin } private void addHostPlugin(HostSpecification hostSpec, Set added, - Map> map, List entries) throws CoreException { + Map> map, List entries, boolean test) throws CoreException { BaseDescription desc = hostSpec.getSupplier(); if (desc instanceof BundleDescription host) { // add host plug-in - if (added.add(host) && addPlugin(host, false, map, entries)) { + if (added.add(host) && addPlugin(host, false, map, entries, test)) { BundleSpecification[] required = host.getRequiredBundles(); for (BundleSpecification bundleSpec : required) { - addDependency((BundleDescription) bundleSpec.getSupplier(), added, map, entries); + addDependency((BundleDescription) bundleSpec.getSupplier(), added, map, entries, test); } // add Import-Package @@ -434,7 +476,8 @@ private void addHostPlugin(HostSpecification hostSpec, Set ad for (ImportPackageSpecification importSpec : imports) { BaseDescription supplier = importSpec.getSupplier(); if (supplier instanceof ExportPackageDescription exportPackageDescription) { - addDependencyViaImportPackage(exportPackageDescription.getExporter(), added, map, entries); + addDependencyViaImportPackage(exportPackageDescription.getExporter(), added, map, entries, + test); } } } @@ -450,7 +493,8 @@ protected void addExtraClasspathEntries(List entries) { IBuildEntry[] buildEntries = fBuild.getBuildEntries(); for (IBuildEntry entry : buildEntries) { String name = entry.getName(); - if (name.equals(IBuildPropertiesConstants.PROPERTY_JAR_EXTRA_CLASSPATH) || name.startsWith(IBuildPropertiesConstants.PROPERTY_EXTRAPATH_PREFIX)) { + if (name.equals(IBuildPropertiesConstants.PROPERTY_JAR_EXTRA_CLASSPATH) + || name.startsWith(IBuildPropertiesConstants.PROPERTY_EXTRAPATH_PREFIX)) { addExtraClasspathEntries(entries, entry.getTokens()); } } @@ -462,7 +506,8 @@ protected void addExtraClasspathEntries(List entries, String[] if (!path.isAbsolute()) { File file = new File(fModel.getInstallLocation(), path.toString()); if (file.exists()) { - IFile resource = PDECore.getWorkspace().getRoot().getFileForLocation(IPath.fromOSString(file.getAbsolutePath())); + IFile resource = PDECore.getWorkspace().getRoot() + .getFileForLocation(IPath.fromOSString(file.getAbsolutePath())); if (resource != null && resource.getProject().equals(fModel.getUnderlyingResource().getProject())) { addExtraLibrary(resource.getFullPath(), null, entries); continue; @@ -531,7 +576,10 @@ private void addJunit5RuntimeDependencies(Set added, List> rules = Map.of(desc, List.of()); - addPlugin(desc, true, rules, entries); + // TODO actually it IS a test dependency but is often used not so, + // probably check if there is a test-folder in the project? + boolean test = false; + addPlugin(desc, true, rules, entries, test); } } @@ -558,7 +606,7 @@ private void addSecondaryDependencies(BundleDescription desc, Set added, List entries, - String pluginId) throws CoreException { - IPluginModelBase model = PluginRegistry.findModel(pluginId); + String pluginId, VersionRange versionRange, boolean test) throws CoreException { + IPluginModelBase model; + if (versionRange == null) { + model = PluginRegistry.findModel(pluginId); + } else { + model = PluginRegistry.findModel(pluginId, versionRange, null); + } if (model != null) { BundleDescription bundleDesc = model.getBundleDescription(); if (added.contains(bundleDesc)) { @@ -624,7 +677,8 @@ private void addExtraLibrary(IPath path, IPluginModelBase model, List 1) { IPath srcPath = null; if (model != null) { - IPath shortPath = path.removeFirstSegments(path.matchingFirstSegments(IPath.fromOSString(model.getInstallLocation()))); + IPath shortPath = path.removeFirstSegments( + path.matchingFirstSegments(IPath.fromOSString(model.getInstallLocation()))); srcPath = ClasspathUtilCore.getSourceAnnotation(model, shortPath.toString()); } else { String filename = ClasspathUtilCore.getSourceZipName(path.lastSegment()); @@ -641,12 +695,12 @@ private void addExtraLibrary(IPath path, IPluginModelBase model, List getAllProjectDependencies() { IWorkspaceRoot root = PDECore.getWorkspace().getRoot(); diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bnd/PDEPluginsContainer.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bnd/PDEPluginsContainer.java new file mode 100644 index 0000000000..a1b99cc891 --- /dev/null +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bnd/PDEPluginsContainer.java @@ -0,0 +1,11 @@ +package org.eclipse.pde.internal.core.bnd; + +import aQute.bnd.osgi.PluginsContainer; + +public class PDEPluginsContainer extends PluginsContainer { + + public PDEPluginsContainer() { + add(TargetRepository.getTargetRepository()); + } + +} diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/builders/BuildErrorReporter.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/builders/BuildErrorReporter.java index 5be80fd74f..153638d4db 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/builders/BuildErrorReporter.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/builders/BuildErrorReporter.java @@ -22,12 +22,15 @@ import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.xml.parsers.DocumentBuilder; import javax.xml.xpath.XPath; @@ -60,6 +63,7 @@ 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.bnd.TargetRepository; import org.eclipse.pde.internal.core.build.WorkspaceBuildModel; import org.eclipse.pde.internal.core.builders.IncrementalErrorReporter.VirtualMarker; import org.eclipse.pde.internal.core.ibundle.IBundleFragmentModel; @@ -72,10 +76,16 @@ import org.eclipse.pde.internal.core.util.CoreUtility; import org.eclipse.pde.internal.core.util.PatternConstructor; import org.osgi.framework.Constants; +import org.osgi.resource.Capability; +import org.osgi.resource.Requirement; +import org.osgi.service.repository.Repository; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import aQute.bnd.header.Parameters; +import aQute.bnd.osgi.resource.CapReqBuilder; + public class BuildErrorReporter extends ErrorReporter implements IBuildPropertiesConstants { private static final String DEF_SOURCE_ENTRY = PROPERTY_SOURCE_PREFIX + '.'; @@ -227,6 +237,7 @@ private void validateBuild(IBuild build) { IBuildEntry javacTarget = null; IBuildEntry jreCompilationProfile = null; IBuildEntry javaProjectWarnings = null; + IBuildEntry testRequirements = null; ArrayList javacWarnings = new ArrayList<>(); ArrayList javacErrors = new ArrayList<>(); ArrayList sourceEntries = new ArrayList<>(1); @@ -274,6 +285,9 @@ private void validateBuild(IBuild build) { // nothing to validate in custom builds return; } + } else if (name.equals(IBuildEntry.TEST_REQUIREMENTS)) { + + testRequirements = entry; } // non else if statement to catch all names @@ -287,6 +301,9 @@ private void validateBuild(IBuild build) { if (bundleList != null) { validateDependencyManagement(bundleList); } + if (testRequirements != null) { + validateTestRequirements(testRequirements); + } } if (jarsExtra != null) { @@ -324,6 +341,24 @@ private void validateBuild(IBuild build) { validateJavaCompilerSettings(javaProjectWarnings); } + private void validateTestRequirements(IBuildEntry buildEntry) { + Map> requirements = Arrays.stream(buildEntry.getTokens()) + .collect(Collectors.toMap(s -> s, token -> { + Parameters parameters = new Parameters(token); + return CapReqBuilder.getRequirementsFrom(parameters); + })); + Repository repository = TargetRepository.getTargetRepository(); + for (Entry> entry : requirements.entrySet()) { + Map> map = repository.findProviders(entry.getValue()); + if (map.values().stream().flatMap(Collection::stream).count() == 0) { + prepareError(buildEntry.getName(), entry.getKey(), + NLS.bind(PDECoreMessages.BuildErrorReporter_cannotRequirement, entry.getKey()), + PDEMarkerFactory.M_ONLY_CONFIG_SEV, fClasspathSeverity, CompilerFlags.P_UNRESOLVED_IMPORTS, + PDEMarkerFactory.CAT_OTHER); + } + } + } + /** * Given a list of source library entries, returns the list of library names. * diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/pderesources.properties b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/pderesources.properties index 44eb4c4905..33e6f3de3e 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/pderesources.properties +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/pderesources.properties @@ -122,6 +122,7 @@ BuildErrorReporter_srcIncludesSourceFolder=Source folders should not be added to BuildErrorReporter_srcIncludesSourceFolder1=''{0}'' should not be added to src.includes list as it is not required in source build. BuildErrorReporter_cannotFindJar={0} cannot be resolved BuildErrorReporter_cannotFindBundle=Bundle {0} cannot be resolved +BuildErrorReporter_cannotRequirement=Nothing matches requirement {0} in current target platform BuildErrorReporter_CompilercomplianceLevel=Compiler compliance level Builders_Manifest_key_not_found = Key ''{0}'' is not found in localization properties file: {1} Builders_Manifest_useless_file = A plug-in manifest must contain at least one extension or extension point diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/PDESourceLookupDirector.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/PDESourceLookupDirector.java index 68252514a8..c9f9390cdd 100644 --- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/PDESourceLookupDirector.java +++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/sourcelookup/PDESourceLookupDirector.java @@ -161,7 +161,7 @@ List getSourceContainers(String location, String id) throws Co for (IPluginModelBase model : models) { if (isPerfectMatch(model, IPath.fromOSString(location))) { // try all source zips found in the source code locations - IClasspathEntry[] entries = PDEClasspathContainer.getExternalEntries(model); + IClasspathEntry[] entries = PDEClasspathContainer.getExternalEntries(model, false); for (IClasspathEntry entrie : entries) { IRuntimeClasspathEntry rte = convertClasspathEntry(entrie); if (rte != null)