Gradle buildSrc classes not initated - android

I have a simple Properties class in the buildSrc directory, without any additional package subdirectory.
buildSrc/src/main/kotlin/Properties.kt
The class is a simple properties util file to make global the properties taken from files to the rest of the project:
import java.io.File
import java.io.FileInputStream
import java.util.Properties
object Properties {
val myProps = getProperties("myProps.properties")
private fun getProperties(path: String): Properties {
val props = Properties()
val propertiesFile = File(path)
props.load(FileInputStream(propertiesFile))
return props
}
}
This property object is used accross different modules at their build.gradle.kts configuration. No explicit import is used because the Properties object is compiled at the root of kotlin buildSrc directory:
android {
buildTypes {
getByName("release") {
buildConfigField("String", "SOME_PROP", Properties.myProps.getProperty("MY_PROP"))
...
But I constantly face build errors mentioning that the Properties object doesn't exist, as if it wasn't compiled before the modules' build script.
Normally with some clean caches this is solved but I tried to run project in a new machine and it simply won't work and I get some LocationAwareException.
Any idea of what can be wrong?
org.gradle.internal.exceptions.LocationAwareException: Build file '/Users/hector/MyAwesomeProject/myModule/build.gradle.kts' line: 23
Could not initialize class Properties
at org.gradle.kotlin.dsl.execution.InterpreterKt$locationAwareExceptionFor$2.invoke(Interpreter.kt:600)
at org.gradle.kotlin.dsl.execution.InterpreterKt.locationAwareExceptionFor(Interpreter.kt:607)
at org.gradle.kotlin.dsl.execution.InterpreterKt.locationAwareExceptionHandlingFor(Interpreter.kt:573)
at org.gradle.kotlin.dsl.execution.InterpreterKt.access$locationAwareExceptionHandlingFor(Interpreter.kt:1)
at org.gradle.kotlin.dsl.execution.Interpreter$ProgramHost.handleScriptException(Interpreter.kt:409)
at Program.execute(Unknown Source)
at org.gradle.kotlin.dsl.execution.Interpreter$ProgramHost.eval(Interpreter.kt:531)
at org.gradle.kotlin.dsl.execution.Interpreter$ProgramHost.evaluateSecondStageOf(Interpreter.kt:455)
at Program.execute(Unknown Source)
at org.gradle.kotlin.dsl.execution.Interpreter$ProgramHost.eval(Interpreter.kt:531)
at org.gradle.kotlin.dsl.execution.Interpreter.eval(Interpreter.kt:204)
at org.gradle.kotlin.dsl.provider.StandardKotlinScriptEvaluator.evaluate(KotlinScriptEvaluator.kt:114)
at org.gradle.kotlin.dsl.provider.KotlinScriptPluginFactory$create$1.invoke(KotlinScriptPluginFactory.kt:51)
at org.gradle.kotlin.dsl.provider.KotlinScriptPluginFactory$create$1.invoke(KotlinScriptPluginFactory.kt:36)
at org.gradle.kotlin.dsl.provider.KotlinScriptPlugin.apply(KotlinScriptPlugin.kt:34)
at org.gradle.configuration.BuildOperationScriptPlugin$1.run(BuildOperationScriptPlugin.java:65)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
Caused by: java.lang.NoClassDefFoundError: Could not initialize class Properties
at Build_gradle$1$1.invoke(build.gradle.kts:23)
at Build_gradle$1$1.invoke(build.gradle.kts:1)
at com.android.build.gradle.internal.dsl.CommonExtensionImpl.defaultConfig(CommonExtensionImpl.kt:200)
at com.android.build.gradle.internal.dsl.BaseAppModuleExtension.defaultConfig(BaseAppModuleExtension.kt)
at Build_gradle$1.execute(build.gradle.kts:18)
at Build_gradle$1.execute(build.gradle.kts:1)
at org.gradle.internal.extensibility.ExtensionsStorage$ExtensionHolder.configure(ExtensionsStorage.java:173)
at org.gradle.internal.extensibility.ExtensionsStorage.configureExtension(ExtensionsStorage.java:64)
at org.gradle.internal.extensibility.DefaultConvention.configure(DefaultConvention.java:194)
at org.gradle.kotlin.dsl.Accessors377twfxlhpj2n65rquy9ybeqsKt.android(Unknown Source)
at Build_gradle.<init>(build.gradle.kts:14)
... 174 more
buildSrc/build.gradle.kts
plugins {
`kotlin-dsl`
`kotlin-dsl-precompiled-script-plugins`
java
}
repositories {
google()
mavenCentral()
jcenter()
}
dependencies {
// android gradle plugin, required by custom plugin
implementation("com.android.tools.build:gradle:7.0.3")
// kotlin plugin, required by custom plugin
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.0")
}

Ok now I see, buildSrc is meant for configurations and objects that are not tied to the project itself. as the BuildSrc and Project builds are compiled separately.
It was an error to try to use reference to properties files from the BuildSrc initialization (although it could be possible by moving properties to same folder).
What I did was to change how Properties were initiated so that the project sets the project path (I couldn't find a way to get a static project reference from kotlin Object)
import java.io.File
import java.io.FileInputStream
import java.util.Properties
import java.nio.file.Paths
object ProjectProperties {
val myProps by lazy { getProperties("myProps.properties") }
private lateinit var project: File
fun setPath(path: File) {
project = path
}
private fun getProperties(path: String): Properties {
val props = Properties()
val propertiesFile = File(project.getAbsolutePath(), path)
println("Getting property at: ${path}")
props.load(FileInputStream(propertiesFile))
return props
}
}
Then on the root build.gradle.kts add the following on the main project configuration (not for all projects):
ProjectProperties.setPath(project.projectDir)

Related

Extension of type 'LibraryExtension' does not exist

I created a gradle plugin to unify some settings between the various modules of the application. the summary of the error is this:
org.gradle.api.plugins.InvalidPluginException: An exception occurred applying plugin request [id: 'common.plugin']
Caused by: org.gradle.api.UnknownDomainObjectException: Extension of type 'LibraryExtension' does not exist. Currently registered extension types: [ExtraPropertiesExtension,....]
This is a summary image of the project architecture:
CommonPluginClass:
class CommonPluginClass : Plugin<Project> {
private val consumerProguardFileName = "consumer-rules.pro"
private val proguardFileName = "proguard-rules.pro"
private val sdkToCompile = 33
override fun apply(project: Project) {
println(">>> Adding sugar to gradle files!")
with(project)
{
applyPlugins(this)
androidConfig(this)
}
println(">>> Sugar added for core Module!")
}
private fun applyPlugins(project: Project) {
println(">>> apply plugins!")
project.pluginManager.run {
apply("com.android.application")
apply("kotlin-android")
apply("kotlin-kapt")
}
println(">>> end apply plugins!")
}
private fun androidConfig(project: Project) {
project.extensions.configure<LibraryExtension>{
defaultConfig.targetSdk = 33
}
}
}
the error occurs inside the androidConfig function when calling configure
I was inspired by now android, dependencies and imports are similar but not build. Can someone unlock it please.
build-logic:convention build.gradle
plugins {
alias(libs.plugins.kotlin.jvm) apply false
id "org.gradle.kotlin.kotlin-dsl" version "2.4.1"
}
dependencies {
compileOnly(libs.android.pluginGradle)
compileOnly(libs.kotlin.pluginGradle)
}
gradlePlugin {
plugins {
commonPlugin {
id = "common.plugin"
implementationClass = "CommonPluginClass"
}
}
}
LITTLE UPDATE:
I've noticed that any module I enter always identifies it to me as ApplicationExtension
I found the solution and as usual it's a stupid thing.
the application extension use plugin
apply("com.android.application")
the library or module use plugin
apply("com.android.library")

Kotlin Multiplatform: Can't reference class in commonMain sourceset from another module's androidMain sourceset

I have two multiplatform modules shared and other in a standard multiplatform template project that targets Android and iOS.
shared defines a class in commonMain source set
class SharedGreeting()
other is setup to depend on shared like this in the gradle file:
val commonMain by getting {
dependencies {
implementation(project(":shared"))
}
}
And in its androidMain sourceset it tries to reference SharedGreeting in some class, fx:
class AndroidGreeter{
val foo = SharedGreeting()
}
But no matter what I try, I get IDE errors when I try to reference the shared class, and I have to manually add an import statement.
The code compiles and deploys without problems though!
Any ideas on what I am missing or misunderstanding? Or is this a bug in KMM?
Full copy of other gradle file:
plugins {
kotlin("multiplatform")
kotlin("native.cocoapods")
id("com.android.library")
}
version = "1.0"
kotlin {
android()
iosX64()
iosArm64()
iosSimulatorArm64()
cocoapods {
summary = "Some description for the Shared Module"
homepage = "Link to the Shared Module homepage"
ios.deploymentTarget = "14.1"
framework {
baseName = "other"
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation(project(":shared"))
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val androidMain by getting
val androidTest by getting
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
}
val iosX64Test by getting
val iosArm64Test by getting
val iosSimulatorArm64Test by getting
val iosTest by creating {
dependsOn(commonTest)
iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this)
iosSimulatorArm64Test.dependsOn(this)
}
}
}
android {
compileSdk = 32
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdk = 24
targetSdk = 32
}
}
For full project source code:
https://github.com/RabieJradi/kmm_import_error_sample
IDE suggested action for adding a dependency doesn't unfortunately do anything.
Turns out it is a bug in KMM and a fix have been implemented by the Jetbrains team here https://youtrack.jetbrains.com/issue/KTIJ-22056/KMM-Cant-reference-shared-module-commonMain-source-set-classes-from-another-modules-androidMain-source-set
You cannot do this in that way.
To make your project work you have to work with expect and actual words.
So, make these changes:
change your SharedGreeting class in shared module like that:
expect class SharedGreeting {
fun greeting(): String
}
Then your IDE wants you to make two changes, in shared module.
Add a class in both modules, iosMain and androidMain named SharedGreeting. The code is the same for both classes:
actual class SharedGreeting {
actual fun greeting(): String {
return "Hello, ${Platform().platform}!"
}
}
The work is done, now your other library has no errors. From your androidMain module inside "other" you can work only on other androidMain modules inside other MKK libraries.

Android - Gradle - Unresolved reference: VersionCatalogsExtension

I'm trying to utilize Gradle versionCatalogs and put all the versions there including Android-specific compileSdkVersion, minSdkVersion, targetSdkVersion.
It's possible to access these versions with the following code:
val libs = rootProject.extensions.getByType(VersionCatalogsExtension::class.java).named("libs")
libs.findVersion("compileSdkVersion").get().toString() as Integer
But my project is multi-module and I don't want to copy-paste boilerplate code. So, I created extension functions in a separate module.
root-level settings.gradle.kts:
includeBuild("gradle-dependencies")
module-level build.gradle.kts:
plugins {
`kotlin-dsl`
`java-gradle-plugin`
}
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
group = "com.foobar.androidtools.gradle"
version = "SNAPSHOT"
dependencies {
implementation(gradleApi())
}
gradlePlugin {
plugins.register("Gradle extensions") {
id = "foobar-gradle-dependencies"
implementationClass = "com.foobar.gradle_dependencies.DependenciesPlugin"
}
}
src/main/kotlin/com/foobar/gradle_dependencies/Extensions.kt:
package com.foobar.gradle_dependencies
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.getByType
fun Project.version(key: String): String = this.extensions
.getByType<VersionCatalogsExtension>()
.named("libs")
.findVersion(key)
.get()
.requiredVersion
fun Project.versionInt(key: String) = version(key).toInt()
val Project.KOTLIN_JVM_TARGET_VERSION get() = version("kotlinJvmTargetVersion")
val Project.ANDROID_COMPILE_SDK_VERSION get() = versionInt("compileSdkVersion")
val Project.ANDROID_MIN_SDK_VERSION get() = versionInt("minSdkVersion")
val Project.ANDROID_TARGET_SDK_VERSION get() = versionInt("targetSdkVersion")
Trying to build the project I see the following error:
e: /AndroidTools/gradle-dependencies/src/main/kotlin/com/foobar/gradle_dependencies/Extensions.kt: (4, 33): Unresolved reference: VersionCatalogsExtension
Why the dependency can't be resolved taking into account that import works fine?
AGP: 7.1.3
Gradle: https://services.gradle.org/distributions/gradle-7.4.2-bin.zip

Kotlin Multiplatform: use Android specific libs inside 'androidMain'

I followed the Targeting iOS and Android with Kotlin Multiplatform tutorial and was able to setup the working project for both ios and android.
As a next step I want to use android specific libs inside src/androidMain/. Example:
package com.test.android
import android.os.Build
actual fun getBuildInfo(): String {
return Build.MODEL
}
But for the import I receive: Unresolved reverence: os
Question: Which additional steps are needed to use android specific libs inside androidMain?
The: build.gradle.kts
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins {
kotlin("multiplatform")
}
kotlin {
//select iOS target platform depending on the Xcode environment variables
val iOSTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget =
if (System.getenv("SDK_NAME")?.startsWith("iphoneos") == true)
::iosArm64
else
::iosX64
iOSTarget("ios") {
binaries {
framework {
baseName = "multi_module_be_connection"
}
}
}
jvm("android")
sourceSets["commonMain"].dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-common")
}
sourceSets["androidMain"].dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib")
}
}
val packForXcode by tasks.creating(Sync::class) {
val targetDir = File(buildDir, "xcode-frameworks")
/// selecting the right configuration for the iOS
/// framework depending on the environment
/// variables set by Xcode build
val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
val framework = kotlin.targets
.getByName<KotlinNativeTarget>("ios")
.binaries.getFramework(mode)
inputs.property("mode", mode)
dependsOn(framework.linkTask)
from({ framework.outputDirectory })
into(targetDir)
/// generate a helpful ./gradlew wrapper with embedded Java path
doLast {
val gradlew = File(targetDir, "gradlew")
gradlew.writeText("#!/bin/bash\n"
+ "export 'JAVA_HOME=${System.getProperty("java.home")}'\n"
+ "cd '${rootProject.rootDir}'\n"
+ "./gradlew \$#\n")
gradlew.setExecutable(true)
}
}
tasks.getByName("build").dependsOn(packForXcode)
Source of the project is here: https://github.com/kotlin-hands-on/mpp-ios-android
In order to make use of android specific API in a MPP module you can include the android lib plugin:
The: build.gradle.kts
plugins {
id("com.android.library")
kotlin("multiplatform")
}
When doing so it's mandatory to include an AndroidManifest.xml, this can be placed in your src/androidMain/ and by including it in your script for the android build with:
The: build.gradle.kts
android {
sourceSets {
getByName("main") {
manifest.srcFile ("src/androidMain/AndroidManifest.xml")
}
}
}
The: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest package="org.path.to.package.common"/>
NOTE: change the package path.

