Can I configure a Kotlin Multiplatform module in a Gradle plugin? - android-studio

I'm building a project with several Kotlin Multiplatform modules.
As the Gradle documentation suggests, to share configuration between those modules I've created a custom plugin. This plugin is supposed to apply the kotlin-multiplatform plugin and the shared configuration but unfortunately it's unable to resolve the kotlin extension for applying the multiplatform configuration.
My plugin (buildSrc\src\main\kotlin\my.pugin.gradle.kts):
plugins {
kotlin("multiplatform")
}
kotlin {
val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
sourceSets {
val commonMain by getting
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
}
jvm {
val main by compilations.getting {
kotlinOptions {
jvmTarget = "1.8"
}
}
}
}
The error:
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

Apparently, what you need is to add a dependency of the buildSrc on the kotlin-gradle-plugin in buildSrc\build.gradle.kts:
dependencies {
//...
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
//...
}
EDIT: $kotlin_version is unavailable at this scope, you need to specify it explicitly.

Related

Creating JS library with kotlin multi platform and use it on angular 2 via npm

I created and simple kotlin multi platform application and i want to use it on angular project via publishing on npm.
Here is my build.gradle.kts file
plugins {
kotlin("multiplatform")
kotlin("native.cocoapods")
id("com.android.library")
}
version = "1.0"
kotlin {
android()
iosX64()
iosArm64()
iosSimulatorArm64()
js {
browser {
webpackTask {
output.libraryTarget = "this"
}
binaries.executable()
}
}
sourceSets {
val commonMain by getting
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 = 21
targetSdk = 32
}
}
Sample Greeting Class File in KMP Project
#JsExport
data class Greeting(val title: String, val row: Int) {
fun helloWorld() {
print("TEST Hello World")
}
}
When i run gradle build command js folder appearing under the build folder and its looks like this.
I removed this JS folder from the project and put into the new github repository and then i publish the new github repository to the npm.
Here is the project that i publish on npm.
https://www.npmjs.com/package/bf-shared-mw
It has been great until now
After then i created a angular project and add this library as a dependecy
"bf-shared-mw": "^1.0.1",
It's depended with successfully
But when i want to use it in components classes like the codes as you see below
import { Component, OnInit } from '#angular/core';
import { Greeting } from 'bf-shared-mw';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
ngOnInit(): void {
const greetings = Greeting();
}
title = 'angular-tour-of-heroes';
}
bf-shared-mw Throwing errors and i cant access the Greeting class
What am i doing wrong ? How can i use my kotlin multi platform library on react or angular applications ?

How to use Dagger for Groovy?

How to configure dagger to inject groovy classes, and to inject into groovy classes?
I was initially trying to get dagger to inject a groovy class into my java app, and I found dagger was complaining the groovy class is not found. Looking at the log, it seems that compileGroovy happens after compileJava. And the annotation processing of dagger compiler seems to be in compileJava. I guessed that might be the problem -- no groovy classes are available at this time. But I've yet figured out a way to coerce either of dagger or groovy to work with the other.
It seems I could not upload a .tar.gz. But if anyone needs a minimal demo code for what I meant to achieve, these might help (with gradle 7):
build.gradle:
plugins {
id 'groovy'
id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
annotationProcessor 'com.google.dagger:dagger-compiler:2.+'
implementation 'com.google.dagger:dagger:2.+'
implementation 'org.codehaus.groovy:groovy-all:3.+'
}
settings.gradle:
rootProject.name = 'groovy-dagger1'
src/main/groovy/org/example/dagger/MainComponent.groovy:
package org.example.dagger
import dagger.Component
#Component(modules = [
MainModule,
])
interface MainComponent {
String message();
}
src/main/groovy/org/example/dagger/MainModule.groovy:
package org.example.dagger
import dagger.Module
import dagger.Provides
#Module
final class MainModule {
#Provides
static String message() {
return 'Hello Groovy Dagger!'
}
}
src/main/groovy/org/example/main/Main.groovy:
package org.example.main;
class Main {
static void main(String[] args) {
// Dagger component does not exist :/
// println DaggerMainComponent.create().message()
}
}
By default, the groovy compiler will not run the java annotation processors...
You can add this to your build.gradle:
compileGroovy {
groovyOptions.javaAnnotationProcessing = true
}
You will of course need to add an import
import org.example.dagger.DaggerMainComponent
To Main.groovy

