Browse Source
Compose resources can be located in different KMP source sets in the `composeResources` directory. For each resource an accessor will be generated in the suitable kotlin source set.support/1.6.10 v1.6.10-dev1575
Konstantin
1 month ago
committed by
GitHub
39 changed files with 714 additions and 365 deletions
@ -0,0 +1,133 @@ |
|||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
import org.gradle.api.Project |
||||||
|
import org.gradle.api.provider.Provider |
||||||
|
import org.jetbrains.compose.ComposePlugin |
||||||
|
import org.jetbrains.compose.internal.utils.uppercaseFirstChar |
||||||
|
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension |
||||||
|
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet |
||||||
|
import java.io.File |
||||||
|
|
||||||
|
internal fun Project.configureComposeResourcesGeneration( |
||||||
|
kotlinExtension: KotlinProjectExtension, |
||||||
|
resClassSourceSetName: String, |
||||||
|
config: Provider<ResourcesExtension>, |
||||||
|
generateModulePath: Boolean |
||||||
|
) { |
||||||
|
logger.info("Configure compose resources generation") |
||||||
|
|
||||||
|
//lazy check a dependency on the Resources library |
||||||
|
val shouldGenerateCode = config.map { |
||||||
|
when (it.generateResClass) { |
||||||
|
ResourcesExtension.ResourceClassGeneration.Auto -> { |
||||||
|
configurations.run { |
||||||
|
val commonSourceSet = kotlinExtension.sourceSets.getByName(resClassSourceSetName) |
||||||
|
//because the implementation configuration doesn't extend the api in the KGP ¯\_(ツ)_/¯ |
||||||
|
getByName(commonSourceSet.implementationConfigurationName).allDependencies + |
||||||
|
getByName(commonSourceSet.apiConfigurationName).allDependencies |
||||||
|
}.any { dep -> |
||||||
|
val depStringNotation = dep.let { "${it.group}:${it.name}:${it.version}" } |
||||||
|
depStringNotation == ComposePlugin.CommonComponentsDependencies.resources |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ResourcesExtension.ResourceClassGeneration.Always -> true |
||||||
|
ResourcesExtension.ResourceClassGeneration.Never -> false |
||||||
|
} |
||||||
|
} |
||||||
|
val packageName = config.getResourcePackage(project) |
||||||
|
val makeAccessorsPublic = config.map { it.publicResClass } |
||||||
|
val packagingDir = config.getModuleResourcesDir(project) |
||||||
|
|
||||||
|
kotlinExtension.sourceSets.all { sourceSet -> |
||||||
|
if (sourceSet.name == resClassSourceSetName) { |
||||||
|
configureResClassGeneration( |
||||||
|
sourceSet, |
||||||
|
shouldGenerateCode, |
||||||
|
packageName, |
||||||
|
makeAccessorsPublic, |
||||||
|
packagingDir, |
||||||
|
generateModulePath |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
//common resources must be converted (XML -> CVR) |
||||||
|
val preparedResourcesTask = registerPrepareComposeResourcesTask(sourceSet) |
||||||
|
val preparedResources = preparedResourcesTask.flatMap { it.outputDir.asFile } |
||||||
|
configureResourceAccessorsGeneration( |
||||||
|
sourceSet, |
||||||
|
preparedResources, |
||||||
|
shouldGenerateCode, |
||||||
|
packageName, |
||||||
|
makeAccessorsPublic, |
||||||
|
packagingDir, |
||||||
|
generateModulePath |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
//setup task execution during IDE import |
||||||
|
tasks.configureEach { importTask -> |
||||||
|
if (importTask.name == "prepareKotlinIdeaImport") { |
||||||
|
importTask.dependsOn(tasks.withType(CodeGenerationTask::class.java)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun Project.configureResClassGeneration( |
||||||
|
resClassSourceSet: KotlinSourceSet, |
||||||
|
shouldGenerateCode: Provider<Boolean>, |
||||||
|
packageName: Provider<String>, |
||||||
|
makeAccessorsPublic: Provider<Boolean>, |
||||||
|
packagingDir: Provider<File>, |
||||||
|
generateModulePath: Boolean |
||||||
|
) { |
||||||
|
logger.info("Configure Res class generation for ${resClassSourceSet.name}") |
||||||
|
|
||||||
|
val genTask = tasks.register( |
||||||
|
"generateComposeResClass", |
||||||
|
GenerateResClassTask::class.java |
||||||
|
) { task -> |
||||||
|
task.packageName.set(packageName) |
||||||
|
task.shouldGenerateCode.set(shouldGenerateCode) |
||||||
|
task.makeAccessorsPublic.set(makeAccessorsPublic) |
||||||
|
task.codeDir.set(layout.buildDirectory.dir("$RES_GEN_DIR/kotlin/commonResClass")) |
||||||
|
|
||||||
|
if (generateModulePath) { |
||||||
|
task.packagingDir.set(packagingDir) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//register generated source set |
||||||
|
resClassSourceSet.kotlin.srcDir(genTask.map { it.codeDir }) |
||||||
|
} |
||||||
|
|
||||||
|
private fun Project.configureResourceAccessorsGeneration( |
||||||
|
sourceSet: KotlinSourceSet, |
||||||
|
resourcesDir: Provider<File>, |
||||||
|
shouldGenerateCode: Provider<Boolean>, |
||||||
|
packageName: Provider<String>, |
||||||
|
makeAccessorsPublic: Provider<Boolean>, |
||||||
|
packagingDir: Provider<File>, |
||||||
|
generateModulePath: Boolean |
||||||
|
) { |
||||||
|
logger.info("Configure resource accessors generation for ${sourceSet.name}") |
||||||
|
|
||||||
|
val genTask = tasks.register( |
||||||
|
"generateResourceAccessorsFor${sourceSet.name.uppercaseFirstChar()}", |
||||||
|
GenerateResourceAccessorsTask::class.java |
||||||
|
) { task -> |
||||||
|
task.packageName.set(packageName) |
||||||
|
task.sourceSetName.set(sourceSet.name) |
||||||
|
task.shouldGenerateCode.set(shouldGenerateCode) |
||||||
|
task.makeAccessorsPublic.set(makeAccessorsPublic) |
||||||
|
task.resDir.set(resourcesDir) |
||||||
|
task.codeDir.set(layout.buildDirectory.dir("$RES_GEN_DIR/kotlin/${sourceSet.name}ResourceAccessors")) |
||||||
|
|
||||||
|
if (generateModulePath) { |
||||||
|
task.packagingDir.set(packagingDir) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//register generated source set |
||||||
|
sourceSet.kotlin.srcDir(genTask.map { it.codeDir }) |
||||||
|
} |
@ -1,147 +1,59 @@ |
|||||||
package org.jetbrains.compose.resources |
package org.jetbrains.compose.resources |
||||||
|
|
||||||
import org.gradle.api.DefaultTask |
import org.gradle.api.DefaultTask |
||||||
|
import org.gradle.api.file.DirectoryProperty |
||||||
|
import org.gradle.api.file.RegularFile |
||||||
import org.gradle.api.provider.Property |
import org.gradle.api.provider.Property |
||||||
|
import org.gradle.api.provider.Provider |
||||||
import org.gradle.api.tasks.* |
import org.gradle.api.tasks.* |
||||||
import java.io.File |
import java.io.File |
||||||
import java.io.RandomAccessFile |
|
||||||
import java.nio.file.Path |
|
||||||
import kotlin.io.path.relativeTo |
|
||||||
|
|
||||||
/** |
/** |
||||||
* This task should be FAST and SAFE! Because it is being run during IDE import. |
* This task should be FAST and SAFE! Because it is being run during IDE import. |
||||||
*/ |
*/ |
||||||
internal abstract class GenerateResClassTask : DefaultTask() { |
internal abstract class CodeGenerationTask : DefaultTask() |
||||||
|
|
||||||
|
internal abstract class GenerateResClassTask : CodeGenerationTask() { |
||||||
|
companion object { |
||||||
|
private const val RES_FILE_NAME = "Res" |
||||||
|
} |
||||||
|
|
||||||
@get:Input |
@get:Input |
||||||
abstract val packageName: Property<String> |
abstract val packageName: Property<String> |
||||||
|
|
||||||
@get:Input |
@get:Input |
||||||
@get:Optional |
@get:Optional |
||||||
abstract val moduleDir: Property<File> |
abstract val packagingDir: Property<File> |
||||||
|
|
||||||
@get:Input |
@get:Input |
||||||
abstract val shouldGenerateResClass: Property<Boolean> |
abstract val shouldGenerateCode: Property<Boolean> |
||||||
|
|
||||||
@get:Input |
@get:Input |
||||||
abstract val makeResClassPublic: Property<Boolean> |
abstract val makeAccessorsPublic: Property<Boolean> |
||||||
|
|
||||||
@get:InputFiles |
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE) |
|
||||||
abstract val resDir: Property<File> |
|
||||||
|
|
||||||
@get:OutputDirectory |
@get:OutputDirectory |
||||||
abstract val codeDir: Property<File> |
abstract val codeDir: DirectoryProperty |
||||||
|
|
||||||
@TaskAction |
@TaskAction |
||||||
fun generate() { |
fun generate() { |
||||||
try { |
try { |
||||||
val kotlinDir = codeDir.get() |
val dir = codeDir.get().asFile |
||||||
logger.info("Clean directory $kotlinDir") |
dir.deleteRecursively() |
||||||
kotlinDir.deleteRecursively() |
dir.mkdirs() |
||||||
kotlinDir.mkdirs() |
|
||||||
|
|
||||||
if (shouldGenerateResClass.get()) { |
if (shouldGenerateCode.get()) { |
||||||
val rootResDir = resDir.get() |
logger.info("Generate $RES_FILE_NAME.kt") |
||||||
logger.info("Generate resources for $rootResDir") |
|
||||||
|
|
||||||
//get first level dirs |
val pkgName = packageName.get() |
||||||
val dirs = rootResDir.listNotHiddenFiles() |
val moduleDirectory = packagingDir.getOrNull()?.let { it.invariantSeparatorsPath + "/" } ?: "" |
||||||
|
val isPublic = makeAccessorsPublic.get() |
||||||
dirs.forEach { f -> |
getResFileSpec(pkgName, RES_FILE_NAME, moduleDirectory, isPublic).writeTo(dir) |
||||||
if (!f.isDirectory) { |
|
||||||
error("${f.name} is not directory! Raw files should be placed in '${rootResDir.name}/files' directory.") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//type -> id -> resource item |
|
||||||
val resources: Map<ResourceType, Map<String, List<ResourceItem>>> = dirs |
|
||||||
.flatMap { dir -> |
|
||||||
dir.listNotHiddenFiles() |
|
||||||
.mapNotNull { it.fileToResourceItems(rootResDir.toPath()) } |
|
||||||
.flatten() |
|
||||||
} |
|
||||||
.groupBy { it.type } |
|
||||||
.mapValues { (_, items) -> items.groupBy { it.name } } |
|
||||||
getResFileSpecs( |
|
||||||
resources, |
|
||||||
packageName.get(), |
|
||||||
moduleDir.getOrNull()?.let { it.invariantSeparatorsPath + "/" } ?: "", |
|
||||||
makeResClassPublic.get() |
|
||||||
).forEach { it.writeTo(kotlinDir) } |
|
||||||
} else { |
} else { |
||||||
logger.info("Generation Res class is disabled") |
logger.info("Generation Res class is disabled") |
||||||
} |
} |
||||||
} catch (e: Exception) { |
} catch (e: Exception) { |
||||||
//message must contain two ':' symbols to be parsed by IDE UI! |
//message must contain two ':' symbols to be parsed by IDE UI! |
||||||
logger.error("e: GenerateResClassTask was failed:", e) |
logger.error("e: $name task was failed:", e) |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
private fun File.fileToResourceItems( |
|
||||||
relativeTo: Path |
|
||||||
): List<ResourceItem>? { |
|
||||||
val file = this |
|
||||||
val dirName = file.parentFile.name ?: return null |
|
||||||
val typeAndQualifiers = dirName.split("-") |
|
||||||
if (typeAndQualifiers.isEmpty()) return null |
|
||||||
|
|
||||||
val typeString = typeAndQualifiers.first().lowercase() |
|
||||||
val qualifiers = typeAndQualifiers.takeLast(typeAndQualifiers.size - 1) |
|
||||||
val path = file.toPath().relativeTo(relativeTo) |
|
||||||
|
|
||||||
|
|
||||||
if (typeString == "string") { |
|
||||||
error("Forbidden directory name '$dirName'! String resources should be declared in 'values/strings.xml'.") |
|
||||||
} |
|
||||||
|
|
||||||
if (typeString == "files") { |
|
||||||
if (qualifiers.isNotEmpty()) error("The 'files' directory doesn't support qualifiers: '$dirName'.") |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
if (typeString == "values" && file.extension.equals(XmlValuesConverterTask.CONVERTED_RESOURCE_EXT, true)) { |
|
||||||
return getValueResourceItems(file, qualifiers, path) |
|
||||||
} |
|
||||||
|
|
||||||
val type = ResourceType.fromString(typeString) ?: error("Unknown resource type: '$typeString'.") |
|
||||||
return listOf(ResourceItem(type, qualifiers, file.nameWithoutExtension.asUnderscoredIdentifier(), path)) |
|
||||||
} |
|
||||||
|
|
||||||
private fun getValueResourceItems(dataFile: File, qualifiers: List<String>, path: Path) : List<ResourceItem> { |
|
||||||
val result = mutableListOf<ResourceItem>() |
|
||||||
dataFile.bufferedReader().use { f -> |
|
||||||
var offset = 0L |
|
||||||
var line: String? = f.readLine() |
|
||||||
while (line != null) { |
|
||||||
val size = line.encodeToByteArray().size |
|
||||||
|
|
||||||
//first line is meta info |
|
||||||
if (offset > 0) { |
|
||||||
result.add(getValueResourceItem(line, offset, size.toLong(), qualifiers, path)) |
|
||||||
} |
|
||||||
|
|
||||||
offset += size + 1 // "+1" for newline character |
|
||||||
line = f.readLine() |
|
||||||
} |
|
||||||
} |
|
||||||
return result |
|
||||||
} |
|
||||||
|
|
||||||
private fun getValueResourceItem( |
|
||||||
recordString: String, |
|
||||||
offset: Long, |
|
||||||
size: Long, |
|
||||||
qualifiers: List<String>, |
|
||||||
path: Path |
|
||||||
) : ResourceItem { |
|
||||||
val record = ValueResourceRecord.createFromString(recordString) |
|
||||||
return ResourceItem(record.type, qualifiers, record.key.asUnderscoredIdentifier(), path, offset, size) |
|
||||||
} |
|
||||||
} |
} |
||||||
|
|
||||||
internal fun File.listNotHiddenFiles(): List<File> = |
|
||||||
listFiles()?.filter { !it.isHidden }.orEmpty() |
|
||||||
|
|
||||||
internal fun String.asUnderscoredIdentifier(): String = |
|
||||||
replace('-', '_') |
|
||||||
.let { if (it.isNotEmpty() && it.first().isDigit()) "_$it" else it } |
|
@ -0,0 +1,157 @@ |
|||||||
|
package org.jetbrains.compose.resources |
||||||
|
|
||||||
|
import org.gradle.api.file.DirectoryProperty |
||||||
|
import org.gradle.api.provider.Property |
||||||
|
import org.gradle.api.tasks.Input |
||||||
|
import org.gradle.api.tasks.InputFiles |
||||||
|
import org.gradle.api.tasks.Optional |
||||||
|
import org.gradle.api.tasks.OutputDirectory |
||||||
|
import org.gradle.api.tasks.PathSensitive |
||||||
|
import org.gradle.api.tasks.PathSensitivity |
||||||
|
import org.gradle.api.tasks.SkipWhenEmpty |
||||||
|
import org.gradle.api.tasks.TaskAction |
||||||
|
import java.io.File |
||||||
|
import java.nio.file.Path |
||||||
|
import kotlin.io.path.relativeTo |
||||||
|
|
||||||
|
internal abstract class GenerateResourceAccessorsTask : CodeGenerationTask() { |
||||||
|
@get:Input |
||||||
|
abstract val packageName: Property<String> |
||||||
|
|
||||||
|
@get:Input |
||||||
|
abstract val sourceSetName: Property<String> |
||||||
|
|
||||||
|
@get:Input |
||||||
|
@get:Optional |
||||||
|
abstract val packagingDir: Property<File> |
||||||
|
|
||||||
|
@get:Input |
||||||
|
abstract val shouldGenerateCode: Property<Boolean> |
||||||
|
|
||||||
|
@get:Input |
||||||
|
abstract val makeAccessorsPublic: Property<Boolean> |
||||||
|
|
||||||
|
@get:InputFiles |
||||||
|
@get:SkipWhenEmpty |
||||||
|
@get:PathSensitive(PathSensitivity.RELATIVE) |
||||||
|
abstract val resDir: Property<File> |
||||||
|
|
||||||
|
@get:OutputDirectory |
||||||
|
abstract val codeDir: DirectoryProperty |
||||||
|
|
||||||
|
@TaskAction |
||||||
|
fun generate() { |
||||||
|
try { |
||||||
|
val kotlinDir = codeDir.get().asFile |
||||||
|
val rootResDir = resDir.get() |
||||||
|
val sourceSet = sourceSetName.get() |
||||||
|
|
||||||
|
logger.info("Clean directory $kotlinDir") |
||||||
|
kotlinDir.deleteRecursively() |
||||||
|
kotlinDir.mkdirs() |
||||||
|
|
||||||
|
if (shouldGenerateCode.get()) { |
||||||
|
logger.info("Generate accessors for $rootResDir") |
||||||
|
|
||||||
|
//get first level dirs |
||||||
|
val dirs = rootResDir.listNotHiddenFiles() |
||||||
|
|
||||||
|
dirs.forEach { f -> |
||||||
|
if (!f.isDirectory) { |
||||||
|
error("${f.name} is not directory! Raw files should be placed in '${rootResDir.name}/files' directory.") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//type -> id -> resource item |
||||||
|
val resources: Map<ResourceType, Map<String, List<ResourceItem>>> = dirs |
||||||
|
.flatMap { dir -> |
||||||
|
dir.listNotHiddenFiles() |
||||||
|
.mapNotNull { it.fileToResourceItems(rootResDir.toPath()) } |
||||||
|
.flatten() |
||||||
|
} |
||||||
|
.groupBy { it.type } |
||||||
|
.mapValues { (_, items) -> items.groupBy { it.name } } |
||||||
|
|
||||||
|
val pkgName = packageName.get() |
||||||
|
val moduleDirectory = packagingDir.getOrNull()?.let { it.invariantSeparatorsPath + "/" } ?: "" |
||||||
|
val isPublic = makeAccessorsPublic.get() |
||||||
|
getAccessorsSpecs( |
||||||
|
resources, pkgName, sourceSet, moduleDirectory, isPublic |
||||||
|
).forEach { it.writeTo(kotlinDir) } |
||||||
|
} else { |
||||||
|
logger.info("Generation accessors for $rootResDir is disabled") |
||||||
|
} |
||||||
|
} catch (e: Exception) { |
||||||
|
//message must contain two ':' symbols to be parsed by IDE UI! |
||||||
|
logger.error("e: $name task was failed:", e) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun File.fileToResourceItems( |
||||||
|
relativeTo: Path |
||||||
|
): List<ResourceItem>? { |
||||||
|
val file = this |
||||||
|
val dirName = file.parentFile.name ?: return null |
||||||
|
val typeAndQualifiers = dirName.split("-") |
||||||
|
if (typeAndQualifiers.isEmpty()) return null |
||||||
|
|
||||||
|
val typeString = typeAndQualifiers.first().lowercase() |
||||||
|
val qualifiers = typeAndQualifiers.takeLast(typeAndQualifiers.size - 1) |
||||||
|
val path = file.toPath().relativeTo(relativeTo) |
||||||
|
|
||||||
|
|
||||||
|
if (typeString == "string") { |
||||||
|
error("Forbidden directory name '$dirName'! String resources should be declared in 'values/strings.xml'.") |
||||||
|
} |
||||||
|
|
||||||
|
if (typeString == "files") { |
||||||
|
if (qualifiers.isNotEmpty()) error("The 'files' directory doesn't support qualifiers: '$dirName'.") |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
if (typeString == "values" && file.extension.equals(XmlValuesConverterTask.CONVERTED_RESOURCE_EXT, true)) { |
||||||
|
return getValueResourceItems(file, qualifiers, path) |
||||||
|
} |
||||||
|
|
||||||
|
val type = ResourceType.fromString(typeString) ?: error("Unknown resource type: '$typeString'.") |
||||||
|
return listOf(ResourceItem(type, qualifiers, file.nameWithoutExtension.asUnderscoredIdentifier(), path)) |
||||||
|
} |
||||||
|
|
||||||
|
private fun getValueResourceItems(dataFile: File, qualifiers: List<String>, path: Path): List<ResourceItem> { |
||||||
|
val result = mutableListOf<ResourceItem>() |
||||||
|
dataFile.bufferedReader().use { f -> |
||||||
|
var offset = 0L |
||||||
|
var line: String? = f.readLine() |
||||||
|
while (line != null) { |
||||||
|
val size = line.encodeToByteArray().size |
||||||
|
|
||||||
|
//first line is meta info |
||||||
|
if (offset > 0) { |
||||||
|
result.add(getValueResourceItem(line, offset, size.toLong(), qualifiers, path)) |
||||||
|
} |
||||||
|
|
||||||
|
offset += size + 1 // "+1" for newline character |
||||||
|
line = f.readLine() |
||||||
|
} |
||||||
|
} |
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
private fun getValueResourceItem( |
||||||
|
recordString: String, |
||||||
|
offset: Long, |
||||||
|
size: Long, |
||||||
|
qualifiers: List<String>, |
||||||
|
path: Path |
||||||
|
): ResourceItem { |
||||||
|
val record = ValueResourceRecord.createFromString(recordString) |
||||||
|
return ResourceItem(record.type, qualifiers, record.key.asUnderscoredIdentifier(), path, offset, size) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal fun File.listNotHiddenFiles(): List<File> = |
||||||
|
listFiles()?.filter { !it.isHidden }.orEmpty() |
||||||
|
|
||||||
|
internal fun String.asUnderscoredIdentifier(): String = |
||||||
|
replace('-', '_') |
||||||
|
.let { if (it.isNotEmpty() && it.first().isDigit()) "_$it" else it } |
@ -1,65 +0,0 @@ |
|||||||
package org.jetbrains.compose.resources |
|
||||||
|
|
||||||
import org.gradle.api.Project |
|
||||||
import org.gradle.api.provider.Provider |
|
||||||
import org.jetbrains.compose.ComposePlugin |
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet |
|
||||||
import java.io.File |
|
||||||
|
|
||||||
internal fun Project.configureGenerationComposeResClass( |
|
||||||
commonComposeResourcesDir: Provider<File>, |
|
||||||
commonSourceSet: KotlinSourceSet, |
|
||||||
config: Provider<ResourcesExtension>, |
|
||||||
generateModulePath: Boolean |
|
||||||
) { |
|
||||||
logger.info("Configure accessors for '${commonSourceSet.name}'") |
|
||||||
|
|
||||||
//lazy check a dependency on the Resources library |
|
||||||
val shouldGenerateResClass = config.map { |
|
||||||
when (it.generateResClass) { |
|
||||||
ResourcesExtension.ResourceClassGeneration.Auto -> { |
|
||||||
configurations.run { |
|
||||||
//because the implementation configuration doesn't extend the api in the KGP ¯\_(ツ)_/¯ |
|
||||||
getByName(commonSourceSet.implementationConfigurationName).allDependencies + |
|
||||||
getByName(commonSourceSet.apiConfigurationName).allDependencies |
|
||||||
}.any { dep -> |
|
||||||
val depStringNotation = dep.let { "${it.group}:${it.name}:${it.version}" } |
|
||||||
depStringNotation == ComposePlugin.CommonComponentsDependencies.resources |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
ResourcesExtension.ResourceClassGeneration.Always -> { |
|
||||||
true |
|
||||||
} |
|
||||||
|
|
||||||
ResourcesExtension.ResourceClassGeneration.Never -> { |
|
||||||
false |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
val genTask = tasks.register( |
|
||||||
"generateComposeResClass", |
|
||||||
GenerateResClassTask::class.java |
|
||||||
) { task -> |
|
||||||
task.packageName.set(config.getResourcePackage(project)) |
|
||||||
task.shouldGenerateResClass.set(shouldGenerateResClass) |
|
||||||
task.makeResClassPublic.set(config.map { it.publicResClass }) |
|
||||||
task.resDir.set(commonComposeResourcesDir) |
|
||||||
task.codeDir.set(layout.buildDirectory.dir("$RES_GEN_DIR/kotlin").map { it.asFile }) |
|
||||||
|
|
||||||
if (generateModulePath) { |
|
||||||
task.moduleDir.set(config.getModuleResourcesDir(project)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//register generated source set |
|
||||||
commonSourceSet.kotlin.srcDir(genTask.map { it.codeDir }) |
|
||||||
|
|
||||||
//setup task execution during IDE import |
|
||||||
tasks.configureEach { |
|
||||||
if (it.name == "prepareKotlinIdeaImport") { |
|
||||||
it.dependsOn(genTask) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,26 @@ |
|||||||
|
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class) |
||||||
|
|
||||||
|
package my.lib.res |
||||||
|
|
||||||
|
import kotlin.OptIn |
||||||
|
import org.jetbrains.compose.resources.ExperimentalResourceApi |
||||||
|
import org.jetbrains.compose.resources.StringResource |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
private object AndroidMainString0 { |
||||||
|
public val android_str: StringResource by |
||||||
|
lazy { init_android_str() } |
||||||
|
} |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
public val Res.string.android_str: StringResource |
||||||
|
get() = AndroidMainString0.android_str |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
private fun init_android_str(): StringResource = org.jetbrains.compose.resources.StringResource( |
||||||
|
"string:android_str", "android_str", |
||||||
|
setOf( |
||||||
|
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.androidMain.cvr", 10, |
||||||
|
39), |
||||||
|
) |
||||||
|
) |
@ -0,0 +1,26 @@ |
|||||||
|
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class) |
||||||
|
|
||||||
|
package my.lib.res |
||||||
|
|
||||||
|
import kotlin.OptIn |
||||||
|
import org.jetbrains.compose.resources.ExperimentalResourceApi |
||||||
|
import org.jetbrains.compose.resources.StringResource |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
private object DesktopMainString0 { |
||||||
|
public val desktop_str: StringResource by |
||||||
|
lazy { init_desktop_str() } |
||||||
|
} |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
public val Res.string.desktop_str: StringResource |
||||||
|
get() = DesktopMainString0.desktop_str |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
private fun init_desktop_str(): StringResource = org.jetbrains.compose.resources.StringResource( |
||||||
|
"string:desktop_str", "desktop_str", |
||||||
|
setOf( |
||||||
|
org.jetbrains.compose.resources.ResourceItem(setOf(), |
||||||
|
"values/desktop_strings.desktopMain.cvr", 10, 39), |
||||||
|
) |
||||||
|
) |
@ -0,0 +1,26 @@ |
|||||||
|
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class) |
||||||
|
|
||||||
|
package app.group.resources_test.generated.resources |
||||||
|
|
||||||
|
import kotlin.OptIn |
||||||
|
import org.jetbrains.compose.resources.ExperimentalResourceApi |
||||||
|
import org.jetbrains.compose.resources.StringResource |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
private object AndroidMainString0 { |
||||||
|
public val android_str: StringResource by |
||||||
|
lazy { init_android_str() } |
||||||
|
} |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
internal val Res.string.android_str: StringResource |
||||||
|
get() = AndroidMainString0.android_str |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
private fun init_android_str(): StringResource = org.jetbrains.compose.resources.StringResource( |
||||||
|
"string:android_str", "android_str", |
||||||
|
setOf( |
||||||
|
org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.androidMain.cvr", 10, |
||||||
|
39), |
||||||
|
) |
||||||
|
) |
@ -0,0 +1,26 @@ |
|||||||
|
@file:OptIn(org.jetbrains.compose.resources.InternalResourceApi::class) |
||||||
|
|
||||||
|
package app.group.resources_test.generated.resources |
||||||
|
|
||||||
|
import kotlin.OptIn |
||||||
|
import org.jetbrains.compose.resources.ExperimentalResourceApi |
||||||
|
import org.jetbrains.compose.resources.StringResource |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
private object DesktopMainString0 { |
||||||
|
public val desktop_str: StringResource by |
||||||
|
lazy { init_desktop_str() } |
||||||
|
} |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
internal val Res.string.desktop_str: StringResource |
||||||
|
get() = DesktopMainString0.desktop_str |
||||||
|
|
||||||
|
@ExperimentalResourceApi |
||||||
|
private fun init_desktop_str(): StringResource = org.jetbrains.compose.resources.StringResource( |
||||||
|
"string:desktop_str", "desktop_str", |
||||||
|
setOf( |
||||||
|
org.jetbrains.compose.resources.ResourceItem(setOf(), |
||||||
|
"values/desktop_strings.desktopMain.cvr", 10, 39), |
||||||
|
) |
||||||
|
) |
@ -0,0 +1,3 @@ |
|||||||
|
<resources> |
||||||
|
<string name="android_str">Android string</string> |
||||||
|
</resources> |
@ -0,0 +1,3 @@ |
|||||||
|
<resources> |
||||||
|
<string name="desktop_str">Desktop string</string> |
||||||
|
</resources> |
@ -0,0 +1,3 @@ |
|||||||
|
<resources> |
||||||
|
<string name="android_str">Android string</string> |
||||||
|
</resources> |
@ -0,0 +1,10 @@ |
|||||||
|
package me.sample.app |
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import kmpresourcepublication.appmodule.generated.resources.Res |
||||||
|
import kmpresourcepublication.appmodule.generated.resources.android_str |
||||||
|
import org.jetbrains.compose.resources.stringResource |
||||||
|
|
||||||
|
@Composable |
||||||
|
actual fun getPlatformSpecificString(): String = |
||||||
|
stringResource(Res.string.android_str) |
@ -0,0 +1,3 @@ |
|||||||
|
<resources> |
||||||
|
<string name="iOS_str">Ios string</string> |
||||||
|
</resources> |
@ -0,0 +1,10 @@ |
|||||||
|
package me.sample.app |
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import kmpresourcepublication.appmodule.generated.resources.Res |
||||||
|
import kmpresourcepublication.appmodule.generated.resources.iOS_str |
||||||
|
import org.jetbrains.compose.resources.stringResource |
||||||
|
|
||||||
|
@Composable |
||||||
|
actual fun getPlatformSpecificString(): String = |
||||||
|
stringResource(Res.string.iOS_str) |
@ -0,0 +1,3 @@ |
|||||||
|
<resources> |
||||||
|
<string name="js_str">JS string</string> |
||||||
|
</resources> |
@ -0,0 +1,10 @@ |
|||||||
|
package me.sample.app |
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import kmpresourcepublication.appmodule.generated.resources.Res |
||||||
|
import kmpresourcepublication.appmodule.generated.resources.js_str |
||||||
|
import org.jetbrains.compose.resources.stringResource |
||||||
|
|
||||||
|
@Composable |
||||||
|
actual fun getPlatformSpecificString(): String = |
||||||
|
stringResource(Res.string.js_str) |
@ -0,0 +1,3 @@ |
|||||||
|
<resources> |
||||||
|
<string name="desktop_str">Desktop string</string> |
||||||
|
</resources> |
@ -0,0 +1,10 @@ |
|||||||
|
package me.sample.app |
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import kmpresourcepublication.appmodule.generated.resources.Res |
||||||
|
import kmpresourcepublication.appmodule.generated.resources.desktop_str |
||||||
|
import org.jetbrains.compose.resources.stringResource |
||||||
|
|
||||||
|
@Composable |
||||||
|
actual fun getPlatformSpecificString(): String = |
||||||
|
stringResource(Res.string.desktop_str) |
@ -0,0 +1,3 @@ |
|||||||
|
<resources> |
||||||
|
<string name="wasm_str">Wasm string</string> |
||||||
|
</resources> |
@ -0,0 +1,10 @@ |
|||||||
|
package me.sample.app |
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import kmpresourcepublication.appmodule.generated.resources.Res |
||||||
|
import kmpresourcepublication.appmodule.generated.resources.wasm_str |
||||||
|
import org.jetbrains.compose.resources.stringResource |
||||||
|
|
||||||
|
@Composable |
||||||
|
actual fun getPlatformSpecificString(): String = |
||||||
|
stringResource(Res.string.wasm_str) |
Loading…
Reference in new issue