Skip to content

Commit

Permalink
Fixes bug related with packages processing
Browse files Browse the repository at this point in the history
  • Loading branch information
GuilhE committed Jul 6, 2024
1 parent bb302e9 commit 418fa25
Show file tree
Hide file tree
Showing 16 changed files with 148 additions and 87 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Changelog
---

## [2.0.20-Beta1-1.6.11-BETA-5]

- Fixes EmptyFrameworkBaseNameException bug

---

## [2.0.20-Beta1-1.6.11-BETA-4]

- Adds experimental feature to import types from external modules.
Expand Down
3 changes: 1 addition & 2 deletions CHANGELOG_PLUGIN.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@

## [1.1.2]

- Compatible with 2.0.20-Beta1-1.6.11-BETA-4

- Compatible with 2.0.20-Beta1-1.6.11-BETA-5

---

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ buildscript {

allprojects {
group = "com.github.guilhe.kmp"
version = "2.0.20-Beta1-1.6.11-BETA-4"
version = "2.0.20-Beta1-1.6.11-BETA-5"
}

tasks.register("publishLibraryModules") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.github.guilhe.kmp.composeuiviewcontroller.common
import kotlinx.serialization.Serializable

@Serializable
public data class Module(val name: String, val packageName: String, val frameworkBaseName: String)
public data class ModuleMetadata(val name: String, val packageName: Set<String>, val frameworkBaseName: String)

public const val TEMP_FILES_FOLDER: String = "composeuiviewcontroller"
public const val FILE_NAME_ARGS: String = "modules.json"
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.github.guilhe.kmp.composeuiviewcontroller.gradle

import com.github.guilhe.kmp.composeuiviewcontroller.common.FILE_NAME_ARGS
import com.github.guilhe.kmp.composeuiviewcontroller.common.Module
import com.github.guilhe.kmp.composeuiviewcontroller.common.ModuleMetadata
import com.github.guilhe.kmp.composeuiviewcontroller.common.TEMP_FILES_FOLDER
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
Expand Down Expand Up @@ -44,7 +44,7 @@ public class KmpComposeUIViewControllerPlugin : Plugin<Project> {
finalizeFrameworkTasks(this)
}
afterEvaluate {
writeArgsToDisk(configureModuleJson(retrievePackage(), retrieveFrameworkBaseNames()))
writeModuleMetadataToDisk(configureFrameworkToPackage(retrieveModulePackageFromCommonMain(), retrieveFrameworkBaseNamesFromIosTargets()))
}
}
}
Expand All @@ -65,7 +65,7 @@ public class KmpComposeUIViewControllerPlugin : Plugin<Project> {
}
}

