mirror of
https://github.com/ethauvin/kobalt.git
synced 2025-04-29 09:38:11 -07:00
361 lines
14 KiB
Kotlin
361 lines
14 KiB
Kotlin
package com.beust.kobalt.plugin.packaging
|
|
|
|
import com.beust.kobalt.*
|
|
import com.beust.kobalt.api.*
|
|
import com.beust.kobalt.api.annotation.Directive
|
|
import com.beust.kobalt.api.annotation.ExportedProjectProperty
|
|
import com.beust.kobalt.api.annotation.Task
|
|
import com.beust.kobalt.archive.*
|
|
import com.beust.kobalt.internal.IncrementalManager
|
|
import com.beust.kobalt.internal.JvmCompilerPlugin
|
|
import com.beust.kobalt.internal.ParallelLogger
|
|
import com.beust.kobalt.maven.DependencyManager
|
|
import com.beust.kobalt.maven.Md5
|
|
import com.beust.kobalt.maven.PomGenerator
|
|
import com.beust.kobalt.misc.KFiles
|
|
import com.beust.kobalt.misc.KobaltExecutors
|
|
import com.beust.kobalt.misc.benchmarkMillis
|
|
import java.io.File
|
|
import java.nio.file.Paths
|
|
import javax.inject.Inject
|
|
import javax.inject.Singleton
|
|
|
|
@Singleton
|
|
class PackagingPlugin @Inject constructor(val dependencyManager : DependencyManager,
|
|
val incrementalManagerFactory: IncrementalManager.IFactory,
|
|
val executors: KobaltExecutors, val jarGenerator: JarGenerator, val warGenerator: WarGenerator,
|
|
val zipGenerator: ZipGenerator, val taskContributor: TaskContributor,
|
|
val kobaltLog: ParallelLogger,
|
|
val pomFactory: PomGenerator.IFactory, val configActor: ConfigActor<InstallConfig>)
|
|
: BasePlugin(), ITaskContributor, IIncrementalAssemblyContributor,
|
|
IConfigActor<InstallConfig> by configActor {
|
|
|
|
companion object {
|
|
const val PLUGIN_NAME = "Packaging"
|
|
|
|
@ExportedProjectProperty(doc = "Where the libraries are saved", type = "String")
|
|
const val LIBS_DIR = "libsDir"
|
|
|
|
@ExportedProjectProperty(doc = "The list of packages produced for this project",
|
|
type = "List<PackageConfig>")
|
|
const val PACKAGES = "packages"
|
|
|
|
const val TASK_ASSEMBLE: String = "assemble"
|
|
const val TASK_INSTALL: String = "install"
|
|
}
|
|
|
|
override val name = PLUGIN_NAME
|
|
|
|
private val packages = arrayListOf<PackageConfig>()
|
|
|
|
override fun apply(project: Project, context: KobaltContext) {
|
|
super.apply(project, context)
|
|
project.projectProperties.put(LIBS_DIR, KFiles.libsDir(project))
|
|
project.projectProperties.put(PACKAGES, packages)
|
|
|
|
taskContributor.addVariantTasks(this, project, context, "assemble", group = "build",
|
|
dependsOn = listOf("compile"),
|
|
runTask = { doTaskAssemble(project) })
|
|
taskContributor.addVariantTasks(this, project, context, "install",
|
|
dependsOn = listOf("assemble"),
|
|
runTask = { taskInstall(project) })
|
|
}
|
|
|
|
override fun assemble(project: Project, context: KobaltContext) : IncrementalTaskInfo {
|
|
val allConfigs = packages.filter { it.project.name == project.name }
|
|
val zipToFiles = hashMapOf<String, List<IncludedFile>>()
|
|
|
|
val benchmark = benchmarkMillis {
|
|
if (true) {
|
|
//
|
|
// This loop prepares the data so we can calculate input and output checksums for the
|
|
// assemble task:
|
|
// - Input: Calculate the list of all the included files for every archive (jar/war/zip) and
|
|
// store them in the `zipToFiles` map.
|
|
// - Output: Calculate all the output archive files into `outputFiles`
|
|
//
|
|
// `zipToFiles` is used again after this loop so we can pass the list of included files we just
|
|
// calculated for all the archives in the actual execution of the task, so we don't have to
|
|
// look for them a second time.
|
|
//
|
|
val allIncludedFiles = arrayListOf<IncludedFile>()
|
|
val outputFiles = arrayListOf<File>()
|
|
val jarsWithMainClass = arrayListOf<String>()
|
|
|
|
allConfigs.forEach { packageConfig ->
|
|
packageConfig.jars.forEach {
|
|
if (it.attributes.any{ it.first == "Main-Class"}) {
|
|
jarsWithMainClass.add(it.name)
|
|
}
|
|
}
|
|
|
|
listOf(packageConfig.jars, packageConfig.wars, packageConfig.zips).forEach { archives ->
|
|
archives.forEach {
|
|
val files = jarGenerator.findIncludedFiles(packageConfig.project, context, it)
|
|
val outputFile = jarGenerator.fullArchiveName(project, context, it.name)
|
|
outputFiles.add(outputFile)
|
|
allIncludedFiles.addAll(files)
|
|
zipToFiles[it.name] = files
|
|
}
|
|
}
|
|
}
|
|
|
|
if (jarsWithMainClass.any()) {
|
|
project.projectProperties.put(Archives.JAR_NAME_WITH_MAIN_CLASS, jarsWithMainClass[0])
|
|
}
|
|
project.projectProperties.put(Archives.JAR_NAME,
|
|
context.variant.archiveName(project, null, ".jar"))
|
|
|
|
// Turn the IncludedFiles into actual Files
|
|
val inputFiles = KFiles.materializeIncludedFiles(project, allIncludedFiles)
|
|
|
|
val inMd5 = Md5.toMd5Directories(inputFiles)
|
|
val outMd5 = Md5.toMd5Directories(outputFiles)
|
|
Pair(inMd5, outMd5)
|
|
} else {
|
|
Pair(null, null)
|
|
}
|
|
}
|
|
|
|
context.logger.log(project.name, 2, " Time to calculate packaging checksum: ${benchmark.first} ms")
|
|
|
|
val (inMd5, outMd5) = benchmark.second
|
|
|
|
return IncrementalTaskInfo(
|
|
{ -> inMd5 },
|
|
{ -> outMd5 },
|
|
{ project ->
|
|
try {
|
|
fun findFiles(ff: ArchiveGenerator, zip: Zip) : List<IncludedFile> {
|
|
val archiveName = ff.fullArchiveName(project, context, zip.name).name
|
|
return zipToFiles[archiveName]!!
|
|
}
|
|
|
|
allConfigs.forEach { packageConfig ->
|
|
val pairs = listOf(
|
|
Pair(packageConfig.jars, jarGenerator),
|
|
Pair(packageConfig.wars, warGenerator),
|
|
Pair(packageConfig.zips, zipGenerator)
|
|
)
|
|
|
|
pairs.forEach { pair ->
|
|
val zips = pair.first
|
|
val generator = pair.second
|
|
zips.forEach {
|
|
generator.generateArchive(packageConfig.project, context, it,
|
|
findFiles(generator, it))
|
|
}
|
|
}
|
|
if (packageConfig.generatePom) {
|
|
pomFactory.create(project).generateAndSave()
|
|
}
|
|
}
|
|
TaskResult()
|
|
} catch(ex: Exception) {
|
|
throw KobaltException(ex)
|
|
}}, context)
|
|
}
|
|
|
|
@Task(name = TASK_ASSEMBLE, description = "Package the artifacts", group = JvmCompilerPlugin.GROUP_BUILD,
|
|
dependsOn = arrayOf(JvmCompilerPlugin.TASK_COMPILE))
|
|
fun doTaskAssemble(project: Project) : TaskResult {
|
|
// Incremental assembly contributors
|
|
context.pluginInfo.incrementalAssemblyContributors.forEach {
|
|
val taskInfo = it.assemble(project, context)
|
|
val closure = incrementalManagerFactory.create().toIncrementalTaskClosure(TASK_ASSEMBLE, {
|
|
_: Project -> taskInfo }, context.variant)
|
|
val thisResult = closure.invoke(project)
|
|
if (! thisResult.success) {
|
|
// Abort at the first failure
|
|
return thisResult
|
|
}
|
|
}
|
|
|
|
// Regular assembly contributors
|
|
context.pluginInfo.assemblyContributors.forEach {
|
|
val thisResult = it.assemble(project, context)
|
|
if (! thisResult.success) {
|
|
// Abort at the first failure
|
|
return thisResult
|
|
}
|
|
}
|
|
return TaskResult()
|
|
}
|
|
|
|
fun addPackage(p: PackageConfig) {
|
|
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()
|
|
// }
|
|
|
|
|
|
@Task(name = PackagingPlugin.TASK_INSTALL, description = "Install the artifacts",
|
|
dependsOn = arrayOf(PackagingPlugin.TASK_ASSEMBLE))
|
|
fun taskInstall(project: Project) : TaskResult {
|
|
val config = configurationFor(project) ?: InstallConfig()
|
|
val buildDir = project.projectProperties.getString(LIBS_DIR)
|
|
val buildDirFile = File(buildDir)
|
|
if (buildDirFile.exists()) {
|
|
|
|
if (config.includedFiles.isEmpty()) {
|
|
context.logger.log(project.name, 1, " Installing from $buildDir to ${config.target}")
|
|
val toDir = KFiles.makeDir(config.target)
|
|
File(buildDir).copyRecursively(toDir, overwrite = true)
|
|
} else {
|
|
// Delete all target directories
|
|
config.includedFiles.map { File(it.to) }.distinct().forEach { targetFile ->
|
|
val isFile = targetFile.isFile
|
|
context.logger.log(project.name, 2, " Deleting target dir $targetFile")
|
|
targetFile.deleteRecursively()
|
|
if (! isFile) targetFile.mkdirs()
|
|
}
|
|
// Perform the installations
|
|
config.includedFiles.forEach { inf ->
|
|
val targetFile = File(inf.to)
|
|
val files = KFiles.materializeIncludedFiles(project, listOf(inf))
|
|
files.forEach {
|
|
context.logger.log(project.name, 1, " Installing $it to $targetFile")
|
|
KFiles.copyRecursively(it, targetFile, true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return TaskResult()
|
|
}
|
|
|
|
//ITaskContributor
|
|
override fun tasksFor(project: Project, context: KobaltContext): List<DynamicTask> = taskContributor.dynamicTasks
|
|
}
|
|
|
|
@Directive
|
|
fun Project.install(init: InstallConfig.() -> Unit) {
|
|
InstallConfig().let {
|
|
it.init()
|
|
(Kobalt.findPlugin(PackagingPlugin.PLUGIN_NAME) as PackagingPlugin).addConfiguration(this, it)
|
|
}
|
|
}
|
|
|
|
class InstallConfig(var target : String = "libs") : IncludeFromTo()
|
|
|
|
@Directive
|
|
fun Project.assemble(init: PackageConfig.(p: Project) -> Unit): PackageConfig = let {
|
|
PackageConfig(this).apply { init(it) }
|
|
}
|
|
|
|
class PackageConfig(val project: Project) : AttributeHolder {
|
|
val jars = arrayListOf<Jar>()
|
|
val wars = arrayListOf<War>()
|
|
val zips = arrayListOf<Zip>()
|
|
var generatePom: Boolean = false
|
|
|
|
init {
|
|
(Kobalt.findPlugin(PackagingPlugin.PLUGIN_NAME) as PackagingPlugin).addPackage(this)
|
|
}
|
|
|
|
@Directive
|
|
fun jar(init: Jar.(p: Jar) -> Unit) = Jar(project).apply {
|
|
init(this)
|
|
jars.add(this)
|
|
}
|
|
|
|
@Directive
|
|
fun zip(init: Zip.(p: Zip) -> Unit) = Zip(project).apply {
|
|
init(this)
|
|
zips.add(this)
|
|
}
|
|
|
|
@Directive
|
|
fun war(init: War.(p: War) -> Unit) = War(project).apply {
|
|
init(this)
|
|
wars.add(this)
|
|
}
|
|
|
|
/**
|
|
* Package all the jar files necessary for a maven repo: classes, sources, javadocs.
|
|
*/
|
|
@Directive
|
|
fun mavenJars(init: MavenJars.(p: MavenJars) -> Unit) : MavenJars {
|
|
val m = MavenJars(this)
|
|
m.init(m)
|
|
|
|
val mainJar = jar {
|
|
fatJar = m.fatJar
|
|
}
|
|
jar {
|
|
name = "${project.name}-${project.version}-sources.jar"
|
|
project.sourceDirectories.forEach {
|
|
if (File(project.directory, it).exists()) {
|
|
include(From(it), To(""), glob("**"))
|
|
}
|
|
}
|
|
}
|
|
jar {
|
|
name = "${project.name}-${project.version}-javadoc.jar"
|
|
val fromDir = KFiles.joinDir(project.buildDirectory, JvmCompilerPlugin.DOCS_DIRECTORY)
|
|
include(From(fromDir), To(""), glob("**"))
|
|
}
|
|
|
|
mainJarAttributes.forEach {
|
|
mainJar.addAttribute(it.first, it.second)
|
|
}
|
|
|
|
generatePom = true
|
|
|
|
return m
|
|
}
|
|
|
|
val mainJarAttributes = arrayListOf<Pair<String, String>>()
|
|
|
|
override fun addAttribute(k: String, v: String) {
|
|
mainJarAttributes.add(Pair(k, v))
|
|
}
|
|
|
|
class MavenJars(val ah: AttributeHolder, var fatJar: Boolean = false, var manifest: Manifest? = null) :
|
|
AttributeHolder by ah {
|
|
fun manifest(init: Manifest.(p: Manifest) -> Unit) : Manifest {
|
|
val m = Manifest(this)
|
|
m.init(m)
|
|
return m
|
|
}
|
|
}
|
|
}
|
|
|
|
class Pom {
|
|
|
|
}
|
|
|
|
fun main(args: Array<String>) {
|
|
val realSource = File("/tmp/a")
|
|
val sourceDir = File(realSource, "b").apply { mkdirs() }
|
|
val from = File(sourceDir, "foo").apply { writeText("I'm a file") }
|
|
val to = File("/tmp/to").apply {
|
|
deleteRecursively()
|
|
mkdirs()
|
|
}
|
|
val sourcePath = Paths.get(realSource.toURI())
|
|
val targetPath = Paths.get(to.toURI())
|
|
// Files.walkFileTree(sourcePath, KFiles.Companion.CopyFileVisitor(targetPath))
|
|
|
|
if (! to.isDirectory) {
|
|
throw AssertionError("Should be a directory")
|
|
}
|
|
}
|