I cannot figure out how to get a commonMain dependency to work in a kotlin multiplatform project. I have read and re-read the documentation many times and have looked at many of the examples, but it just isn't working. Here is the smallest example that I believe should work. What am I doing wrong?
multiplatform-lib
plugins {
kotlin("multiplatform") version "1.3.61"
`maven-publish`
}
group = "github.fatalcatharsis"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
mavenLocal()
}
kotlin {
/* Targets configuration omitted.
* To find out how to configure the targets, please follow the link:
* https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#setting-up-targets */
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
}
}
src/commonMain/kotlin/Test.kt
data class Test (
val test : Int
)
multiplatform-test
plugins {
kotlin("multiplatform") version "1.3.61"
}
group = "github.fatalcatharsis"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
mavenLocal()
}
kotlin {
/* Targets configuration omitted.
* To find out how to configure the targets, please follow the link:
* https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#setting-up-targets */
js {
browser()
}
jvm()
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
implementation("github.fatalcatharsis:multiplatform-lib-metadata:1.0-SNAPSHOT")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
}
}
src/commonMain/kotlin/Tester.kt
import github.fatalcatharsis.Test
fun test() {
val meh = Test()
}
intellij says it resolved the dependency just fine on the project menu, but highlights github as Red. No autocomplete available for "Test". Errors with multiplatform-test\src\commonMain\kotlin\Tester.kt: (1, 8): Unresolved reference: github. Just looks like the dependency content isn't available in the commonMain. I feel like I've missed something subtle and obvious. Any ideas?
Edit: Doing the opposite of what the documentation says for common dependencies, if I change the dependency to:
implementation("github.fatalcatharsis:multiplatform-lib:1.0-SNAPSHOT")
it produces the error:
Could not determine the dependencies of task ':jsPackageJson'.
> Could not resolve all dependencies for configuration ':jsNpm'.
> Could not resolve github.fatalcatharsis:multiplatform-lib:1.0-SNAPSHOT.
Required by:
project :
> Unable to find a matching variant of github.fatalcatharsis:multiplatform-lib:1.0-SNAPSHOT:
- Variant 'metadata-api':
- Found org.gradle.status 'integration' but wasn't required.
- Required org.gradle.usage 'kotlin-runtime' and found incompatible value 'kotlin-api'.
- Required org.jetbrains.kotlin.platform.type 'js' and found incompatible value 'common'.
Assuming you've published locally, and that was successful, then the first thing to change is the dependency:
implementation("github.fatalcatharsis:multiplatform-lib:1.0-SNAPSHOT")
You probably don't want the metadata artifact.
Next, add the following to your test app's settings.gradle file.
enableFeaturePreview("GRADLE_METADATA")
After that, try building on command line. Sometimes intellij does see everything.
If things still aren't working, I'd start looking at your publish config.
Related
I'm trying to build convention plugins for a common configuration, e.g. ktlint. They are stored under build-configuration/plugin/convention.
class KtlintConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
return with(target) {
pluginManager.apply(deps.plugins.ktlint.get().pluginId)
extensions.configure<KtlintExtension> {
// ...
}
}
}
}
build-configuration/plugin/convention/build.gradle.kts
plugins {
`kotlin-dsl`
}
group = "com.example"
dependencies {
compileOnly(deps.kotlin.jvm.gradle.plugin)
compileOnly(deps.ktor.gradle.plugin)
compileOnly(deps.ktlint.gradle.plugin)
compileOnly(files(deps.javaClass.superclass.protectionDomain.codeSource.location))
}
gradlePlugin {
plugins {
register("ktlint-plugin") {
id = "ktlint-plugin"
implementationClass = "KtlintConventionPlugin"
}
}
}
I also have an extension function to get dependencies defined in TOML file (stored in gradle/deps.versions.toml) in build-configuration/plugin/convention/src/main/kotlin/com/example/VersionCatalog.kt
import org.gradle.accessors.dm.LibrariesForDeps
import org.gradle.api.Project
import org.gradle.kotlin.dsl.the
internal val Project.deps: LibrariesForDeps
get() = the()
build-configuration/settings.gradle.kts
dependencyResolutionManagement {
repositories {
gradlePluginPortal()
mavenLocal()
mavenCentral()
}
versionCatalogs {
create("deps") {
from(files("../gradle/deps.versions.toml"))
}
}
}
rootProject.name = "build-configuration"
include(":plugin:convention")
When I include ktlint plugin in root build.gradle.kts, Gradle synchronization always fails.
An exception occurred applying plugin request [id: 'ktlint-plugin']
> Failed to apply plugin 'ktlint-plugin'.
> Type org.gradle.accessors.dm.LibrariesForDeps not present
I can see the class generated in .gradle/.../LibrariesForDeps, but under the project's root, not the plugin's one.
The whole build-configuration module is included in root settings.gradle.kts:
includeBuild("build-configuration")
Using the libs doesn't work in convention plugins, but there are some workarounds in the issue: https://github.com/gradle/gradle/issues/15383
But I don't think you need it. Instead the version can be defined in a single place: the included build's build.gradle.kts, which has access to the version catalog DSL.
define the Maven coordinates of the ktlint plugin in libs.versions.toml
[libraries]
gradlePlugin-ktlint = { module = "org.jlleitschuh.gradle:ktlint-gradle", version = "11.1.0" }
I prefer prefixing such dependencies with gradlePlugin to distinguish them from 'regular' project dependencies.
(The Maven coordinates are listed in the Gradle Plugin Portal, under the 'legacy' application as a classpath dependency)
Add a dependency on ktlint in the included build, build-configuration/build.gradle.kts
plugins {
`kotlin-dsl`
}
dependencies {
implementation(libs.deps.gradlePlugin.ktlint)
}
Now the ktlint plugin will be available on the classpath, and you can apply it as normal in a convention plugin, no version necessary.
In a precompiled script plugin:
// ./build-configuration/src/main/kotlin/my-ktlint-convention.gradle.kts
plugins {
id("org.jlleitschuh.gradle.ktlint") // version is provided by build.gradle.kts
}
Or in a traditional class plugin (which will require registering via gradlePlugins {}):
// ./build-configuration/src/main/kotlin/KtlintConventionPlugin.kt
class KtlintConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
target.apply(plugin = "org.jlleitschuh.gradle.ktlint")
target.extensions.configure<KtlintExtension> {
// ...
}
}
}
See also
For more information about buildSrc convention plugins, these answers are related:
https://stackoverflow.com/a/71562588/4161471
https://stackoverflow.com/a/71892685/4161471
I have created a new project and followed this approach. There is only one difference. I had to have the block below in both settings.gradle.kts files, root, and build-configuration:
dependencyResolutionManagement {
versionCatalogs {
create("deps") {
from(files("../gradle/deps.versions.toml"))
}
}
}
It just doesn't work for me without this duplication.
Also, I'm not using any module within the build-configuration plugin.
Converted my gradle file to a kts to use kotlin dsl. Im not sure how to convert these two sections:
sourceSets {
test.resources.srcDirs += 'src/test/res'
}
testOptions {
unitTests.all {
useJUnitPlatform()
}
}
All my unit testand instrumentation tests ar enow failing because it cant locate the test data anymore. Could someone help me convert the above two snippets into dsl for the kts gradle file?
I've tried:
java.sourceSets.create("src/test/res")
sourceSets {
named("test") {
java.srcDir("src/test/res")
}
}
However both did not work.
After more research and trial and error I found this to work:
sourceSets {
getByName("test").resources.srcDir("src/test/res")
}
testOptions {
unitTests.all {
it.useJUnitPlatform()
}
}
I am new to gradle. I am working on a kotlin library that requires JavaFX. I wanted to use gradle to manage my dependencies and make it easier to include my library in a new project.
My library project applies and configures the javafx plugin and its build file looks like this (kotlin dsl):
plugins {
`java-library`
id("org.openjfx.javafxplugin")
}
group = "me.shai"
version = "1.0"
javafx {
version = "15.0.1"
modules = listOf("javafx.controls", "javafx.fxml")
}
dependencies {
// Other dependencies
testImplementation(kotlin("test-junit5"))
testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.0")
}
tasks.test {
useJUnitPlatform()
}
kotlin {
explicitApi()
}
Apparently, a project that wants to use my library also has to apply and configure JavaFX. Here's an example for a build file of a demo project that consumes my library:
plugins {
id("org.openjfx.javafxplugin")
}
group = "me.shai"
version = "1.0"
javafx {
version = "15.0.1"
modules = listOf("javafx.controls", "javafx.fxml")
}
dependencies {
implementation(project(":MyLib")) // Pretend the library is published so we use a url instead of local project
}
This is slightly annoying, so I decided to do something similar to JavaFX: write a custom plugin to include my project. The plugin needs to:
Add my library as a dependency
Apply the JavaFX plugin
Configure the JavaFX plugin
I managed to achieve steps 1 and 2, but I am stuck at step 3. How can I configure the JavaFX plugin from a custom plugin kotlin class?
Here's what I have so far:
package mylib.gradle
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.DependencyResolutionListener
import org.gradle.api.artifacts.ResolvableDependencies
import org.gradle.kotlin.dsl.project
class MyLibPlugin : Plugin<Project> {
override fun apply(project: Project) {
// Apply the javafx plugin
project.plugins.apply("org.openjfx.javafxplugin")
// How to configure javafx here?
val implementationDeps = project.configurations.getByName("implementation").dependencies
project.gradle.addListener(object : DependencyResolutionListener {
override fun beforeResolve(dependencies: ResolvableDependencies) {
// Add dependency on my library project.
implementationDeps.add(project.dependencies.project(":MyLib"))
project.gradle.removeListener(this)
}
override fun afterResolve(dependencies: ResolvableDependencies) {}
})
}
}
I am basically trying to write the equivalent of the build script block:
javafx {
version = "15.0.1"
modules = listOf("javafx.controls", "javafx.fxml")
}
And I can't figure out how to achieve this. I'd appreciate any help on the subject.
You would configure the javafx extension just as you would in the build file:
// build.gradle[.kts]
dependencies {
implementation("org.openjfx:javafx-plugin:0.0.10")
}
And the plugin implementation:
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPlugin
import org.openjfx.gradle.JavaFXOptions
open class MyLibPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.pluginManager.apply("org.openjfx.javafxplugin")
val myLibDependency = project.dependencies.project(mapOf("path" to ":MyLib"))
project.dependencies.add(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, myLibDependency)
project.extensions.configure(JavaFXOptions::class.java) {
it.version = "15.0.1"
it.modules = listOf("javafx.controls", "javafx.fxml")
}
}
}
I am trying to make a universal framework for iOS in KMP.
Here is my module build.gradle file
import org.jetbrains.kotlin.gradle.tasks.FatFrameworkTask
buildscript {
ext.serialization_version = "0.20.0"
repositories {
mavenCentral()
jcenter()
maven { url "https://dl.bintray.com/jetbrains/kotlin-native-dependencies" }
}
}
repositories {
mavenCentral()
maven { url "https://kotlin.bintray.com/kotlinx" }
}
apply plugin: 'kotlin-multiplatform'
apply plugin: 'kotlinx-serialization'
def serialization_version = "0.20.0"
kotlin{
targets {
fromPreset(presets.jvm, 'android')
iosArm32("ios32")
iosArm64("ios64")
iosX64("simulator")
configure([ios32, ios64, simulator]) {
binaries.framework('Shared')
}
}
//we have 3 different sourceSets for common, android and iOS.
//each sourceSet can have their own set of dependencies and configurations
sourceSets {
commonMain.dependencies {
api 'org.jetbrains.kotlin:kotlin-stdlib-common'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version"
}
androidMain.dependencies {
api 'org.jetbrains.kotlin:kotlin-stdlib'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"
}
iosMain {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serialization_version")
}
}
ios32.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serialization_version")
}
ios64.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serialization_version")
}
simulator.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serialization_version")
}
}
task fatFramework(type: FatFrameworkTask) {
// the fat framework must have the same base name as the initial frameworks
baseName = "Shared"
final File frameworkDir = new File(buildDir, "xcode-frameworks")
destinationDir = frameworkDir
// specify the frameworks to be merged
from(
targets.ios32.binaries.getFramework('Shared', 'RELEASE'),
targets.ios64.binaries.getFramework('Shared', 'RELEASE'),
targets.simulator.binaries.getFramework('Shared', 'RELEASE')
)
doLast {
new File(frameworkDir, 'gradlew').with {
text = "#!/bin/bash\nexport 'JAVA_HOME=${System.getProperty("java.home")}'\ncd '${rootProject.rootDir}'\n./gradlew \$#\n"
setExecutable(true)
}
}
}
}
configurations {
compileClasspath
}
tasks.build.dependsOn fatFramework
When I try to build my Module it gives me this error
Execution failed for task ':Shared:linkSharedReleaseFrameworkIos32'.
> Resolving configuration 'ios32Api' directly is not allowed
Am I missing something in my configuration?
I was unable to reproduce the error as the question features only part of the project. So, while modifying it to make things work as a separate project rather than a module, I accidentally lost the original problem. But here are my thoughts on the possible cause.
This snippet seems to be missing the difference between source sets and targets. I mean, as there are three named targets ios32, ios64, simulator, the kotlin-multiplatform plugin creates six default source sets: ios32Main, ios64Main, simulatorMain, ios32Test, ios64Test, simulatorTest. But in this code, I see new source sets named ios32 etc. being created instead of setting defaults' dependencies. This can be a problem, as there are no explicit connections between those source sets and declared targets.
For jvm i can use:
plugins {
kotlin("jvm") version "1.2.60"
}
I have found no equivanent string for "jvm" for use with javascript and am using the following:
plugins {
id("kotlin2js") version "1.2.60"
}
So the questions.
Is there an equivalent string to "jvm" to for javascript or some other more direct equivalent to the 'kotlin("jvm")'?
So the answer is taken as "not yet" - and no current plan for that to change either. So stick with the id().
It doesn't work since plugin for JS isn't published yet on Gradle Plugins Portal. Feel free to vote for the issue.
As a workaround, you can add to your settings.gradle:
pluginManagement {
resolutionStrategy {
eachPlugin {
if (requested.id.id == "kotlin2js") {
useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}")
}
}
}
}
And then in your build.gradle.kts files you can write like
plugins {
id("kotlin2js") version "1.3.10"
}
repositories {
mavenCentral()
}
dependencies {
implementation(kotlin("stdlib-js"))
}