ktor dependency in commonMain of Android Studio multiplatform project unresolved in IDE but code runs

I have an issue with the Android Studio IDE when using the ktor dependency for the commonMain sourceset with kotlin multiplatform. The problem is that the IDE does not recognize this dependency, but the program compiles and runs fine. Furthermore, in the androidMain sourceset the dependency is recognized. I have seen other questions on similar problems, but I have not seen anyone with this problem where the program compiles and runs.
Gradle dependencies
The following is in the build.gradle.kts in the shared folder of the project.
kotlin {
android()
ios {
binaries {
framework {
baseName = "shared"
}
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation("io.ktor:ktor-client-core:1.5.1")
implementation("io.ktor:ktor-client-cio:1.5.1")
}
}
val androidMain by getting {
dependencies {
implementation("com.google.android.material:material:1.2.1")
implementation("io.ktor:ktor-client-android:1.5.1")
}
}
...
}
}
where the dots represent dependencies for other sourcesets, e.g. iosMain which is empty.
In the commonMain code, I have a class KtorTest:
package com.example.myapplication222.shared
import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.request.*
class KtorTest {
val client: HttpClient = HttpClient(CIO)
suspend fun get(): String {
val res: String = client.get("http://www.7timer.info/bin/api.pl?lon=113.17&lat=23.09&product=astro&output=json")
return res
}
}
Main Activity
In the main activity I import and use the KtorTest class to perform a get request.
package com.example.myapplication222.androidApp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.myapplication222.shared.KtorTest
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var response = ""
val c = GlobalScope.launch {
response = get()
}
c.invokeOnCompletion {
println("***RESPONSE***");
println(response) }
}
suspend fun get(): String {
val a = KtorTest()
return a.get()
}
}
Result
The program builds and runs and prints out the following.
I/System.out: ***RESPONSE***
{
"product" : "astro" ,
"init" : "2021021700" ,
"dataseries" : [
{
"timepoint" : 3,
"cloudcover" : 4,
I/System.out: "seeing" : 6,
"transparency" : 2,
"lifted_index" : 15,
"rh2m" : 5,
"wind10m" : {
"direction" : "NE",
"speed" : 3
},
"temp2m" : 20,
"prec_type" : "none"
},
...
}
where the response is cut short for brevity
Screenshot of Android Studio:
The first screenshot is of KtorTest presented above.
KtorTest in commonMain of shared code in Android Studio kotlin multiplatform project
The second screenshot is of the class KtorTest2, which is exactly the same as KtorTest above except that it is located in the androidMain folder of the shared folder in the multiplatform project.
KtorTest2 in androidMain of shared code in Android Studio kotlin multiplatform project
In these images you can see that the IDE complains about ktor in commonMain, but not in androidMain.
You only need to include io.ktor:ktor-client-core in commonMain and actual HTTP engine implementation in desired target. If you want to use CIO engine in android, just include io.ktor:ktor-client-cio in androidMain. Ktor will automatically select available HTTP client available for the platform. You can update the KtorTest class like so (note the absence of engine specification):
class KtorTest {
val client: HttpClient = HttpClient
}
Updating kotlin-gradle-plugin to 1.4.31 in build.gradle.kts of the kotlin multiplatform project fixed this issue
See the following answer for more details: https://stackoverflow.com/a/66913665/14635103

Unable to load class get.spock.GebSpec due to missing dependency org/spockframework/mock/MockController

What is wrong with my test framework, that I cannot run my test?
my build.gradle
version '1.0-SNAPSHOT'
//task wrapper(type: Wrapper) {
// gebVersion = '0.13.1'
// seleniumVersion = '2.52.0'
//
// distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
//}
apply plugin: 'groovy'
repositories {
mavenCentral()
}
sourceCompatibility = 1.5
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
// geb
compile 'org.codehaus.geb:geb-core:0.7.2'
compile 'org.codehaus.geb:geb-spock:0.7.2'
compile "org.seleniumhq.selenium:selenium-firefox-driver:2.52.0"
compile "org.seleniumhq.selenium:selenium-chrome-driver:2.52.0"
compile "org.seleniumhq.selenium:selenium-safari-driver:2.52.0"
compile "org.seleniumhq.selenium:selenium-support:2.52.0"
// spock
compile "org.codehaus.groovy:groovy-all:2.4.1"
testCompile "org.spockframework:spock-core:1.0-groovy-2.4"
}
My GebConfig.groovy
//import geb.driver.SauceLabsDriverFactory
import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.firefox.FirefoxDriver
import org.openqa.selenium.safari.SafariDriver
//baseUrl = 'http://madison.craigslist.org'
// default driver...
System.setProperty('webdriver.chrome.driver', "../resources/chromedriver")
driver = {new ChromeDriver()}
environments {
'chrome' {
def chromeDriver = new File('src/test/resources/chromedriver') // add .exe for Windows...
System.setProperty('webdriver.chrome.driver', chromeDriver.absolutePath)
driver = { new ChromeDriver() }
}
// 'ff' {
// driver = { new FirefoxDriver() }
// driver.manage().window().maximize()
// }
'safari' {
driver = { new SafariDriver() }
}
}
waiting {
timeout = 6
retryInterval = 0.5
slow { timeout = 12 }
reallySlow { timeout = 24 }
}
reportsDir = "geb-reports"
page class
package pages
import geb.Page
import geb.Browser
class LoginPage extends Page{
static url = "https://qmdev.quickblox.com"
// static at = {heading.isDisplayed()}
static at = {title == "Q-municate"}
static content = {
heading {$("div#l-welcome_block l-welcome_text_description")}
logInByEmailOrSocial {$("button", text:"login by email or social")}
logInPageTitle {$("div.l-welcome_block l-welcome_text_login")}
}
}
test class
import geb.spock.GebReportingSpec
import pages.LoginPage
import spock.lang.*
import geb.spock.GebSpec
#Stepwise
class LoginPageTest extends GebReportingSpec{
def "log in Q-municate"(){
given: "Open Log In page"
to LoginPage
when: "chose log in by email"
LoginPage.logInByEmailOrSocial.click()
then: "Ensure that we are on LogIn page"
LoginPage.logInPageTitle.text() == "Log In"
}
}
About my framework:
I use web-spock-groovy-gradle bundle for web UI automation and I'm a fresh-user with Gradle.
Please tell me, what is wrong with my build.gradle and GebConfig.groovy.
your Geb-Spock version of 0.7.2 is quite old and could be the cause of your problem. I suggest changing those 2 dependencies up to version 1.1.1:
compile 'org.codehaus.geb:geb-core:0.7.2' //change to 1.1.1
compile 'org.codehaus.geb:geb-spock:0.7.2' //change to 1.1.1
Further more I check the Spock API version 1.1 for "org/spockframework/mock/MockController" and it does not appear to exist.
(http://spockframework.org/spock/javadoc/1.1-SNAPSHOT/index.html)
As you can see in this picture there are only 2 classes that are part of the spock.mock package, and MockController is not one of them. try updating your geb version and let us know if that helps

How to get topologically sorted list of Gradle sub-projects?

I have a tool that needs a topological sort of the sub-projects in a multi-module project. Does Gradle have a way to provide this?
Define topological sort. You can get the list of subprojects in your rootProject's build.gradle file by referencing the subprojects. Like this:
subprojects.each { p ->
println p.path
}
You can sort these by their path (which is the fully qualified name (ie. :module:submodule)
The following works for my purposes.
class ProjectCategory {
static List<Project> topologicallySortedProjectDependencies(Project project) {
topologicallySortedProjectDependenciesImpl(project).unique().reverse()
}
private static List<Project> topologicallySortedProjectDependenciesImpl(Project project) {
final dependencies = project.configurations.compile.dependencies.findAll { dependency ->
dependency instanceof ProjectDependency
} collect { ProjectDependency projectDependency ->
topologicallySortedProjectDependenciesImpl(projectDependency.dependencyProject)
} flatten()
dependencies + project
}
}
task topologicallySortedProjects << {
println project(':').toString().capitalize()
use(ProjectCategory) {
project.topologicallySortedProjectDependencies().each { projectDependency ->
println "+--- ${projectDependency.toString().capitalize()}"
}
}
}
The following works for me:
final class ProjectManager {
private static printSubProjects(Project project) {
project.subprojects.each { p ->
println p.path
}
}
}
rootProject {
task listAllProjects << {
println project(':').toString()
ProjectManager.printSubProjects(project)
}
}
cd root_gradle_project
gradle listAllProjects

Resources