bndtools / bnd

Bnd/Bndtools. Tooling to build OSGi bundles including Eclipse, Maven, and Gradle plugins.
https://bndtools.org
Other
532 stars 305 forks source link

[bnd-maven-plugin] computing the lowest possible version range? #5301

Closed laeubi closed 2 years ago

laeubi commented 2 years ago

Lets assume I have a maven project and use

<dependency>
    <groupId>org.osgi</groupId>
    <artifactId>osgi.core</artifactId>
    <version>8.0.0</version>
    <scope>provided</scope>
</dependency>

and of course always upgrade version, so my upgrade-scanner do not complain and of course I might want to benefit from never features.

But also assume, my bundle only uses very "basic" API and thus can actually run as low as <version>4.3.1</version> as well, would it be possible for bnd-maven-plugin to download all previous versions and compute the lowest possible version range based on used API, e.g in this case (4.3.1, 8] for package imports (just for simplicity assuming that package-version=bundle-version=maven-version)?

For sure this will mean more downloads, and osgi.core is just an example here, but maintain this manually always has the risk of miss some never API used in code on the other hand "raising the barrier" for no good reason makes it harder for other to adopt never versions and harder for the resolver to find a good solution (e.g. a system running osgi.core 7 would suddenly be forced to maybe upgrade all other as well).

juergen-albert commented 2 years ago

I understand your desire and where you are coming from. This might be doable to a certain extend but if we really think about it it would mean the following:

This may take ages, depending on the Version history of your dependencies and the result will become a bit unpredictable in the end. It might seem dowable, but this process needs to be repeated with each dependency you have. You as the developer know best, what you want to build against and what "old" versions you are willing to stick to in relation of new functionality you are sacrificing for doing so. If I build an Application, I tend to take the latest and greatest. If I build something that can be considered a general Library or Framework, I try to stick to the oldest feasible dependencies.

laeubi commented 2 years ago

@juergen-albert I don't think one needs to actually compile against an older version, but only check if the older version has a binary compatible method signature (something that bnd-baseline already do for a user bundle).

You as the developer know best, what you want to build against and what "old" versions you are willing to stick to in relation of new functionality you are sacrificing for doing so.

The problem is, that it is completely unrealistic (as you have outlined) that one really compile/test against older versions, so if one is not absolutely dedicated to this it simply either won't happen, wrong data is published, because one forget to udpate it or breaking older versions by accident.

If I build something that can be considered a general Library or Framework, I try to stick to the oldest feasible dependencies

But this is often undesirable as described above, if dependency checker jelling at you, or you upgrade a bigger library with many bundles this is very unrealistic beside trivial examples and not automating this is in my opinion the major blocker for using useful version ranges today.

bjhargrave commented 2 years ago

Bnd is not going to figure out how to download older versions of every jar on the class path to perform baseline analysis against your code to see if it can work on the older API version.

So the answer is quite simple. You compile against the lowest API version you intend to support. That is the only solution here. You can tell dependabot to ignore the older API artifact version because you, the developer, have make an explicit choice to support the older API.

laeubi commented 2 years ago

Bnd is not going to figure out how to download older versions of every jar on the class path

Not BND in general, but bnd-maven-plugin can (and bnd-basline-plugin already does) query maven for all available versions of a declared dependency.

So the answer is quite simple.

Yes simple for trivial use-cases but not for bigger setups where for example you get transitive dependencies pulled in.

the developer, have make an explicit choice to support the older API

Yes a developer can make an explicit choice, but still could implicitly support older versions, as doing this explicit is most often not desirable, the idea is that the "one tool to bnd them all" helps me here as it is to cumbersome to teach the people taking care of not upgrading "blindly" to never dependencies.

“If you want to teach people a new way of thinking, don't bother trying to teach them. Instead, give them a tool, the use of which will lead to new ways of thinking.” ― R. Buckminster Fuller From https://bnd.bndtools.org/

e.g. I often ask opensource project to add OSGi metadata, they often do not know much about OSGi but are willing to add another plugin to make there users happy, but the will just upgrade to never version of (whatever) and I can't ask them to be so kind and support older versions or version ranges (WAHT??) ...

laeubi commented 2 years ago

By the way one can generalize this more (and this is not only for older versions), give I have an Analyzer instance, and do Anzyzer#addClasspath(jar) and some of the jars contain the same package in different versions, I'd like the Analyzer to compute the most broad version range of my analyzed code that is binary compatible for that given package.

bjhargrave commented 2 years ago

I'd like the Analyzer to compute the most broad version range of my analyzed code that is binary compatible for that given package.

I would suggest you author an AnalyzerPlugin (or VerifierPlugin) to perform this function. I don't see building this into the base Analyzer is of interested to the Bnd team.

Not BND in general, but bnd-maven-plugin

Any such feature should be supported by all Bnd drivers including gradle and Eclipse as well as Bnd Workspace model. An AnalyzerPlugin could work in all places.

laeubi commented 2 years ago

Any such feature should be supported by all Bnd drivers including gradle and Eclipse as well as Bnd Workspace model.

It could work everywhere, I just don't know enough about gradle or bnd workspaces, but as BND supports the concepts of "repositories" for sure it would be there possible as well to find alternative versions.

I don't see building this into the base Analyzer is of interested to the Bnd team.

Funny enough I assumed that BND it to help the BND users and not exclusively "The B(ND)-Team"...

bjhargrave commented 2 years ago

Funny enough I assumed that BND it to help the BND users and not exclusively "The B(ND)-Team"...

Funnily enough, open source is developed by volunteers who provide their time and resources. Sometimes that does not include implementing every request made as their time and resources are limited. As an open source project, anyone is free to contribute a proposed change.

