diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/TaskContributor.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/TaskContributor.kt index a83f71a3..8c68be94 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/TaskContributor.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/TaskContributor.kt @@ -50,12 +50,14 @@ class TaskContributor @Inject constructor(val incrementalManagerFactory: Increme reverseDependsOn : List = emptyList(), runBefore : List = emptyList(), runAfter : List = emptyList(), + alwaysRunAfter: List = emptyList(), runTask: (Project) -> TaskResult) { dynamicTasks.add(DynamicTask(plugin, taskName, description, group, project, dependsOn = dependsOn, reverseDependsOn = reverseDependsOn, runBefore = runBefore, runAfter = runAfter, + alwaysRunAfter = alwaysRunAfter, closure = { p: Project -> runTask(project) })) diff --git a/src/main/kotlin/com/beust/kobalt/Options.kt b/src/main/kotlin/com/beust/kobalt/Options.kt index 615b4d64..ef4cbfd4 100644 --- a/src/main/kotlin/com/beust/kobalt/Options.kt +++ b/src/main/kotlin/com/beust/kobalt/Options.kt @@ -1,8 +1,9 @@ package com.beust.kobalt import com.beust.jcommander.JCommander +import com.beust.kobalt.api.ITask import com.beust.kobalt.api.Kobalt -import com.beust.kobalt.api.PluginTask +import com.beust.kobalt.api.KobaltContext import com.beust.kobalt.api.Project import com.beust.kobalt.app.ProjectFinder import com.beust.kobalt.app.ProjectGenerator @@ -109,7 +110,7 @@ class Options @Inject constructor( Option( { args.tasks }, { // --tasks runIfSuccessfulBuild(buildError) { - displayTasks() + displayTasks(allProjects, Kobalt.context!!) } }), Option( { args.checkVersions }, { @@ -177,19 +178,29 @@ class Options @Inject constructor( } } - private fun displayTasks() { + private fun displayTasks(projects: List, context: KobaltContext) { // // List of tasks, --tasks // - val tasksByPlugins = HashMultimap.create() - taskManager.annotationTasks.forEach { - tasksByPlugins.put(it.plugin.name, it) + val tasksByPlugins = HashMultimap.create() + projects.forEach { project -> + pluginInfo.taskContributors.forEach { + val tasks = it.tasksFor(project, context) + tasks.forEach { + tasksByPlugins.put(it.plugin.name, it) + } + } + } + listOf(taskManager.annotationTasks, taskManager.dynamicTasks).forEach { tasks -> + tasks.forEach { + tasksByPlugins.put(it.plugin.name, it) + } } val sb = StringBuffer("List of tasks\n") tasksByPlugins.keySet().forEach { name -> sb.append("\n " + AsciiArt.horizontalDoubleLine + " $name " + AsciiArt.horizontalDoubleLine + "\n") - tasksByPlugins[name].distinctBy(PluginTask::name).sortedBy(PluginTask::name).forEach { task -> + tasksByPlugins[name].distinctBy(ITask::name).sortedBy(ITask::name).forEach { task -> sb.append(" ${task.name}\t\t${task.doc}\n") } } diff --git a/src/main/kotlin/com/beust/kobalt/plugin/osgi/OsgiPlugin.kt b/src/main/kotlin/com/beust/kobalt/plugin/osgi/OsgiPlugin.kt new file mode 100644 index 00000000..6ca476b3 --- /dev/null +++ b/src/main/kotlin/com/beust/kobalt/plugin/osgi/OsgiPlugin.kt @@ -0,0 +1,103 @@ +package com.beust.kobalt.plugin.osgi + +import aQute.bnd.osgi.Analyzer +import com.beust.kobalt.TaskResult +import com.beust.kobalt.api.* +import com.beust.kobalt.api.annotation.Directive +import com.beust.kobalt.archive.Archives +import com.beust.kobalt.maven.DependencyManager +import com.beust.kobalt.misc.KFiles +import com.beust.kobalt.plugin.packaging.PackagingPlugin +import com.google.common.reflect.ClassPath +import com.google.inject.Inject +import com.google.inject.Singleton +import java.io.File +import java.net.URI +import java.net.URLClassLoader +import java.nio.file.FileSystems +import java.nio.file.Files +import java.nio.file.StandardOpenOption + +/** + * Generate OSGi attributes in the MANIFEST.MF if an osgi{} directive was found in the project. + */ +@Singleton +class OsgiPlugin @Inject constructor(val configActor: ConfigActor, val taskContributor: TaskContributor, + val dependencyManager: DependencyManager) + : BasePlugin(), ITaskContributor by taskContributor, IConfigActor by configActor { + companion object { + const val PLUGIN_NAME = "Osgi" + } + override val name: String = PLUGIN_NAME + + override fun apply(project: Project, context: KobaltContext) { + super.apply(project, context) + + configurationFor(project)?.let { config -> + taskContributor.addTask(this, project, "generateOsgiManifest", + description = "Generate the OSGi information in the manifest", + group = "build", + alwaysRunAfter = listOf(PackagingPlugin.TASK_ASSEMBLE), + runTask = { generateManifest(project, context) }) + } + } + + private fun generateManifest(project: Project, context: KobaltContext): TaskResult { + val jarName = project.projectProperties.get(Archives.JAR_NAME) as String + val jarFile = File(KFiles.libsDir(project), jarName) + val cp = ClassPath.from(URLClassLoader(arrayOf(jarFile.toURI().toURL()), null)) + + val packages = cp.allClasses.map { it.packageName }.distinct() + val exportPackageLine = packages.map { + it + ";version=\"" + project.version + "\"" + }.joinToString(",") + + val analyzer = Analyzer().apply { + jar = aQute.bnd.osgi.Jar(jarName) + val dependencies = project.compileDependencies + project.compileRuntimeDependencies + dependencyManager.calculateDependencies(project, context, passedDependencies = dependencies).forEach { + addClasspath(it.jarFile.get()) + } + setProperty(Analyzer.BUNDLE_VERSION, project.version) + setProperty(Analyzer.BUNDLE_NAME, project.group + "." + project.artifactId) + setProperty(Analyzer.BUNDLE_DESCRIPTION, project.description) + setProperty(Analyzer.IMPORT_PACKAGE, "*") + setProperty(Analyzer.EXPORT_PACKAGE, exportPackageLine) + project.pom?.let { pom -> + if (pom.licenses.any()) { + setProperty(Analyzer.BUNDLE_LICENSE, pom.licenses[0].url) + } + } + } + + val manifest = analyzer.calcManifest() + val lines = manifest.mainAttributes.map { + it.key.toString() + ": " + it.value.toString() + } + + context.logger.log(project.name, 2, " Generated manifest:") + lines.forEach { + context.logger.log(project.name, 2, " $it") + } + + val uri = URI.create("jar:file:" + jarFile.absolutePath) + val options = hashMapOf() + FileSystems.newFileSystem(uri, options).use { fs -> + val jarManifest = fs.getPath("/META-INF/MANIFEST.MF") + Files.write(jarManifest, lines, StandardOpenOption.APPEND) + } + return TaskResult() + } +} + +class OsgiConfig + +@Directive +fun Project.osgi(init: OsgiConfig.() -> Unit) { + OsgiConfig().let { + it.init() + (Kobalt.findPlugin(OsgiPlugin.PLUGIN_NAME) as OsgiPlugin).addConfiguration(this, it) + } +} + + diff --git a/src/main/kotlin/com/beust/kobalt/plugin/packaging/PackagingPlugin.kt b/src/main/kotlin/com/beust/kobalt/plugin/packaging/PackagingPlugin.kt index 0cf2b46f..6ea3fd95 100644 --- a/src/main/kotlin/com/beust/kobalt/plugin/packaging/PackagingPlugin.kt +++ b/src/main/kotlin/com/beust/kobalt/plugin/packaging/PackagingPlugin.kt @@ -27,7 +27,7 @@ class PackagingPlugin @Inject constructor(val dependencyManager : DependencyMana val zipGenerator: ZipGenerator, val taskContributor: TaskContributor, val kobaltLog: ParallelLogger, val pomFactory: PomGenerator.IFactory, val configActor: ConfigsActor) - : BasePlugin(), ITaskContributor, IIncrementalAssemblyContributor, + : BasePlugin(), ITaskContributor by taskContributor, IIncrementalAssemblyContributor, IConfigsActor by configActor { companion object { @@ -196,27 +196,6 @@ class PackagingPlugin @Inject constructor(val dependencyManager : DependencyMana packages.add(p) } -// @Task(name = "generateOsgiManifest", alwaysRunAfter = arrayOf(TASK_ASSEMBLE)) -// fun generateManifest(project: Project): TaskResult { -// val analyzer = Analyzer().apply { -// jar = aQute.bnd.osgi.Jar(project.projectProperties.get(Archives.JAR_NAME) as String) -// val dependencies = project.compileDependencies + project.compileRuntimeDependencies -// dependencyManager.calculateDependencies(project, context, passedDependencies = dependencies).forEach { -// addClasspath(it.jarFile.get()) -// } -// setProperty(Analyzer.BUNDLE_VERSION, project.version) -// setProperty(Analyzer.BUNDLE_NAME, project.group) -// setProperty(Analyzer.BUNDLE_DESCRIPTION, project.description) -// setProperty(Analyzer.IMPORT_PACKAGE, "*") -// setProperty(Analyzer.EXPORT_PACKAGE, "*;-noimport:=false;version=" + project.version) -// } -// -// val manifest = analyzer.calcManifest() -// manifest.write(System.out) -// return TaskResult() -// } - - private fun taskInstall(project: Project, context: KobaltContext, config: InstallConfig) : TaskResult { val buildDir = project.projectProperties.getString(LIBS_DIR) val buildDirFile = File(buildDir) @@ -248,9 +227,6 @@ class PackagingPlugin @Inject constructor(val dependencyManager : DependencyMana return TaskResult() } - - //ITaskContributor - override fun tasksFor(project: Project, context: KobaltContext): List = taskContributor.dynamicTasks } @Directive diff --git a/src/main/resources/META-INF/kobalt-core-plugin.xml b/src/main/resources/META-INF/kobalt-core-plugin.xml index fbcca6c6..57354ff2 100644 --- a/src/main/resources/META-INF/kobalt-core-plugin.xml +++ b/src/main/resources/META-INF/kobalt-core-plugin.xml @@ -15,6 +15,7 @@ com.beust.kobalt.plugin.groovy.GroovyPlugin com.beust.kobalt.internal.JvmCompilerPlugin com.beust.kobalt.internal.BuildListeners + com.beust.kobalt.plugin.osgi.OsgiPlugin com.beust.kobalt.app.Templates