private fun Project.retrieveFrameworkBaseNames(): Set<String> {
private fun Project.retrieveFrameworkBaseNamesFromIosTargets(): Set<String> {
val kmp = extensions.getByType(KotlinMultiplatformExtension::class.java)
val frameworkNames = mutableSetOf<String>()
kmp.targets.configureEach { target ->
Expand All @@ -78,59 +78,48 @@ public class KmpComposeUIViewControllerPlugin : Plugin<Project> {
return frameworkNames
}

private fun Project.retrievePackage(): String {
private fun Project.retrieveModulePackageFromCommonMain(): Set<String> {
val kmp = extensions.getByType(KotlinMultiplatformExtension::class.java)
val commonMainSourceSet = kmp.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
val srcDirs = commonMainSourceSet.kotlin.srcDirs
for (srcDir in srcDirs) {
srcDir.walkTopDown().forEach { file ->
val packages = mutableSetOf<String>()
commonMainSourceSet.kotlin.srcDirs.forEach { dir ->
dir.walkTopDown().forEach { file ->
if (file.isFile && file.extension == "kt") {
val relativePath = file.relativeTo(srcDir).parentFile?.path ?: ""
val relativePath = file.relativeTo(dir).parentFile?.path ?: ""
val packagePath = relativePath.replace(File.separator, ".")
if (packagePath.isNotEmpty()) {
return packagePath
packages.add(packagePath)
}
}
}
}
if (group.toString().isNotEmpty()) {
return group.toString()
}
throw IllegalStateException("Cloud not determine project's package nor group")
return packages.ifEmpty { throw GradleException("Cloud not determine project's package") }
}

private fun Project.configureModuleJson(packageName: String, frameworkNames: Set<String>): Map<String, String> {
packageName.ifEmpty { return emptyMap() }
private fun Project.configureFrameworkToPackage(packageNames: Set<String>, frameworkNames: Set<String>): Map<String, Set<String>> {
packageNames.ifEmpty { return emptyMap() }
frameworkNames.ifEmpty { return emptyMap() }
val args = mutableMapOf<String, String>()
val frameworkBaseName = frameworkNames.first() //let's assume for now all targets will have the same frameworkBaseName
val kotlin = extensions.getByType(KotlinMultiplatformExtension::class.java)
kotlin.targets.configureEach { target ->
if (target.fromIosFamily()) {
args[frameworkBaseName] = packageName
val map = mutableMapOf<String, Set<String>>()
extensions.getByType(KotlinMultiplatformExtension::class.java).run {
targets.configureEach { target ->
if (target.fromIosFamily()) {
map[frameworkBaseName] = packageNames
}
}
}
return args
return map
}

private fun Project.writeArgsToDisk(args: Map<String, String>) {
private fun Project.writeModuleMetadataToDisk(args: Map<String, Set<String>>) {
val file = rootProject.layout.buildDirectory.file("$TEMP_FILES_FOLDER/$FILE_NAME_ARGS").get().asFile
val modules = try {
Json.decodeFromString<MutableSet<Module>>(file.readText())
val moduleMetadata = try {
Json.decodeFromString<MutableSet<ModuleMetadata>>(file.readText())
} catch (e: Exception) {
mutableSetOf()
}
args.forEach { (key, value) -> modules.add(Module(name = name.toString(), packageName = value, frameworkBaseName = key)) }
file.writeText(Json.encodeToString(modules))
}

private fun Project.finalizeFrameworkTasks(extensionParameters: ComposeUiViewControllerParameters) {
tasks.matching { it.name == TASK_EMBED_AND_SING_APPLE_FRAMEWORK_FOR_XCODE || it.name == TASK_SYNC_FRAMEWORK }.configureEach { task ->
if (extensionParameters.autoExport) {
println("\n> $LOG_TAG:\n\t> ${task.name}will be finalizedBy $TASK_COPY_FILES_TO_XCODE task")
task.finalizedBy(TASK_COPY_FILES_TO_XCODE)
}
}
args.forEach { (key, value) -> moduleMetadata.add(ModuleMetadata(name = name.toString(), packageName = value, frameworkBaseName = key)) }
file.writeText(Json.encodeToString(moduleMetadata))
}

private fun Project.registerCopyFilesToXcodeTask(project: Project, extensionParameters: ComposeUiViewControllerParameters) {
Expand Down Expand Up @@ -176,8 +165,17 @@ public class KmpComposeUIViewControllerPlugin : Plugin<Project> {
}
}

private fun Project.finalizeFrameworkTasks(extensionParameters: ComposeUiViewControllerParameters) {
tasks.matching { it.name == TASK_EMBED_AND_SING_APPLE_FRAMEWORK_FOR_XCODE || it.name == TASK_SYNC_FRAMEWORK }.configureEach { task ->
if (extensionParameters.autoExport) {
println("\n> $LOG_TAG:\n\t> ${task.name}will be finalizedBy $TASK_COPY_FILES_TO_XCODE task")
task.finalizedBy(TASK_COPY_FILES_TO_XCODE)
}
}
}

internal companion object {
private const val VERSION_LIBRARY = "2.0.20-Beta1-1.6.11-BETA-4"
private const val VERSION_LIBRARY = "2.0.20-Beta1-1.6.11-BETA-5"
private const val LOG_TAG = "KmpComposeUIViewControllerPlugin"
internal const val PLUGIN_KMP = "org.jetbrains.kotlin.multiplatform"
internal const val PLUGIN_KSP = "com.google.devtools.ksp"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ copy_files "$files_source" "$files_destination"
if [ "$copied_count" -gt 0 ]; then
echo "> Checking for new references to be added to xcodeproj"
cd ..
cd ..
cd $iosApp_project_folder || exit
add_file_references "$xcodeproj_path" "$iosApp_target_name" "$group_name"
echo "> Done"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package composeuiviewcontroller

import com.github.guilhe.kmp.composeuiviewcontroller.common.FILE_NAME_ARGS
import com.github.guilhe.kmp.composeuiviewcontroller.common.Module
import com.github.guilhe.kmp.composeuiviewcontroller.common.ModuleMetadata
import com.github.guilhe.kmp.composeuiviewcontroller.common.TEMP_FILES_FOLDER
import com.github.guilhe.kmp.composeuiviewcontroller.gradle.KmpComposeUIViewControllerPlugin
import com.github.guilhe.kmp.composeuiviewcontroller.gradle.KmpComposeUIViewControllerPlugin.Companion.ERROR_MISSING_KMP
Expand Down Expand Up @@ -120,21 +120,30 @@ class PluginTest {
fun `Method configureModuleJson creates and saves in disk modules metadata`() {
with(project) {
extensions.getByType(KotlinMultiplatformExtension::class.java).apply {
group = "com.composables.module"
jvm()
iosSimulatorArm64().binaries.framework { baseName = "ComposablesFramework" }
}

val folder = File(projectDir.path, "src/commonMain/kotlin/com/composables/module").apply { mkdirs() }
val classFile = File(folder, "File.kt")
classFile.writeText(
"""
com.composables.module
class Test()
""".trimIndent()
)
assertTrue(classFile.exists())

println("> $state")
(this as DefaultProject).evaluate()
println("> $state")

val file = rootProject.layout.buildDirectory.file("$TEMP_FILES_FOLDER/$FILE_NAME_ARGS").get().asFile
assertTrue(file.exists())

val module = Json.decodeFromString<List<Module>>(file.readText()).first()
assertTrue(module.frameworkBaseName == "ComposablesFramework")
assertTrue(module.packageName == "com.composables.module")
val moduleMetadata = Json.decodeFromString<List<ModuleMetadata>>(file.readText()).first()
assertTrue(moduleMetadata.frameworkBaseName == "ComposablesFramework")
assertTrue(moduleMetadata.packageName.contains("com.composables.module"))
}
}

Expand All @@ -151,6 +160,16 @@ class PluginTest {
@Test
fun `Task CopyFilesToXcode will inject the extension parameters into exportToXcode file`(@TempDir tempDir: File) {
projectDir = File(tempDir, "testProject").apply { mkdirs() }
val folder = File(projectDir.path, "src/commonMain/kotlin/com/test").apply { mkdirs() }
val classFile = File(folder, "File.kt")
classFile.writeText(
"""
package com.test
class Test()
""".trimIndent()
)
assertTrue(classFile.exists())

val buildFile = File(projectDir, "build.gradle.kts")
buildFile.writeText(
"""
Expand All @@ -167,10 +186,9 @@ class PluginTest {
exportFolderName = "Composables"
autoExport = true
}
group = "com.test"
""".trimIndent()
)
assertTrue(buildFile.exists())

val result = GradleRunner.create()
.withProjectDir(projectDir)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
@file:Suppress("RedundantVisibilityModifier")

package com.github.guilhe.kmp.composeuiviewcontroller.ksp

import com.github.guilhe.kmp.composeuiviewcontroller.common.FILE_NAME_ARGS
import com.github.guilhe.kmp.composeuiviewcontroller.common.Module
import com.github.guilhe.kmp.composeuiviewcontroller.common.ModuleMetadata
import com.github.guilhe.kmp.composeuiviewcontroller.common.TEMP_FILES_FOLDER
import com.google.devtools.ksp.containingFile
import com.google.devtools.ksp.processing.CodeGenerator
Expand Down Expand Up @@ -55,30 +57,26 @@ internal class Processor(
.also { if (parameters.size != it.size + 1) throw InvalidParametersException() }
}
val packageName = file.packageName.asString()
val imports = extractImportsFromExternalPackages(packageName, makeParameters, parameters)
val modules = getFrameworkMetadataFromJson()
val externalModuleTypes = buildExternalModuleParameters(modules, imports)
val externalImports = extractImportsFromExternalPackages(packageName, makeParameters, parameters)
val modulesMetadata = getFrameworkMetadataFromDisk()
val externalModuleTypes = buildExternalModuleParameters(modulesMetadata, externalImports)
// val frameworkBaseNames = getFrameworkBaseNames(composable, node, makeParameters, parameters)
val currentFramework = trimFrameworkBaseNames(node, modulesMetadata, packageName)

if (stateParameter == null) {
createKotlinFileWithoutState(packageName, imports, composable, makeParameters, parameters).also {
createKotlinFileWithoutState(packageName, externalImports, composable, makeParameters, parameters).also {
logger.info("${composable.name()}UIViewController created!")
}

// val frameworkBaseNames = getFrameworkBaseNames(composable, node, makeParameters, parameters)
val currentFramework = trimFrameworkBaseNames(node, packageName)
createSwiftFileWithoutState(listOf(currentFramework), composable, makeParameters, externalModuleTypes).also {
logger.info("${composable.name()}Representable created!")
}
} else {
val stateParameterName = stateParameter.name()
createKotlinFileWithState(
packageName, imports, composable, stateParameterName, stateParameter, makeParameters, parameters
packageName, externalImports, composable, stateParameterName, stateParameter, makeParameters, parameters
).also {
logger.info("${composable.name()}UIViewController created!")
}

// val frameworkBaseNames = getFrameworkBaseNames(composable, node, makeParameters, parameters)
val currentFramework = trimFrameworkBaseNames(node, packageName)
createSwiftFileWithState(
listOf(currentFramework), composable, stateParameterName, stateParameter, makeParameters, externalModuleTypes
).also {
Expand All @@ -104,14 +102,14 @@ internal class Processor(
return stateParameters
}

private fun getFrameworkMetadataFromJson(): List<Module> {
private fun getFrameworkMetadataFromDisk(): List<ModuleMetadata> {
val file = File("./build/$TEMP_FILES_FOLDER/$FILE_NAME_ARGS")
val modules = try {
Json.decodeFromString<List<Module>>(file.readText())
val moduleMetadata = try {
Json.decodeFromString<List<ModuleMetadata>>(file.readText())
} catch (e: Exception) {
throw ModuleDecodeException()
}
return modules
return moduleMetadata
}

/**
Expand All @@ -137,13 +135,13 @@ internal class Processor(
*
* [https://stackoverflow.com/a/78707072/1423773](https://stackoverflow.com/a/78707072/1423773)
*/
private fun buildExternalModuleParameters(modules: List<Module>, imports: List<String>): MutableMap<String, String> {
private fun buildExternalModuleParameters(moduleMetadata: List<ModuleMetadata>, imports: List<String>): MutableMap<String, String> {
val result = mutableMapOf<String, String>()
imports.forEach { it ->
val type = it.split(".").last()
val import = it.split(".$type").first()
modules
.filter { module -> module.packageName == import }
moduleMetadata
.filter { module -> module.packageName.contains(import) }
.forEach { module ->
val capitalized = module.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
val replaced = capitalized.replace("-", "_")
Expand All @@ -154,16 +152,14 @@ internal class Processor(
}

/**
* This will be needed until [buildExternalModuleParameters] is needed to. When (if) KPM limitation is addressed this will become deprecated and substituted by [getFrameworkBaseNames]
* This will be needed until [buildExternalModuleParameters] is needed too. When (if) KPM limitation is addressed this will become deprecated and substituted by [getFrameworkBaseNames]
*/
private fun trimFrameworkBaseNames(node: KSAnnotated, packageName: String): String {
val metadata = getFrameworkMetadataFromJson()
if (metadata.isEmpty()) {
private fun trimFrameworkBaseNames(node: KSAnnotated, moduleMetadata: List<ModuleMetadata>, packageName: String): String {
if (moduleMetadata.isEmpty()) {
val framework = getFrameworkBaseNameFromAnnotation(node) ?: throw EmptyFrameworkBaseNameException()
return framework
} else {
val framework =
metadata.firstOrNull { it.packageName == packageName }?.frameworkBaseName?: ""
val framework = moduleMetadata.firstOrNull { it.packageName.contains(packageName) }?.frameworkBaseName ?: ""
framework.ifEmpty { return getFrameworkBaseNameFromAnnotation(node) ?: throw EmptyFrameworkBaseNameException() }
return framework
}
Expand All @@ -180,7 +176,7 @@ internal class Processor(
frameworkBaseNames.addAll(
extractFrameworkBaseNames(
composable,
getFrameworkMetadataFromJson(),
getFrameworkMetadataFromDisk(),
makeParameters,
parameters,
stateParameter
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package com.github.guilhe.kmp.composeuiviewcontroller.ksp

import com.github.guilhe.kmp.composeuiviewcontroller.common.FILE_NAME_ARGS
import com.github.guilhe.kmp.composeuiviewcontroller.common.Module
import com.github.guilhe.kmp.composeuiviewcontroller.common.ModuleMetadata
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSFile
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSTypeReference
import com.google.devtools.ksp.symbol.KSValueParameter
Expand Down Expand Up @@ -89,7 +88,7 @@ internal fun extractImportsFromExternalPackages(

internal fun extractFrameworkBaseNames(
composable: KSFunctionDeclaration,
modules: List<Module>,
moduleMetadata: List<ModuleMetadata>,
makeParameters: List<KSValueParameter>,
parameters: List<KSValueParameter>,
stateParameter: KSValueParameter? = null
Expand All @@ -112,9 +111,9 @@ internal fun extractFrameworkBaseNames(

parameterPackages.add(composable.packageName.asString())

return parameterPackages.mapNotNull { pkg ->
modules.find { it.packageName == pkg }?.frameworkBaseName
}.distinct()
return parameterPackages
.mapNotNull { pkg -> moduleMetadata.find { it.packageName.contains(pkg) }?.frameworkBaseName }
.distinct()
}

internal fun String.name() = split(".").last()
Expand Down
Loading

0 comments on commit 418fa25

Please sign in to comment.