Do not follow generic requirements when computing the classpath#2256
Do not follow generic requirements when computing the classpath#2256laeubi merged 2 commits intoeclipse-pde:masterfrom
Conversation
| TestUtils.waitForJobs("ClasspathResolutionTest.testRequiredPluginsViaCapabilityForFragment", 200, 30_000); | ||
|
|
||
| List<String> requiredPluginContainers = getRequiredPluginContainerEntries(projectConsumer); | ||
| assertThat(requiredPluginContainers).contains("A1_0_0"); |
There was a problem hiding this comment.
This check seems to be causing problems now, but the important check is the one below. Should it simply be removed?
There was a problem hiding this comment.
Strange it runs locally if I execute it alone but fails when running the full test-suite... I will take a look.. maybe simply rename the project would already help.
There was a problem hiding this comment.
Renaming does not change anything... but it reproducible succeeds when executed alone and fails when executed with all tests...
There was a problem hiding this comment.
I found that the "loadTargetPlatform" of the other test somehow broke the test. It then finds the host but the host is not resolved. So it seems that somehow even though tried it does not.
Looking at the test itself it more is about system packages from target platorms so I will no try to extract your testcase in a separate class instead...
There was a problem hiding this comment.
@ptziegler I have now extracted this into "real" projects making it easier to investigate/debug.
The problem is that the setup seem to have some (intermediate) problem and the target state complains:
java.lang.AssertionError:
Expecting empty but was: [Constraints from the fragment conflict with the host: Require-Capability: some.test.capability]
at org.eclipse.pde.core.tests.internal.classpath.ClasspathResolutionTest2.testRequiredPluginsViaCapabilityForFragment(ClasspathResolutionTest2.java:802)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.eclipse.pde.ui.tests.runtime.TestUtils$1.evaluate(TestUtils.java:267)
at org.eclipse.pde.ui.tests.runtime.TestUtils$1.evaluate(TestUtils.java:267)
interestingly one can make it work by making some changes to the EE and then switch back.
This happens because the ordering of resolve is important here... so importing the host first and then the *fragment triggers the error.
I'll check if we can avoid that... to me it seems if one asks for an incremental resolve of a fragment the host must also be part of that set...
HannesWell
left a comment
There was a problem hiding this comment.
This totally makes sense to me and it should prevent problematic cycles (at least those that don't lead to cycle-errors on a workspace project level too).
However I have alternative suggestions for the specific implementation.
| Set<BundleDescription> closure = DependencyManager.findRequirementsClosure(added, | ||
| INCLUDE_OPTIONAL_DEPENDENCIES); | ||
| INCLUDE_OPTIONAL_DEPENDENCIES, EXCLUDE_REQUIRED_CAPABILITY); |
There was a problem hiding this comment.
In fact we already have a facility in PDE that computes what you want to obtain here:
| Set<BundleDescription> closure = DependencyManager.findRequirementsClosure(added, | |
| INCLUDE_OPTIONAL_DEPENDENCIES); | |
| INCLUDE_OPTIONAL_DEPENDENCIES, EXCLUDE_REQUIRED_CAPABILITY); | |
| Set<BundleDescription> closure = BuildDependencyCollector.collectBuildRelevantDependencies(added) |
It's currently an own implementation, but I have just opened a PR that migrates it to use the DependencyManager (after the change was just on my computer for a long time):
In fact you should already be able to use that code now, even without my PR.
Then you don't have to modify DependencyManager at all and introduce a new constant for it.
And then further down below the system bundle does not have to be filtered anymore.
| BundleCapability capability = wire.getCapability(); | ||
| if (excludeGenericRequiredCapability) { | ||
| String namespace = capability.getNamespace(); | ||
| if (!BundleNamespace.BUNDLE_NAMESPACE.equals(namespace) | ||
| && !PackageNamespace.PACKAGE_NAMESPACE.equals(namespace) | ||
| && !HostNamespace.HOST_NAMESPACE.equals(namespace) | ||
| && !ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE.equals(namespace)) { | ||
| // If it is not one of the framework namespaces it is a | ||
| // generic one see https://docs.osgi.org/specification/osgi.core/8.0.0/framework.namespaces.html | ||
| continue; | ||
| } | ||
| } | ||
| BundleRevision provider = capability.getRevision(); |
There was a problem hiding this comment.
For that particular change I have a counter proposal in:
This doesn't require a new constant within the DependencyManager, which is also quite specific to PDE respectively the build time requirement. In general I would like to keep DependencyManager generic in the hope that it could be up-streamed to OSGi in some form at some day.
Plus I'm not sure if it's necessary to follow EE-namespace wires. See also
OSGi uses a generic Provide-Capability/Require-Capability model that allows to declare requirements beyond the classic Import-Package and Require-Bunlde. A common example is a bundle that provides a DS component and needs an extender to actually construct the component and inject the services. Another is a logging API that needs a provider. These are especially needed because in such scenario a provider/consumer do not know each other and should not depend directly (only loosely coupled). Currently such providers are still pulled in even though they are not contribute to the compilation classpath as they are completely invisible at runtime - there is a wiring but no classloader access granted. This now only follows build time requirements to avoid the situation.
OSGi uses a generic Provide-Capability/Require-Capability model that
allows to declare requirements beyond the classic Import-Package and
Require-Bunlde. A common example is a bundle that provides a DS
component and needs an extender to actually construct the component and
inject the services. Another is a logging API that needs a provider.
These are especially needed because in such scenario a provider/consumer
do not know each other and should not depend directly (only loosely
coupled). Currently such providers are still pulled in even though they
are not contribute to the compilation classpath as they are completely
invisible at runtime - there is a wiring but no classloader access
granted.
This now adds a new option to the DependencyManager to exclude generic
requirements from the computation of the dependency closure and the
RequiredPluginsClasspathContainer is using that option to avoid follow
such generic wiring contracts.
Includes the testcase from PR #2254