Gradle Bnd Plugin manifest issue

We are finally migrating from the deprecated OSGi Gradle plugin to the Bnd Gradle plugin to build our OSGi bundle. We have been using the deprecated plugin along with the com.github.johnrengelman.shadow plugin to create an “uber” jar with some flattened dependencies for our bundle and it has been working well.

After migrating we are running into an issue where the uber jar generated by the shadow plugin has a blank manifest (just one line Manifest-Version: 1.0) instead of the manifest generated by Bnd. The “regular” jar artifact from the jar task does have the correct manifest with all the macros executed, etc.

By default the shadow plugin inherits from the manifest of the standard jar task - so I assume the Bnd plugin must be doing something a little different and that’s not getting set anymore. It’s possible to override the manifest used by shadow but I can’t figure out where to get the manifest object generated by Bnd.

I’m coming up empty googling this - has anyone run into a similar issue or have any ideas/suggestions on how to get this working? Thanks!!

Old build.gradle:

apply plugin: 'osgi'
apply plugin: 'com.github.johnrengelman.shadow'

jar {
  manifest {
    instruction 'Bundle-Activator', '${classes;IMPLEMENTS;org.osgi.framework.BundleActivator}'
    instruction 'Private-Package', 'com.mycompany.plugin'
    instruction 'Export-Package', ''
    instruction 'Import-Package', "$someHardcodedPackages,*;resolution:=optional;version=!"
  }
}

New build.gradle:

apply plugin: 'biz.aQute.bnd.builder'
apply plugin: 'com.github.johnrengelman.shadow'

jar {
 bundle {
   bnd('Bundle-Activator': '${classes;CONCRETE;IMPLEMENTS;org.osgi.framework.BundleActivator}',
       'Private-Package': 'com.mycompany.plugin',
       'Export-Package': '',
       'Import-Package': "$someHardcodedPackages,*;resolution:=optional;version=!")
}
shadowJar {
  manifest {
    inheritFrom somewhere.manifest //TODO how can I get this?
  }
}

Hi,

I think I’ve done something similar in the past, except that I created the shadowJar first, and then used that to create a bundle.

import aQute.bnd.gradle.Bundle

plugins {
    id 'com.github.johnrengelman.shadow'
    id 'biz.aQute.bnd.builder' apply false
}

tasks.named('jar', Jar) {
    enabled = false
}

def shadowJar = tasks.named('shadowJar', Jar) {
    archiveAppendix = 'shadow'
    archiveClassifier = ''
}

def bundle = tasks.register('bundle', Bundle) {
    from shadowJar.map { zipTree(it.archiveFile) }

    archiveClassifier = ''

    bundle {
        bnd shadowJar.flatMap { jar ->
            jar.archiveFile.map { file ->
                "-include: jar:${file.asFile.toURI()}!/META-INF/MANIFEST.MF"
            }
        }
    }
}

artifacts {
    archives bundle
}

Cheers,
Chris

Bnd does not use the Gradle Manifest object of the jar task for anything other than an input source. You can set manifest values through the jar’s manifest object which Bnd will use as input.

But the Bnd gradle plugin does not write the generated manifest back into the jar’s manifest object. It also does not supply the generated manifest in a form which could be used by Manifest.from(Object…). The generated manifest just exists as an entry in the output jar.

To be honest, I never considered any use cases like this. Perhaps you can open a Bnd issues describing what you need in terms of a change, and I can work on proposing some solution.

This is actually a better order since Bnd will see the shadowed classes when generating the manifest.

@chrisr3 thanks for the response! For some reason I did not have luck w/ creating the shadowJar before the bundle - kept running into heap space gradle crashes. My bundle has a decent amount of dependencies. I’ve come up with this “hack” which seems to be working ok for now…

apply plugin: 'biz.aQute.bnd.builder'
apply plugin: 'com.github.johnrengelman.shadow'

jar {
 bundle {
   bnd('Bundle-Activator': '${classes;CONCRETE;IMPLEMENTS;org.osgi.framework.BundleActivator}',
       'Private-Package': 'com.mycompany.plugin',
       'Export-Package': '',
       'Import-Package': "$someHardcodedPackages,*;resolution:=optional;version=!")
}

task unpackJarManifest(type: Copy) {
  from ({zipTree(jar.outputs.files.singleFile)}) {
    include 'META-INF/MANIFEST.MF'
  }
  into layout.buildDirectory.dir('tmp')
}

shadowJar {
  manifest {
    inheritFrom layout.buildDirectory.file('tmp/META-INF/MANIFEST.MF')
  }
}

@bjhargrave I think it would be great if the bnd plugin wrote the generated manifest back to the jar object (similar to how deprecated osgi plugin worked), or if the bnd plugin provided a method to generate the manifest object to use in other tasks. I will work on creating a bnd issue to see if it is something you might consider. Thanks!

For the forthcoming Bnd 6.3, I updated BundleTaskExtension to make the generated manifest available from the task’s manifest.effectiveManifest property.

I have finally looped back to this a year later :crazy_face: Thanks so much @bjhargrave - the manifest property is now working for me!! In case it helps anyone else - here is my configuration that is working w/ bnd plugin version 6.4.0 and shadow plugin version 7.1.2.

apply plugin: 'biz.aQute.bnd.builder'
apply plugin: 'com.github.johnrengelman.shadow'

jar {
 bundle {
   bnd('Bundle-Activator': '${classes;CONCRETE;IMPLEMENTS;org.osgi.framework.BundleActivator}',
       'Private-Package': 'com.mycompany.plugin',
       'Export-Package': '',
       'Import-Package': "$someHardcodedPackages,*;resolution:=optional;version=!")
}

shadowJar {
  manifest {
    inheritFrom jar.manifest.effectiveManifest
  }
}

shadowJar.dependsOn jar