Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PDE API Tools Mojo for Tycho #1328

Closed
wants to merge 1 commit into from

Conversation

HannesWell
Copy link
Member

@HannesWell HannesWell commented Sep 13, 2022

The goal of this PR is to provide an API-anysis Mojo as discussed in #1233.

In contrast to my suggestion for an initial implementation of that mojo in the mentioned discussion this implementation goes one step further and uses PDE's IApiAnalyzer directly. Thanks to sisu-osgi-connect I assume it is possible to now use that directly and to run the api-analysis in a operating Equinox instance?
Compared to the current approach used by Eclipse-Platform and suggested in the discussion this has the advantage that there is now Eclipse instance to assemble and launch (or at least it is much cheaper), the project does not have to be recompiled and the baseline artifact is cached in the .m2/repository Maven cache, which makes it instantly available after the first run.

The setup of the API-baseline could still be enhanced, but for now I'm presenting my preliminary current state of work because I'm struggling to launch the connected Equinox/Eclipse properly.

After the Equinox instance is started in the APIAnalyser I get many errors like the following:

Caused by: java.lang.ClassCastException: class org.eclipse.osgi.internal.log.ExtendedLogServiceImpl cannot be cast to class org.eclipse.equinox.log.ExtendedLogService (org.eclipse.osgi.internal.log.ExtendedLogServiceImpl is in unnamed module of loader org.codehaus.plexus.classworlds.realm.ClassRealm @6e948f1c; org.eclipse.equinox.log.ExtendedLogService is in unnamed module of loader org.codehaus.plexus.classworlds.realm.ClassRealm @6aa12a1a)
    at org.eclipse.core.internal.runtime.Activator.getPlatformWriter (Activator.java:99)
    at org.eclipse.core.internal.runtime.Activator.start (Activator.java:80)
    at org.eclipse.osgi.internal.framework.BundleContextImpl$2.run (BundleContextImpl.java:818)
    at org.eclipse.osgi.internal.framework.BundleContextImpl$2.run (BundleContextImpl.java:1)
    at java.security.AccessController.doPrivileged (AccessController.java:569)
    at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator (BundleContextImpl.java:810)
    at org.eclipse.osgi.internal.framework.BundleContextImpl.start (BundleContextImpl.java:767)
    at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0 (EquinoxBundle.java:1032)
    at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker (EquinoxBundle.java:371)
    at org.eclipse.osgi.container.Module.doStart (Module.java:605)
    at org.eclipse.osgi.container.Module.start (Module.java:468)

Subsequently there are also some resolution errors like the following but I assume I just have to update the connect.bundles accordingly.

org.osgi.framework.BundleException: Could not resolve module: org.eclipse.tycho.embedder-api [78]
  Unresolved requirement: Import-Package: org.apache.maven.execution

Furthermore I'm not sure if I use sisu-osgi-connect correctly. My goal is to run IApiAnalyzer.analyzeComponent() and the creation of the IApiBaseline within a running Equinox instance without a workspace, which is supportd by PDE-apitools. I have the impression that it was even the intention to support to run it in non OSGi-environment but it looks like that does not work well.
However the reasons for my doubt regarding correct use is that the Equinox-instance is only launched after I got a service from the EquinoxServiceFactory. Is this just a design quirk or am I doing something fundamentally wrong?

@laeubi can you help me with some advice?

@github-actions
Copy link

github-actions bot commented Sep 13, 2022

Test Results

0 files   - 362  0 suites   - 362   0s ⏱️ - 2h 18m 47s
0 tests  - 337  0 ✔️  - 327  0 💤  - 10  0 ±0 
0 runs   - 674  0 ✔️  - 653  0 💤  - 21  0 ±0 

Results for commit d837101. ± Comparison against base commit b38da64.

♻️ This comment has been updated with latest results.

@laeubi
Copy link
Member

laeubi commented Sep 14, 2022

