jmini / ecentral

Script and data around the Eclipse Platform artifacts published on maven central.
https://jmini.github.io/ecentral/
Eclipse Public License 2.0
6 stars 2 forks source link

Dealing with os/architecture for SWT in gradle #21

Closed restlessronin closed 1 year ago

restlessronin commented 2 years ago

Firstly. Thanks for the project. I like the simplicity of the approach, where instead of depending on a gradle plugin, one depends directly on the generated BOM.

I tried using your example for a gradle build using

    implementation platform('fr.jmini.ecentral:eclipse-platform-dependencies:4.22')

but I had problems with SWT.

 > Could not resolve org.eclipse.platform:org.eclipse.swt.${osgi.platform}:3.118.0.

There's a workaround for this (forgot where I found it, so I can't reference the original)

 configurations.all {
    resolutionStrategy {
        dependencySubstitution {
            // The maven property ${osgi.platform} is not handled by Gradle
            // so we replace the dependency, using the osgi platform from the project settings
            substitute module('org.eclipse.platform:org.eclipse.swt.${osgi.platform}') with module("org.eclipse.platform:org.eclipse.swt.cocoa.macosx.aarch64:3.120.0")
        }
    }
}

Unfortunately, this requires plugging in the SWT version, which sort of defeats the purpose of using a platform (I didn't write the original code, so there may be a simple fix I don't know about).

Am I missing something obvious?

jmini commented 2 years ago

@restlessronin thank you for your question here.

Personally I do not have dependencies to UI toolkit such as SWT in my builds.

For gradle, in parallel of working on this project that generates BOM files, I have worked a lot on improving @nedtwigg's gradle plugin com.diffplug.eclipse.mavencentral (that I am sometimes using in my builds as well) where I know that you can do:

buildscript {
    dependencies {
        classpath 'com.diffplug.gradle:goomph:3.37.0'
    }
}
apply plugin: 'com.diffplug.eclipse.mavencentral'

eclipseMavenCentral {
    release '4.24.0', {
        implementation 'org.eclipse.jdt.core'

        // specify this to add the native jars for this platform
        useNativesForRunningPlatform()

        // specify that all transitive dependencies should be from this release
        constrainTransitivesToThisRelease()
    }
}

Project https://github.com/diffplug/goomph/

useNativesForRunningPlatform does what you need.


Now if I understand your problem correctly:

Some dependencies like:

<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.swt</artifactId>
<version>3.120.0</version>

Have in their dependency in the dependencies list:

<dependencies>
   <dependency>
      <groupId>org.eclipse.platform</groupId>
      <artifactId>org.eclipse.swt.${osgi.platform}</artifactId>
      <version>[3.120.0,3.120.0]</version>
   </dependency>
</dependencies>

Source: org.eclipse.swt-3.120.0.pom

And this is not supported by Gradle?


I am not sure to understand what I can do at BOM file level to help you with this case.

restlessronin commented 2 years ago

Apparently

<artifactId>org.eclipse.swt.${osgi.platform}</artifactId>

is a maven'ism that gradle doesn't understand.

But, no worries. I kind of expected there wasn't a simple answer, so I'm not disappointed.

I actually found this repo through nedtwiggs goomph repo. I'll use the com.diffplug.eclipse.mavencentral plugin as you suggest.

jmini commented 2 years ago

About having ${osgi.platform} in the artifactId I think this is a big hack even for maven.

The approach is presented in this video: Eclipse Artifacts on Maven Central | EclipseCon Europe 2018 at 20:56.

Stephan Herrmann the author of this approach knows it.


I think this works in Gradle:

configurations.all {
    resolutionStrategy {
        eachDependency {
            if (requested.name.contains('${osgi.platform}')) {
                // use MacOs X86 as "osgi.platform" when computing the dependencies
                useTarget requested.toString().replace('${osgi.platform}', 'cocoa.macosx.x86_64')
            }
        }
    }
}

Of course cocoa.macosx.x86_64 is hard coded, but should match with the platform you are targeting.

Since I am not a gradle expert, I can't tell you the difference with the dependencySubstitution you proposed, but the advantage is that with this eachDependency is that you do not have to specify the SWT Version.

I was inspired by this issue https://github.com/gradle/gradle/issues/14396 to come up with my solution.


And on twitter I got an other answer.

Adding a line:

withModule<TypesafeConfigGuiceRule>(TypesafeConfigGuiceRule.TYPESAFE_CONFIG_GUICE_MODULE)

But you need to have this TypesafeConfigGuiceRule class, provided by a plugin.

And this is for an other problem, so the code would be different.

restlessronin commented 2 years ago

@jmini, thank you. 🙏🏽

Stephen Herrman's solution that you suggested works for me. It isn't very scalable, but in my (and for many others I suspect) case, SWT is the only module that's involved, so scalability is not an issue.

I'm very far from being a gradle expert, so I didn't look too far into the other solution that you pointed at.

🙏🏽🙏🏽

restlessronin commented 2 years ago