laeubi commented 2 years ago

Sure but who then qualifies for "BND Team"? Everyone ever contributed to BND? Anyone (currently) has write access? Maybe everyone who potentially could contribute, so having a github account?

Will stating "I don't see ... is of interested to the Bnd team" make people starting to contribute? Or more make them feel there is some kind of exclusive circle allowed to make "good" suggestion.

By the way also reporting possible improvements is "contribute their time and resources" .... so just some thoughts.

laeubi commented 2 years ago

Just to give some more context assume a multi-module maven build with managed dependencies but their modules might be not using the full set of dependencies (e.g. osgi, certain service interfaces, slf4j,...). so one has currently only the following options:

  1. manage the version range manually (error prone)
  2. force all versions to be the same (undesirable in terms of good version ranging)
  3. override the versions of several dependencies in the module (defeats the purpose of using managed dependencies)
pkriens commented 2 years ago

I think this is a rat hole under the Hades leading straight to the underworld :-)

The model we have with the bnd workspace is imho close to what you want without the mind boggling self inflicted complexity you seem to want.

Undoubtedly there are use cases that your idea will do better but the yaw dropping complexity it requires, and thus the enormous number of potential error cases, make it crystal clear that I do not see this functionality as desirable.

That said, demonstrate the advantages of the idea in a prototype and we can talk again, I am always interested in new ideas and history shows I can be wrong :-)

I propose to close this issue.

laeubi commented 2 years ago

@pkriens how does a bnd-workspace helps me when doing a maven build? How will it solve the issue of e.g having versions defined in a paren-tpom-dependecy management section leading to increase the version in all sub-module?

If BND-Workspace can do all that magic, why can't bnd-maven-plugin do?

Clients of the API include only the API jar

Well this is all about "the clients", but "the client" don't want to use always highest "marketing version" as per maven but import range should be computed automatically.

pkriens commented 2 years ago

If BND-Workspace can do all that magic, why can't bnd-maven-plugin do?

The maven bnd can do this magic!

However, the bnd-workspace was designed from the ground up to be focused on API-Implementation separation, which actually is the root cause of all the goodies. This is for example why the model is based on package dependencies, a resolver, and compiling against the lowest available version.

In contrast, Maven was designed around a transitive implementation dependency model that by defaults to runtime and compile time being the same. I think your ideas with Bundle versions are caused by this difference; I never used them for the compatibility semantics because there was no need. Agree , it sounds logical but it tends to get hairy too quickly. Packages dependencies & a resolver avoid lots of issues.

I also shamelessly stereotype Maven users to think that the tool should do it all even if it creates humongous amounts of complexity under the covers. I tend to believe in simplicity even if I have to pay a, sometimes steep, price. In my experience, most of the complexity tends to be in the last few percentages and I think the resulting Ruby Goldberg contraptions are often not worth the benefits.

image

Better is in our world too often the enemy of good. Too often in my life I've been defending simplicity against the onslaught of enterprise software. (Some lurkers are probably feeling sentimental right now :-)

That said, I am quite conservative in this area, but love to be proven wrong! Show me that it can work and you might convince me. But until then, this issue can be closed from my point of view to not obscure the more urgent issues.

laeubi commented 2 years ago

The maven bnd can do this magic!

Do you have an example how to enable the described usecase, e.g. just asume a parent with lets say slf4j.api and two child modules inheriting it and one of them only uses "old" API so it could be have a lower version bound?

This is for example why the model is based on package dependencies, a resolver, and compiling against the lowest available version. Packages dependencies & a resolver avoid lots of issues.

I don't understand why this should be fundamentally different from maven, where one could also get a list of "older" artifacts, then BND computes the packages and can find out what is the lowest possible version of that package (!) I don't mind the artifact here, just wanted to have a lower bound than from the single compile-time artifact.

So given I can't change the world (forcing everyone using bnd workspace instead of a maven-first-build) why should "the resolver" not be able to do its work also in maven, given I pass him all possible versions (aka API bundles) a set of compiled class files (aka something that generates a package import by BND-Anlyzer) to get a version range on the package-import?

Show me that it can work and you might convince me.

I don't know enough about BND-internals to prove/show an implementation, I can only tell that bnd-baseline-plugin already can do something similar:

  1. It uses maven (among other possible source) to fetch an "older" (that is the baseline) artifact
  2. It can compare another artifact (that is the one currently build) with that in a way to tell me that there are any (binary) incompatibility and suggest me to increase my version exports

So what how it would work here is:

  1. One needs to compare even "older" (e.g. more than one)
  2. One need to not compare the current build artifact but each of those API-artifacts compared to what my current build-artifact uses from them in terms of an imported package (what already BND can do).

But until then, this issue can be closed from my point of view to not obscure the more urgent issues.

How does it obscure anything to have an issue open? I assume no one will stop-working right because there is one unresolved issue :-)

laeubi commented 2 years ago

By the way as mentioned before it might be the wrong (but for me natural) entry point to request something for the maven-plugin at first place, so to summarize, what I wan't to archive could be simplified as:

  1. I have an Analyzer instance
  2. I add a classpath entries with an API that export package A in version 1
  3. I add a classpath entries with an API that export package A in version 2
  4. I add my compiled classes and perform the caluclateManifest
  5. Given my compiled classes are using package A and are compatible with version 1 and 2 i'd like to have an import range on the package A that include 1..2 for the resulting manifest.

I hope this is more reasonable and decouples the request from any specific workspace or dependency technique. If this is already possible with BND, that's great and would maybe possible to enhance the maven-part here but need some guidance if/how I enable this specifically.