java.lang.ClassCastException: class org.eclipse.osgi.internal.log.ExtendedLogServiceImpl cannot be cast to class org.eclipse.equinox.log.ExtendedLogService (org.eclipse.osgi.internal.log.ExtendedLogServiceImpl is in unnamed module of loader org.codehaus.plexus.classworlds.realm.ClassRealm @6e948f1c; org.eclipse.equinox.log.ExtendedLogService is in unnamed module of loader org.codehaus.plexus.classworlds.realm.ClassRealm @6aa12a1a)

This is most likely because the classpath of your mojo contains some OSGi API in addition to the equinox one, e.g. BND sadly pulls in some stuff at compile scope, so it needs to be excluded.

org.osgi.framework.BundleException: Could not resolve module: org.eclipse.tycho.embedder-api [78]
Unresolved requirement: Import-Package: org.apache.maven.execution

The question is more: Why do you want org.eclipse.tycho.embedder-api be part of your framework? As it is not part of eclipse you should be able to drop that, please note that this currently is only required in Tycho because of "historic reasons", a much more cleaner approach would be (an that what the tycho-p2-maven plugin does) to select one entry-point service (that is for P2 the IAgent) or if no such service exits/required simply call the classes as-is.

Furthermore I'm not sure if I use sisu-osgi-connect correctly. My goal is to run IApiAnalyzer.analyzeComponent() and the creation of the IApiBaseline within a running Equinox instance without a workspace, which is supportd by PDE-apitools.

Actually there is not much special, just keep in mind that you can only get one shared instance! So the question is, how do your normally access the IApiAnalyzer.analyzeComponent() and IApiBaseline, e.g. are there services/factories?

Copy link
Member

@laeubi laeubi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somme comments below

@@ -0,0 +1,92 @@
package org.eclipse.tycho.apitools;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Header is missing


// TODO: or encapsulate this into own class?
@Requirement(hint = "connect")
private EquinoxServiceFactory serviceFactory;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The encapsualtion is only done to share it across mojos in Tycho, I dont think it is required here.

// TODO: See
// org.eclipse.pde.api.tools.model.tests.TestSuiteHelper.createTestingBaseline();

ITargetPlatformService tpService = serviceFactory.getService(ITargetPlatformService.class);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be aware that fetching a service can return null, so if you require the service, its better to use Object.requireNonNull(..., "Some usefull message"); this will simplify debugging a lot.

getLog().info("Skipped");
return;
}
if (new File(project.getBasedir(), JarFile.MANIFEST_NAME).isFile()) { // TODO: consider non PDE bundles
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a more general approach would be to take a look at the project artifact and check if:

  1. it is a jar
  2. it is an OSGi Bundle

}

@SuppressWarnings("removal")
public Set<File> collectProjectDependencyPaths() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I think it might be even better to actually inspect the compile path of the maven project here but we could investigate this later on...

@laeubi
Copy link
Member

laeubi commented Sep 14, 2022

@HannesWell I think the quirks you see here are because you use Tycho (core) APIs here, I think this should not be required (as it is not requires as of today):

  1. Dependencies should be fetched from the maven project instead of the (Tycho) P2 data
  2. Targetplatform should be created by however it is created in ApiAnalysisPlatform today

@laeubi
Copy link
Member

laeubi commented Sep 14, 2022

Thanks to sisu-osgi-connect I assume it is possible to now use that directly and to run the api-analysis in a operating Equinox instance?

Yes I really like the idea and this would be a good case to polish the sisu-osgi-connect that might still have some issues or improvements 👍

@laeubi
Copy link
Member

laeubi commented Sep 15, 2022

@HannesWell do you plan to work on this further soon so we can include it in the 3.0 release end of month? Or should we mark this for 3.1?

@HannesWell
Copy link
Member Author

Thank you Christoph for your help, I'm about to try out your hints and I'm glad you like the idea.
In general sisu-osgi-connect is a really a great achievement from you.

@HannesWell do you plan to work on this further soon so we can include it in the 3.0 release end of month? Or should we mark this for 3.1?

Yes I would like to have it in the 3.0 release (if it is working until then), this is currently my number one priority work for Eclipse.

@HannesWell HannesWell added this to the 3.0 milestone Sep 15, 2022
@laeubi
Copy link
Member

laeubi commented Sep 18, 2022

