<commit_before>package com.beust.kobalt.plugin.packaging

import aQute.bnd.osgi.Analyzer
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.IncludedFile
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.benchmarkMillis
import java.io.File
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) })
    }

    val zipToFiles = hashMapOf<String, List<IncludedFile>>()

    override fun assemble(project: Project, context: KobaltContext) : IncrementalTaskInfo {
        val allConfigs = packages.filter { it.project.name == project.name }

        val benchmark = benchmarkMillis {
            if (true) {
                val allIncludedFiles = arrayListOf<IncludedFile>()
                val outputArchives = arrayListOf<File>()
                allConfigs.forEach { packageConfig ->
                    listOf(packageConfig.jars, packageConfig.wars, packageConfig.zips).forEach { archives ->
                        archives.forEach {
                            val files = jarGenerator.findIncludedFiles(packageConfig.project, context, it)
                            val suffixIndex = it.name.lastIndexOf(".")
                            val suffix = it.name.substring(suffixIndex)
                            val outputFile = jarGenerator.fullArchiveName(project, context, it.name)
                            outputArchives.add(outputFile)
                            allIncludedFiles.addAll(files)
                            zipToFiles[it.name] = files
                        }
                    }
                }
                val allFiles = allIncludedFiles.fold(arrayListOf<File>()) { files, includedFile: IncludedFile ->
                    val foundFiles = includedFile.allFromFiles(project.directory)
                    val absFiles = foundFiles.map {
                        File(KFiles.joinDir(project.directory, includedFile.from, it.path))
                    }
                    files.addAll(absFiles)
                    files
                }

                val inMd5 = Md5.toMd5Directories(allFiles)
                val outMd5 = Md5.toMd5Directories(outputArchives)
                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 {
                        project.projectProperties.put(Archives.JAR_NAME,
                                context.variant.archiveName(project, null, ".jar"))

                        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, {
                p: 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)<commit_msg>Clarify and document.<commit_after>package com.beust.kobalt.plugin.packaging

import aQute.bnd.osgi.Analyzer
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.IncludedFile
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.benchmarkMillis
import java.io.File
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 `allFiles`
                //
                // `zipToFiles` is used again so we can pass that list of included files to the actual execution
                // of the task, so we don't have to look for them a second time
                //
                val allIncludedFiles = arrayListOf<IncludedFile>()
                val outputArchives = arrayListOf<File>()
                allConfigs.forEach { packageConfig ->
                    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)
                            outputArchives.add(outputFile)
                            allIncludedFiles.addAll(files)
                            zipToFiles[it.name] = files
                        }
                    }
                }
                val inputFiles = allIncludedFiles.fold(arrayListOf<File>()) { files, includedFile: IncludedFile ->
                    val foundFiles = includedFile.allFromFiles(project.directory)
                    val absFiles = foundFiles.map {
                        File(KFiles.joinDir(project.directory, includedFile.from, it.path))
                    }
                    files.addAll(absFiles)
                    files
                }

                val inMd5 = Md5.toMd5Directories(inputFiles)
                val outMd5 = Md5.toMd5Directories(outputArchives)
                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 {
                        project.projectProperties.put(Archives.JAR_NAME,
                                context.variant.archiveName(project, null, ".jar"))

                        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)
{'fraction_non_alphanumeric': 0.08385293485271984, 'fraction_numerical': 0.0015050526768436896, 'mean_word_length': 1.879139585267719, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '679519', 'n_tokens_mistral': 4908, 'n_tokens_neox': 4633, 'n_words': 947}