In my multi-project codebase, we used to have this...
# community-support
s
In my multi-project codebase, we used to have this piece of code to apply some common dependencies which were needed by all projects
Copy code
private fun Project.configureCommonDependencies(libs: LibrariesForLibs) {
  dependencies {
    val koinBom = libs.koin.bom
    add("implementation", platform(koinBom))
    add("implementation", project(":logging-public")) ...
    }
  }
}
Which lives in a convention plugin which we apply to all of our projects. Now, we are switching some of these projects to be Kotlin Multiplatform projects. The problem arises that we can no longer just do
add("implementation", ...
as it does not exist. Using the kotlin DSL it instead then looks something like
Copy code
kotlin {
  sourceSets {
    commonMain.dependencies {
      implementation(...)
    }
  }
}
Is there some way for me to still rating this piece of code in this common place in my convention plugin, and conditionally do one or the other thing, depending on if the project in which it is being applied to is multiplatform or not? And if yes, how would the syntax have to look for that? I am having a bit of a hard time figuring this out myself to be honest
v
You don't need to use the Kotlin DSL, it is just convenience, but it just adds the dependencies to a configuration called
commonMainImplementation
. So you could e. g. give the configuration name to your function and supply the according one where you want it added.
s
Yeah I mentioned the dsl just to put emphasis on how it looks different there. I still wonder if there's a reasonable way for me to be able to infer which one of the two I should apply. Btw, how do I get to that commonMainImplementation from the dsl? Jumping to sources in the IDE always leads to some stubs where I can't read more into what they're actually doing.
e
I'd suggest a
Copy code
fun KotlinDependencyHandler.configureCommonDependencies() {
  implementation(...)
}
which could be used like
Copy code
kotlin {
  sourceSets {
    commonMain {
      dependencies {
        configureCommonDependencies()
but unfortunately it's unrelated to the built-in
DependencyHandler
type :-/
a
If you make it a plugin project, and apply it after the definition of Android or multi platform projects you could detect if the plugin is applied then branch your logic for configuring dependencies
Like (apologies on mobile): Override fun apply(project: Project){ If project.hasPlugin(id) // configure the Android or multi platform extension
e
better to react with
pluginManager.withPlugin
v
I still wonder if there's a reasonable way for me to be able to infer which one of the two I should apply.
For example
Copy code
fun KotlinDependencyHandler.configureCommonDependencies(libs: LibrariesForLibs) {
    configureCommonDependencies(libs, "commonMainImplementation")
}
fun DependencyHandlerScope.configureCommonDependencies(libs: LibrariesForLibs) {
    configureCommonDependencies(libs, "implementation")
}
private fun Project.configureCommonDependencies(libs: LibrariesForLibs, configurationName: String) {
  dependencies {
    add(configurationName, project(":logging-public"))
  }
}
and let the consumer call it at an appropriate place. Or if you want it more implicit like the others suggested, react to the plugins that get applied. But like @ephemient said, not what @Andrew Grosner said. Requiring plugin application order for proper function is bad practice. πŸ™‚
s
I ended up with this in the end
Copy code
private fun Project.configureCommonDependencies(libs: LibrariesForLibs) {
  pluginManager.withPlugin(libs.plugins.kotlinMultiplatform.get().pluginId) {
    project.extensions.configure<KotlinMultiplatformExtension> {
      sourceSets.configureEach {
        dependencies {
          configureCommonDependencies(project, libs)
        }
      }
    }
  }
  pluginManager.withPlugin(libs.plugins.kotlinJvm.get().pluginId) {
    dependencies {
      configureCommonDependencies(project, libs)
    }
  }
  pluginManager.withPlugin(libs.plugins.kotlin.get().pluginId) {
    dependencies {
      configureCommonDependencies(project, libs)
    }
  }
}

@Suppress("UnusedReceiverParameter")
private fun KotlinDependencyHandler.configureCommonDependencies(project: Project, libs: LibrariesForLibs) {
  project.configureCommonDependencies(libs, "commonMainImplementation")
}

@Suppress("UnusedReceiverParameter")
private fun DependencyHandlerScope.configureCommonDependencies(project: Project, libs: LibrariesForLibs) {
  project.configureCommonDependencies(libs, "implementation")
}

private fun Project.configureCommonDependencies(libs: LibrariesForLibs, configurationName: String) {
  dependencies {
    val koinBom = libs.koin.bom
    add(configurationName, platform(koinBom))
    add(configurationName, project(":logging-public")) ...
  }
}
And it… looks to be working as far as I can tell πŸ˜„
v
Does it? You don't use the
configurationName
s
Copy-pasted my snippet, but I wanted to omit all the details inside the last function for brevity, so I wrongly copy-pasted from my comment above. In the real implementation I do use
configurationName
as you suggested of course πŸ˜„
πŸ‘Œ 1