Compare commits

...

14 Commits

Author SHA1 Message Date
Dmitriy Dolovov
748878291b Rename module: 'commonizer' -> 'kotlin-native-commonizer' 2020-01-22 14:35:20 +07:00
Dmitriy Dolovov
5842d586ec Add missed dependency on compiler in 'util-klib-metadata' 2020-01-22 13:22:30 +07:00
Dmitriy Dolovov
368cd45499 Start publishing 'kotlin-native-library-utils' to Maven 2020-01-22 13:22:14 +07:00
Dmitriy Dolovov
de36c747ea Rename 'kotlin-native-library-reader' module to 'kotlin-native-library-utils' 2020-01-22 13:06:55 +07:00
Dmitriy Dolovov
e7c5c85244 Minor: Rename NativePlatformConfigurator for consistency 2020-01-22 12:46:52 +07:00
Dmitriy Dolovov
99e22cdbd5 [Commonizer] Log commonization progress in Gradle plugin 2020-01-21 14:47:43 +07:00
Dmitriy Dolovov
bfe44ac528 [Commonizer] Restore warn message about K/N distribution missing stdlib
See also #KT-35033
2020-01-20 21:38:00 +07:00
Dmitriy Dolovov
85824fcbb9 [Commonizer] Run commonizer on IDE import from Gradle only for HMPP projects 2020-01-20 18:07:30 +07:00
Dmitriy Dolovov
4cd09a290c [Commonizer] Drop useless ambiguous commonized source sets warning in Gradle 2020-01-20 18:06:39 +07:00
Dmitriy Dolovov
c47bd2abcd [Commonizer] Revert propagation of platform libraries to common-native modules
See also #KT-33999
2020-01-20 18:04:58 +07:00
Dmitriy Dolovov
b7d85473c4 [Commonizer] Integrate commonizer to the Gradle plugin
Note: The most common source sets such as "commonMain" and "commonTest"
should not be used as Native intermediate source sets.
2020-01-20 18:03:59 +07:00
Dmitriy Dolovov
38641ae831 [Commonizer] Don't use experimental Kotlin time in commonizer 2020-01-20 13:21:38 +07:00
Dmitriy Dolovov
488a55d6ba [Commonizer] Process endorsed libraries in CLI 2020-01-20 13:21:38 +07:00
Dmitriy Dolovov
0eaf3792fc [Commonizer] Fix classpath dependency for CLI 2020-01-20 13:21:37 +07:00
54 changed files with 472 additions and 243 deletions

View File