Migrating the build.gradle to build.gradle.Kts : Not able to resolve Properties class

While converting the build.gradle to build.gradle.kts is a manual process, I'm struggling in conversion in the below piece of code.
I tried with invalidating the cache and restarting the studio many times. However, android.varinatFilter is not recognized.
android.variantFilter { variant ->
if (variant.buildType.name == 'release'
&& variant.getFlavors().get(0).name == 'development') {
variant.setIgnore(true)
}
if (variant.buildType.name == 'debug'
&& variant.getFlavors().get(0).name == 'production') {
variant.setIgnore(true)
}
}
Properties class of Java.util.Properties dependency doesn't get resolved in .kts file also the FileInputStream class of Java.io is not recognized.
def getProps(path) {
Properties props = new Properties()
props.load(new FileInputStream(file(path)))
return props
}
Also while applying the kotlin annotation processor
kapt 'androidx.lifecycle:lifecycle-common-java8:2.1.0' To
kapt {'androidx.lifecycle:lifecycle-common-java8:2.1.0'}
doesn't work and returns compile-time error.
Any help would be appreciated.
UPDATE
Properties class of Java.util.Properties dependency doesn't get resolved in .kts file also the FileInputStream class of Java.io is not recognized.
This will get resolved with Invalidate cache and Restart.(Start refactoring project level gradle then settings.gradle and then app.gradle file in sequence)
For kapt {'androidx.lifecycle:lifecycle-common-java8:2.1.0'} - please use double quotes, e.g. kapt {"androidx.lifecycle:lifecycle-common-java8:2.1.0"}, please check details here.
Please also use the following syntax for method:
import java.io.FileInputStream
import java.util.Properties
/***/
fun getProps(path: String): Properties {
val props = Properties()
props.load(FileInputStream(file(path)))
return props
}
Changes:
You need imports with java packages at the beginning of the file.
Use fun instead of def
Parameter types are required for methods, use ':' for this - path: String
new keyword is not needed
Variable declaration can started with val, e.g. if compiler is able to understand the type, you don't need to enter it manually.
Return type is mandatory if your result is not Unit.
For filter - I didn't worked with this. However please consider:
Replacing quotes ' to "
Replacing variant.getFlavors().get(0).name to variant.flavors[0].name
Replacing variant.setIgnore(true) to variant.ignore=true
That would be
android.variantFilter {
if (buildType.name == "release" && flavors[0].name == "development") {
ignore = true
}
if (buildType.name == "debug" && flavors[0].name == "production") {
ignore = true
}
}
although I think that a more correct approach for
flavors[0].name = "xyz"
should be
flavors.map { it.name }.contains("xyz")

Categories

Resources