Apply Kotlin multiplatform plugin via gradle legacy plugin application - android

I'm doing some in-depth hands-on with Kotlin Multiplatform Mobile and I'm forced to apply Gradle plugins with the legacy way of applying plugins.
I'm using Kotlin DSL for Gradle and I didn't manage to include the kotlin-multiplatform plugin.
Essentially, there are two ways to include a gradle plugin in your project:
via Gradle Plugins DSL (a modern way)
via legacy plugin application (deprecated but more flexible).
I've basically created a very blank gradle project (gradle init), not related to any IDE or any other dependencies, which both are common for KMM projects - to isolate the issue as much as possible.
The build.gradle.kts below works just fine, when run via ./gradlew clean build (via Plugins DSL)
plugins {
id("org.jetbrains.kotlin.multiplatform") version "1.4.10"
}
kotlin {
jvm()
}
repositories {
jcenter()
}
However, this won't work (via legacy plugin application):
buildscript {
repositories {
maven {
url = uri("https://plugins.gradle.org/m2/")
}
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10")
}
}
apply(plugin = "org.jetbrains.kotlin.multiplatform")
kotlin {
jvm()
}
repositories {
jcenter()
}
It fails with this error:
* What went wrong:
Script compilation errors:
Line 12: kotlin {
^ Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public fun DependencyHandler.kotlin(module: String, version: String? = ...): Any defined in org.gradle.kotlin.dsl
public fun PluginDependenciesSpec.kotlin(module: String): PluginDependencySpec defined in org.gradle.kotlin.dsl
Line 13: jvm()
^ Unresolved reference: jvm
2 errors
It can't resolve the kotlin {} block which is essentially the entry point in KMM projects.
Interestingly, using Groovy instead of Kotlin for Gradle - works in both cases.
But I would like to use Kotlin DSL for Gradle and apply the plugins via the legacy way, since this way I can apply plugins dynamically, i.e. under certain conditions.

In general, you can use apply false in the plugins DSL and call apply conditionally. Apply alone will NEVER work with Kotlin DSL, it only works with dynamic Groovy. Kotlin can work like Groovy if you also use the plugins DSL + apply false in it.
Example:
plugins {
kotlin("multiplatform") apply false // Applied conditionally later
}
For more detailed discussion of the problem check this.

Related

Generating flavorDimensions and productFlavor via Gradle Plugin in multi-module project

I am building a Gradle Plugin that should generate both flavorDimensions and productFlavors on Android. Everything works fine when this plugin is being applied directly to the Android module. My goal however is to apply this plugin to another module within the same project as the Android module. Currently this does not work due to a AgpDslLockedException being thrown during Gradle Sync.
My plugin is implemented as follows:
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
// Traverse whole project tree to find Android project
project.rootProject.subprojects.forEach { subproject ->
val android = subproject.extensions.findByName("android") as? AppExtension ?: return#forEach
// throws AgpDslLockedException when being applied to other module than android
android.flavorDimensions.add("dimension")
android.productFlavors.create("flavor") { it.dimension = "dimension" }
}
}
A minimal client for the Gradle Plugin could be structured as follows:
- android
- build.gradle
- other
- build.gradle
- settings.gradle
// android/build.gradle
plugins {
id 'com.android.application'
// MyPlugin would work if being applied here
}
android {
// Neither flavorDimensions nor productFlavors are declared as they should be generated by MyPlugin
}
// other/build.gradle
plugins {
// MyPlugin does not work due to AgpDslLockedException being thrown
}
// settings.gradle
include(":android")
include(":other")
The AgpDslLockedException:
It is too late to modify flavorDimensions.
It has already been read to configure this project.
Consider either moving this call to be during evaluation, or using the variant API.
Am I missing something or is there another way to generate flavorDimensions and productFlavors from a third-party build.gradle file?
So far I have tried adding the flavorDimension and creating the productFlavor on project.beforeEvaluate/afterEvaluate but to not avail. Maybe there is a lifecycle method I am missing.
Thanks in advance and have a nice day!

SonarQube not finding Kotlin Junit5 tests

I'm trying to set up a sample android studio project in Github which integrates also its CI connected to SonarQube.
I'm having problems with sonarQube due to it says there is no test coverage applied. However there is, in kotlin and with Juni5, but there is no way to set sonar.tests for recognizing it.
this it the sonar-project.properties
test are run fine either locally and in the CI when a branch of the project is build.
Also I've added this
Gradle has been written with Kotlin-DSL.
if you are using gradle there is no need to define the sources and the tests in the sonar-project.properties. The gradle sonarqube task will pick them up automatically based on your sourcesets.
What i think you are missing is a tool to generate the coverage, sonarqube will not generate coverage data for you. SonarQube utilizes in Java eg. JaCoCo so you also need to apply a plugin for that.
so your build gradle (behold this is groovy dsl, but i will provide a link to one in kotlin) will look something like the following code snippet. This will generate everything automatically
plugins {
id 'java'
id 'jacoco'
id 'eclipse' // optional (to generate Eclipse project files)
id 'idea' // optional (to generate IntelliJ IDEA project files)
id "org.sonarqube" version "2.8"
}
repositories {
jcenter()
}
dependencies {
testImplementation('org.junit.jupiter:junit-jupiter:5.6.0')
}
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
sonarqube {
properties {
/*
UPDATE SECTION START
Please fill in your data
*/
property "sonar.projectName", "SonarCloud Github Actions with gradle"
property "sonar.projectKey", "aepfli_SonarCloud-GitHubActions-Gradle-example"
property "sonar.organization", "aepfli"
/*
UPDATE SECTION END
*/
property "sonar.host.url", "https://sonarcloud.io"
}
}
jacocoTestReport {
reports {
xml.enabled = true
}
}
Alternatively if you are looking for an example in kotlin DSL, i can recommend this one from JUnit Pioneer.
I am still not sure how good the support for kotlin is with JaCoCo. if there is a different tool like JaCoCo in the Kotlin world, you can also try to generate and XML report and provide this xml report via property sonar.coverage.jacoco.xmlReportPaths base on Sonarqube doc