@@ -605,8 +605,8 @@ tasks {
register("konan-tests") {
dependsOn("dist")
dependsOn(
":kotlin-native:kotlin-native-library-reader:test",
":kotlin-native:commonizer:test"
":kotlin-native:kotlin-native-library-utils:test",
":kotlin-native:kotlin-native-commonizer:test"
)
}

View File

@@ -1,16 +1,29 @@
import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer.COMPILE
plugins {
maven
kotlin("jvm")
id("jps-compatible")
}
val mavenCompileScope by configurations.creating {
the<MavenPluginConvention>()
.conf2ScopeMappings
.addMapping(0, this, COMPILE)
}
description = "Common klib metadata reader and writer"
dependencies {
// Compile-only dependencies are needed for compilation of this module:
compileOnly(project(":compiler:cli-common"))
compileOnly(project(":compiler:frontend"))
compileOnly(project(":core:deserialization"))
compileOnly(project(":compiler:serialization"))
// This dependency is necessary to keep the right dependency record inside of POM file:
mavenCompileScope(project(":kotlin-compiler"))
compile(kotlinStdlib())
compile(project(":kotlin-util-io"))
compile(project(":kotlin-util-klib"))

View File

@@ -129,7 +129,7 @@ dependencies {
testCompile(commonDep("junit:junit"))
testCompileOnly(intellijPluginDep("coverage"))
testRuntime(project(":kotlin-native:kotlin-native-library-reader")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-utils")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-utils")) { isTransitive = false }
testRuntime(commonDep("org.jetbrains", "markdown"))

View File

@@ -35,7 +35,7 @@ dependencies {
testCompile(project(":idea:idea-native")) { isTransitive = false }
testCompile(project(":idea:idea-gradle-native")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-reader")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-utils")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-utils")) { isTransitive = false }
testCompile(intellijDep())

View File

@@ -6,7 +6,7 @@ plugins {
dependencies {
testRuntime(intellijDep())
compile(project(":kotlin-native:kotlin-native-library-reader"))
compile(project(":kotlin-native:kotlin-native-library-utils"))
compileOnly(project(":idea:idea-gradle"))
compileOnly(project(":idea:idea-native"))

View File

@@ -44,7 +44,7 @@ dependencies {
testCompile(project(":idea:idea-native")) { isTransitive = false }
testCompile(project(":idea:idea-gradle-native")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-reader")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-utils")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-utils")) { isTransitive = false }
testRuntime(project(":idea:idea-new-project-wizard"))

View File

@@ -476,18 +476,6 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
}
}
private data class CompilationWithDependencies(
val compilation: KotlinCompilation,
val substitutedDependencies: List<ExternalDependency>
) {
val konanTarget: String?
get() = compilation.nativeExtensions.konanTarget
val dependencyNames: Map<String, ExternalDependency> by lazy {
substitutedDependencies.associateBy { it.name.removeSuffixIfPresent(" [$konanTarget]") }
}
}
fun populateModuleDependencies(
gradleModule: IdeaModule,
ideProject: DataNode<ProjectData>,
@@ -498,7 +486,6 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
val sourceSetMap = ideProject.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS) ?: return
val artifactsMap = ideProject.getUserData(CONFIGURATION_ARTIFACTS) ?: return
val substitutor = KotlinNativeLibrariesDependencySubstitutor(mppModel, gradleModule, resolverCtx)
val sourceSetToCompilations = mutableMapOf<String, MutableList<CompilationWithDependencies>>()
val processedModuleIds = HashSet<String>()
processCompilations(gradleModule, mppModel, ideModule, resolverCtx) { dataNode, compilation ->
if (processedModuleIds.add(getKotlinModuleId(gradleModule, compilation, resolverCtx))) {
@@ -514,11 +501,6 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
)
KotlinNativeLibrariesFixer.applyTo(dataNode, ideProject)
for (sourceSet in compilation.sourceSets) {
(sourceSet.dependsOnSourceSets + sourceSet.name).forEach {
sourceSetToCompilations
.getOrPut(it) { mutableListOf() }
.add(CompilationWithDependencies(compilation, substitutedDependencies))
}
if (sourceSet.fullName() == compilation.fullName()) continue
val targetDataNode = getSiblingKotlinModuleData(sourceSet, gradleModule, ideModule, resolverCtx) ?: continue
addDependency(dataNode, targetDataNode, sourceSet.isTestModule)
@@ -587,14 +569,6 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
val mergedDependencies = LinkedHashSet<KotlinDependency>().apply {
addAll(sourceSet.dependencies.mapNotNull { mppModel.dependencyMap[it] })
dependeeSourceSets.flatMapTo(this) { it.dependencies.mapNotNull { mppModel.dependencyMap[it] } }
if (mppModel.extraFeatures.isNativeDependencyPropagationEnabled
&& mppModel.extraFeatures.isHMPPEnabled
&& sourceSet.actualPlatforms.getSinglePlatform() == KotlinPlatform.NATIVE
) {
sourceSetToCompilations[sourceSet.name]?.let { compilations ->
addAll(propagatedNativeDependencies(compilations))
}
}
}
val substitutedDependencies =
substitutor.substituteDependencies(mergedDependencies)
@@ -606,65 +580,10 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
preprocessDependencies(substitutedDependencies),
ideProject
)
@Suppress("UNCHECKED_CAST")
KotlinNativeLibrariesFixer.applyTo(fromDataNode as DataNode<GradleSourceSetData>, ideProject)
}
}
}
// We can't really commonize native platform libraries yet.
// But APIs for different targets may be very similar.
// E.g. ios_arm64 and ios_x64 have almost identical platform libraries.
// We handle these special cases and resolve common sources for such
// targets against libraries of one of them. E.g. common sources for
// ios_x64 and ios_arm64 will be resolved against ios_arm64 libraries.
//
// Currently such special casing is available for Apple platforms
// (iOS, watchOS and tvOS) and native Android (ARM, X86).
// TODO: Do we need to support user's interop libraries too?
private fun propagatedNativeDependencies(compilations: List<CompilationWithDependencies>): List<ExternalDependency> {
if (compilations.size <= 1) {
return emptyList()
}
val copyFrom = when {
compilations.all { it.isAppleCompilation } ->
compilations.selectFirstAvailableTarget(
"watchos_arm64", "watchos_arm32", "watchos_x86",
"ios_arm64", "ios_arm32", "ios_x64",
"tvos_arm64", "tvos_x64"
)
compilations.all { it.konanTarget?.startsWith("android") == true } ->
compilations.selectFirstAvailableTarget(
"android_arm64", "android_arm32", "android_x64", "android_x86"
)
else -> return emptyList()
}
return copyFrom.dependencyNames.mapNotNull { (name, dependency) ->
when {
!name.startsWith(KOTLIN_NATIVE_LIBRARY_PREFIX) -> null // Support only default platform libs for now.
compilations.all { it.dependencyNames.containsKey(name) } -> dependency
else -> null
}
}
}
private val CompilationWithDependencies.isAppleCompilation: Boolean
get() = konanTarget?.let {
it.startsWith("ios") || it.startsWith("watchos") || it.startsWith("tvos")
} ?: false
private fun Iterable<CompilationWithDependencies>.selectFirstAvailableTarget(vararg targetsByPriority: String): CompilationWithDependencies {
for (target in targetsByPriority) {
val result = firstOrNull { it.konanTarget == target }
if (result != null) {
return result
}
}
return first()
}
private fun KotlinModule.toSourceSet(mppModel: KotlinMPPGradleModel) = when (this) {
is KotlinSourceSet -> this
is KotlinCompilation -> mppModel.sourceSets[fullName()]

View File

@@ -11,7 +11,7 @@ dependencies {
compile(project(":compiler:cli-common"))
compile(project(":compiler:frontend.java"))
compile(project(":js:js.frontend"))
compile(project(":kotlin-native:kotlin-native-library-reader"))
compile(project(":kotlin-native:kotlin-native-library-utils"))
compileOnly(intellijDep())
compileOnly(jpsStandalone()) { includeJars("jps-model") }
}

View File

@@ -38,7 +38,7 @@ dependencies {
}
testCompile(project(":idea:idea-native")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-reader")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-utils")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-utils")) { isTransitive = false }
testRuntime(project(":kotlin-reflect"))

View File

@@ -12,7 +12,7 @@ dependencies {
Platform[192].orHigher {
compileOnly(intellijPluginDep("java"))
}
compile(project(":kotlin-native:kotlin-native-library-reader"))
compile(project(":kotlin-native:kotlin-native-library-utils"))
testCompileOnly(intellijDep())
testRuntimeOnly(intellijDep())

View File

@@ -32,7 +32,7 @@ dependencies {
testRuntime(project(":idea:idea-jvm"))
testRuntime(project(":idea:idea-native")) { isTransitive = false }
testRuntime(project(":idea:idea-gradle-native")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-reader")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-utils")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-utils")) { isTransitive = false }
testRuntime(project(":kotlin-reflect"))

View File

@@ -162,7 +162,6 @@ interface KotlinTestTask : Serializable {
interface ExtraFeatures : Serializable {
val coroutinesState: String?
val isHMPPEnabled: Boolean
val isNativeDependencyPropagationEnabled: Boolean
}
interface KotlinMPPGradleModel : Serializable {

View File

@@ -64,7 +64,7 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
return KotlinMPPGradleModelImpl(
filterOrphanSourceSets(sourceSetMap, targets, project),
targets,
ExtraFeaturesImpl(coroutinesState, isHMPPEnabled(project), isNativeDependencyPropagationEnabled(project)),
ExtraFeaturesImpl(coroutinesState, isHMPPEnabled(project)),
kotlinNativeHome,
dependencyMapper.toDependencyMap()
)
@@ -89,10 +89,6 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
return (project.findProperty("kotlin.mpp.enableGranularSourceSetsMetadata") as? String)?.toBoolean() ?: false
}
private fun isNativeDependencyPropagationEnabled(project: Project): Boolean {
return (project.findProperty("kotlin.native.enableDependencyPropagation") as? String)?.toBoolean() ?: true
}
private fun reportUnresolvedDependencies(targets: Collection<KotlinTarget>) {
targets.asSequence()
.flatMap { it.compilations.asSequence() }

View File

@@ -194,8 +194,7 @@ data class KotlinTestTaskImpl(
data class ExtraFeaturesImpl(
override val coroutinesState: String?,
override val isHMPPEnabled: Boolean,
override val isNativeDependencyPropagationEnabled: Boolean
override val isHMPPEnabled: Boolean
) : ExtraFeatures
data class KotlinMPPGradleModelImpl(
@@ -220,8 +219,7 @@ data class KotlinMPPGradleModelImpl(
}.toList(),
ExtraFeaturesImpl(
mppModel.extraFeatures.coroutinesState,
mppModel.extraFeatures.isHMPPEnabled,
mppModel.extraFeatures.isNativeDependencyPropagationEnabled
mppModel.extraFeatures.isHMPPEnabled
),
mppModel.kotlinNativeHome,
mppModel.dependencyMap.map { it.key to it.value.deepCopy(cloningCache) }.toMap()

View File

@@ -32,7 +32,7 @@ dependencies {
testCompile(project(":idea:idea-native")) { isTransitive = false }
testCompile(project(":idea:idea-gradle-native")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-reader")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-utils")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-utils")) { isTransitive = false }
testRuntime(project(":plugins:kapt3-idea")) { isTransitive = false }
testRuntime(project(":idea:idea-jvm"))

View File

@@ -15,6 +15,7 @@ val mavenCompileScope by configurations.creating {
description = "Kotlin/Native library commonizer"
dependencies {
compileOnly(project(":compiler:cli-common"))
compileOnly(project(":compiler:frontend"))
compileOnly(project(":compiler:ir.serialization.common"))
@@ -25,7 +26,7 @@ dependencies {
compile(project(":kotlin-util-klib-metadata"))
compile(project(":kotlin-native:kotlin-native-utils")) { isTransitive = false }
compile(project(":kotlin-native:kotlin-native-library-reader")) { isTransitive = false }
compile(project(":kotlin-native:kotlin-native-library-utils")) { isTransitive = false }
testCompile(commonDep("junit:junit"))
testCompile(projectTests(":compiler:tests-common"))

View File

@@ -9,9 +9,7 @@ import org.jetbrains.kotlin.descriptors.commonizer.konan.NativeDistributionCommo
import org.jetbrains.kotlin.konan.target.HostManager
import java.io.File
import kotlin.system.exitProcess
import kotlin.time.ExperimentalTime
@ExperimentalTime
fun main(args: Array<String>) {
if (args.isEmpty()) printUsageAndExit()
@@ -34,6 +32,8 @@ fun main(args: Array<String>) {
repository = repository,
targets = targets,
destination = destination,
copyStdlib = true,
copyEndorsedLibs = true,
withStats = withStats,
handleError = ::printErrorAndExit,
log = ::println

View File

@@ -27,18 +27,18 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.konan.impl.KlibResolvedModuleDescriptorsFactoryImpl
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import java.io.File
import kotlin.time.ExperimentalTime
import org.jetbrains.kotlin.konan.file.File as KFile
class NativeDistributionCommonizer(
private val repository: File,
private val targets: List<KonanTarget>,
private val destination: File,
private val withStats: Boolean,
private val copyStdlib: Boolean = false,
private val copyEndorsedLibs: Boolean = false,
private val withStats: Boolean = false,
private val handleError: (String) -> Nothing,
private val log: (String) -> Unit
) {
@ExperimentalTime
fun run() {
checkPreconditions()
@@ -181,16 +181,23 @@ class NativeDistributionCommonizer(
originalModulesByTargets: Map<InputTarget, List<NativeModuleForCommonization>>,
result: CommonizationPerformed
) {
// optimization: stdlib effectively remains the same across all Kotlin/Native targets,
// so it can be just copied to the new destination without running serializer
val stdlibOrigin = originalModulesByTargets.values.asSequence()
.flatten()
.filterIsInstance<DeserializedModule>()
.map { it.location }
.first { it.endsWith(KONAN_STDLIB_NAME) }
val stdlibDestination = destination.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR).resolve(KONAN_STDLIB_NAME)
stdlibOrigin.copyRecursively(stdlibDestination)
// optimization: stdlib and endorsed libraries effectively remain the same across all Kotlin/Native targets,
// so they can be just copied to the new destination without running serializer
if (copyStdlib || copyEndorsedLibs) {
repository.resolve(KONAN_DISTRIBUTION_KLIB_DIR)
.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR)
.listFiles()
?.filter { it.isDirectory }
?.let {
if (copyStdlib) {
if (copyEndorsedLibs) it else it.filter { dir -> dir.endsWith(KONAN_STDLIB_NAME) }
} else
it.filter { dir -> !dir.endsWith(KONAN_STDLIB_NAME) }
}?.forEach { libraryOrigin ->
val libraryDestination = destination.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR).resolve(libraryOrigin.name)
libraryOrigin.copyRecursively(libraryDestination)
}
}
val originalModulesManifestData = originalModulesByTargets.mapValues { (_, modules) ->
modules.asSequence()

View File

@@ -5,15 +5,46 @@
package org.jetbrains.kotlin.descriptors.commonizer.utils
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
import kotlin.time.MonoClock
import java.lang.System.currentTimeMillis
@ExperimentalTime
internal class ResettableClockMark {
private val startMark = MonoClock.markNow()
internal class Period(val start: Long, val end: Long) {
override fun toString(): String {
var remainder = end - start
val millis = remainder % 1000
remainder /= 1000
val seconds = remainder % 60
remainder /= 60
val minutes = remainder % 60
val hours = remainder / 60
// human-friendly formatted duration
return buildString {
if (hours > 0) append(hours).append("h ")
if (minutes > 0 || isNotEmpty()) append(minutes).append("m ")
if (seconds > 0 || isNotEmpty()) append(seconds).append("s ")
if (millis > 0 || isNotEmpty()) append(millis).append("ms")
}
}
}
private val startMark = currentTimeMillis()
private var lastMark = startMark
fun elapsedSinceLast(): Duration = lastMark.elapsedNow().also { lastMark = lastMark.plus(it) }
fun elapsedSinceStart(): Duration = startMark.elapsedNow()
fun elapsedSinceLast(): Period = Period(lastMark, currentTimeMillis()).also { lastMark = it.end }
fun elapsedSinceStart(): Period = Period(startMark, currentTimeMillis())
}
// TODO: this is how it should be when Kotlin Time will become non-experimental
//@ExperimentalTime
//internal class ResettableClockMark {
// private val startMark = MonoClock.markNow()
// private var lastMark = startMark
//
// fun elapsedSinceLast(): Duration = lastMark.elapsedNow().also { lastMark = lastMark.plus(it) }
// fun elapsedSinceStart(): Duration = startMark.elapsedNow()
//}

View File

@@ -1,9 +1,10 @@
plugins {
maven
kotlin("jvm")
id("jps-compatible")
}
description = "Kotlin/Native library utils"
dependencies {
compileOnly(project(":compiler:frontend"))
compileOnly(project(":compiler:frontend.java"))
@@ -22,4 +23,6 @@ sourceSets {
"test" { projectDefault() }
}
publish()
standardPublicJars()

View File

@@ -13,5 +13,5 @@ object NativePlatformAnalyzerServices : PlatformDependentAnalyzerServices() {
result.add(ImportPath.fromString("kotlin.native.*"))
}
override val platformConfigurator: PlatformConfigurator = KonanPlatformConfigurator
override val platformConfigurator: PlatformConfigurator = NativePlatformConfigurator
}

View File

@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
import org.jetbrains.kotlin.resolve.jvm.checkers.SuperCallWithDefaultArgumentsChecker
object KonanPlatformConfigurator : PlatformConfiguratorBase(
object NativePlatformConfigurator : PlatformConfiguratorBase(
additionalCallCheckers = listOf(SuperCallWithDefaultArgumentsChecker())
) {
override fun configureModuleComponents(container: StorageComponentContainer) {

View File

@@ -36,6 +36,7 @@ dependencies {
compile(kotlinStdlib())
compile(project(":kotlin-native:kotlin-native-utils"))
compile(project(":kotlin-native:kotlin-native-commonizer"))
compile(project(":kotlin-util-klib"))
compileOnly(project(":kotlin-reflect-api"))
compileOnly(project(":kotlin-android-extensions"))

View File

@@ -10,16 +10,13 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.jetbrains.kotlin.compilerRunner.konanHome
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.targets.native.DisabledNativeTargetsReporter
import org.jetbrains.kotlin.gradle.targets.native.KotlinNativeHostTestRun
import org.jetbrains.kotlin.gradle.targets.native.KotlinNativeSimulatorTestRun
import org.jetbrains.kotlin.gradle.targets.native.internal.setUpKotlinNativePlatformDependencies
import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
import org.jetbrains.kotlin.gradle.utils.SingleWarningPerBuild
import org.jetbrains.kotlin.gradle.utils.SingleActionPerBuild
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
@@ -54,27 +51,6 @@ abstract class AbstractKotlinNativeTargetPreset<T : KotlinNativeTarget>(
}
}
private fun nativeLibrariesList(directory: String) = with(project) {
file("$konanHome/klib/$directory")
.listFiles { file -> file.isDirectory }
?.sortedBy { dir -> dir.name.toLowerCase() }
}
// We declare default K/N dependencies (default and platform libraries) as files to avoid searching them in remote repos (see KT-28128).
private fun defaultLibs(stdlibOnly: Boolean = false): List<Dependency> = with(project) {
var filesList = nativeLibrariesList("common")
if (stdlibOnly) {
filesList = filesList?.filter { dir -> dir.name == "stdlib" }
}
filesList?.map { dir -> dependencies.create(files(dir)) } ?: emptyList()
}
private fun platformLibs(target: KonanTarget): List<Dependency> = with(project) {
val filesList = nativeLibrariesList("platform/${target.name}")
filesList?.map { dir -> dependencies.create(files(dir)) } ?: emptyList()
}
protected abstract fun createTargetConfigurator(): KotlinTargetConfigurator<T>
protected abstract fun instantiateTarget(name: String): T
@@ -93,7 +69,9 @@ abstract class AbstractKotlinNativeTargetPreset<T : KotlinNativeTarget>(
createTargetConfigurator().configureTarget(result)
addDependenciesOnLibrariesFromDistribution(result)
project.whenEvaluated {
SingleActionPerBuild.run(project, ::setUpKotlinNativePlatformDependencies.name, ::setUpKotlinNativePlatformDependencies)
}
if (!konanTarget.enabledOnCurrentHost) {
with(HostManager()) {
@@ -105,69 +83,8 @@ abstract class AbstractKotlinNativeTargetPreset<T : KotlinNativeTarget>(
return result
}
// Allow IDE to resolve the libraries provided by the compiler by adding them into dependencies.
private fun addDependenciesOnLibrariesFromDistribution(result: T) {
result.compilations.all { compilation ->
val target = compilation.konanTarget
project.whenEvaluated {
// First, put common libs:
defaultLibs(!compilation.enableEndorsedLibs).forEach {
project.dependencies.add(compilation.compileDependencyConfigurationName, it)
}
// Then, platform-specific libs:
platformLibs(target).forEach {
project.dependencies.add(compilation.compileDependencyConfigurationName, it)
}
}
}
// Add dependencies to stdlib-native for intermediate single-backend source-sets (like 'allNative')
project.whenEvaluated {
val compilationsBySourceSets = CompilationSourceSetUtil.compilationsBySourceSets(this)
fun KotlinSourceSet.isIntermediateNativeSourceSet(): Boolean {
val compilations = compilationsBySourceSets[this] ?: return false
if (compilations.all { it.defaultSourceSet == this })
return false
return compilations.all { it.target.platformType == KotlinPlatformType.native }
}
val stdlib = defaultLibs(stdlibOnly = true).singleOrNull() ?: run {
warnAboutMissingNativeStdlib(project)
return@whenEvaluated
}
project.kotlinExtension.sourceSets
.filter { it.isIntermediateNativeSourceSet() }
.forEach {
it.dependencies { implementation(stdlib) }
}
}
}
companion object {
private const val KOTLIN_NATIVE_HOME_PRIVATE_PROPERTY = "konanHome"
private fun warnAboutMissingNativeStdlib(project: Project) {
if (!project.hasProperty("kotlin.native.nostdlib")) {
SingleWarningPerBuild.show(
project,
buildString {
append(NO_NATIVE_STDLIB_WARNING)
if (PropertiesProvider(project).nativeHome != null)
append(NO_NATIVE_STDLIB_PROPERTY_WARNING)
}
)
}
}
internal const val NO_NATIVE_STDLIB_WARNING =
"The Kotlin/Native distribution used in this build does not provide the standard library. "
internal const val NO_NATIVE_STDLIB_PROPERTY_WARNING =
"Make sure that the '${PropertiesProvider.KOTLIN_NATIVE_HOME}' property points to a valid Kotlin/Native distribution."
}
}

View File

@@ -0,0 +1,328 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.targets.native.internal
import org.gradle.api.Project
import org.jetbrains.kotlin.compilerRunner.konanHome
import org.jetbrains.kotlin.daemon.common.toHexString
import org.jetbrains.kotlin.descriptors.commonizer.konan.NativeDistributionCommonizer
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.internal.isInIdeaSync
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.KOTLIN_NATIVE_HOME
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.plugin.mpp.CompilationSourceSetUtil
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
import org.jetbrains.kotlin.gradle.targets.native.internal.NativePlatformDependency.*
import org.jetbrains.kotlin.gradle.utils.SingleWarningPerBuild
import org.jetbrains.kotlin.gradle.utils.lifecycleWithDuration
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_COMMON_LIBS_DIR
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_KLIB_DIR
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
import java.io.File
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
internal fun Project.setUpKotlinNativePlatformDependencies() {
if (multiplatformExtensionOrNull == null) {
// not a multiplatform project, nothing to set up
return
}
val dependencyResolver = NativePlatformDependencyResolver(this)
// run commonizer only for HMPP projects and only on IDE sync
val allowCommonizer = isKotlinGranularMetadataEnabled && isInIdeaSync
findSourceSetsToAddDependencies(allowCommonizer).forEach { (sourceSet: KotlinSourceSet, sourceSetDeps: Set<NativePlatformDependency>) ->
sourceSetDeps.map(dependencyResolver::resolve).flatten().forEach { dir ->
project.dependencies.add(sourceSet.implementationMetadataConfigurationName, dependencies.create(files(dir)))
}
}
}
private sealed class NativePlatformDependency {
/* Non-commonized target-neutral libraries taken directly from the Kotlin/Native distribution */
data class OutOfDistributionCommon(val includeEndorsedLibs: Boolean) : NativePlatformDependency()
/* Non-commonized libraries for the specific target taken directly from the Kotlin/Native distribution */
data class OutOfDistributionPlatform(val target: KonanTarget) : NativePlatformDependency()
/* Commonized (common) libraries */
data class CommonizedCommon(val targets: Set<KonanTarget>) : NativePlatformDependency()
/* Commonized libraries for a specific target platform */
data class CommonizedPlatform(val target: KonanTarget, val common: CommonizedCommon) : NativePlatformDependency()
}
private class NativePlatformDependencyResolver(val project: Project) {
private val distributionDir = File(project.konanHome)
private val distributionLibsDir = distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR)
private val resolvedDependencies = mutableMapOf<NativePlatformDependency, Set<File>>()
private val commonizationResults = mutableMapOf<CommonizedCommon, File>()
fun resolve(dependency: NativePlatformDependency): Set<File> = resolvedDependencies.computeIfAbsent(dependency) {
when (dependency) {
is OutOfDistributionCommon -> {
/* stdlib, endorsed libs */
var hasStdlib = false
val libs = libsInCommonDir(distributionLibsDir) { dir ->
val isStdlib = dir.endsWith(KONAN_STDLIB_NAME)
hasStdlib = hasStdlib || isStdlib
return@libsInCommonDir isStdlib || dependency.includeEndorsedLibs
}
if (!hasStdlib) warnAboutMissingNativeStdlib()
libs
}
is OutOfDistributionPlatform -> {
/* platform libs for a specific target */
libsInPlatformDir(distributionLibsDir, dependency.target)
}
is CommonizedCommon -> {
/* commonized platform libs with expect declarations */
val commonizedLibsDir = commonize(dependency)
libsInCommonDir(commonizedLibsDir)
}
is CommonizedPlatform -> {
/* commonized platform libs with actual declarations */
val commonizedLibsDir = commonize(dependency.common)
libsInPlatformDir(commonizedLibsDir, dependency.target)
}
}
}
// returns a directory with the commonized libraries
private fun commonize(dependency: CommonizedCommon): File = commonizationResults.computeIfAbsent(dependency) {
project.runCommonizer(distributionDir, dependency.targets)
}
private fun warnAboutMissingNativeStdlib() {
if (!project.hasProperty("kotlin.native.nostdlib")) {
SingleWarningPerBuild.show(
project,
buildString {
append(NO_NATIVE_STDLIB_WARNING)
if (PropertiesProvider(project).nativeHome != null)
append(NO_NATIVE_STDLIB_PROPERTY_WARNING)
}
)
}
}
companion object {
private fun libsInCommonDir(basePath: File, predicate: (File) -> Boolean = { true }) =
basePath.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR).listFiles()?.filter { predicate(it) }?.toSet() ?: emptySet()
private fun libsInPlatformDir(basePath: File, target: KonanTarget) =
basePath.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR).resolve(target.name).listFiles()?.toSet() ?: emptySet()
}
}
private fun Project.findSourceSetsToAddDependencies(allowCommonizer: Boolean): Map<KotlinSourceSet, Set<NativePlatformDependency>> {
val sourceSetsToAddDeps = mutableMapOf<KotlinSourceSet, Set<NativePlatformDependency>>()
if (allowCommonizer) {
sourceSetsToAddDeps += findSourceSetsToAddCommonizedPlatformDependencies()
}
val compilationsBySourceSets = CompilationSourceSetUtil.compilationsBySourceSets(this)
val nativeCompilations = compilationsBySourceSets.values.flattenTo(mutableSetOf()).filterIsInstance<KotlinNativeCompilation>()
nativeCompilations.associate { it.defaultSourceSet to (it.konanTarget to it.enableEndorsedLibs) }
.forEach { (defaultSourceSet, details) ->
if (defaultSourceSet !in sourceSetsToAddDeps) {
val (target, includeEndorsedLibs) = details
sourceSetsToAddDeps[defaultSourceSet] = setOf(
OutOfDistributionCommon(includeEndorsedLibs),
OutOfDistributionPlatform(target)
)
}
}
return sourceSetsToAddDeps
}
private fun Project.findSourceSetsToAddCommonizedPlatformDependencies(): Map<KotlinSourceSet, Set<NativePlatformDependency>> {
val sourceSetsToAddDeps = mutableMapOf<KotlinSourceSet, Set<NativePlatformDependency>>()
val compilationsBySourceSets = CompilationSourceSetUtil.compilationsBySourceSets(this)
val nativeCompilations = compilationsBySourceSets.values.flattenTo(mutableSetOf()).filterIsInstance<KotlinNativeCompilation>()
nativeCompilations.forEach { nativeCompilation ->
// consider source sets in compilation only one step above the default source set
// TODO: reconsider this restriction
val commonSourceSetCandidates = nativeCompilation.allKotlinSourceSets intersect nativeCompilation.defaultSourceSet.dependsOn
commonSourceSetCandidates.forEach sourceSet@{ sourceSet ->
if (sourceSet.name == KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
|| sourceSet.name == KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME
) {
// exclude the most common source sets
return@sourceSet
}
if (sourceSet in sourceSetsToAddDeps) {
// already processed
return@sourceSet
}
val leafSourceSets = mutableMapOf<KotlinSourceSet, Pair<KonanTarget, /* include endorsed? */ Boolean>>()
for (compilation in compilationsBySourceSets.getValue(sourceSet)) {
if (compilation !is KotlinNativeCompilation) {
// the source set participates in non-native compilation
return@sourceSet
}
val defaultSourceSet = compilation.defaultSourceSet
if (defaultSourceSet == sourceSet) {
// there is a compilation where the source set is the default source set
return@sourceSet
}
leafSourceSets[defaultSourceSet] = compilation.konanTarget to compilation.enableEndorsedLibs
}
val allTargets = leafSourceSets.values.mapTo(mutableSetOf()) { /* target */ it.first }
if (allTargets.isEmpty())
return@sourceSet
val commonizedCommonDep = CommonizedCommon(allTargets)
val includeEndorsedLibsToCommonSourceSet = leafSourceSets.values.all { /* include endorsed? */ it.second }
sourceSetsToAddDeps[sourceSet] = setOf(
OutOfDistributionCommon(includeEndorsedLibsToCommonSourceSet),
commonizedCommonDep
)
leafSourceSets.forEach { (leafSourceSet, details) ->
val existingDep = sourceSetsToAddDeps[leafSourceSet]
if (existingDep == null) {
val (target, includeEndorsedLibs) = details
val commonizedPlatformDep = CommonizedPlatform(target, commonizedCommonDep)
sourceSetsToAddDeps[leafSourceSet] = setOf(
OutOfDistributionCommon(includeEndorsedLibs),
commonizedPlatformDep
)
} /*else if (existingDep != leafDep) {
// do nothing, the warning will be logged later
}*/
}
}
}
return sourceSetsToAddDeps
}
private fun Project.runCommonizer(distributionDir: File, targets: Set<KonanTarget>): File {
if (targets.size == 1) {
// no need to commonize, just use the libraries from the distribution
return distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR)
}
val baseDestinationDir = rootProject.buildDir.resolve("commonizedLibraries")
check(baseDestinationDir.isDirectory || !baseDestinationDir.exists()) { "$baseDestinationDir is not a directory" }
val kotlinVersion = getKotlinPluginVersion()?.onlySafeCharacters
// naive up-to-date check
val definitelyNotUpToDate = when {
kotlinVersion == null || kotlinVersion.endsWith("SNAPSHOT", ignoreCase = true) -> {
// "X.Y.Z-SNAPSHOT" is not enough to uniquely identify the concrete version of Kotlin plugin,
// therefore lets assume that it's always not up to date
true
}
!distributionDir.startsWith(File(System.getProperty("user.home")).resolve(".konan")) -> {
// a distribution installed in non-standard location, probably built by user
true
}
else -> false
}
// need stable order of targets for consistency
val orderedTargets = targets.sortedBy { it.name }
val discriminator = buildString {
append(kotlinVersion ?: "unknown")
append("-")
append(distributionDir.path.md5String)
append("-")
orderedTargets.joinTo(this, separator = "-")
}
val destinationDir = baseDestinationDir.resolve(discriminator)
if (!definitelyNotUpToDate && destinationDir.isDirectory) {
// it's up to date
return destinationDir
}
val suffix = estimateLibrariesCount(distributionDir, targets)?.let { " ($it items)" } ?: ""
logger.lifecycle("\nPreparing commonized Kotlin/Native libraries for target platforms $orderedTargets$suffix")
logger.lifecycleWithDuration("Preparing commonized Kotlin/Native libraries for target platforms $orderedTargets finished,") {
destinationDir.deleteRecursively()
val destinationTmpDir = destinationDir.parentFile.resolve(destinationDir.name + ".tmp")
NativeDistributionCommonizer(
repository = distributionDir,
targets = orderedTargets,
destination = destinationTmpDir,
handleError = ::error,
log = { logger.info("[COMMONIZER] $it") }
).run()
destinationTmpDir.renameTo(destinationDir)
}
return destinationDir
}
private fun estimateLibrariesCount(distributionDir: File, targets: Set<KonanTarget>): Int? {
val targetNames = targets.map { it.name }
return distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR)
.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR)
.listFiles()
?.filter { it.name in targetNames }
?.mapNotNull { it.listFiles() }
?.flatMap { it.toList() }
?.size
}
private val String.md5String
get() = MessageDigest.getInstance("MD5").digest(toByteArray(StandardCharsets.UTF_8)).toHexString()
private val String.onlySafeCharacters
get() = StringBuilder().also { builder ->
for (ch in this) {
builder.append(
when (ch) {
in '0'..'9', in 'a'..'Z', in 'A'..'Z' -> ch
else -> '_'
}
)
}
}.toString()
internal const val NO_NATIVE_STDLIB_WARNING =
"The Kotlin/Native distribution used in this build does not provide the standard library. "
internal const val NO_NATIVE_STDLIB_PROPERTY_WARNING =
"Make sure that the '$KOTLIN_NATIVE_HOME' property points to a valid Kotlin/Native distribution."

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.utils
import org.gradle.api.Project
import java.util.*
internal object SingleActionPerBuild {
private val rootModulePerformedActions = WeakHashMap<Project, MutableSet<String>>()
fun run(project: Project, actionId: String, action: () -> Unit) {
val performedActions = rootModulePerformedActions.computeIfAbsent(project) { mutableSetOf() }
if (performedActions.add(actionId)) {
action()
}
}
}

View File

@@ -6,15 +6,11 @@
package org.jetbrains.kotlin.gradle.utils
import org.gradle.api.Project
import java.util.*
internal object SingleWarningPerBuild {
private val rootModuleReceivedWarning = WeakHashMap<Project, MutableSet<String>>()
private const val ACTION_ID_SHOW_WARNING = "show-warning:"
fun show(project: Project, warningText: String) {
val receivedWarnings = rootModuleReceivedWarning.computeIfAbsent(project.rootProject) { mutableSetOf() }
if (receivedWarnings.add(warningText)) {
project.logger.warn(warningText)
}
fun show(project: Project, warningText: String) = SingleActionPerBuild.run(project, ACTION_ID_SHOW_WARNING + warningText) {
project.logger.warn(warningText)
}
}
}

View File

@@ -5,28 +5,28 @@
package org.jetbrains.kotlin.gradle.internals
import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinNativeTargetPreset.Companion.NO_NATIVE_STDLIB_PROPERTY_WARNING
import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinNativeTargetPreset.Companion.NO_NATIVE_STDLIB_WARNING
import org.jetbrains.kotlin.gradle.plugin.KOTLIN_12X_MPP_DEPRECATION_WARNING
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin.Companion.GRADLE_NO_METADATA_WARNING
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinProjectStructureMetadata
import org.jetbrains.kotlin.gradle.plugin.mpp.MULTIPLATFORM_PROJECT_METADATA_FILE_NAME
import org.jetbrains.kotlin.gradle.plugin.mpp.parseKotlinSourceSetMetadataFromXml
import org.jetbrains.kotlin.gradle.targets.native.DisabledNativeTargetsReporter
import org.jetbrains.kotlin.gradle.targets.native.internal.NO_NATIVE_STDLIB_PROPERTY_WARNING
import org.jetbrains.kotlin.gradle.targets.native.internal.NO_NATIVE_STDLIB_WARNING
import org.w3c.dom.Document
fun parseKotlinSourceSetMetadataFromXml(document: Document): KotlinProjectStructureMetadata? =
org.jetbrains.kotlin.gradle.plugin.mpp.parseKotlinSourceSetMetadataFromXml(document)
parseKotlinSourceSetMetadataFromXml(document)
const val MULTIPLATFORM_PROJECT_METADATA_FILE_NAME =
org.jetbrains.kotlin.gradle.plugin.mpp.MULTIPLATFORM_PROJECT_METADATA_FILE_NAME
const val MULTIPLATFORM_PROJECT_METADATA_FILE_NAME = MULTIPLATFORM_PROJECT_METADATA_FILE_NAME
const val GRADLE_NO_METADATA_WARNING =
org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin.GRADLE_NO_METADATA_WARNING
const val GRADLE_NO_METADATA_WARNING = GRADLE_NO_METADATA_WARNING
const val DISABLED_NATIVE_TARGETS_REPORTER_DISABLE_WARNING_PROPERTY_NAME =
DisabledNativeTargetsReporter.DISABLE_WARNING_PROPERTY_NAME
const val DISABLED_NATIVE_TARGETS_REPORTER_DISABLE_WARNING_PROPERTY_NAME = DisabledNativeTargetsReporter.DISABLE_WARNING_PROPERTY_NAME
const val DISABLED_NATIVE_TARGETS_REPORTER_WARNING_PREFIX: String =
DisabledNativeTargetsReporter.WARNING_PREFIX
const val DISABLED_NATIVE_TARGETS_REPORTER_WARNING_PREFIX: String = DisabledNativeTargetsReporter.WARNING_PREFIX
const val NO_NATIVE_STDLIB_WARNING = NO_NATIVE_STDLIB_WARNING
const val NO_NATIVE_STDLIB_PROPERTY_WARNING = NO_NATIVE_STDLIB_PROPERTY_WARNING
val KOTLIN_12X_MPP_DEPRECATION_WARNING = org.jetbrains.kotlin.gradle.plugin.KOTLIN_12X_MPP_DEPRECATION_WARNING
val KOTLIN_12X_MPP_DEPRECATION_WARNING = KOTLIN_12X_MPP_DEPRECATION_WARNING

View File

@@ -34,7 +34,7 @@ dependencies {
testCompile(commonDep("junit:junit"))
testCompile(project(":idea:idea-native")) { isTransitive = false }
testCompile(project(":idea:idea-gradle-native")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-reader")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-utils")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-utils")) { isTransitive = false }
testRuntime(project(":kotlin-reflect"))
testCompile(intellijPluginDep("android"))

View File

@@ -40,7 +40,7 @@ dependencies {
testCompile(project(":idea:idea-native")) { isTransitive = false }
testCompile(project(":idea:idea-gradle-native")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-reader")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-library-utils")) { isTransitive = false }
testRuntime(project(":kotlin-native:kotlin-native-utils")) { isTransitive = false }
testRuntime(project(":kotlin-reflect"))
testRuntime(project(":idea:idea-android"))

View File

@@ -66,7 +66,7 @@ val projectsToShadow by extra(listOf(
":js:js.serializer",
":js:js.translator",
":kotlin-native:kotlin-native-utils",
":kotlin-native:kotlin-native-library-reader",
":kotlin-native:kotlin-native-library-utils",
":compiler:light-classes",
":compiler:plugin-api",
":kotlin-preloader",

View File

@@ -123,8 +123,8 @@ include ":kotlin-build-common",
":js:js.dce",
":js:js.tests",
":kotlin-native:kotlin-native-utils",
":kotlin-native:kotlin-native-library-reader",
":kotlin-native:commonizer",
":kotlin-native:kotlin-native-library-utils",
":kotlin-native:kotlin-native-commonizer",
":jps-plugin",
":kotlin-jps-plugin",
":core:descriptors",
@@ -396,8 +396,8 @@ project(':kotlin-util-io').projectDir = "$rootDir/compiler/util-io" as File
project(':kotlin-util-klib').projectDir = "$rootDir/compiler/util-klib" as File
project(':kotlin-util-klib-metadata').projectDir = "$rootDir/compiler/util-klib-metadata" as File
project(':kotlin-native:kotlin-native-utils').projectDir = "$rootDir/konan/utils" as File
project(':kotlin-native:kotlin-native-library-reader').projectDir = "$rootDir/konan/library-reader" as File
project(':kotlin-native:commonizer').projectDir = "$rootDir/konan/commonizer" as File
project(':kotlin-native:kotlin-native-library-utils').projectDir = "$rootDir/konan/library-utils" as File
project(':kotlin-native:kotlin-native-commonizer').projectDir = "$rootDir/konan/commonizer" as File
project(':kotlin-jps-plugin').projectDir = "$rootDir/prepare/jps-plugin" as File
project(':idea:idea-android-output-parser').projectDir = "$rootDir/idea/idea-android/idea-android-output-parser" as File
project(':plugins:android-extensions-compiler').projectDir = "$rootDir/plugins/android-extensions/android-extensions-compiler" as File