@jmini I just read your blog https://jmini.github.io/blog/2021/2021-01-27_osgi-java-inject.html. I'm having exactly this problem now. Having used your mavencentral derived platform, I can resolve most of the dependencies in my RCP project.

There are now a handful of dependencies that (I guess) are not marked as being transitive dependencies in the meta-data but which prevent the bundles from being loaded by OSGI. I'm guessing, because gradle doesn't show them as being unresolved in the dependencies report.

I went through the error log on the OSGI launch, and found that these were all the missing packages

com.ibm.icu.util
javax.inject
com.ibm.icu.text
javax.servlet
org.osgi.util.function
org.bouncycastle.openpgp
org.bouncycastle.bcpg
org.slf4j.impl (org.slf4j)
org.osgi.service.event

Of these, javax.servlet was marked optional in some (but not all) references.

Do you have any suggestion on how to work with these?

My idea was to create a "missing" platform and insert these dependencies manually as "runtimeOnly" into the classpath. Perhaps even publish a BOM similar to what you've done with the platform? Is there a better way to approach this?

Of course, ideally the Eclipse guys would add the 'Orbit' drops into maven central.

jmini commented 2 years ago

Your problem is that you need the dependencies with proper OSGi metadata (in the MANIFEST.MF file inside the jar).

For some of them (referenced in the transitive dependencies list of the published POM in maven central), the OSGi metadata is not present. Meaning that this is not the exact same jar as the one used by the Eclipse project (coming typically from the Eclipse orbit project).

I think that once approach is to publish the jars somewhere in a Maven Repo and then work with dependency substitutions in Gradle.

I remember that we did it for:

You are right this might be the case for other dependencies you have listed.


That said I am not an OSGi expert, and it seems that nowadays, some people are also patching the jars directly during the build. I got this feedback https://github.com/jmini/jmini.github.io/issues/2#issue-795075766 when I wrote my blog article.

jmini commented 2 years ago

@restlessronin: You might be interested in a tool I just published RepubJar


If you try to start jdt-core version 3.20.0 (published with release 2019-12 - version 4.14) inside an OSGi runtime you will notice that you need icu4j, other it fails with:

Resolution failed. Capabilities satisfying the following requirements could not be found:
    [<<INITIAL>>]
      ⇒ osgi.identity: (osgi.identity=lerimpoc-impl)
          ⇒ [lerimpoc-impl version=1.0.0.SNAPSHOT]
              ⇒ osgi.wiring.package: (osgi.wiring.package=org.eclipse.jdt.core)
                  ⇒ [org.eclipse.jdt.core version=3.20.0.v20191203-2131]
                      ⇒ osgi.wiring.bundle: (&(osgi.wiring.bundle=org.eclipse.text)(bundle-version>=3.6.0)(!(bundle-version>=4.0.0)))
                          ⇒ [org.eclipse.text version=3.10.0.v20191122-2108]
                              ⇒ osgi.wiring.package: (osgi.wiring.package=com.ibm.icu.text)

Note If I am not mistaken, newer version of bundle org.eclipse.text no longer requires icu4j

So you have to start the bundle com.ibm.icu.text as well.

com.ibm.icu:icu4j:64.2 on maven central will not help you, since it does not contains the proper OSGi metadata.

You need to have icu4j jar that was published on Update Site of the 2019-12 release: com.ibm.icu_64.2.0.v20190507-1337.jar (update site https://download.eclipse.org/eclipse/updates/4.14/)

But consuming the jar (and the companion sources jar) directly from their URL in not convenient:

It would be better to have them in a maven repository, so that Maven or Gradle can easily access them.

The RepubJar tool can download the two jars and the pom from Maven Central and republish everything.

restlessronin commented 2 years ago

@jmini thank you. this looks like a really useful tool. It would be even better if it could somehow be run automatically on all missing dependencies.

For context, I was trying to recreate the PDE build and launch for a simple eclipse RCP application in pure Gradle, so that I could use IntelliJ IDEA to run/debug the RCP program, after importing the gradle project.

Gradle does a good job of finding all the transitive dependencies that it knows about, but AFAICT, it doesn't look at the OSGI manifests. So one only finds out that a dependency is missing when the osgi runtime complains.

I spent a couple of days trying to track down missing dependencies and looking for alternative repositories for each one. Sometimes the alternative sources are using different package names for the same jar, which further complicates things. There are no canonical maven GAV sources for many standard java libraries, including those put out by the apache foundation.

What would be great is if there was a way to automate the process of finding the missing dependences (going through all the OSGI manifests?), mapping them to the relevant p2/update sites (knowledge of p2 metadata needed?), and then calling RepubJar to get them into mavenLocal.

I'm a gradle newbie and my knowledge of OSGI is only that of a casual (long-time) user (via RCP apps), so I don't know enough to be able to do either of these tasks efficiently. I took a look at the BNDtools work, but wasn't willing to take the learning curve to use it. At this point, I've temporarily given up on solving the problem and gone back to using eclipse only.

So unfortunately, I won't be able to test out your tool, since I'm not chasing dependencies down manually anymore (which would have to be repeated each time the target platform was updated to a newer eclipse release). But it sounds like an important building block for the total solution.