Just a gentle reminder: I think we should create the 3.0.x branch and switch the master to 3.1.0-SNAPSHOT at 21.09.2022 what would mean no new features (just bugfixes), so if you want your changes to be included it would be good to have them until 20.09.2022 EOD.

@HannesWell
Copy link
Member Author

Just a gentle reminder: I think we should create the 3.0.x branch and switch the master to 3.1.0-SNAPSHOT at 21.09.2022 what would mean no new features (just bugfixes), so if you want your changes to be included it would be good to have them until 20.09.2022 EOD.

I'm not sure I'll be able to complete this until then, probably not.

I investigated the ClassCastException in more detail in the last days and it looks like they are not caused by some dependencies being pulled in but due to imports of plugin ClassRealms.
In detail the problem is that the ClassRealm of the apitools-plugin imports the ClassRealm of the root project of the current build ( ClassRealm[project>org.eclipse.m2e:m2e-core:2.0.0-SNAPSHOT, parent: ClassRealm[maven.api, parent: null]], I'm testing this with the m2e build), which in turn imports the ClassRealm[extension>org.eclipse.tycho:tycho-maven-plugin...

This has the effect that all OSGi-framework instances are created by the same classloader and (probably) because osgi.parentClassloader=fwk is used, all classes in bundles that are also installed in the first framework launched (which is the one of the p2-maven-plugin) are loaded from that classloader.
This causes errors in classes that are designed to be singletons like RegistryProviderFactory.
At the same time it can obviously happen that some classes are loaded with a different classloader (probably the context realm), which maybe leads to the ClassCastExceptions reported initially. However I was able to fix that by reverting the order in which the realms are installed.

To tackel the shared classloader issue I tried different approaches, like wrapping the ClassRealm to ignore imported realms but that did not succeed when it comes to classes like RegistryProviderFactory (probably because the original realm is used to load that which considers the imports).

I would see two other options:

  • Use a completely isolated class-loader with new URLClassLoader(realm.getURLs()) to load the framework instance.
  • use osgi.parentClassloader=boot

But as far as I understand it this will lead to ClassCastExceptions when exchanging objects between the OSGi-framework and the mojo because different classloaders are used and the classes are not the same.
Currently I have no good idea how to solve that easily and don't know if there is a good solution at all?
In general those problems seem to be rather complex.

@laeubi
Copy link
Member

laeubi commented Sep 18, 2022

As said above, you should not import any Tycho specific parts, this will simply not work for this use case and is not necessary. The maven class realms are very hard to manage so one should try to keep them as much separated as possible.

@laeubi
Copy link
Member

laeubi commented Sep 18, 2022

@HannesWell if there are any changes required for the sisu part please open a separate PR so we can merge these as a perquisite. Beside that, please note there is already a tycho-apitools-plugin where this new mojo should be placed instead of tycho-extras.

And then it would be good to have a minimal example in the integration tests (see for example https://github.com/eclipse-tycho/tycho/blob/master/tycho-its/src/test/java/org/eclipse/tycho/test/apitools/ApiToolsTest.java), such an integration test must not really do much useful stuff, but at least show how to configure / run the mojo, I might then also be able to help debugging class-loading problems.

@HannesWell
Copy link
Member Author

@HannesWell I think the quirks you see here are because you use Tycho (core) APIs here, I think this should not be required (as it is not requires as of today):

1. Dependencies should be fetched from the maven project instead of the (Tycho) P2 data

2. Targetplatform should be created by however it is created in ApiAnalysisPlatform today

Can you tell/point to some code, how that should be done better? The current implementation is basically a stripped version from CompareWithBaselineMojo. But as noted in the added comment, I think we should have an AbstractBaselineMojo or some Tycho-Service to fetch the baseline version of a project from a List of baseline repos that is used by the CompareWithBaselineMojo, P2MetadataMojo and the new APIAnalysisMojo.

While looking up the latter, I noticed the BaselineService.Do you mean that?
In general I would like to use Tycho's dependency caching in the local Maven-Cache of a user, to prevent the baseline version from being downloaded repeatedly if not cached in a project/build specific folder.

As said above, you should not import any Tycho specific parts, this will simply not work for this use case and is not necessary. The maven class realms are very hard to manage so one should try to keep them as much separated as possible.

I tried it with even with only basic maven dependency and pde.core without any transitive dependencies (the current state), but even then each Equinox-instance is created with the same classloader of extension>org.eclipse.tycho:tycho-maven-plugin:3.0.0-SNAPSHOT (parent = null). Thus all classes that are reachable from this classloader are always loaded with that one making it impossible to have 'singletons' per framework like RegistryProviderFactory.

@HannesWell if there are any changes required for the sisu part please open a separate PR so we can merge these as a perquisite. Beside that, please note there is already a tycho-apitools-plugin where this new mojo should be placed instead of tycho-extras.

Yes I already had the intention do create a dedicated issue, that's why I made these changes in a separate commit (some clean-ups and actual changes separated). I first wanted to make this API-analysis story work in general, but I think I will do the separate PR earlier.

The new mojo is already in the tycho-apitools-plugin. I just refactored the ListDependenciesMojo to re-use some code. But I can also just copy past that. IMHO with this change that plug-in becomes obsolet anyway. Or are there other use-cases?

And then it would be good to have a minimal example in the integration tests (see for example https://github.com/eclipse-tycho/tycho/blob/master/tycho-its/src/test/java/org/eclipse/tycho/test/apitools/ApiToolsTest.java), such an integration test must not really do much useful stuff, but at least show how to configure / run the mojo, I might then also be able to help debugging class-loading problems.

Yes, I had the intention to create that. But I was lazy until now and just used a correspondingly modified M2E build for that.
But I will setup the integration test to also demonstrate my classloader problems better, because I can't get any further in that regard and I'm running out of ideas. Not even some dirty hacks worked. I'm even considering to try to run the API-analysis in plain-java without OSGi running.

@HannesWell
Copy link
Member Author

After investigating the 'plain'-Java mode of PDE apitools a bit deeper and figuring out why my previous test failed I went back to that approach and now plan to continue without OSGi. And yes it looks promising. The little test case I added already worked. I also tried the M2E build but that failed, but I haven't yet investigated further.
Besides fixing those some more things have to be worked out (.apifilter consideration, problem analysis) and polished, but at least I was able to create one working case.
But I won't be able to complete that until EOD today, so I'll postpone this to 3.1 so that we also have some time to test this as a snapshot.

One reason I would like to have that in 3.0 is that with this mojo the tycho-dependency-tools-plugin becomes obsolet and we could have removed that then for Tycho 3. But the time is not sufficient to test and migrate the Eclipse Platform build so we have to postpone that to Tycho 4.

@HannesWell HannesWell modified the milestones: 3.0, 3.1 Sep 19, 2022
@laeubi
Copy link
Member

laeubi commented Sep 20, 2022

If you like to suggest any mojo removal that should have a separate issue, assign the issue to 4.x Milestone and as a first step create a PR to deprecate the mojo so users can get aware of it.

@mickaelistria
Copy link
Contributor

FWIW, I think we can deprecate mojo when we have a good alternative, make it warn with a replacement, keep it for 3-4 releases and remove it after that. No need to a major release to remove such non-core mojos IMO.

@laeubi
Copy link
Member

laeubi commented Sep 29, 2022

This causes errors in classes that are designed to be singletons like RegistryProviderFactory.

The error is actually caused by the cleanup of the connect framework cleaning up one important method see:

@HannesWell
Copy link
Member Author

This causes errors in classes that are designed to be singletons like RegistryProviderFactory.

The error is actually caused by the cleanup of the connect framework cleaning up one important method see:

* [Backport fix for missing method #1434](https://github.com/eclipse-tycho/tycho/pull/1434)

🤦🏽‍♂️ Looks like I shot myself in the foot with that clean up. Sorry for that, I hope it didn't cause to much extra work. At least I then wasted half my of a weekend to circumvent that problem. Thanks for the fix.
Nevertheless I will first continue with the non OSGi approach because until now it looks promising and my guess is that it will run faster without a OSGi-framework if it works. But I have the intention to test both as a follow up.

@laeubi
Copy link
Member

laeubi commented Sep 29, 2022

No problem, I have no idea why I didn't noticed that at the review... anyways I played around a bit and further proceed with getting things out of OSGi into maven world. While this works quite good it now was required to export org.eclipse.core.runtime from the Tycho core realm because otherwise there are classloader conflicts.

This revealed, the the API tools seem so do some Platform.IsRunning() (which now return true because it is inside the split package) and then it tries to access the ResourcesPlugin. While I was able to enable the Resourceplugin, this then further load a lot of other stuff.

Because of this I have to disabled the APIFileGenerator again, and I fear we will run into similar issues as well later on. So I can think of two things: We try to fire up a connect framework as you initially tried, or we need some flag in PD that is used to disable the stuff not on Platform.IsRunning() but some kind of "non-osgi-mode" ... this seems the revenge of the singleton here :-\

@laeubi laeubi modified the milestones: 3.1, 4.0 Oct 19, 2022
@laeubi
Copy link
Member

laeubi commented Oct 19, 2022

@HannesWell as we have a new major release we can "break" things, so if you like to consider that for your change just add a note to the migration guide.

@laeubi laeubi changed the title Add API-analysis Mojo for Tycho Add PDE API Tools Mojo for Tycho Jan 25, 2023
@laeubi
Copy link
Member

laeubi commented Mar 15, 2023

@HannesWell can you probably make the integration test an own PR I'd like to experiment with a new way for mojos to fire up an OSGi framework and this seems perfectly suited, so as a first step we might can get something similar to just a 1:1 replace ant by a maven-mojo call and build on it later on to improve things!?

@HannesWell
Copy link
Member Author

Yes I can do that, but I first have to check that the test actually makes sense.

For now I rebased the current state of the PR an resolved all conflicts respectively reworked what has changed. In general this seems to work, but I had the impression that there are some flaws in the PDE/Equinox-Resolver state, because for the attached test case I get API problems reports like:

API problem: 
	severity: WARNING
	category: API_COMPONENT_RESOLUTION
	element kind: RESOURCE
	kind: API_COMPONENT_RESOLUTION
	flags: NO_FLAGS
	message id: 99
	message: API analysis aborted: api-bundle-2 has unresolved constraints: JavaSE 0.0.0

And JavaSE 0.0.0 is strange. I remberer there where issues in this area when I worked last on it in the end of last year, but I have to check it again.

@laeubi
Copy link
Member

laeubi commented May 18, 2023

@HannesWell is this still relevant? If yes can you rebase/resolve conflicts?

@laeubi laeubi removed this from the 4.0 milestone May 31, 2023
@laeubi
Copy link
Member

laeubi commented Sep 23, 2023

@HannesWell this is now > 1 year old and currently has some conflicts, should we close this for now and probably have a fresh start once this becomes relevant again.

@laeubi laeubi closed this Sep 23, 2023
@HannesWell
Copy link
Member Author

HannesWell commented Sep 23, 2023

@HannesWell this is now > 1 year old and currently has some conflicts, should we close this for now and probably have a fresh start once this becomes relevant again.

Frankly I don't understand how it helps to close this now and start again in another PR, as long as there is a chance that it is completed on day? Some tasks take longer to complete, may it be because of the task itself or because other more important things come up in the meantime, but that doesn't mean they are obsolete.
At least for me the chances that it never completes are just decreased if the PR is closed, because then I don't have a reminder to work on it.
Starting again in another PR makes it harder to follow the previews discussion and conflicts have to be resolved anyways.

@laeubi
Copy link
Member

laeubi commented Sep 23, 2023

You can also reopen this PR but if it is in conflicting state for so long time we can't even simply rebase the PR with current master, so probably this is a sign that the change is (no longer) important.

@HannesWell
Copy link
Member Author

You can also reopen this PR but if it is in conflicting state for so long time we can't even simply rebase the PR with current master, so probably this is a sign that the change is (no longer) important.

I would say not as important as other things, but not obsolete.
But I will re-open it when I continue to work on this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants