{"id":13605024,"url":"https://github.com/2BAB/Koncat","last_synced_at":"2025-04-12T02:32:40.509Z","repository":{"id":37051045,"uuid":"433810120","full_name":"2BAB/Koncat","owner":"2BAB","description":"Aggregate Kotlin Symbols from multi-modules in compile-time.","archived":false,"fork":false,"pushed_at":"2022-07-18T13:38:38.000Z","size":502,"stargazers_count":59,"open_issues_count":3,"forks_count":5,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-10-28T14:24:13.436Z","etag":null,"topics":["android","annotation-processor","build-tool","kotlin"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/2BAB.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-12-01T12:03:51.000Z","updated_at":"2024-09-30T13:41:24.000Z","dependencies_parsed_at":"2022-06-25T05:24:17.711Z","dependency_job_id":null,"html_url":"https://github.com/2BAB/Koncat","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/2BAB%2FKoncat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/2BAB%2FKoncat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/2BAB%2FKoncat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/2BAB%2FKoncat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/2BAB","download_url":"https://codeload.github.com/2BAB/Koncat/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223489714,"owners_count":17153811,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","annotation-processor","build-tool","kotlin"],"created_at":"2024-08-01T19:00:53.827Z","updated_at":"2024-11-07T09:31:25.239Z","avatar_url":"https://github.com/2BAB.png","language":"Kotlin","funding_links":[],"categories":["Kotlin"],"sub_categories":[],"readme":"# Koncat\n\n\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/me.2bab/koncat-processor-api/badge.svg)](https://search.maven.org/artifact/me.2bab/koncat-processor-api) \n[![Actions Status](https://github.com/2bab/Koncat/workflows/CI/badge.svg)](https://github.com/2bab/Koncat/actions) \n[![Apache 2](https://img.shields.io/badge/License-Apache%202-brightgreen.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n\nAggregate Kotlin Symbols based on KSP for multi-modules development in compile-time. For instance, when you want to gather all implementations of an interface across multi-modules, Koncat must be the tool your shouldn't miss.\n\n## Usage\n\n**0x01. Add the plugin to classpath:**\n\n``` kotlin\n// Option 1.\n// Add `mavenCentral` to `pluginManagement{}` on settings.gradle.kts,\n// and koncat plugins ids.\npluginManagement {\n\tval koncatVer = \"2.1.0\"\n\trepositories {\n        ...\n        mavenCentral()\n    }\n    plugins {\n    \t...\n    \tid (\"me.2bab.koncat.android.app\") version koncatVer apply false\n        id (\"me.2bab.koncat.android.lib\") version koncatVer apply false\n        id (\"me.2bab.koncat.jvm\") version koncatVer apply false\n    }\n}\n\n\n// Option 2.\n// Using classic `buildscript{}` block in root build.gradle.kts.\nbuildscript {\n    repositories {\n        ...\n        mavenCentral()\n    }\n    dependencies {\n    \t...\n        classpath(\"me.2bab:koncat-gradle-plugin:2.1.0\")\n    }\n}\n```\n\n**0x02. Add Koncat Gradle Plugins, and config the Koncat DSL for per module:**\n\nWhere you applied KSP plugin should append the `me.2bab.koncat.*` plugin as well.\n\n``` kotlin\n// For Android Application module\nplugins {\n    id(\"com.android.application\")\n    kotlin(\"android\")\n    id(\"com.google.devtools.ksp\")\n    // .android.app plugin will set `declaredAsMainProject` as true by default\n    id(\"me.2bab.koncat.android.app\")  \u003c--\n}\n\n// For Android Library module\nplugins {\n    id(\"com.android.library\")\n    kotlin(\"android\")\n    id(\"com.google.devtools.ksp\")\n    id(\"me.2bab.koncat.android.lib\")  \u003c--\n}\n\n// For JVM library module\nplugins {\n    kotlin(\"jvm\") // or `java`, `groovy` plugins, etc.\n    id(\"com.google.devtools.ksp\")\n    id(\"me.2bab.koncat.jvm\")  \u003c--\n}\n\n\n// Common DSL for all above plugins\nkoncat {\n    /**\n     * To specify classes that annotated by the annotation list below should be aggregated.\n     * Anonymous classes are not supported.\n     */\n    annotations.addAll(\"me.xx2bab.koncat.sample.annotation.ExportActivity\")\n\n    /**\n     * To specify top-level classes that extend or implement from supertype list below\n     * should be aggregated. Indirect type search are supported.\n     * For example, `android.app.Activity` is passed into [classTypes],\n     * so that `BaseActivity` `MainActivity` which are implementations of `Activity` will be aggregated still.\n     */\n    classTypes.addAll(\"me.xx2bab.koncat.sample.interfaze.DummyAPI\")\n\n    /**\n     * To specify top-level properties that are declared as one of the type list below\n     * should be aggregated. Indirect type search are supported.\n     */\n    propertyTypes.addAll(\"org.koin.core.module.Module\")\n    \n    \n    /**\n     * To declare current working project (Gradle module in another word) as Main Project,\n     * the Main Project will collect all Koncat metadata from dependencies.\n     */\n    val declaredAsMainProject: Property\u003cBoolean\u003e = objects.property\u003cBoolean\u003e().convention(false)\n}\n```\n\n**0x03. Add koncat-processor \u0026 runtime APIs, and build your App!:**\n\n``` kotlin\ndependencies {\n    ksp(\"me.2bab:koncat-processor:${sameVersionAsPlugin}\")\n    implementation(\"me.2bab:koncat-runtime:${sameVersionAsPlugin}\")\n}\n```\n\nThe DSL configuration of `koncat{}` will be working with below APIs to retrieve aggregations.\n\n``` kotlin\nval koncat = Koncat()\n\n// Case 1: check an Activity permission request before you navigate to Koncat#getAnnotatedClasses(...)\nval libActivityMemberLvRequirement = koncat.getAnnotatedClasses(ExportActivity::class)!!\n    .first { it.name == \"me.xx2bab.koncat.sample.android.AndroidLibraryActivity\" }\n    .annotations\n    .first { it.name == \"me.xx2bab.koncat.sample.annotation.MemberRequired\" }\n    .arguments[\"level\"]\n\n\n// Case 2: register or run a set of services together with Koncat#getTypedClasses(...)\nval collectedInterfaces = koncat.getTypedClasses(DummyAPI::class)!!.map { constructor -\u003e\n    constructor().onCall(\"...\")\n}\n\n\n// Case 3: setup Koin modules with Koncat#getTypedProperties(...)\nstartKoin {\n    modules(koncat.getTypedProperties(Module::class) ?: listOf())\n}\n```\n\nCheck more on [here](./sample/app/src/main/kotlin/me/xx2bab/koncat/sample).\n\n**0x04. (Optional) Custom the Koncat final class generation:**\n\nFirstly, enable `generateExtensionClass` to export metadata from Koncat. You can also disable `generateAggregationClass` for default aggregation class generation if you don't use it anymore. (It will invalid the function of `Koncat` runtime API as well). \n\n``` kotlin\nkoncat {\n    /**\n     * To enable/disable the Aggregation Class generation.\n     * The Aggregation Class is actually `me.xx2bab.koncat.runtime.KoncatAggregation`,\n     * that will be used by `koncat-runtime` library in runtime,\n     * to replace the `koncat-stub` one which is an empty \u0026 compile-only placeholder.\n     */\n    val generateAggregationClass: Property\u003cBoolean\u003e = objects.property\u003cBoolean\u003e().convention(true)\n\n    /**\n     * To enable/disable the Extension Class generation.\n     * The Extension Class is actually `me.xx2bab.koncat.runtime.KoncatAggregatedMeta`,\n     * that will be used by 3rd party developers to customize the process of aggregated metadata.\n     * For example, to generate a custom Aggregation Class, or to generate an API/Route report\n     * during compile time.\n     */\n    val generateExtensionClass: Property\u003cBoolean\u003e = objects.property\u003cBoolean\u003e().convention(false)\n}\n```\n\nSecondly, create your own processor project and add `koncat-process-api` to your dependencies:\n\n``` kotlin\ndependencies {\n    implementation(\"me.2bab:koncat-processor-api:$latestVersion\")\n}\n```\n\nThen you should construct a KoncatProcAPI to your processor, Koncat will deal with the aggregating procedure and pass the final result to your custom processor:\n\n``` kotlin\nclass ExtensionProcessorProvider : SymbolProcessorProvider {\n    override fun create(\n        environment: SymbolProcessorEnvironment\n    ): SymbolProcessor {\n        return ExtensionProcessor(\n            environment.codeGenerator,\n            environment.logger,\n            KoncatProcAPIImpl(KSPAdapter(environment))  ①\n        )\n    }\n}\n\nclass ExtensionProcessor(\n    ...\n    private val koncat: KoncatProcAPI\n) : SymbolProcessor {\n\n    private var holder: KoncatProcMetadataHolder? = null\n\n    override fun process(resolver: Resolver): List\u003cKSAnnotated\u003e {\n        holder = koncat.syncAggregatedMetadata(resolver)  ②\n        return emptyList()\n    }\n\n    @OptIn(KotlinPoetKspPreview::class)\n    override fun finish {\n        super.finish()\n        holder?.apply {  ③\n            val fileSpec = RouterClassBuilder(resolve()).build()\n            fileSpec.writeTo(codeGenerator, Dependencies(false, dependency))\n        }\n    }\n\n    inner class RouterClassBuilder(\n        private val data: KoncatProcMetadata\n    ) {\n        fun build(): FileSpec {\n            val exportAPIs = data.typedClasses[\"me.xx2bab.koncat.sample.interfaze.DummyAPI\"]!!\n                .joinToString(separator = \", \") { \"\\\"$it\\\"\" }\n            ...\n        }\n    }\n}\n```\n\n- ① Initialize `KoncatProcAPI` by passing the `KSPAdapter` with current `SymbolProcessorEnvironment`.\n- ② When running on main project, koncat helps aggregate all intermediates from sub projects by `Koncat#syncAggregatedMetadata()`. To support multi-rounds process, we need to retain the latest one in a holder.\n- ③ On `finish()`, retrieve the latest `KoncatProcMetadataHolder`, and then \n    + Call `resolve()` to get the real `KoncatProcMetadata` object.\n    + Pass the built-in `dependency` to `Dependencies(...)`\n\nLastly, add the custom-processor to your main project(Android Application for example):\n\n``` kotlin\ndependencies {\n    ksp(\"com.company:custom-processor:$procVersion\")\n}\n```\n\nCheck more on [here](./sample/custom-processor/src/main/kotlin/me/xx2bab/koncat/sample/kotlin).\n\n## Compatible\n\nScratchPaper is only supported \u0026 tested on LATEST 2 Minor versions of Android Gradle Plugin and KSP. \n\nKoncat (Per minor version) |Suggested Env\n-----------|-----------------\n2.1.x | KSP 1.6.21-1.0.5 x AGP 7.2/7.3\n2.0.x | KSP 1.6.21-1.0.5 x AGP 7.1/7.2 \n1.0.x | KSP 1.6.10-1.0.4 x AGP 7.1/7.2\n\n\n## Why Koncat?\n\nA few precondition for Koncat used scenarios:\n\n1. The project has multiple Gradle modules.\n2. It requies to gather all meta info of annotated elements, for example a permission anntation on an Activity.\n3. And later generate a aggregated class for quering/reporting/etc.\n\nIf DI frameworks suits well with current project, for example using the `Multibinding` feature from Koin/Dagger/Hilt, then Koncat is not necessary for it.\n\nKoncat runs in compiler time, enhance the annotation processor capability:\n\n- It can save hundreds of millseconds for launch-time of the app comparing to the runtime aggregation.\n- It would be much earsier to generate source code file during AnnotationProcessor stage comparing to generate byte code during transforming stage.\n\n\n## Git Commit Check\n\nCheck this [link](https://medium.com/walmartlabs/check-out-these-5-git-tips-before-your-next-commit-c1c7a5ae34d1) to make sure everyone will make a **meaningful** commit message.\n\nSo far we haven't added any hook tool, but follow the regex below:\n\n```\n(chore|feature|doc|fix|refactor|style|test|hack|release|clean)(:)( )(.{0,80})\n```\n\n\n## License\n\n\u003e\n\u003e Copyright 2022 2BAB\n\u003e\n\u003eLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\u003e\n\u003e   http://www.apache.org/licenses/LICENSE-2.0\n\u003e\n\u003e Unless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F2BAB%2FKoncat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F2BAB%2FKoncat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F2BAB%2FKoncat/lists"}