From 0c30ca8f649feda2732b364320840e3be65dc043 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Sat, 15 Jun 2024 13:54:07 +0300 Subject: [PATCH 01/24] Prepare project for kmp --- build.gradle.kts | 9 +++------ gradle.properties | 4 ++++ gradle/libs.versions.toml | 4 ++++ gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 15 +++++++++++++++ 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index f6220c5..5a88a6c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,8 @@ val VERSION_NAME: String by project val GROUP: String by project plugins { - kotlin("jvm") version libs.versions.kotlin.get() + kotlin("jvm") version libs.versions.kotlin.get() apply false + kotlin("multiplatform") version libs.versions.kotlin.get() apply false alias(libs.plugins.spotless) alias(libs.plugins.mavenPublish) apply false } @@ -12,10 +13,6 @@ plugins { group = GROUP version = VERSION_NAME -repositories { - mavenCentral() -} - tasks.withType { kotlinOptions.jvmTarget = "1.8" } @@ -32,4 +29,4 @@ subprojects { licenseHeaderFile(rootProject.file("spotless/copyright.kt")) } } -} \ No newline at end of file +} diff --git a/gradle.properties b/gradle.properties index b06f8b6..32efd4c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,8 @@ kotlin.code.style=official +# KMP +kotlin.js.compiler=ir +kotlin.mpp.androidSourceSetLayoutVersion=2 +kotlin.mpp.androidGradlePluginCompatibility.nowarn=true GROUP=dev.shreyaspatil.compose-compiler-report-generator VERSION_NAME=1.3.1 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4b3c98f..52e9937 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,6 +5,8 @@ coroutines = "1.8.1" spotless = "6.25.0" kotlinxHtml = "0.11.0" kotlinxCli = "0.3.6" +okio = "3.9.0" +compose = "1.6.10-rc03" mavenPublish = "0.28.0" androidGradlePlugin = "8.3.0" gradlePluginPublish = "1.2.1" @@ -14,8 +16,10 @@ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serializa kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } kotlinx-html-jvm = { module = "org.jetbrains.kotlinx:kotlinx-html-jvm", version.ref = "kotlinxHtml" } kotlinx-cli = { module = "org.jetbrains.kotlinx:kotlinx-cli", version.ref = "kotlinxCli" } +okio = { module = "com.squareup.okio:okio", version.ref = "okio" } android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "androidGradlePlugin" } kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +compose-multiplatform-gradle-plugin = { module = "org.jetbrains.compose:compose-gradle-plugin", version.ref = "compose" } [plugins] spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1b298ae..a8382d7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 789ebe2..af7bd0c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,18 @@ +pluginManagement { + repositories { + mavenCentral() + google() + gradlePluginPortal() + maven(url = "https://plugins.gradle.org/m2/") + } +} + +dependencyResolutionManagement { + repositories { + mavenCentral() + google() + } +} rootProject.name = "compose-report-to-html" From 37cbecbeadc0324c9c4aa9e80ac30a9b4e28b784 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Sat, 15 Jun 2024 14:00:18 +0300 Subject: [PATCH 02/24] Convert core module to multiplatform --- core/build.gradle.kts | 19 ++++++---- .../core/ComposeCompilerMetricsProvider.kt | 0 .../core/ComposeCompilerRawReportProvider.kt | 28 +++++++-------- .../core/ComposeMetricsContentProvider.kt | 25 +++++++++++-- .../core/exception/ParsingException.kt | 0 .../core/file/ReportAndMetricsFileFinder.kt | 19 +++++----- .../core/mapper/ConditionMapper.kt | 0 .../core/model/Condition.kt | 0 .../core/model/DetailedStatistics.kt | 0 .../core/model/RawContent.kt | 0 .../core/model/classes/ClassDetail.kt | 0 .../core/model/classes/ClassesReport.kt | 0 .../model/composables/ComposableDetail.kt | 0 .../model/composables/ComposablesReport.kt | 0 .../core/parser/ClassReportParser.kt | 0 .../core/parser/ComposableReportParser.kt | 0 .../core/parser/Parser.kt | 0 .../core/utils/CamelCaseToWord.kt | 0 .../core/utils/FileUtils.kt | 36 +++++++++++-------- 19 files changed, 77 insertions(+), 50 deletions(-) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerMetricsProvider.kt (100%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerRawReportProvider.kt (78%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeMetricsContentProvider.kt (71%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/exception/ParsingException.kt (100%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/file/ReportAndMetricsFileFinder.kt (83%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/mapper/ConditionMapper.kt (100%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/Condition.kt (100%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/DetailedStatistics.kt (100%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/RawContent.kt (100%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassDetail.kt (100%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassesReport.kt (100%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposableDetail.kt (100%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposablesReport.kt (100%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ClassReportParser.kt (100%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ComposableReportParser.kt (100%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/Parser.kt (100%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/CamelCaseToWord.kt (100%) rename core/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/FileUtils.kt (69%) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index d7042db..7ed9e46 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,14 +1,19 @@ plugins { - kotlin("jvm") + kotlin("multiplatform") kotlin("plugin.serialization") version libs.versions.kotlin.get() id(libs.plugins.mavenPublish.get().pluginId) } -repositories { - mavenCentral() -} +kotlin { + applyDefaultHierarchyTemplate() + + jvm() -dependencies { - implementation(kotlin("stdlib")) - implementation(libs.kotlinx.serialization.json) + sourceSets { + commonMain.dependencies { + implementation(libs.okio) + implementation(kotlin("stdlib")) + implementation(libs.kotlinx.serialization.json) + } + } } diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerMetricsProvider.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerMetricsProvider.kt similarity index 100% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerMetricsProvider.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerMetricsProvider.kt diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerRawReportProvider.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerRawReportProvider.kt similarity index 78% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerRawReportProvider.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerRawReportProvider.kt index 7a97b28..5f964b5 100644 --- a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerRawReportProvider.kt +++ b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerRawReportProvider.kt @@ -26,25 +26,25 @@ package dev.shreyaspatil.composeCompilerMetricsGenerator.core import dev.shreyaspatil.composeCompilerMetricsGenerator.core.file.ReportAndMetricsFileFinder import dev.shreyaspatil.composeCompilerMetricsGenerator.core.utils.ensureDirectory import dev.shreyaspatil.composeCompilerMetricsGenerator.core.utils.ensureFileExists -import java.io.File +import okio.Path /** * Provide files of compose compiler metrics and reports */ sealed interface ComposeCompilerRawReportProvider { - val briefStatisticsJsonFiles: List - val detailedStatisticsCsvFiles: List - val composableReportFiles: List - val classesReportFiles: List + val briefStatisticsJsonFiles: List + val detailedStatisticsCsvFiles: List + val composableReportFiles: List + val classesReportFiles: List /** * Provides report from individual files */ class FromIndividualFiles( - override val briefStatisticsJsonFiles: List, - override val detailedStatisticsCsvFiles: List, - override val composableReportFiles: List, - override val classesReportFiles: List, + override val briefStatisticsJsonFiles: List, + override val detailedStatisticsCsvFiles: List, + override val composableReportFiles: List, + override val classesReportFiles: List, ) : ComposeCompilerRawReportProvider { init { validateComposeCompilerRawReportProvider() @@ -54,13 +54,13 @@ sealed interface ComposeCompilerRawReportProvider { /** * Searches for files in the given [directory] and provides report and metric files found in that directory. */ - class FromDirectory(directory: File) : ComposeCompilerRawReportProvider { + class FromDirectory(directory: Path) : ComposeCompilerRawReportProvider { private val finder = ReportAndMetricsFileFinder(directory) - override val briefStatisticsJsonFiles: List = finder.findBriefStatisticsJsonFile() - override val detailedStatisticsCsvFiles: List = finder.findDetailsStatisticsCsvFile() - override val composableReportFiles: List = finder.findComposablesReportTxtFile() - override val classesReportFiles: List = finder.findClassesReportTxtFile() + override val briefStatisticsJsonFiles: List = finder.findBriefStatisticsJsonFile() + override val detailedStatisticsCsvFiles: List = finder.findDetailsStatisticsCsvFile() + override val composableReportFiles: List = finder.findComposablesReportTxtFile() + override val classesReportFiles: List = finder.findClassesReportTxtFile() init { ensureDirectory(directory) { "Directory '$directory' not exists" } diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeMetricsContentProvider.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeMetricsContentProvider.kt similarity index 71% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeMetricsContentProvider.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeMetricsContentProvider.kt index db64f48..7bf51f6 100644 --- a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeMetricsContentProvider.kt +++ b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeMetricsContentProvider.kt @@ -23,12 +23,31 @@ */ package dev.shreyaspatil.composeCompilerMetricsGenerator.core +import okio.FileSystem +import okio.Path + /** * Provides content of a composable report and metrics */ class ComposeMetricsContentProvider(private val fileProvider: ComposeCompilerRawReportProvider) { - val briefStatisticsContents: List get() = fileProvider.briefStatisticsJsonFiles.map { it.readText() } + val briefStatisticsContents: List get() = fileProvider.briefStatisticsJsonFiles.map { it.readUtf8() } val detailedStatisticsCsvRows: List get() = fileProvider.detailedStatisticsCsvFiles.flatMap { it.readLines() } - val composablesReportContents: String get() = fileProvider.composableReportFiles.joinToString(separator = "\n") { it.readText() } - val classesReportContents: String get() = fileProvider.classesReportFiles.joinToString(separator = "\n") { it.readText() } + val composablesReportContents: String get() = fileProvider.composableReportFiles.joinToString(separator = "\n") { it.readUtf8() } + val classesReportContents: String get() = fileProvider.classesReportFiles.joinToString(separator = "\n") { it.readUtf8() } + private val fileSystem = FileSystem.SYSTEM + + private fun Path.readUtf8(): String { + return fileSystem.read(this) { readUtf8() } + } + + private fun Path.readLines(): List { + val result = mutableListOf() + fileSystem.read(this) { + while (true) { + val line = readUtf8Line() ?: break + result += line + } + } + return result + } } diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/exception/ParsingException.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/exception/ParsingException.kt similarity index 100% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/exception/ParsingException.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/exception/ParsingException.kt diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/file/ReportAndMetricsFileFinder.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/file/ReportAndMetricsFileFinder.kt similarity index 83% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/file/ReportAndMetricsFileFinder.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/file/ReportAndMetricsFileFinder.kt index e781505..3d9d37a 100644 --- a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/file/ReportAndMetricsFileFinder.kt +++ b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/file/ReportAndMetricsFileFinder.kt @@ -23,33 +23,30 @@ */ package dev.shreyaspatil.composeCompilerMetricsGenerator.core.file -import java.io.File +import okio.FileSystem +import okio.Path /** * Finds reports and metrics files generated by Compose compiler * * @param directory The directory in which files will be searched */ -class ReportAndMetricsFileFinder(directory: File) { - private val allFiles = - directory - .listFiles() - ?.filterNotNull() - ?: emptyList() +class ReportAndMetricsFileFinder(directory: Path) { + private val allFiles = FileSystem.SYSTEM.list(directory) - fun findBriefStatisticsJsonFile(): List { + fun findBriefStatisticsJsonFile(): List { return allFiles.filter { it.name.endsWith(FileSuffixes.MODULE_REPORT_JSON) } } - fun findDetailsStatisticsCsvFile(): List { + fun findDetailsStatisticsCsvFile(): List { return allFiles.filter { it.name.endsWith(FileSuffixes.COMPOSABLES_STATS_METRICS_CSV) } } - fun findComposablesReportTxtFile(): List { + fun findComposablesReportTxtFile(): List { return allFiles.filter { it.name.endsWith(FileSuffixes.COMPOSABLES_REPORT_TXT) } } - fun findClassesReportTxtFile(): List { + fun findClassesReportTxtFile(): List { return allFiles.filter { it.name.endsWith(FileSuffixes.CLASSES_REPORT_TXT) } } diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/mapper/ConditionMapper.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/mapper/ConditionMapper.kt similarity index 100% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/mapper/ConditionMapper.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/mapper/ConditionMapper.kt diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/Condition.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/Condition.kt similarity index 100% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/Condition.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/Condition.kt diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/DetailedStatistics.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/DetailedStatistics.kt similarity index 100% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/DetailedStatistics.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/DetailedStatistics.kt diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/RawContent.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/RawContent.kt similarity index 100% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/RawContent.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/RawContent.kt diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassDetail.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassDetail.kt similarity index 100% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassDetail.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassDetail.kt diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassesReport.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassesReport.kt similarity index 100% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassesReport.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassesReport.kt diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposableDetail.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposableDetail.kt similarity index 100% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposableDetail.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposableDetail.kt diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposablesReport.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposablesReport.kt similarity index 100% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposablesReport.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposablesReport.kt diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ClassReportParser.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ClassReportParser.kt similarity index 100% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ClassReportParser.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ClassReportParser.kt diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ComposableReportParser.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ComposableReportParser.kt similarity index 100% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ComposableReportParser.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ComposableReportParser.kt diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/Parser.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/Parser.kt similarity index 100% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/Parser.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/Parser.kt diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/CamelCaseToWord.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/CamelCaseToWord.kt similarity index 100% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/CamelCaseToWord.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/CamelCaseToWord.kt diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/FileUtils.kt b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/FileUtils.kt similarity index 69% rename from core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/FileUtils.kt rename to core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/FileUtils.kt index b5e4c25..1e858a7 100644 --- a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/FileUtils.kt +++ b/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/FileUtils.kt @@ -23,9 +23,10 @@ */ package dev.shreyaspatil.composeCompilerMetricsGenerator.core.utils -import java.io.File -import java.io.FileNotFoundException -import java.nio.file.Paths +import okio.FileNotFoundException +import okio.FileSystem +import okio.Path +import okio.Path.Companion.toPath /** * Checks whether directory with [path] exists or not. @@ -35,8 +36,8 @@ inline fun ensureDirectory( path: String, lazyMessage: () -> Any, ) { - val file = File(Paths.get(path).toAbsolutePath().toString()) - ensureDirectory(file, lazyMessage) + val absolutePath = FileSystem.SYSTEM.canonicalize(path.toPath()) + ensureDirectory(absolutePath, lazyMessage) } /** @@ -44,11 +45,14 @@ inline fun ensureDirectory( * Else throws [FileNotFoundException]. */ inline fun ensureDirectory( - directory: File, + directory: Path, lazyMessage: () -> Any, ) { - println("Checking directory '${directory.absolutePath}'") - if (!directory.isDirectory) { + val fileSystem = FileSystem.SYSTEM + val absolutePath = if (directory.isAbsolute) directory else fileSystem.canonicalize(directory) + println("Checking directory '$absolutePath'") + val isDirectory = fileSystem.metadataOrNull(directory)?.isDirectory == true + if (!isDirectory) { val message = lazyMessage() throw FileNotFoundException(message.toString()) } @@ -60,20 +64,22 @@ inline fun ensureDirectory( inline fun ensureFileExists( filename: String, lazyMessage: () -> Any, -): File { - val file = File(Paths.get(filename).toAbsolutePath().toString()) - return ensureFileExists(file, lazyMessage) +): Path { + val absolutePath = FileSystem.SYSTEM.canonicalize(filename.toPath()) + return ensureFileExists(absolutePath, lazyMessage) } /** * Checks whether [file] with exists or not. Else throws [FileNotFoundException]. */ inline fun ensureFileExists( - file: File, + file: Path, lazyMessage: () -> Any, -): File { - println("Checking file '${file.absolutePath}'") - if (!file.exists()) { +): Path { + val fileSystem = FileSystem.SYSTEM + val absolutePath = if (file.isAbsolute) file else fileSystem.canonicalize(file) + println("Checking file '$absolutePath'") + if (!fileSystem.exists(file)) { val message = lazyMessage() throw FileNotFoundException(message.toString()) } From c12ee30b375944d23371181f00af4f08bd3abb85 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Sat, 15 Jun 2024 14:06:19 +0300 Subject: [PATCH 03/24] Convert report-generator module to multiplatform --- report-generator/build.gradle.kts | 23 +++++++++++-------- .../generator/HtmlReportGenerator.kt | 0 .../generator/ReportSpec.kt | 0 .../generator/content/BriefStatistics.kt | 0 .../generator/content/ClassReport.kt | 0 .../generator/content/ComposableReport.kt | 0 .../generator/content/DetailedStatistics.kt | 0 .../generator/content/ErrorReports.kt | 0 .../generator/content/Footer.kt | 0 .../generator/content/MainContent.kt | 0 .../content/common/CollapsibleContent.kt | 0 .../generator/content/common/EmptyContent.kt | 0 .../generator/content/common/IconText.kt | 0 .../generator/content/common/Icons.kt | 0 .../generator/content/common/Svg.kt | 0 .../generator/script/CollapsibleScript.kt | 0 .../generator/style/Colors.kt | 0 .../generator/style/CssStyle.kt | 0 .../generator/style/Fonts.kt | 0 .../generator/style/PageStyle.kt | 0 .../generator/utils/ListExt.kt | 0 .../generator/utils/StringExt.kt | 0 22 files changed, 14 insertions(+), 9 deletions(-) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/HtmlReportGenerator.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/ReportSpec.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/BriefStatistics.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ClassReport.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ComposableReport.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/DetailedStatistics.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ErrorReports.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/Footer.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/MainContent.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/CollapsibleContent.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/EmptyContent.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/IconText.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Icons.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Svg.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/script/CollapsibleScript.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Colors.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/CssStyle.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Fonts.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/PageStyle.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/ListExt.kt (100%) rename report-generator/src/{main => commonMain}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/StringExt.kt (100%) diff --git a/report-generator/build.gradle.kts b/report-generator/build.gradle.kts index fcbc698..e78f607 100644 --- a/report-generator/build.gradle.kts +++ b/report-generator/build.gradle.kts @@ -1,15 +1,20 @@ plugins { - kotlin("jvm") + kotlin("multiplatform") id(libs.plugins.mavenPublish.get().pluginId) } -repositories { - mavenCentral() -} -dependencies { - implementation(project(":core")) - implementation(kotlin("stdlib")) +kotlin { + applyDefaultHierarchyTemplate() + + jvm() + + sourceSets { + commonMain.dependencies { + implementation(project(":core")) + implementation(kotlin("stdlib")) - implementation(libs.kotlinx.coroutines.core) - implementation(libs.kotlinx.html.jvm) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.html.jvm) + } + } } diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/HtmlReportGenerator.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/HtmlReportGenerator.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/HtmlReportGenerator.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/HtmlReportGenerator.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/ReportSpec.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/ReportSpec.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/ReportSpec.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/ReportSpec.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/BriefStatistics.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/BriefStatistics.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/BriefStatistics.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/BriefStatistics.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ClassReport.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ClassReport.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ClassReport.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ClassReport.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ComposableReport.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ComposableReport.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ComposableReport.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ComposableReport.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/DetailedStatistics.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/DetailedStatistics.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/DetailedStatistics.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/DetailedStatistics.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ErrorReports.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ErrorReports.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ErrorReports.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ErrorReports.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/Footer.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/Footer.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/Footer.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/Footer.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/MainContent.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/MainContent.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/MainContent.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/MainContent.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/CollapsibleContent.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/CollapsibleContent.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/CollapsibleContent.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/CollapsibleContent.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/EmptyContent.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/EmptyContent.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/EmptyContent.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/EmptyContent.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/IconText.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/IconText.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/IconText.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/IconText.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Icons.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Icons.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Icons.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Icons.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Svg.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Svg.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Svg.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Svg.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/script/CollapsibleScript.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/script/CollapsibleScript.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/script/CollapsibleScript.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/script/CollapsibleScript.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Colors.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Colors.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Colors.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Colors.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/CssStyle.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/CssStyle.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/CssStyle.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/CssStyle.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Fonts.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Fonts.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Fonts.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Fonts.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/PageStyle.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/PageStyle.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/PageStyle.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/PageStyle.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/ListExt.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/ListExt.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/ListExt.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/ListExt.kt diff --git a/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/StringExt.kt b/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/StringExt.kt similarity index 100% rename from report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/StringExt.kt rename to report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/StringExt.kt From ccdf1e29cd849eec8571171764509555d6565a4e Mon Sep 17 00:00:00 2001 From: MR3Y Date: Sat, 15 Jun 2024 14:07:37 +0300 Subject: [PATCH 04/24] Update core usages in cli module --- cli/build.gradle.kts | 5 +---- .../shreyaspatil/composeCompilerMetricsGenerator/cli/Main.kt | 5 +++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cli/build.gradle.kts b/cli/build.gradle.kts index 97846f4..3485468 100644 --- a/cli/build.gradle.kts +++ b/cli/build.gradle.kts @@ -7,15 +7,12 @@ application { mainClass.set(mainCliClassName) } -repositories { - mavenCentral() -} - dependencies { implementation(project(":core")) implementation(project(":report-generator")) implementation(kotlin("stdlib")) + implementation(libs.okio) implementation(libs.kotlinx.cli) } diff --git a/cli/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/cli/Main.kt b/cli/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/cli/Main.kt index 9422ab5..0d93a83 100644 --- a/cli/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/cli/Main.kt +++ b/cli/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/cli/Main.kt @@ -34,6 +34,7 @@ import kotlinx.cli.ArgParser import kotlinx.cli.ArgType import kotlinx.cli.default import kotlinx.cli.required +import okio.Path.Companion.toOkioPath import java.io.File import java.nio.file.Path import java.nio.file.Paths @@ -205,10 +206,10 @@ class CliArguments(args: Array, private val path: Path) { } private fun getRawReportProviderFromDirectory(directory: String): ComposeCompilerRawReportProvider.FromDirectory { - return ComposeCompilerRawReportProvider.FromDirectory(File(Paths.get(directory).toAbsolutePath().toString())) + return ComposeCompilerRawReportProvider.FromDirectory(File(Paths.get(directory).toAbsolutePath().toString()).toOkioPath()) } - private fun files(filenames: String): List { + private fun files(filenames: String): List { return filenames.split(",").map { ensureFileExists(it) { "File not exist: $it" } } } } From e412b7a3e87c35255ab444dc70a773db285b4eb7 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Sat, 15 Jun 2024 17:17:38 +0300 Subject: [PATCH 05/24] Support compose multiplatform in gradle plugin --- gradle-plugin/build.gradle.kts | 9 +- .../plugin/ReportGenPlugin.kt | 110 ++++++++++++++++-- .../task/ComposeCompilerReportGenerateTask.kt | 24 +++- 3 files changed, 124 insertions(+), 19 deletions(-) diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index a3ee88f..6d40140 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -9,13 +9,6 @@ plugins { alias(libs.plugins.gradle.plugin.publish) } -repositories { - mavenCentral() - google() - gradlePluginPortal() - maven(url = "https://plugins.gradle.org/m2/") -} - group = GROUP version = VERSION_NAME @@ -24,9 +17,11 @@ dependencies { compileOnly(kotlin("stdlib")) compileOnly(libs.kotlin.gradle.plugin) compileOnly(libs.android.gradle.plugin) + compileOnly(libs.compose.multiplatform.gradle.plugin) implementation(project(":core")) implementation(project(":report-generator")) + implementation(libs.okio) } tasks.getByName("test") { diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt index 486b695..9ce02ce 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt @@ -26,12 +26,16 @@ package dev.shreyaspatil.composeCompilerMetricsGenerator.plugin import com.android.build.api.dsl.CommonExtension import com.android.build.api.variant.AndroidComponentsExtension import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.executingComposeCompilerReportGenerationGradleTask +import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.registerComposeCompilerReportGenTaskForTarget import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.registerComposeCompilerReportGenTaskForVariant import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.getByType +import org.jetbrains.compose.ComposeExtension import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions +import org.jetbrains.kotlin.gradle.dsl.KotlinCommonToolOptions +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension @Suppress("UnstableApiUsage") class ReportGenPlugin : Plugin { @@ -41,25 +45,109 @@ class ReportGenPlugin : Plugin { val android = runCatching { target.extensions.getByType(AndroidComponentsExtension::class.java) - }.getOrElse { error("This plugin is only applicable for Android modules") } + }.getOrNull() + val jvm = + runCatching { + target.extensions.getByType(KotlinJvmProjectExtension::class.java) + }.getOrNull() + val multiplatform = + runCatching { + target.extensions.getByType(KotlinMultiplatformExtension::class.java) + }.getOrNull() + val composeMultiplatform = + runCatching { + target.extensions.getByType(ComposeExtension::class.java) + }.getOrNull() + + when { + composeMultiplatform != null -> { + when { + jvm != null -> { // if kotlin jvm is applied + target.configureKotlinJvmComposeCompilerReports(jvm) + } + multiplatform != null -> { // if kotlin multiplatform is applied + target.configureKotlinMultiplatformComposeCompilerReports(multiplatform) + } + android != null -> { // if kotlin android is applied + target.configureKotlinAndroidComposeCompilerReports(android, checkForComposeFlag = false) + } + } + } + android != null -> { + target.configureKotlinAndroidComposeCompilerReports(android, checkForComposeFlag = true) + } + } + } - val commonExtension = runCatching { target.extensions.getByType(CommonExtension::class.java) }.getOrNull() + private fun Project.configureKotlinJvmComposeCompilerReports(jvmExt: KotlinJvmProjectExtension) { + // Create gradle tasks for generating report + registerComposeCompilerReportGenTaskForTarget(jvmExt.target) - android.onVariants { variant -> + afterEvaluate { + // When this method returns true it means gradle task for generating report is executing otherwise + // normal compilation task is executing. + val isFromReportGenGradleTask = executingComposeCompilerReportGenerationGradleTask() + if (isFromReportGenGradleTask) { + jvmExt.target { + compilations.filter { !it.name.endsWith("Test") }.forEach { + it.kotlinOptions { + configureKotlinOptionsForComposeCompilerReport(this@configureKotlinJvmComposeCompilerReports) + } + } + } + } + } + } + + private fun Project.configureKotlinMultiplatformComposeCompilerReports(multiplatformExt: KotlinMultiplatformExtension) { + val kotlinTargets = multiplatformExt.targets.filter { it.name != "metadata" } + // Create gradle tasks for generating report + kotlinTargets.forEach { target -> + if (target.name == "android") { + // register a task for each build type + registerComposeCompilerReportGenTaskForTarget(target, buildType = "Debug") + registerComposeCompilerReportGenTaskForTarget(target, buildType = "Release") + } else { + registerComposeCompilerReportGenTaskForTarget(target) + } + } + + afterEvaluate { + // When this method returns true it means gradle task for generating report is executing otherwise + // normal compilation task is executing. + val isFromReportGenGradleTask = executingComposeCompilerReportGenerationGradleTask() + if (isFromReportGenGradleTask) { + multiplatformExt.targets.flatMap { it.compilations } + .filter { !it.name.endsWith("Test", ignoreCase = true) } + .forEach { + it.kotlinOptions { + configureKotlinOptionsForComposeCompilerReport(this@configureKotlinMultiplatformComposeCompilerReports) + } + } + } + } + } + + private fun Project.configureKotlinAndroidComposeCompilerReports(androidExt: AndroidComponentsExtension<*, *, *>, checkForComposeFlag: Boolean) { + val commonExtension = runCatching { extensions.getByType(CommonExtension::class.java) }.getOrNull() + + androidExt.onVariants { variant -> // Create gradle tasks for generating report - target.registerComposeCompilerReportGenTaskForVariant(variant) + registerComposeCompilerReportGenTaskForVariant(variant) } - target.afterEvaluate { - val isComposeEnabled = commonExtension?.buildFeatures?.compose + afterEvaluate { + if (checkForComposeFlag) { + val isComposeEnabled = commonExtension?.buildFeatures?.compose - if (isComposeEnabled != true) { - error("Jetpack Compose is not found enabled in this module '$name'") + if (isComposeEnabled != true) { + error("Jetpack Compose is not found enabled in this module '$name'") + } } // When this method returns true it means gradle task for generating report is executing otherwise // normal compilation task is executing. - val isFromReportGenGradleTask = target.executingComposeCompilerReportGenerationGradleTask() + val isFromReportGenGradleTask = executingComposeCompilerReportGenerationGradleTask() if (isFromReportGenGradleTask) { val kotlinAndroidExt = extensions.getByType() kotlinAndroidExt.target { @@ -74,7 +162,7 @@ class ReportGenPlugin : Plugin { } } - private fun KotlinJvmOptions.configureKotlinOptionsForComposeCompilerReport(project: Project) { + private fun KotlinCommonToolOptions.configureKotlinOptionsForComposeCompilerReport(project: Project) { val reportExtension = project.extensions.getByType() val outputPath = reportExtension.composeRawMetricsOutputDirectory.absolutePath if (reportExtension.enableReport.get()) { diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt index 8a8efce..0ec9773 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt @@ -30,6 +30,7 @@ import dev.shreyaspatil.composeCompilerMetricsGenerator.generator.HtmlReportGene import dev.shreyaspatil.composeCompilerMetricsGenerator.generator.ReportOptions import dev.shreyaspatil.composeCompilerMetricsGenerator.generator.ReportSpec import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.ComposeCompilerReportExtension +import okio.Path.Companion.toOkioPath import org.gradle.api.DefaultTask import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty @@ -43,6 +44,7 @@ import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskProvider import org.gradle.tooling.GradleConnector +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import java.io.File import java.io.FileNotFoundException @@ -124,7 +126,7 @@ abstract class ComposeCompilerReportGenerateTask : DefaultTask() { val rawReportProvider = ComposeCompilerRawReportProvider.FromDirectory( - directory = composeRawMetricsOutputDirectory.get().asFile, + directory = composeRawMetricsOutputDirectory.get().asFile.toOkioPath(), ) // Provide metric files to generator @@ -156,9 +158,20 @@ abstract class ComposeCompilerReportGenerateTask : DefaultTask() { } } +fun Project.registerComposeCompilerReportGenTaskForTarget(target: KotlinTarget, buildType: String? = null): TaskProvider { + val taskName = target.name + (buildType ?: "") + "ComposeCompilerHtmlReport" + val compileKotlinTaskName = compileKotlinTaskNameFromTarget(target, buildType) + return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName) +} + + fun Project.registerComposeCompilerReportGenTaskForVariant(variant: Variant): TaskProvider { val taskName = variant.name + "ComposeCompilerHtmlReport" val compileKotlinTaskName = compileKotlinTaskNameFromVariant(variant) + return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName) +} + +fun Project.registerComposeCompilerReportGenTask(taskName: String, compileKotlinTaskName: String): TaskProvider { val reportExtension = ComposeCompilerReportExtension.get(project) return tasks.register(taskName, ComposeCompilerReportGenerateTask::class.java) { @@ -192,3 +205,12 @@ fun compileKotlinTaskNameFromVariant(variant: Variant): String { val variantName = variant.name.let { it[0].toUpperCase() + it.substring(1) } return "compile${variantName}Kotlin" } + +/** + * Returns a task name for compileKotlin with [target] + */ +fun compileKotlinTaskNameFromTarget(target: KotlinTarget, buildType: String?): String { + val targetName = target.name.let { it[0].toUpperCase() + it.substring(1) } + val buildTypeName = buildType?.let { it[0].toUpperCase() + it.substring(1) } ?: "" + return "compile${buildTypeName}Kotlin${targetName}" +} \ No newline at end of file From 8385144e5237b0f62499b6a02006f3fac5ac3341 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Sat, 15 Jun 2024 17:22:01 +0300 Subject: [PATCH 06/24] Run spotlessApply --- .../plugin/ReportGenPlugin.kt | 5 ++++- .../task/ComposeCompilerReportGenerateTask.kt | 20 +++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt index 9ce02ce..44c2fba 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt @@ -128,7 +128,10 @@ class ReportGenPlugin : Plugin { } } - private fun Project.configureKotlinAndroidComposeCompilerReports(androidExt: AndroidComponentsExtension<*, *, *>, checkForComposeFlag: Boolean) { + private fun Project.configureKotlinAndroidComposeCompilerReports( + androidExt: AndroidComponentsExtension<*, *, *>, + checkForComposeFlag: Boolean, + ) { val commonExtension = runCatching { extensions.getByType(CommonExtension::class.java) }.getOrNull() androidExt.onVariants { variant -> diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt index 0ec9773..1232181 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt @@ -158,20 +158,25 @@ abstract class ComposeCompilerReportGenerateTask : DefaultTask() { } } -fun Project.registerComposeCompilerReportGenTaskForTarget(target: KotlinTarget, buildType: String? = null): TaskProvider { +fun Project.registerComposeCompilerReportGenTaskForTarget( + target: KotlinTarget, + buildType: String? = null, +): TaskProvider { val taskName = target.name + (buildType ?: "") + "ComposeCompilerHtmlReport" val compileKotlinTaskName = compileKotlinTaskNameFromTarget(target, buildType) return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName) } - fun Project.registerComposeCompilerReportGenTaskForVariant(variant: Variant): TaskProvider { val taskName = variant.name + "ComposeCompilerHtmlReport" val compileKotlinTaskName = compileKotlinTaskNameFromVariant(variant) return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName) } -fun Project.registerComposeCompilerReportGenTask(taskName: String, compileKotlinTaskName: String): TaskProvider { +fun Project.registerComposeCompilerReportGenTask( + taskName: String, + compileKotlinTaskName: String, +): TaskProvider { val reportExtension = ComposeCompilerReportExtension.get(project) return tasks.register(taskName, ComposeCompilerReportGenerateTask::class.java) { @@ -209,8 +214,11 @@ fun compileKotlinTaskNameFromVariant(variant: Variant): String { /** * Returns a task name for compileKotlin with [target] */ -fun compileKotlinTaskNameFromTarget(target: KotlinTarget, buildType: String?): String { +fun compileKotlinTaskNameFromTarget( + target: KotlinTarget, + buildType: String?, +): String { val targetName = target.name.let { it[0].toUpperCase() + it.substring(1) } val buildTypeName = buildType?.let { it[0].toUpperCase() + it.substring(1) } ?: "" - return "compile${buildTypeName}Kotlin${targetName}" -} \ No newline at end of file + return "compile${buildTypeName}Kotlin$targetName" +} From 6147d9cd58d7ee5a956b23e8d53bf7ccece8f909 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Sun, 16 Jun 2024 18:35:30 +0300 Subject: [PATCH 07/24] Register kotlin jvm & multiplatform tasks correctly --- .../plugin/ReportGenPlugin.kt | 16 +++++++++------- .../task/ComposeCompilerReportGenerateTask.kt | 8 ++++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt index 44c2fba..1ff1edc 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt @@ -26,6 +26,7 @@ package dev.shreyaspatil.composeCompilerMetricsGenerator.plugin import com.android.build.api.dsl.CommonExtension import com.android.build.api.variant.AndroidComponentsExtension import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.executingComposeCompilerReportGenerationGradleTask +import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.registerComposeCompilerReportGenTaskForJvmProject import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.registerComposeCompilerReportGenTaskForTarget import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.registerComposeCompilerReportGenTaskForVariant import org.gradle.api.Plugin @@ -81,7 +82,7 @@ class ReportGenPlugin : Plugin { private fun Project.configureKotlinJvmComposeCompilerReports(jvmExt: KotlinJvmProjectExtension) { // Create gradle tasks for generating report - registerComposeCompilerReportGenTaskForTarget(jvmExt.target) + registerComposeCompilerReportGenTaskForJvmProject(jvmExt.target.project.name) afterEvaluate { // When this method returns true it means gradle task for generating report is executing otherwise @@ -100,15 +101,16 @@ class ReportGenPlugin : Plugin { } private fun Project.configureKotlinMultiplatformComposeCompilerReports(multiplatformExt: KotlinMultiplatformExtension) { - val kotlinTargets = multiplatformExt.targets.filter { it.name != "metadata" } // Create gradle tasks for generating report - kotlinTargets.forEach { target -> - if (target.name == "android") { + multiplatformExt.targets.configureEach { + if (this.name == "metadata") return@configureEach + + if (this.name == "android") { // register a task for each build type - registerComposeCompilerReportGenTaskForTarget(target, buildType = "Debug") - registerComposeCompilerReportGenTaskForTarget(target, buildType = "Release") + registerComposeCompilerReportGenTaskForTarget(this, buildType = "Debug") + registerComposeCompilerReportGenTaskForTarget(this, buildType = "Release") } else { - registerComposeCompilerReportGenTaskForTarget(target) + registerComposeCompilerReportGenTaskForTarget(this) } } diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt index 1232181..76e74fb 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt @@ -167,6 +167,14 @@ fun Project.registerComposeCompilerReportGenTaskForTarget( return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName) } +fun Project.registerComposeCompilerReportGenTaskForJvmProject( + projectName: String, +): TaskProvider { + val taskName = projectName + "ComposeCompilerHtmlReport" + val compileKotlinTaskName = "compileKotlin" + return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName) +} + fun Project.registerComposeCompilerReportGenTaskForVariant(variant: Variant): TaskProvider { val taskName = variant.name + "ComposeCompilerHtmlReport" val compileKotlinTaskName = compileKotlinTaskNameFromVariant(variant) From 282a80a269cf983bb1edf4f3bda33449894e5109 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Sun, 16 Jun 2024 18:38:04 +0300 Subject: [PATCH 08/24] Remove checkForCompose flag --- .../plugin/ReportGenPlugin.kt | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt index 1ff1edc..7360a3c 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt @@ -70,12 +70,12 @@ class ReportGenPlugin : Plugin { target.configureKotlinMultiplatformComposeCompilerReports(multiplatform) } android != null -> { // if kotlin android is applied - target.configureKotlinAndroidComposeCompilerReports(android, checkForComposeFlag = false) + target.configureKotlinAndroidComposeCompilerReports(android) } } } android != null -> { - target.configureKotlinAndroidComposeCompilerReports(android, checkForComposeFlag = true) + target.configureKotlinAndroidComposeCompilerReports(android) } } } @@ -132,7 +132,6 @@ class ReportGenPlugin : Plugin { private fun Project.configureKotlinAndroidComposeCompilerReports( androidExt: AndroidComponentsExtension<*, *, *>, - checkForComposeFlag: Boolean, ) { val commonExtension = runCatching { extensions.getByType(CommonExtension::class.java) }.getOrNull() @@ -142,12 +141,10 @@ class ReportGenPlugin : Plugin { } afterEvaluate { - if (checkForComposeFlag) { - val isComposeEnabled = commonExtension?.buildFeatures?.compose + val isComposeEnabled = commonExtension?.buildFeatures?.compose - if (isComposeEnabled != true) { - error("Jetpack Compose is not found enabled in this module '$name'") - } + if (isComposeEnabled != true) { + error("Jetpack Compose is not found enabled in this module '$name'") } // When this method returns true it means gradle task for generating report is executing otherwise From c941ef6ac216b960d67e34a79ec5bc04d7df3d15 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Mon, 17 Jun 2024 02:38:51 +0300 Subject: [PATCH 09/24] Provide more helpful task descriptions --- .../task/ComposeCompilerReportGenerateTask.kt | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt index 76e74fb..6933bad 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt @@ -164,7 +164,13 @@ fun Project.registerComposeCompilerReportGenTaskForTarget( ): TaskProvider { val taskName = target.name + (buildType ?: "") + "ComposeCompilerHtmlReport" val compileKotlinTaskName = compileKotlinTaskNameFromTarget(target, buildType) - return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName) + val descSuffix = buildString { + append("'${target.name}' target") + if (buildType != null) + append(" '$buildType' variant") + append(" in multiplatform project") + } + return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName, descSuffix) } fun Project.registerComposeCompilerReportGenTaskForJvmProject( @@ -172,18 +178,21 @@ fun Project.registerComposeCompilerReportGenTaskForJvmProject( ): TaskProvider { val taskName = projectName + "ComposeCompilerHtmlReport" val compileKotlinTaskName = "compileKotlin" - return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName) + val descSuffix = "'Jvm/Desktop' Project" + return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName, descSuffix) } fun Project.registerComposeCompilerReportGenTaskForVariant(variant: Variant): TaskProvider { val taskName = variant.name + "ComposeCompilerHtmlReport" val compileKotlinTaskName = compileKotlinTaskNameFromVariant(variant) - return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName) + val descSuffix = "'${variant.name}' variant in android project" + return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName, descSuffix) } fun Project.registerComposeCompilerReportGenTask( taskName: String, compileKotlinTaskName: String, + descSuffix: String, ): TaskProvider { val reportExtension = ComposeCompilerReportExtension.get(project) @@ -199,7 +208,7 @@ fun Project.registerComposeCompilerReportGenTask( showOnlyUnstableComposables.set(reportExtension.showOnlyUnstableComposables) group = "compose compiler report" - description = "Generate Compose Compiler Metrics and Report" + description = "Generate Compose Compiler Metrics and Report for $descSuffix" } } From a1d1070f06a18dae8690c821f77662d2aae698e5 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Mon, 17 Jun 2024 02:41:05 +0300 Subject: [PATCH 10/24] Run spotlessApply --- .../plugin/ReportGenPlugin.kt | 4 +--- .../task/ComposeCompilerReportGenerateTask.kt | 18 +++++++++--------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt index 7360a3c..52f1bd1 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt @@ -130,9 +130,7 @@ class ReportGenPlugin : Plugin { } } - private fun Project.configureKotlinAndroidComposeCompilerReports( - androidExt: AndroidComponentsExtension<*, *, *>, - ) { + private fun Project.configureKotlinAndroidComposeCompilerReports(androidExt: AndroidComponentsExtension<*, *, *>) { val commonExtension = runCatching { extensions.getByType(CommonExtension::class.java) }.getOrNull() androidExt.onVariants { variant -> diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt index 6933bad..79ad400 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt @@ -164,18 +164,18 @@ fun Project.registerComposeCompilerReportGenTaskForTarget( ): TaskProvider { val taskName = target.name + (buildType ?: "") + "ComposeCompilerHtmlReport" val compileKotlinTaskName = compileKotlinTaskNameFromTarget(target, buildType) - val descSuffix = buildString { - append("'${target.name}' target") - if (buildType != null) - append(" '$buildType' variant") - append(" in multiplatform project") - } + val descSuffix = + buildString { + append("'${target.name}' target") + if (buildType != null) { + append(" '$buildType' variant") + } + append(" in multiplatform project") + } return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName, descSuffix) } -fun Project.registerComposeCompilerReportGenTaskForJvmProject( - projectName: String, -): TaskProvider { +fun Project.registerComposeCompilerReportGenTaskForJvmProject(projectName: String): TaskProvider { val taskName = projectName + "ComposeCompilerHtmlReport" val compileKotlinTaskName = "compileKotlin" val descSuffix = "'Jvm/Desktop' Project" From 3cff9b2cccd03b9a8808de811a4f165077b82660 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Mon, 17 Jun 2024 13:45:06 +0300 Subject: [PATCH 11/24] Revert "Convert core module to multiplatform" This reverts commit 37cbecbeadc0324c9c4aa9e80ac30a9b4e28b784. --- core/build.gradle.kts | 19 ++++------ .../core/ComposeCompilerMetricsProvider.kt | 0 .../core/ComposeCompilerRawReportProvider.kt | 28 +++++++-------- .../core/ComposeMetricsContentProvider.kt | 25 ++----------- .../core/exception/ParsingException.kt | 0 .../core/file/ReportAndMetricsFileFinder.kt | 19 +++++----- .../core/mapper/ConditionMapper.kt | 0 .../core/model/Condition.kt | 0 .../core/model/DetailedStatistics.kt | 0 .../core/model/RawContent.kt | 0 .../core/model/classes/ClassDetail.kt | 0 .../core/model/classes/ClassesReport.kt | 0 .../model/composables/ComposableDetail.kt | 0 .../model/composables/ComposablesReport.kt | 0 .../core/parser/ClassReportParser.kt | 0 .../core/parser/ComposableReportParser.kt | 0 .../core/parser/Parser.kt | 0 .../core/utils/CamelCaseToWord.kt | 0 .../core/utils/FileUtils.kt | 36 ++++++++----------- 19 files changed, 50 insertions(+), 77 deletions(-) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerMetricsProvider.kt (100%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerRawReportProvider.kt (78%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeMetricsContentProvider.kt (71%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/exception/ParsingException.kt (100%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/file/ReportAndMetricsFileFinder.kt (83%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/mapper/ConditionMapper.kt (100%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/Condition.kt (100%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/DetailedStatistics.kt (100%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/RawContent.kt (100%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassDetail.kt (100%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassesReport.kt (100%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposableDetail.kt (100%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposablesReport.kt (100%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ClassReportParser.kt (100%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ComposableReportParser.kt (100%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/Parser.kt (100%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/CamelCaseToWord.kt (100%) rename core/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/FileUtils.kt (69%) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 7ed9e46..d7042db 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,19 +1,14 @@ plugins { - kotlin("multiplatform") + kotlin("jvm") kotlin("plugin.serialization") version libs.versions.kotlin.get() id(libs.plugins.mavenPublish.get().pluginId) } -kotlin { - applyDefaultHierarchyTemplate() - - jvm() +repositories { + mavenCentral() +} - sourceSets { - commonMain.dependencies { - implementation(libs.okio) - implementation(kotlin("stdlib")) - implementation(libs.kotlinx.serialization.json) - } - } +dependencies { + implementation(kotlin("stdlib")) + implementation(libs.kotlinx.serialization.json) } diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerMetricsProvider.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerMetricsProvider.kt similarity index 100% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerMetricsProvider.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerMetricsProvider.kt diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerRawReportProvider.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerRawReportProvider.kt similarity index 78% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerRawReportProvider.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerRawReportProvider.kt index 5f964b5..7a97b28 100644 --- a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerRawReportProvider.kt +++ b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeCompilerRawReportProvider.kt @@ -26,25 +26,25 @@ package dev.shreyaspatil.composeCompilerMetricsGenerator.core import dev.shreyaspatil.composeCompilerMetricsGenerator.core.file.ReportAndMetricsFileFinder import dev.shreyaspatil.composeCompilerMetricsGenerator.core.utils.ensureDirectory import dev.shreyaspatil.composeCompilerMetricsGenerator.core.utils.ensureFileExists -import okio.Path +import java.io.File /** * Provide files of compose compiler metrics and reports */ sealed interface ComposeCompilerRawReportProvider { - val briefStatisticsJsonFiles: List - val detailedStatisticsCsvFiles: List - val composableReportFiles: List - val classesReportFiles: List + val briefStatisticsJsonFiles: List + val detailedStatisticsCsvFiles: List + val composableReportFiles: List + val classesReportFiles: List /** * Provides report from individual files */ class FromIndividualFiles( - override val briefStatisticsJsonFiles: List, - override val detailedStatisticsCsvFiles: List, - override val composableReportFiles: List, - override val classesReportFiles: List, + override val briefStatisticsJsonFiles: List, + override val detailedStatisticsCsvFiles: List, + override val composableReportFiles: List, + override val classesReportFiles: List, ) : ComposeCompilerRawReportProvider { init { validateComposeCompilerRawReportProvider() @@ -54,13 +54,13 @@ sealed interface ComposeCompilerRawReportProvider { /** * Searches for files in the given [directory] and provides report and metric files found in that directory. */ - class FromDirectory(directory: Path) : ComposeCompilerRawReportProvider { + class FromDirectory(directory: File) : ComposeCompilerRawReportProvider { private val finder = ReportAndMetricsFileFinder(directory) - override val briefStatisticsJsonFiles: List = finder.findBriefStatisticsJsonFile() - override val detailedStatisticsCsvFiles: List = finder.findDetailsStatisticsCsvFile() - override val composableReportFiles: List = finder.findComposablesReportTxtFile() - override val classesReportFiles: List = finder.findClassesReportTxtFile() + override val briefStatisticsJsonFiles: List = finder.findBriefStatisticsJsonFile() + override val detailedStatisticsCsvFiles: List = finder.findDetailsStatisticsCsvFile() + override val composableReportFiles: List = finder.findComposablesReportTxtFile() + override val classesReportFiles: List = finder.findClassesReportTxtFile() init { ensureDirectory(directory) { "Directory '$directory' not exists" } diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeMetricsContentProvider.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeMetricsContentProvider.kt similarity index 71% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeMetricsContentProvider.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeMetricsContentProvider.kt index 7bf51f6..db64f48 100644 --- a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeMetricsContentProvider.kt +++ b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/ComposeMetricsContentProvider.kt @@ -23,31 +23,12 @@ */ package dev.shreyaspatil.composeCompilerMetricsGenerator.core -import okio.FileSystem -import okio.Path - /** * Provides content of a composable report and metrics */ class ComposeMetricsContentProvider(private val fileProvider: ComposeCompilerRawReportProvider) { - val briefStatisticsContents: List get() = fileProvider.briefStatisticsJsonFiles.map { it.readUtf8() } + val briefStatisticsContents: List get() = fileProvider.briefStatisticsJsonFiles.map { it.readText() } val detailedStatisticsCsvRows: List get() = fileProvider.detailedStatisticsCsvFiles.flatMap { it.readLines() } - val composablesReportContents: String get() = fileProvider.composableReportFiles.joinToString(separator = "\n") { it.readUtf8() } - val classesReportContents: String get() = fileProvider.classesReportFiles.joinToString(separator = "\n") { it.readUtf8() } - private val fileSystem = FileSystem.SYSTEM - - private fun Path.readUtf8(): String { - return fileSystem.read(this) { readUtf8() } - } - - private fun Path.readLines(): List { - val result = mutableListOf() - fileSystem.read(this) { - while (true) { - val line = readUtf8Line() ?: break - result += line - } - } - return result - } + val composablesReportContents: String get() = fileProvider.composableReportFiles.joinToString(separator = "\n") { it.readText() } + val classesReportContents: String get() = fileProvider.classesReportFiles.joinToString(separator = "\n") { it.readText() } } diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/exception/ParsingException.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/exception/ParsingException.kt similarity index 100% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/exception/ParsingException.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/exception/ParsingException.kt diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/file/ReportAndMetricsFileFinder.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/file/ReportAndMetricsFileFinder.kt similarity index 83% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/file/ReportAndMetricsFileFinder.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/file/ReportAndMetricsFileFinder.kt index 3d9d37a..e781505 100644 --- a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/file/ReportAndMetricsFileFinder.kt +++ b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/file/ReportAndMetricsFileFinder.kt @@ -23,30 +23,33 @@ */ package dev.shreyaspatil.composeCompilerMetricsGenerator.core.file -import okio.FileSystem -import okio.Path +import java.io.File /** * Finds reports and metrics files generated by Compose compiler * * @param directory The directory in which files will be searched */ -class ReportAndMetricsFileFinder(directory: Path) { - private val allFiles = FileSystem.SYSTEM.list(directory) +class ReportAndMetricsFileFinder(directory: File) { + private val allFiles = + directory + .listFiles() + ?.filterNotNull() + ?: emptyList() - fun findBriefStatisticsJsonFile(): List { + fun findBriefStatisticsJsonFile(): List { return allFiles.filter { it.name.endsWith(FileSuffixes.MODULE_REPORT_JSON) } } - fun findDetailsStatisticsCsvFile(): List { + fun findDetailsStatisticsCsvFile(): List { return allFiles.filter { it.name.endsWith(FileSuffixes.COMPOSABLES_STATS_METRICS_CSV) } } - fun findComposablesReportTxtFile(): List { + fun findComposablesReportTxtFile(): List { return allFiles.filter { it.name.endsWith(FileSuffixes.COMPOSABLES_REPORT_TXT) } } - fun findClassesReportTxtFile(): List { + fun findClassesReportTxtFile(): List { return allFiles.filter { it.name.endsWith(FileSuffixes.CLASSES_REPORT_TXT) } } diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/mapper/ConditionMapper.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/mapper/ConditionMapper.kt similarity index 100% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/mapper/ConditionMapper.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/mapper/ConditionMapper.kt diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/Condition.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/Condition.kt similarity index 100% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/Condition.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/Condition.kt diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/DetailedStatistics.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/DetailedStatistics.kt similarity index 100% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/DetailedStatistics.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/DetailedStatistics.kt diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/RawContent.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/RawContent.kt similarity index 100% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/RawContent.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/RawContent.kt diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassDetail.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassDetail.kt similarity index 100% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassDetail.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassDetail.kt diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassesReport.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassesReport.kt similarity index 100% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassesReport.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/classes/ClassesReport.kt diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposableDetail.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposableDetail.kt similarity index 100% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposableDetail.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposableDetail.kt diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposablesReport.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposablesReport.kt similarity index 100% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposablesReport.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/model/composables/ComposablesReport.kt diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ClassReportParser.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ClassReportParser.kt similarity index 100% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ClassReportParser.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ClassReportParser.kt diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ComposableReportParser.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ComposableReportParser.kt similarity index 100% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ComposableReportParser.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ComposableReportParser.kt diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/Parser.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/Parser.kt similarity index 100% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/Parser.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/Parser.kt diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/CamelCaseToWord.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/CamelCaseToWord.kt similarity index 100% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/CamelCaseToWord.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/CamelCaseToWord.kt diff --git a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/FileUtils.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/FileUtils.kt similarity index 69% rename from core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/FileUtils.kt rename to core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/FileUtils.kt index 1e858a7..b5e4c25 100644 --- a/core/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/FileUtils.kt +++ b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/utils/FileUtils.kt @@ -23,10 +23,9 @@ */ package dev.shreyaspatil.composeCompilerMetricsGenerator.core.utils -import okio.FileNotFoundException -import okio.FileSystem -import okio.Path -import okio.Path.Companion.toPath +import java.io.File +import java.io.FileNotFoundException +import java.nio.file.Paths /** * Checks whether directory with [path] exists or not. @@ -36,8 +35,8 @@ inline fun ensureDirectory( path: String, lazyMessage: () -> Any, ) { - val absolutePath = FileSystem.SYSTEM.canonicalize(path.toPath()) - ensureDirectory(absolutePath, lazyMessage) + val file = File(Paths.get(path).toAbsolutePath().toString()) + ensureDirectory(file, lazyMessage) } /** @@ -45,14 +44,11 @@ inline fun ensureDirectory( * Else throws [FileNotFoundException]. */ inline fun ensureDirectory( - directory: Path, + directory: File, lazyMessage: () -> Any, ) { - val fileSystem = FileSystem.SYSTEM - val absolutePath = if (directory.isAbsolute) directory else fileSystem.canonicalize(directory) - println("Checking directory '$absolutePath'") - val isDirectory = fileSystem.metadataOrNull(directory)?.isDirectory == true - if (!isDirectory) { + println("Checking directory '${directory.absolutePath}'") + if (!directory.isDirectory) { val message = lazyMessage() throw FileNotFoundException(message.toString()) } @@ -64,22 +60,20 @@ inline fun ensureDirectory( inline fun ensureFileExists( filename: String, lazyMessage: () -> Any, -): Path { - val absolutePath = FileSystem.SYSTEM.canonicalize(filename.toPath()) - return ensureFileExists(absolutePath, lazyMessage) +): File { + val file = File(Paths.get(filename).toAbsolutePath().toString()) + return ensureFileExists(file, lazyMessage) } /** * Checks whether [file] with exists or not. Else throws [FileNotFoundException]. */ inline fun ensureFileExists( - file: Path, + file: File, lazyMessage: () -> Any, -): Path { - val fileSystem = FileSystem.SYSTEM - val absolutePath = if (file.isAbsolute) file else fileSystem.canonicalize(file) - println("Checking file '$absolutePath'") - if (!fileSystem.exists(file)) { +): File { + println("Checking file '${file.absolutePath}'") + if (!file.exists()) { val message = lazyMessage() throw FileNotFoundException(message.toString()) } From 496d13400e2a6d3819c541c2483523eaea798dca Mon Sep 17 00:00:00 2001 From: MR3Y Date: Mon, 17 Jun 2024 13:47:02 +0300 Subject: [PATCH 12/24] Revert "Convert report-generator module to multiplatform" This reverts commit c12ee30b375944d23371181f00af4f08bd3abb85. --- report-generator/build.gradle.kts | 23 ++++++++----------- .../generator/HtmlReportGenerator.kt | 0 .../generator/ReportSpec.kt | 0 .../generator/content/BriefStatistics.kt | 0 .../generator/content/ClassReport.kt | 0 .../generator/content/ComposableReport.kt | 0 .../generator/content/DetailedStatistics.kt | 0 .../generator/content/ErrorReports.kt | 0 .../generator/content/Footer.kt | 0 .../generator/content/MainContent.kt | 0 .../content/common/CollapsibleContent.kt | 0 .../generator/content/common/EmptyContent.kt | 0 .../generator/content/common/IconText.kt | 0 .../generator/content/common/Icons.kt | 0 .../generator/content/common/Svg.kt | 0 .../generator/script/CollapsibleScript.kt | 0 .../generator/style/Colors.kt | 0 .../generator/style/CssStyle.kt | 0 .../generator/style/Fonts.kt | 0 .../generator/style/PageStyle.kt | 0 .../generator/utils/ListExt.kt | 0 .../generator/utils/StringExt.kt | 0 22 files changed, 9 insertions(+), 14 deletions(-) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/HtmlReportGenerator.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/ReportSpec.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/BriefStatistics.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ClassReport.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ComposableReport.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/DetailedStatistics.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ErrorReports.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/Footer.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/MainContent.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/CollapsibleContent.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/EmptyContent.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/IconText.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Icons.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Svg.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/script/CollapsibleScript.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Colors.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/CssStyle.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Fonts.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/PageStyle.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/ListExt.kt (100%) rename report-generator/src/{commonMain => main}/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/StringExt.kt (100%) diff --git a/report-generator/build.gradle.kts b/report-generator/build.gradle.kts index e78f607..fcbc698 100644 --- a/report-generator/build.gradle.kts +++ b/report-generator/build.gradle.kts @@ -1,20 +1,15 @@ plugins { - kotlin("multiplatform") + kotlin("jvm") id(libs.plugins.mavenPublish.get().pluginId) } +repositories { + mavenCentral() +} -kotlin { - applyDefaultHierarchyTemplate() - - jvm() - - sourceSets { - commonMain.dependencies { - implementation(project(":core")) - implementation(kotlin("stdlib")) +dependencies { + implementation(project(":core")) + implementation(kotlin("stdlib")) - implementation(libs.kotlinx.coroutines.core) - implementation(libs.kotlinx.html.jvm) - } - } + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.html.jvm) } diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/HtmlReportGenerator.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/HtmlReportGenerator.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/HtmlReportGenerator.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/HtmlReportGenerator.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/ReportSpec.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/ReportSpec.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/ReportSpec.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/ReportSpec.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/BriefStatistics.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/BriefStatistics.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/BriefStatistics.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/BriefStatistics.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ClassReport.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ClassReport.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ClassReport.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ClassReport.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ComposableReport.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ComposableReport.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ComposableReport.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ComposableReport.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/DetailedStatistics.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/DetailedStatistics.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/DetailedStatistics.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/DetailedStatistics.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ErrorReports.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ErrorReports.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ErrorReports.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/ErrorReports.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/Footer.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/Footer.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/Footer.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/Footer.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/MainContent.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/MainContent.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/MainContent.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/MainContent.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/CollapsibleContent.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/CollapsibleContent.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/CollapsibleContent.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/CollapsibleContent.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/EmptyContent.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/EmptyContent.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/EmptyContent.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/EmptyContent.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/IconText.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/IconText.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/IconText.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/IconText.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Icons.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Icons.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Icons.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Icons.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Svg.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Svg.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Svg.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/content/common/Svg.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/script/CollapsibleScript.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/script/CollapsibleScript.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/script/CollapsibleScript.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/script/CollapsibleScript.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Colors.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Colors.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Colors.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Colors.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/CssStyle.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/CssStyle.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/CssStyle.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/CssStyle.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Fonts.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Fonts.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Fonts.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/Fonts.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/PageStyle.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/PageStyle.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/PageStyle.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/style/PageStyle.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/ListExt.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/ListExt.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/ListExt.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/ListExt.kt diff --git a/report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/StringExt.kt b/report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/StringExt.kt similarity index 100% rename from report-generator/src/commonMain/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/StringExt.kt rename to report-generator/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/generator/utils/StringExt.kt From 7562f29a2ddbe4311eada55ac02a5eb9187921b6 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Mon, 17 Jun 2024 13:48:24 +0300 Subject: [PATCH 13/24] Revert "Update core usages in cli module" This reverts commit ccdf1e29cd849eec8571171764509555d6565a4e. --- cli/build.gradle.kts | 5 ++++- .../shreyaspatil/composeCompilerMetricsGenerator/cli/Main.kt | 5 ++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cli/build.gradle.kts b/cli/build.gradle.kts index 3485468..97846f4 100644 --- a/cli/build.gradle.kts +++ b/cli/build.gradle.kts @@ -7,12 +7,15 @@ application { mainClass.set(mainCliClassName) } +repositories { + mavenCentral() +} + dependencies { implementation(project(":core")) implementation(project(":report-generator")) implementation(kotlin("stdlib")) - implementation(libs.okio) implementation(libs.kotlinx.cli) } diff --git a/cli/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/cli/Main.kt b/cli/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/cli/Main.kt index 0d93a83..9422ab5 100644 --- a/cli/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/cli/Main.kt +++ b/cli/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/cli/Main.kt @@ -34,7 +34,6 @@ import kotlinx.cli.ArgParser import kotlinx.cli.ArgType import kotlinx.cli.default import kotlinx.cli.required -import okio.Path.Companion.toOkioPath import java.io.File import java.nio.file.Path import java.nio.file.Paths @@ -206,10 +205,10 @@ class CliArguments(args: Array, private val path: Path) { } private fun getRawReportProviderFromDirectory(directory: String): ComposeCompilerRawReportProvider.FromDirectory { - return ComposeCompilerRawReportProvider.FromDirectory(File(Paths.get(directory).toAbsolutePath().toString()).toOkioPath()) + return ComposeCompilerRawReportProvider.FromDirectory(File(Paths.get(directory).toAbsolutePath().toString())) } - private fun files(filenames: String): List { + private fun files(filenames: String): List { return filenames.split(",").map { ensureFileExists(it) { "File not exist: $it" } } } } From d1df06bff23fab9bdbe825ed2309fd97740df40f Mon Sep 17 00:00:00 2001 From: MR3Y Date: Mon, 17 Jun 2024 14:00:13 +0300 Subject: [PATCH 14/24] Remove unnecessary usages of multiplatform & okio --- build.gradle.kts | 1 - gradle-plugin/build.gradle.kts | 1 - .../plugin/task/ComposeCompilerReportGenerateTask.kt | 3 +-- gradle.properties | 4 ---- gradle/libs.versions.toml | 2 -- 5 files changed, 1 insertion(+), 10 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 5a88a6c..37261c3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,6 @@ val GROUP: String by project plugins { kotlin("jvm") version libs.versions.kotlin.get() apply false - kotlin("multiplatform") version libs.versions.kotlin.get() apply false alias(libs.plugins.spotless) alias(libs.plugins.mavenPublish) apply false } diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index 6d40140..e31611f 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -21,7 +21,6 @@ dependencies { implementation(project(":core")) implementation(project(":report-generator")) - implementation(libs.okio) } tasks.getByName("test") { diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt index 79ad400..505e60a 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt @@ -30,7 +30,6 @@ import dev.shreyaspatil.composeCompilerMetricsGenerator.generator.HtmlReportGene import dev.shreyaspatil.composeCompilerMetricsGenerator.generator.ReportOptions import dev.shreyaspatil.composeCompilerMetricsGenerator.generator.ReportSpec import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.ComposeCompilerReportExtension -import okio.Path.Companion.toOkioPath import org.gradle.api.DefaultTask import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty @@ -126,7 +125,7 @@ abstract class ComposeCompilerReportGenerateTask : DefaultTask() { val rawReportProvider = ComposeCompilerRawReportProvider.FromDirectory( - directory = composeRawMetricsOutputDirectory.get().asFile.toOkioPath(), + directory = composeRawMetricsOutputDirectory.get().asFile, ) // Provide metric files to generator diff --git a/gradle.properties b/gradle.properties index 32efd4c..b06f8b6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,4 @@ kotlin.code.style=official -# KMP -kotlin.js.compiler=ir -kotlin.mpp.androidSourceSetLayoutVersion=2 -kotlin.mpp.androidGradlePluginCompatibility.nowarn=true GROUP=dev.shreyaspatil.compose-compiler-report-generator VERSION_NAME=1.3.1 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 52e9937..6346f20 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,6 @@ coroutines = "1.8.1" spotless = "6.25.0" kotlinxHtml = "0.11.0" kotlinxCli = "0.3.6" -okio = "3.9.0" compose = "1.6.10-rc03" mavenPublish = "0.28.0" androidGradlePlugin = "8.3.0" @@ -16,7 +15,6 @@ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serializa kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } kotlinx-html-jvm = { module = "org.jetbrains.kotlinx:kotlinx-html-jvm", version.ref = "kotlinxHtml" } kotlinx-cli = { module = "org.jetbrains.kotlinx:kotlinx-cli", version.ref = "kotlinxCli" } -okio = { module = "com.squareup.okio:okio", version.ref = "okio" } android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "androidGradlePlugin" } kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } compose-multiplatform-gradle-plugin = { module = "org.jetbrains.compose:compose-gradle-plugin", version.ref = "compose" } From 0c89f0dcecb9369240dd3fe86d922b41f73e54f6 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Tue, 18 Jun 2024 14:33:41 +0300 Subject: [PATCH 15/24] Re-apply kotlin jvm plugin at the root project --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 37261c3..64884ab 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ val VERSION_NAME: String by project val GROUP: String by project plugins { - kotlin("jvm") version libs.versions.kotlin.get() apply false + kotlin("jvm") version libs.versions.kotlin.get() alias(libs.plugins.spotless) alias(libs.plugins.mavenPublish) apply false } From 106d8fff9f8109540b730051cefd5babdb1be7a4 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Tue, 18 Jun 2024 14:35:21 +0300 Subject: [PATCH 16/24] Use pluginManager.withPlugin to check for existence of a plugin --- .../plugin/ReportGenPlugin.kt | 55 ++++++++----------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt index 52f1bd1..ec44b2b 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt @@ -32,7 +32,6 @@ import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.registerComp import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.getByType -import org.jetbrains.compose.ComposeExtension import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinCommonToolOptions import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension @@ -42,40 +41,32 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension class ReportGenPlugin : Plugin { override fun apply(target: Project) { val reportExt = ComposeCompilerReportExtension.create(target) + var pluginApplied = false - val android = - runCatching { - target.extensions.getByType(AndroidComponentsExtension::class.java) - }.getOrNull() - val jvm = - runCatching { - target.extensions.getByType(KotlinJvmProjectExtension::class.java) - }.getOrNull() - val multiplatform = - runCatching { - target.extensions.getByType(KotlinMultiplatformExtension::class.java) - }.getOrNull() - val composeMultiplatform = - runCatching { - target.extensions.getByType(ComposeExtension::class.java) - }.getOrNull() - - when { - composeMultiplatform != null -> { - when { - jvm != null -> { // if kotlin jvm is applied - target.configureKotlinJvmComposeCompilerReports(jvm) - } - multiplatform != null -> { // if kotlin multiplatform is applied - target.configureKotlinMultiplatformComposeCompilerReports(multiplatform) - } - android != null -> { // if kotlin android is applied - target.configureKotlinAndroidComposeCompilerReports(android) - } + with(target) { + pluginManager.withPlugin("org.jetbrains.compose") { + pluginApplied = true + pluginManager.withPlugin("org.jetbrains.kotlin.jvm") { // if kotlin jvm is applied + val jvm = extensions.getByType(KotlinJvmProjectExtension::class.java) + configureKotlinJvmComposeCompilerReports(jvm) + } + pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform") { // if kotlin multiplatform is applied + val multiplatform = extensions.getByType(KotlinMultiplatformExtension::class.java) + configureKotlinMultiplatformComposeCompilerReports(multiplatform) + } + pluginManager.withPlugin("org.jetbrains.kotlin.android") { // if kotlin android is applied + val android = extensions.getByType(AndroidComponentsExtension::class.java) + configureKotlinAndroidComposeCompilerReports(android) } } - android != null -> { - target.configureKotlinAndroidComposeCompilerReports(android) + if (!pluginApplied) { + val android = extensions.getByType(AndroidComponentsExtension::class.java) + pluginManager.withPlugin("com.android.application") { + configureKotlinAndroidComposeCompilerReports(android) + } + pluginManager.withPlugin("com.android.library") { + configureKotlinAndroidComposeCompilerReports(android) + } } } } From 0836ef07cf22821495ac8de1632cc377c5144c74 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Tue, 18 Jun 2024 15:19:39 +0300 Subject: [PATCH 17/24] Rename Jvm project task to prepend jvm instead of project name --- .../composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt | 2 +- .../plugin/task/ComposeCompilerReportGenerateTask.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt index ec44b2b..5f64e79 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt @@ -73,7 +73,7 @@ class ReportGenPlugin : Plugin { private fun Project.configureKotlinJvmComposeCompilerReports(jvmExt: KotlinJvmProjectExtension) { // Create gradle tasks for generating report - registerComposeCompilerReportGenTaskForJvmProject(jvmExt.target.project.name) + registerComposeCompilerReportGenTaskForJvmProject() afterEvaluate { // When this method returns true it means gradle task for generating report is executing otherwise diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt index 505e60a..60a3203 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt @@ -174,8 +174,8 @@ fun Project.registerComposeCompilerReportGenTaskForTarget( return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName, descSuffix) } -fun Project.registerComposeCompilerReportGenTaskForJvmProject(projectName: String): TaskProvider { - val taskName = projectName + "ComposeCompilerHtmlReport" +fun Project.registerComposeCompilerReportGenTaskForJvmProject(): TaskProvider { + val taskName = "jvmComposeCompilerHtmlReport" val compileKotlinTaskName = "compileKotlin" val descSuffix = "'Jvm/Desktop' Project" return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName, descSuffix) From 48af55a5f54cd0434d1ce0127a852cc2d8a3fc37 Mon Sep 17 00:00:00 2001 From: MR3Y Date: Tue, 18 Jun 2024 16:13:56 +0300 Subject: [PATCH 18/24] Query android variants instead of hardcoding them --- .../plugin/ReportGenPlugin.kt | 8 +++++--- .../task/ComposeCompilerReportGenerateTask.kt | 16 ++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt index 5f64e79..e8693ce 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt @@ -97,9 +97,11 @@ class ReportGenPlugin : Plugin { if (this.name == "metadata") return@configureEach if (this.name == "android") { - // register a task for each build type - registerComposeCompilerReportGenTaskForTarget(this, buildType = "Debug") - registerComposeCompilerReportGenTaskForTarget(this, buildType = "Release") + // register a task for each variant + val androidExt = extensions.getByType(AndroidComponentsExtension::class.java) + androidExt.onVariants { variant -> + registerComposeCompilerReportGenTaskForTarget(this, variant) + } } else { registerComposeCompilerReportGenTaskForTarget(this) } diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt index 60a3203..deaa018 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt @@ -159,15 +159,16 @@ abstract class ComposeCompilerReportGenerateTask : DefaultTask() { fun Project.registerComposeCompilerReportGenTaskForTarget( target: KotlinTarget, - buildType: String? = null, + variant: Variant? = null, ): TaskProvider { - val taskName = target.name + (buildType ?: "") + "ComposeCompilerHtmlReport" - val compileKotlinTaskName = compileKotlinTaskNameFromTarget(target, buildType) + val variantName = variant?.name?.let { it[0].toUpperCase() + it.substring(1) } ?: "" + val taskName = target.name + variantName + "ComposeCompilerHtmlReport" + val compileKotlinTaskName = compileKotlinTaskNameFromTarget(target, variantName) val descSuffix = buildString { append("'${target.name}' target") - if (buildType != null) { - append(" '$buildType' variant") + if (variant != null) { + append(" '${variant.name}' variant") } append(" in multiplatform project") } @@ -232,9 +233,8 @@ fun compileKotlinTaskNameFromVariant(variant: Variant): String { */ fun compileKotlinTaskNameFromTarget( target: KotlinTarget, - buildType: String?, + variantName: String, ): String { val targetName = target.name.let { it[0].toUpperCase() + it.substring(1) } - val buildTypeName = buildType?.let { it[0].toUpperCase() + it.substring(1) } ?: "" - return "compile${buildTypeName}Kotlin$targetName" + return "compile${variantName}Kotlin$targetName" } From 1a418ad0b1459314b6120ba3d1e9a971e651cc23 Mon Sep 17 00:00:00 2001 From: Shreyas Patil Date: Sun, 28 Jul 2024 13:12:47 +0530 Subject: [PATCH 19/24] Refactor: move methods to appropriate packages --- .../plugin/ReportGenPlugin.kt | 135 ++++-------------- .../plugin/multiplatform/android/configure.kt | 39 +++++ .../plugin/multiplatform/configure.kt | 44 ++++++ .../plugin/multiplatform/jvm/configure.kt | 23 +++ .../task/ComposeCompilerReportGenerateTask.kt | 21 ++- 5 files changed, 152 insertions(+), 110 deletions(-) create mode 100644 gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/android/configure.kt create mode 100644 gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/configure.kt create mode 100644 gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/jvm/configure.kt diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt index e8693ce..6670bd8 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/ReportGenPlugin.kt @@ -23,16 +23,13 @@ */ package dev.shreyaspatil.composeCompilerMetricsGenerator.plugin -import com.android.build.api.dsl.CommonExtension import com.android.build.api.variant.AndroidComponentsExtension -import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.executingComposeCompilerReportGenerationGradleTask -import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.registerComposeCompilerReportGenTaskForJvmProject -import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.registerComposeCompilerReportGenTaskForTarget -import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.registerComposeCompilerReportGenTaskForVariant +import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.multiplatform.android.configureKotlinAndroidComposeCompilerReports +import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.multiplatform.configureKotlinMultiplatformComposeCompilerReports +import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.multiplatform.jvm.configureKotlinJvmComposeCompilerReports import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.getByType -import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinCommonToolOptions import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension @@ -41,11 +38,11 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension class ReportGenPlugin : Plugin { override fun apply(target: Project) { val reportExt = ComposeCompilerReportExtension.create(target) - var pluginApplied = false + var isAppliedForKmp = false with(target) { pluginManager.withPlugin("org.jetbrains.compose") { - pluginApplied = true + isAppliedForKmp = true pluginManager.withPlugin("org.jetbrains.kotlin.jvm") { // if kotlin jvm is applied val jvm = extensions.getByType(KotlinJvmProjectExtension::class.java) configureKotlinJvmComposeCompilerReports(jvm) @@ -59,118 +56,40 @@ class ReportGenPlugin : Plugin { configureKotlinAndroidComposeCompilerReports(android) } } - if (!pluginApplied) { + var isAppliedForAndroid = false + if (!isAppliedForKmp) { val android = extensions.getByType(AndroidComponentsExtension::class.java) pluginManager.withPlugin("com.android.application") { configureKotlinAndroidComposeCompilerReports(android) + isAppliedForAndroid = true } pluginManager.withPlugin("com.android.library") { configureKotlinAndroidComposeCompilerReports(android) + isAppliedForAndroid = true } } - } - } - - private fun Project.configureKotlinJvmComposeCompilerReports(jvmExt: KotlinJvmProjectExtension) { - // Create gradle tasks for generating report - registerComposeCompilerReportGenTaskForJvmProject() - - afterEvaluate { - // When this method returns true it means gradle task for generating report is executing otherwise - // normal compilation task is executing. - val isFromReportGenGradleTask = executingComposeCompilerReportGenerationGradleTask() - if (isFromReportGenGradleTask) { - jvmExt.target { - compilations.filter { !it.name.endsWith("Test") }.forEach { - it.kotlinOptions { - configureKotlinOptionsForComposeCompilerReport(this@configureKotlinJvmComposeCompilerReports) - } - } - } - } - } - } - - private fun Project.configureKotlinMultiplatformComposeCompilerReports(multiplatformExt: KotlinMultiplatformExtension) { - // Create gradle tasks for generating report - multiplatformExt.targets.configureEach { - if (this.name == "metadata") return@configureEach - - if (this.name == "android") { - // register a task for each variant - val androidExt = extensions.getByType(AndroidComponentsExtension::class.java) - androidExt.onVariants { variant -> - registerComposeCompilerReportGenTaskForTarget(this, variant) - } - } else { - registerComposeCompilerReportGenTaskForTarget(this) - } - } - - afterEvaluate { - // When this method returns true it means gradle task for generating report is executing otherwise - // normal compilation task is executing. - val isFromReportGenGradleTask = executingComposeCompilerReportGenerationGradleTask() - if (isFromReportGenGradleTask) { - multiplatformExt.targets.flatMap { it.compilations } - .filter { !it.name.endsWith("Test", ignoreCase = true) } - .forEach { - it.kotlinOptions { - configureKotlinOptionsForComposeCompilerReport(this@configureKotlinMultiplatformComposeCompilerReports) - } - } - } - } - } - - private fun Project.configureKotlinAndroidComposeCompilerReports(androidExt: AndroidComponentsExtension<*, *, *>) { - val commonExtension = runCatching { extensions.getByType(CommonExtension::class.java) }.getOrNull() - - androidExt.onVariants { variant -> - // Create gradle tasks for generating report - registerComposeCompilerReportGenTaskForVariant(variant) - } - - afterEvaluate { - val isComposeEnabled = commonExtension?.buildFeatures?.compose - - if (isComposeEnabled != true) { + if (!isAppliedForAndroid && !isAppliedForKmp) { error("Jetpack Compose is not found enabled in this module '$name'") } - - // When this method returns true it means gradle task for generating report is executing otherwise - // normal compilation task is executing. - val isFromReportGenGradleTask = executingComposeCompilerReportGenerationGradleTask() - if (isFromReportGenGradleTask) { - val kotlinAndroidExt = extensions.getByType() - kotlinAndroidExt.target { - // Exclude for test variants, no use! - compilations.filter { !it.name.endsWith("Test") }.forEach { - it.kotlinOptions { - configureKotlinOptionsForComposeCompilerReport(this@afterEvaluate) - } - } - } - } } } +} - private fun KotlinCommonToolOptions.configureKotlinOptionsForComposeCompilerReport(project: Project) { - val reportExtension = project.extensions.getByType() - val outputPath = reportExtension.composeRawMetricsOutputDirectory.absolutePath - if (reportExtension.enableReport.get()) { - freeCompilerArgs += - listOf( - "-P", - "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=$outputPath", - ) - } - if (reportExtension.enableMetrics.get()) { - freeCompilerArgs += - listOf( - "-P", - "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=$outputPath", - ) - } +fun KotlinCommonToolOptions.configureKotlinOptionsForComposeCompilerReport(project: Project) { + val reportExtension = project.extensions.getByType() + val outputPath = reportExtension.composeRawMetricsOutputDirectory.absolutePath + if (reportExtension.enableReport.get()) { + freeCompilerArgs += + listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=$outputPath", + ) + } + if (reportExtension.enableMetrics.get()) { + freeCompilerArgs += + listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=$outputPath", + ) } } diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/android/configure.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/android/configure.kt new file mode 100644 index 0000000..be80a31 --- /dev/null +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/android/configure.kt @@ -0,0 +1,39 @@ +package dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.multiplatform.android + +import com.android.build.api.dsl.CommonExtension +import com.android.build.api.variant.AndroidComponentsExtension +import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.multiplatform.configureKotlinOptionsForComposeCompilerReport +import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.executingComposeCompilerReportGenerationGradleTask +import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.registerComposeCompilerReportGenTaskForVariant +import org.gradle.api.Project +import org.gradle.kotlin.dsl.getByType +import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension + +fun Project.configureKotlinAndroidComposeCompilerReports(android: AndroidComponentsExtension<*, *, *>) { + val commonExtension = + runCatching { extensions.getByType(CommonExtension::class.java) }.getOrNull() + + android.onVariants { variant -> + // Create gradle tasks for generating report + registerComposeCompilerReportGenTaskForVariant(variant) + } + + afterEvaluate { + val isComposeEnabled = commonExtension?.buildFeatures?.compose + + if (isComposeEnabled != true) { + error("Jetpack Compose is not found enabled in this module '$name'") + } + + // When this method returns true it means gradle task for generating report is executing otherwise + // normal compilation task is executing. + val isFromReportGenGradleTask = executingComposeCompilerReportGenerationGradleTask() + if (isFromReportGenGradleTask) { + val kotlinAndroidExt = extensions.getByType() + kotlinAndroidExt.target { + // Exclude for test variants, no use! + configureKotlinOptionsForComposeCompilerReport(compilations) + } + } + } +} \ No newline at end of file diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/configure.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/configure.kt new file mode 100644 index 0000000..9c09023 --- /dev/null +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/configure.kt @@ -0,0 +1,44 @@ +package dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.multiplatform + +import com.android.build.api.variant.AndroidComponentsExtension +import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.configureKotlinOptionsForComposeCompilerReport +import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.executingComposeCompilerReportGenerationGradleTask +import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.registerComposeCompilerReportGenTaskForTarget +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation + +fun Project.configureKotlinMultiplatformComposeCompilerReports(multiplatformExt: KotlinMultiplatformExtension) { + // Create gradle tasks for generating report + multiplatformExt.targets.configureEach { + if (this.name == "metadata") return@configureEach + + if (this.name == "android") { + // register a task for each variant + val androidExt = extensions.getByType(AndroidComponentsExtension::class.java) + androidExt.onVariants { variant -> + registerComposeCompilerReportGenTaskForTarget(this, variant) + } + } else { + registerComposeCompilerReportGenTaskForTarget(this) + } + } + + afterEvaluate { + // When this method returns true it means gradle task for generating report is executing otherwise + // normal compilation task is executing. + val isFromReportGenGradleTask = executingComposeCompilerReportGenerationGradleTask() + if (isFromReportGenGradleTask) { + configureKotlinOptionsForComposeCompilerReport(multiplatformExt.targets.flatMap { it.compilations }) + } + } +} + +fun Project.configureKotlinOptionsForComposeCompilerReport(compilations: Collection>) { + compilations.filter { compilation -> !compilation.name.endsWith("Test", ignoreCase = true) } + .forEach { + it.kotlinOptions { + configureKotlinOptionsForComposeCompilerReport(this@configureKotlinOptionsForComposeCompilerReport) + } + } +} diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/jvm/configure.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/jvm/configure.kt new file mode 100644 index 0000000..d93f3b5 --- /dev/null +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/jvm/configure.kt @@ -0,0 +1,23 @@ +package dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.multiplatform.jvm + +import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.multiplatform.configureKotlinOptionsForComposeCompilerReport +import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.executingComposeCompilerReportGenerationGradleTask +import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.task.registerComposeCompilerReportGenTaskForJvmProject +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension + +fun Project.configureKotlinJvmComposeCompilerReports(jvm: KotlinJvmProjectExtension) { + // Create gradle tasks for generating report + registerComposeCompilerReportGenTaskForJvmProject() + + afterEvaluate { + // When this method returns true it means gradle task for generating report is executing otherwise + // normal compilation task is executing. + val isFromReportGenGradleTask = executingComposeCompilerReportGenerationGradleTask() + if (isFromReportGenGradleTask) { + jvm.target { + configureKotlinOptionsForComposeCompilerReport(compilations) + } + } + } +} \ No newline at end of file diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt index deaa018..a79d19a 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/task/ComposeCompilerReportGenerateTask.kt @@ -157,11 +157,16 @@ abstract class ComposeCompilerReportGenerateTask : DefaultTask() { } } +/** + * Register a task for generating Compose Compiler Report for a Kotlin Multiplatform Android target. + * @param target Kotlin Target for which report is to be generated + * @param variant Android Variant for which report is to be generated + */ fun Project.registerComposeCompilerReportGenTaskForTarget( target: KotlinTarget, variant: Variant? = null, ): TaskProvider { - val variantName = variant?.name?.let { it[0].toUpperCase() + it.substring(1) } ?: "" + val variantName = variant?.name?.let { it[0].uppercaseChar() + it.substring(1) } ?: "" val taskName = target.name + variantName + "ComposeCompilerHtmlReport" val compileKotlinTaskName = compileKotlinTaskNameFromTarget(target, variantName) val descSuffix = @@ -175,6 +180,9 @@ fun Project.registerComposeCompilerReportGenTaskForTarget( return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName, descSuffix) } +/** + * Register a task for generating Compose Compiler Report for JVM variant. + */ fun Project.registerComposeCompilerReportGenTaskForJvmProject(): TaskProvider { val taskName = "jvmComposeCompilerHtmlReport" val compileKotlinTaskName = "compileKotlin" @@ -182,13 +190,22 @@ fun Project.registerComposeCompilerReportGenTaskForJvmProject(): TaskProvider { val taskName = variant.name + "ComposeCompilerHtmlReport" val compileKotlinTaskName = compileKotlinTaskNameFromVariant(variant) - val descSuffix = "'${variant.name}' variant in android project" + val descSuffix = "'${variant.name}' variant in Android project" return registerComposeCompilerReportGenTask(taskName, compileKotlinTaskName, descSuffix) } +/** + * Register a task for generating Compose Compiler Report. + * @param taskName Name of the task + * @param compileKotlinTaskName Name of the compileKotlin task + * @param descSuffix Description suffix + */ fun Project.registerComposeCompilerReportGenTask( taskName: String, compileKotlinTaskName: String, From e1d4e98b474391ca9b3d49155e835498a26bf0bc Mon Sep 17 00:00:00 2001 From: Shreyas Patil Date: Sun, 28 Jul 2024 13:12:55 +0530 Subject: [PATCH 20/24] Update version: v1.4.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index b06f8b6..98b81b5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ kotlin.code.style=official GROUP=dev.shreyaspatil.compose-compiler-report-generator -VERSION_NAME=1.3.1 +VERSION_NAME=1.4.0 # Library configuration SONATYPE_HOST=DEFAULT From 6e3835a5d225e605c18630b80c5a2a5fb5b39bc9 Mon Sep 17 00:00:00 2001 From: Shreyas Patil Date: Sun, 28 Jul 2024 13:14:40 +0530 Subject: [PATCH 21/24] Reformat with spotless --- .../plugin/multiplatform/android/configure.kt | 27 ++++++++++++++++++- .../plugin/multiplatform/configure.kt | 25 +++++++++++++++++ .../plugin/multiplatform/jvm/configure.kt | 27 ++++++++++++++++++- 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/android/configure.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/android/configure.kt index be80a31..01bd7de 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/android/configure.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/android/configure.kt @@ -1,3 +1,28 @@ +/** + * MIT License + * + * Copyright (c) 2022 Shreyas Patil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +@file:Suppress("ktlint:standard:filename") + package dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.multiplatform.android import com.android.build.api.dsl.CommonExtension @@ -36,4 +61,4 @@ fun Project.configureKotlinAndroidComposeCompilerReports(android: AndroidCompone } } } -} \ No newline at end of file +} diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/configure.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/configure.kt index 9c09023..9fa6881 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/configure.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/configure.kt @@ -1,3 +1,28 @@ +/** + * MIT License + * + * Copyright (c) 2022 Shreyas Patil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +@file:Suppress("ktlint:standard:filename") + package dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.multiplatform import com.android.build.api.variant.AndroidComponentsExtension diff --git a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/jvm/configure.kt b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/jvm/configure.kt index d93f3b5..39bb08c 100644 --- a/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/jvm/configure.kt +++ b/gradle-plugin/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/plugin/multiplatform/jvm/configure.kt @@ -1,3 +1,28 @@ +/** + * MIT License + * + * Copyright (c) 2022 Shreyas Patil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +@file:Suppress("ktlint:standard:filename") + package dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.multiplatform.jvm import dev.shreyaspatil.composeCompilerMetricsGenerator.plugin.multiplatform.configureKotlinOptionsForComposeCompilerReport @@ -20,4 +45,4 @@ fun Project.configureKotlinJvmComposeCompilerReports(jvm: KotlinJvmProjectExtens } } } -} \ No newline at end of file +} From 9f0fc7bafac92054fdff41d8038b799f7884ed3c Mon Sep 17 00:00:00 2001 From: Shreyas Patil Date: Sun, 28 Jul 2024 13:21:33 +0530 Subject: [PATCH 22/24] Update docs for KMP usage --- ....png => gradle-plugin-example-android.png} | Bin docs/images/gradle-plugin-example-kmp.png | Bin 0 -> 126352 bytes docs/use/using-gradle-plugin.md | 20 ++++++++++++++---- 3 files changed, 16 insertions(+), 4 deletions(-) rename docs/images/{gradle-plugin-example.png => gradle-plugin-example-android.png} (100%) create mode 100644 docs/images/gradle-plugin-example-kmp.png diff --git a/docs/images/gradle-plugin-example.png b/docs/images/gradle-plugin-example-android.png similarity index 100% rename from docs/images/gradle-plugin-example.png rename to docs/images/gradle-plugin-example-android.png diff --git a/docs/images/gradle-plugin-example-kmp.png b/docs/images/gradle-plugin-example-kmp.png new file mode 100644 index 0000000000000000000000000000000000000000..04517bd2957323a334ef56ca3af834184317e867 GIT binary patch literal 126352 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 Date: Sun, 28 Jul 2024 13:43:06 +0530 Subject: [PATCH 23/24] #114 | Update regex for parsing the parameter of function --- .../core/parser/ComposableReportParser.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ComposableReportParser.kt b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ComposableReportParser.kt index bdec102..55e205d 100644 --- a/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ComposableReportParser.kt +++ b/core/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/core/parser/ComposableReportParser.kt @@ -34,7 +34,7 @@ import dev.shreyaspatil.composeCompilerMetricsGenerator.core.model.composables.C */ object ComposableReportParser : Parser { private val REGEX_COMPOSABLE_FUNCTION = "(?:(.*))fun (\\w*)".toRegex() - private val REGEX_COMPOSABLE_PARAMETERS = "(?:(stable|unstable|) (\\w*:\\s.*))".toRegex() + private val REGEX_COMPOSABLE_PARAMETERS = "(?:(stable|unstable|\\w+) (\\w*:\\s.*))".toRegex() /** * Parses all composable functions From e446e6341f86e08a324c393d54fe7a4951abbdc2 Mon Sep 17 00:00:00 2001 From: Shreyas Patil Date: Sun, 28 Jul 2024 13:50:31 +0530 Subject: [PATCH 24/24] Update versions --- .../composeCompilerMetricsGenerator/cli/Main.kt | 2 +- docs/use/using-cli.md | 2 +- docs/use/using-gradle-plugin.md | 8 ++++---- package.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cli/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/cli/Main.kt b/cli/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/cli/Main.kt index 9422ab5..0386e70 100644 --- a/cli/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/cli/Main.kt +++ b/cli/src/main/kotlin/dev/shreyaspatil/composeCompilerMetricsGenerator/cli/Main.kt @@ -222,5 +222,5 @@ fun printHeader(header: String) = ) object Constants { - const val VERSION = "v1.3.1" + const val VERSION = "v1.4.0" } diff --git a/docs/use/using-cli.md b/docs/use/using-cli.md index 24c4a0a..5e90e6c 100644 --- a/docs/use/using-cli.md +++ b/docs/use/using-cli.md @@ -56,7 +56,7 @@ Run the command to know the usage of CLI !!! success "▶️Output" ```shell - Usage: Compose Compiler Report to HTML Generator ~ v1.3.1 options_list + Usage: Compose Compiler Report to HTML Generator ~ v1.4.0 options_list Options: --applicationName, -app -> Application name (To be displayed in the report) (always required) { String } --inputDirectory, -i -> Input directory where composable report and metrics are available { String } diff --git a/docs/use/using-gradle-plugin.md b/docs/use/using-gradle-plugin.md index c7a4dc5..c200e6a 100644 --- a/docs/use/using-gradle-plugin.md +++ b/docs/use/using-gradle-plugin.md @@ -21,7 +21,7 @@ Apply the plugin to the module in which _**compose is enabled**_. ```groovy title="build.gradle" plugins { - id "dev.shreyaspatil.compose-compiler-report-generator" version "1.3.1" + id "dev.shreyaspatil.compose-compiler-report-generator" version "1.4.0" } ``` @@ -29,7 +29,7 @@ Apply the plugin to the module in which _**compose is enabled**_. ```kotlin title="build.gradle.kts" plugins { - id("dev.shreyaspatil.compose-compiler-report-generator") version "1.3.1" + id("dev.shreyaspatil.compose-compiler-report-generator") version "1.4.0" } ``` @@ -47,7 +47,7 @@ Add this to top project level `build.gradle` } } dependencies { - classpath "dev.shreyaspatil.compose-compiler-report-generator:gradle-plugin:1.3.1" + classpath "dev.shreyaspatil.compose-compiler-report-generator:gradle-plugin:1.4.0" } } ``` @@ -68,7 +68,7 @@ Add this to top project level `build.gradle` } } dependencies { - classpath("dev.shreyaspatil.compose-compiler-report-generator:gradle-plugin:1.3.1") + classpath("dev.shreyaspatil.compose-compiler-report-generator:gradle-plugin:1.4.0") } } ``` diff --git a/package.json b/package.json index bd99a51..b2e0176 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "main": "index.js", "preferGlobal": true, "repository": "https://github.com/PatilShreyas/compose-report-to-html", - "version": "1.3.1", + "version": "1.4.0", "jdeploy": { "jdk": false, "javaVersion": "11",