# Bnd macro expansion: ${@class} works but ${@version} doesn't

**URL:** <https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212>\
**Category:** GENERAL\
**Created:** [February 6, 2022, 7:59pm UTC](https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212 "2022-02-06T19:59:01Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![chrisr3](https://yyz2.discourse-cdn.com/free1/user_avatar/bnd.discourse.group/chrisr3/32/58_2.png) [@chrisr3](https://bnd.discourse.group/u/chrisr3)\
**Post date:** [February 6, 2022, 7:59pm UTC](https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212/1 "2022-02-06T19:59:01Z")

</div>

Hi,

I am trying to use a `${@version}` macro in a `@Requirement` annotation inside a Gradle project. I am using Gradle 7.3.2 and Bnd 6.1.0. However, Bnd is refusing to expand `${@version}` to the Gradle `${project.version}` value. I don’t think this can be a syntax problem because it happily expands `${@class}`. Nor can I see anything in the [documentation](https://bnd.bndtools.org/chapters/230-manifest-annotations.html) to suggest I am doing anything wrong.

Could this be a bug caused by Bnd’s recent support for Gradle’s configuration cache, please?

Cheers,  
Chris

---

<div class="post-metadata">

**Author:** ![bjhargrave](https://yyz2.discourse-cdn.com/free1/user_avatar/bnd.discourse.group/bjhargrave/32/10_2.png) [@bjhargrave](https://bnd.discourse.group/u/bjhargrave)\
**Post date:** [February 6, 2022, 9:20pm UTC](https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212/2 "2022-02-06T21:20:43Z")

</div>

I don’t think the configuration cache support could be involved since the `@xxx` macro values are set in the code from known data values.

> <https://github.com/bndtools/bnd/blob/5a3beb66a465ba4733456ef86a2d325562a319c2/biz.aQute.bndlib/src/aQute/bnd/osgi/AnnotationHeaders.java#L948-L962>

It does require that the package has a specified version. If there is no version value known for the package, the `@version` value will not be set.

---

<div class="post-metadata">

**Author:** ![chrisr3](https://yyz2.discourse-cdn.com/free1/user_avatar/bnd.discourse.group/chrisr3/32/58_2.png) [@chrisr3](https://bnd.discourse.group/u/chrisr3)\
**Post date:** [February 7, 2022, 1:22am UTC](https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212/3 "2022-02-07T01:22:12Z")

</div>

OK, now I’m confused. I thought packages _already_ have versions, which are (by default) the same as the bundle version unless overridden individually in the `package-info` file using:

```auto
@aQute.bnd.annotation.Version

```

My bundle _definitely_ has a version, even if none of its packages are actually exported. (Not that exporting one of them makes any difference, mind.) However, I am trying to version a “capability” and so am fine with using the bundle version here.

Cheers,  
Chris

---

<div class="post-metadata">

**Author:** ![chrisr3](https://yyz2.discourse-cdn.com/free1/user_avatar/bnd.discourse.group/chrisr3/32/58_2.png) [@chrisr3](https://bnd.discourse.group/u/chrisr3)\
**Post date:** [February 7, 2022, 12:49pm UTC](https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212/4 "2022-02-07T12:49:31Z")

</div>

So the bottom line is that this does what I expect:

```auto
@Requirement(
    namespace = MY_NAMESPACE,
    name = MY_NAME,
    version = "${@version}"
)
@Version("${project.version}")
package my.package;

```

whereas this does not:

```auto
@Requirement(
    namespace = MY_NAMESPACE,
    name = MY_NAME,
    version = "${project.version}"
)
package my.package;

```

Errmm…?

Cheers,  
Chris

---

<div class="post-metadata">

**Author:** ![bjhargrave](https://yyz2.discourse-cdn.com/free1/user_avatar/bnd.discourse.group/bjhargrave/32/10_2.png) [@bjhargrave](https://bnd.discourse.group/u/bjhargrave)\
**Post date:** [February 7, 2022, 1:05pm UTC](https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212/5 "2022-02-07T13:05:20Z")

</div>

> [@chrisr3](#):
>
> I thought packages _already_ have versions, which are (by default) the same as the bundle version unless overridden individually in the `package-info` file

This only happens at the end of making the bundle for exported packages and is generally bad practice and people should not do that.

> [@chrisr3](#):
>
> `@Version("${project.version}")`

This will not work. The string for the version must be a valid OSGi version as a compile time constant. Bnd does not mutate the bytecode generated by the java compiler to replace the compile time constant string `${project.version}` with the value of a macro evaluation.

The macro evaluation for the `Requirement`annotation processing does not replace the compile time constants in the bytecode. It just processes the values into information to generate the manifest.

The version in the `Version` annotation must be a real OSGi version string.

---

<div class="post-metadata">

**Author:** ![mnlipp](https://yyz2.discourse-cdn.com/free1/user_avatar/bnd.discourse.group/mnlipp/32/13_2.png) [@mnlipp](https://bnd.discourse.group/u/mnlipp)\
**Post date:** [February 7, 2022, 3:12pm UTC](https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212/6 "2022-02-07T15:12:57Z")

</div>

> The string for the version must be a valid OSGi version as a compile time constant.

No, not really. I’m using this

```auto
@org.osgi.annotation.versioning.Version("${api_version}")

```

all over my code (and therefore hope that things don’t change). **However** , `api_version` is defined in `bnd.bnd` and not taken from the gradle script.

---

<div class="post-metadata">

**Author:** ![bjhargrave](https://yyz2.discourse-cdn.com/free1/user_avatar/bnd.discourse.group/bjhargrave/32/10_2.png) [@bjhargrave](https://bnd.discourse.group/u/bjhargrave)\
**Post date:** [February 7, 2022, 3:33pm UTC](https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212/7 "2022-02-07T15:33:38Z")

</div>

Well your generated bundle has invalid package-info.class files which do not contain the actual package version value for your package. So when Bnd see those jars on the build path of another jar, it will process the package-info.class file to locate the version of the package and will use the being-built bundle’s bnd property values to evaluate the version value.

> <https://github.com/bndtools/bnd/blob/5a3beb66a465ba4733456ef86a2d325562a319c2/biz.aQute.bndlib/src/aQute/bnd/osgi/Analyzer.java#L795-L806>

So the value of `api_version` in the bundle being built will be used rather than the correct value from the previously built bundle.

It is a bad idea to use anything other than a real OSGi version string in the `Version` annotation. You want the actual OSGi version baked into the generated class file. Not some value which is interpreted later and can easily be wrong. The semantic version of a package is intrinsic to the source code of the package and thus is itself source code.

---

<div class="post-metadata">

**Author:** ![mnlipp](https://yyz2.discourse-cdn.com/free1/user_avatar/bnd.discourse.group/mnlipp/32/13_2.png) [@mnlipp](https://bnd.discourse.group/u/mnlipp)\
**Post date:** [February 7, 2022, 4:55pm UTC](https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212/8 "2022-02-07T16:55:35Z")

</div>

I’m not sure that I have fully understood this:

> [@bjhargrave](#):
>
> So when Bnd see those jars on the build path of another jar, it will process the package-info.class file to locate the version of the package

So `bnd` does not take the version information from the other jar’s `MANIFEST.MF`, it retrieves the information from the `package-info.class`?

And:

> [@bjhargrave](#):
>
> `version = getReplacer().process(version);`

> [@bjhargrave](#):
>
> The version in the `Version` annotation must be a real OSGi version string.

Assuming that the `replacer()` applies the macro substitution, why is it invoked in the first place, if the version string is only supposed to contain a literal version? (Looks a bit like a trap, because as a user you simply try (macro processing is used _everywhere_ in `bnd`) find that the generated `MANIFEST.MF` is okay, and use that “feature” – which is very useful if you have an API that consists of more that one package and you want the versions to move in sync.)

Finally:

> [@bjhargrave](#):
>
> You want the actual OSGi version baked into the generated class file.

Not really. Honestly, my assumption has always been that the annotation is used to generate the information in `MANFEST.MF` (and up to now, I had never cause to doubt this, tutorials usually mention annotation as a convenient way to provide information that ends up in `MANIFEST.MF`). I wonder how many OSGi users are aware that there is OSGi related information which is not in `MANIFEST.MF` but “spread” in the code base.

But, okay, I’ll move the version information back into the `bnd.bnd`. Maybe this “central” point of maintenance isn’t a bad idea after all.

---

<div class="post-metadata">

**Author:** ![bjhargrave](https://yyz2.discourse-cdn.com/free1/user_avatar/bnd.discourse.group/bjhargrave/32/10_2.png) [@bjhargrave](https://bnd.discourse.group/u/bjhargrave)\
**Post date:** [February 7, 2022, 6:03pm UTC](https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212/9 "2022-02-07T18:03:27Z")

</div>

> [@mnlipp](#):
>
> So `bnd` does not take the version information from the other jar’s `MANIFEST.MF` , it retrieves the information from the `package-info.class` ?

Bnd uses the version information in the package-info.class file as the source of truth. The use of the package-info.class file as the source of version information allows one to build against compiler output folders (`version=project` in Bnd Workspace model) and see the package versions.

> [@mnlipp](#):
>
> Assuming that the `replacer()` applies the macro substitution, why is it invoked in the first place, if the version string is only supposed to contain a literal version?

It has been that way for almost forever (at least since 2010). It is probably not a good idea, but that is what the code has done historically. We could remove this, but it would upset someone 🙂

> [@mnlipp](#):
>
> Not really.

Really you do. The semantic version of a package is an intrinsic part of a package like the name of the package and the members of the package. It is not an external attribute which can be assigned later just like one should not add members to a package later (aka split packages.)

---

<div class="post-metadata">

**Author:** ![bjhargrave](https://yyz2.discourse-cdn.com/free1/user_avatar/bnd.discourse.group/bjhargrave/32/10_2.png) [@bjhargrave](https://bnd.discourse.group/u/bjhargrave)\
**Post date:** [February 7, 2022, 6:07pm UTC](https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212/10 "2022-02-07T18:07:28Z")

</div>

> [@mnlipp](#):
>
> I wonder how many OSGi users are aware that there is OSGi related information which is not in `MANIFEST.MF` but “spread” in the code base.

At runtime, all OSGi metadata is sourced from the manifest. However tools like Bnd are not an OSGi runtime and use annotation information for OSGi information. The end result of building a bundle is that all the information is in the manifest for the OSGi runtime, but Bnd needs to work with code which is not in a built bundle and thus uses information in CLASS retention annotations in class files.

---

<div class="post-metadata">

**Author:** ![mnlipp](https://yyz2.discourse-cdn.com/free1/user_avatar/bnd.discourse.group/mnlipp/32/13_2.png) [@mnlipp](https://bnd.discourse.group/u/mnlipp)\
**Post date:** [February 7, 2022, 7:48pm UTC](https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212/11 "2022-02-07T19:48:27Z")

</div>

> [@bjhargrave](#):
>
> It is probably not a good idea, but that is what the code has done historically. We could remove this […]

Maybe consider to issue a warning if the string doesn’t match the OSGi version pattern. Wouldn’t break anything and might prevent new users such as @chrisr3 and me from following the wrong track.

---

<div class="post-metadata">

**Author:** ![bjhargrave](https://yyz2.discourse-cdn.com/free1/user_avatar/bnd.discourse.group/bjhargrave/32/10_2.png) [@bjhargrave](https://bnd.discourse.group/u/bjhargrave)\
**Post date:** [February 7, 2022, 8:24pm UTC](https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212/12 "2022-02-07T20:24:56Z")

</div>

> [@mnlipp](#):
>
> Maybe consider to issue a warning if the string doesn’t match the OSGi version pattern.

That is a fair idea. Can you please open the issue?

---

<div class="post-metadata">

**Author:** ![mnlipp](https://yyz2.discourse-cdn.com/free1/user_avatar/bnd.discourse.group/mnlipp/32/13_2.png) [@mnlipp](https://bnd.discourse.group/u/mnlipp)\
**Post date:** [February 7, 2022, 10:07pm UTC](https://bnd.discourse.group/t/bnd-macro-expansion-class-works-but-version-doesnt/212/13 "2022-02-07T22:07:32Z")

</div>

> [@bjhargrave](#):
>
> Can you please open the issue?

[Done](https://github.com/bndtools/bnd/issues/5087)