gradle withJavadocJar() and withSourcesJar() for android library

Is there a way to leverage the new functions withSourcesJar() and withJavadocJar() for Android library projects? Currently when I try to use it I get:
> SourceSet with name 'main' not found.
With the latest Gradle version 6.0.1, there seems to be no way to use these new methods in Android library projects. Here’s why I believe that’s the case:
The two methods withJavadocJar() and withSourcesJar() on the java extension have the default main source set hardcoded, see here and here.
There are two methods with the same names (withJavadocJar() and withSourcesJar()) which can be used in feature variant declarations. However, it seems that Android Gradle builds don’t use feature variants, i.e., these methods can’t be used either.
The documentation states, that these come from the JavaPluginExtension - but not from Android DSL. So they can only be used in conjunction with apply plugin: "java" or apply plugin: "java-library", but not with apply plugin: "com.android.application", apply plugin: "com.android.library" and alike. The name of these tasks also suggest that it's common Java (*.jar) and not Android (*.aar). On Android, this would only make sense for a *.jar library, which uses pure Java features, but no Android features at all (which is limited in functionality).
In short, apply plugin: "java-library" would permit accessing these.
With Gradle 7.2 I "recreated" withJavadocJar and withSourcesJar using Gradle Kotlin DSL:
val sourceFiles = android.sourceSets.getByName("main").java.getSourceFiles()
tasks.register<Javadoc>("withJavadoc") {
isFailOnError = false
dependsOn(tasks.named("compileDebugSources"), tasks.named("compileReleaseSources"))
// add Android runtime classpath
android.bootClasspath.forEach { classpath += project.fileTree(it) }
// add classpath for all dependencies
android.libraryVariants.forEach { variant ->
variant.javaCompileProvider.get().classpath.files.forEach { file ->
classpath += project.fileTree(file)
}
}
source = sourceFiles
}
tasks.register<Jar>("withJavadocJar") {
archiveClassifier.set("javadoc")
dependsOn(tasks.named("withJavadoc"))
val destination = tasks.named<Javadoc>("withJavadoc").get().destinationDir
from(destination)
}
tasks.register<Jar>("withSourcesJar") {
archiveClassifier.set("sources")
from(sourceFiles)
}
Put this into your gradle.build.kts file and then run ./gradlew withJavadocJar and ./gradlew withSourcesJar (or use the tasks to create artifacts for publishing).
android.sourceSets.getByName("main").java.getSourceFiles() is the Android specific part to retrieve the "main" source files.

Generate kdoc and watch in browser

I don't know how to watch the generated kdoc in my Browser.
I'm developing for Android with Kotlin and added dokka as dependency in gradle.
The new dokka Task runs without Errors.
I added dokka as dependency in my Projects build.gradle
dependencies {
...
classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:0.9.13"
}
and applied the plugin in my apps build.gradle
apply 'kotlin-android'
apply 'kotlin-android-extensions'
apply 'org.jetbrains.dokka'
But I can't find the Output in the Project Folder.
How can I watch the Output in my browser or where is it?
Thanks
Edit 1: IDE: Intellij; Kotlin Version: 1.1.1
Edit 2 (11.04.2017):
build.Gradle:
dokka {
outputDirectory = "$buildDir/docs"
}
You can control the output directory via the dokka task:
dokka {
outputDirectory = "$buildDir/docs"
}
To generate the docs, run ./gradlew dokka
I've only done this for non-Android projects, but I believe the process is the same.
I got it to work.
This post solved the problem.
Apparently dokka is not able to pick up the sourceSets {...} defined in the android {...}-block.
Workaround:
duplicate the sourceSets{...} outside the android{...}-block

gradle: set sonarRunner settings for projects applying specific plugin

We use a company-wide gradle configuration which is applied to all our gradle projects.
Projects using this config might apply different types of plugins (specificially the plugins java or android & android-library). In order to get the sonarRunner plugin to automatically check the android projects, additional properties have to be applied which are not available for java projects.
Now to the question: How can additional sonarRunner (or other) properties be applied only to projects applying a specific (in this case "android" or "android-library") plugin?
What I have tried so far, but does not work as the AndroidPlugin property is not know in the preparation phase:
plugins.withType(AndroidPlugin) {
sonarRunner {
sonarProperties {
property "sonar.profile", "Android"
...
}
}
}
Sounds like the code is missing an import for the AndroidPlugin class. Alternatively, plugins.withId("android") can be used for current gradle versions or afterEvaluate { if (plugins.hasPlugin("android")) { ... } } for gradle 1.12 and earlier.

Categories

Resources