diff --git a/Interop/Indexer/build.gradle b/Interop/Indexer/build.gradle index 48dd53b3603..6e1ebc037ec 100644 --- a/Interop/Indexer/build.gradle +++ b/Interop/Indexer/build.gradle @@ -18,10 +18,10 @@ buildscript { ext.rootBuildDirectory = file('../..') apply from: "$rootBuildDirectory/gradle/kotlinGradlePlugin.gradle" - apply from: "$rootBuildDirectory/gradle/kotlinNativeShared.gradle" dependencies { classpath "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" + classpath "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" } } apply plugin: 'kotlin' diff --git a/Interop/Runtime/build.gradle b/Interop/Runtime/build.gradle index e9bde72ef76..94f2418d80f 100644 --- a/Interop/Runtime/build.gradle +++ b/Interop/Runtime/build.gradle @@ -21,10 +21,10 @@ buildscript { ext.rootBuildDirectory = file('../..') apply from: "$rootBuildDirectory/gradle/kotlinGradlePlugin.gradle" - apply from: "$rootBuildDirectory/gradle/kotlinNativeShared.gradle" dependencies { classpath "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" + classpath "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" } } import org.jetbrains.kotlin.konan.target.ClangArgs diff --git a/Interop/StubGenerator/build.gradle b/Interop/StubGenerator/build.gradle index 69f3ac0c61c..0ed363c5d95 100644 --- a/Interop/StubGenerator/build.gradle +++ b/Interop/StubGenerator/build.gradle @@ -34,7 +34,7 @@ dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" compile "org.jetbrains.kotlin:kotlin-compiler:$buildKotlinVersion" compile project(':Interop:Indexer') - compile kotlinNativeSharedModule + compile "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" } compileKotlin { diff --git a/backend.native/build.gradle b/backend.native/build.gradle index 5ccf8d1906b..d50417e9d63 100644 --- a/backend.native/build.gradle +++ b/backend.native/build.gradle @@ -112,7 +112,6 @@ configurations { kotlin_stdlib_jar kotlin_reflect_jar kotlin_script_runtime_jar - kotlin_native_shared_jar trove4j_jar kotlinCommonSources @@ -132,7 +131,6 @@ dependencies { kotlin_stdlib_jar "$kotlinStdLibModule@jar" kotlin_reflect_jar "$kotlinReflectModule@jar" kotlin_script_runtime_jar "$kotlinScriptRuntimeModule@jar" - kotlin_native_shared_jar "$kotlinNativeSharedModule@jar" [kotlinCommonStdlibModule, kotlinTestCommonModule, kotlinTestAnnotationsCommonModule].each { kotlinCommonSources(it) { transitive = false } @@ -143,11 +141,11 @@ dependencies { compilerCompile kotlinCompilerModule compilerCompile kotlinNativeInterop['llvm'].configuration compilerCompile kotlinNativeInterop['hash'].configuration - compilerCompile kotlinNativeSharedModule + compilerCompile "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" compilerCompile "org.jetbrains.kotlin:konan.serializer:$konanVersion" cli_bcCompile kotlinCompilerModule - cli_bcCompile kotlinNativeSharedModule + cli_bcCompile "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" cli_bcCompile sourceSets.compiler.output cli_bcCompile "org.jetbrains.kotlin:konan.serializer:$konanVersion" @@ -234,7 +232,7 @@ jar { dependsOn ':runtime:hostRuntime', 'external_jars' } -def externalJars = ['compiler', 'stdlib', 'reflect', 'script_runtime', 'native_shared'] +def externalJars = ['compiler', 'stdlib', 'reflect', 'script_runtime'] task trove4jCopy(type: Copy) { from configurations.getByName("trove4j_jar") { diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index 1ec43707df3..df775955436 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -20,7 +20,6 @@ import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.Services -import org.jetbrains.kotlin.konan.CURRENT import org.jetbrains.kotlin.konan.KonanVersion import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.target.CompilerOutputKind diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index d4ac4bfb7c2..babe5f25404 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -14,15 +14,18 @@ import org.jetbrains.kotlin.cli.common.messages.GroupingMessageCollector import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.konan.* +import org.jetbrains.kotlin.konan.TempFiles import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.library.KonanLibrary import org.jetbrains.kotlin.konan.library.defaultResolver import org.jetbrains.kotlin.konan.library.libraryResolver import org.jetbrains.kotlin.konan.properties.loadProperties import org.jetbrains.kotlin.konan.target.* +import org.jetbrains.kotlin.konan.KonanAbiVersion +import org.jetbrains.kotlin.konan.KonanVersion import org.jetbrains.kotlin.konan.library.resolver.TopologicalLibraryOrder import org.jetbrains.kotlin.konan.library.toUnresolvedLibraries +import org.jetbrains.kotlin.konan.parseKonanVersion import org.jetbrains.kotlin.konan.util.Logger import kotlin.system.exitProcess diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DebugUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DebugUtils.kt index cf212ba0ca3..9c1ac3f0a23 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DebugUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DebugUtils.kt @@ -17,7 +17,6 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName -import org.jetbrains.kotlin.konan.CURRENT import org.jetbrains.kotlin.konan.KonanVersion import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.resolve.descriptorUtil.classId diff --git a/backend.native/debugger-tests/build.gradle b/backend.native/debugger-tests/build.gradle index 86b84555e6c..2ccc5501249 100644 --- a/backend.native/debugger-tests/build.gradle +++ b/backend.native/debugger-tests/build.gradle @@ -4,7 +4,7 @@ apply plugin: 'kotlin' dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" compile project(path: ':backend.native', configuration: 'cli_bc') - compile kotlinNativeSharedModule + compile "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" compile 'junit:junit:4.12' } diff --git a/build.gradle b/build.gradle index 636012b74be..e4beabfc745 100644 --- a/build.gradle +++ b/build.gradle @@ -21,8 +21,11 @@ import org.jetbrains.kotlin.konan.* buildscript { apply from: "gradle/kotlinGradlePlugin.gradle" - apply from: "gradle/kotlinNativeShared.gradle" + dependencies { + classpath "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" + } } +import org.jetbrains.kotlin.konan.* // Allows generating wrappers for the root build and all the samples during execution of the default 'wrapper' task. // Run './gradlew wrapper --gradle-version ' to update all the wrappers. @@ -49,14 +52,13 @@ ext { kotlinTestAnnotationsCommonModule="org.jetbrains.kotlin:kotlin-test-annotations-common:${kotlinStdlibVersion}:sources" kotlinReflectModule="org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}" kotlinScriptRuntimeModule="org.jetbrains.kotlin:kotlin-script-runtime:${kotlinVersion}" - kotlinNativeSharedModule="org.jetbrains.kotlin:kotlin-native-shared:$sharedVersion" - konanVersionFull = KonanVersionGeneratedKt.getCurrentKonanVersion() + konanVersionFull = KonanVersion.Companion.CURRENT if (konanVersionFull.meta == MetaVersion.RELEASE) { gradlePluginVersion = kotlinVersion } else { - gradlePluginVersion = "$kotlinVersion-native-${konanVersionFull.toString()}" + gradlePluginVersion = "$kotlinVersion-native-${KonanVersion.Companion.CURRENT.toString()}" } } @@ -75,9 +77,6 @@ allprojects { maven { url kotlinCompilerRepo } - maven { - url sharedRepo - } } setupHostAndTarget() @@ -205,8 +204,8 @@ dependencies { } } -task versionJar { - dependsOn gradle.includedBuild('kotlin-native-version').task(':jar') +task sharedJar { + dependsOn gradle.includedBuild('kotlin-native-shared').task(':jar') } task gradlePluginJar { @@ -263,7 +262,7 @@ task distCompiler(type: Copy) { dependsOn ':backend.native:jar' dependsOn ':utilities:jar' dependsOn ':klib:jar' - dependsOn ':versionJar' + dependsOn ':sharedJar' destinationDir distDir @@ -323,7 +322,7 @@ task distCompiler(type: Copy) { into('konan/lib') } - from(file("${gradle.includedBuild('kotlin-native-version').projectDir}/build/libs")) { + from(file("${gradle.includedBuild('kotlin-native-shared').projectDir}/build/libs")) { into('konan/lib') } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index e40292be8ec..d0dc1e78566 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -21,15 +21,12 @@ buildscript { apply(from = "$rootBuildDirectory/gradle/kotlinGradlePlugin.gradle") } - val buildKotlinCompilerRepo: String by project -val sharedRepo: String by project val repos = listOf( buildKotlinCompilerRepo, "https://cache-redirector.jetbrains.com/maven-central", "https://kotlin.bintray.com/kotlinx", - sharedRepo ) allprojects { diff --git a/buildSrc/plugins/build.gradle b/buildSrc/plugins/build.gradle index 82996c1b9d3..4659de6c244 100644 --- a/buildSrc/plugins/build.gradle +++ b/buildSrc/plugins/build.gradle @@ -38,19 +38,10 @@ dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" compile "org.jetbrains.kotlin:kotlin-reflect:$buildKotlinVersion" compile group: 'com.ullink.slack', name: 'simpleslackapi', version: '0.6.0' + // An artifact from the included build 'shared' cannot be used here due to https://github.com/gradle/gradle/issues/3768 + // TODO: Remove this hack when the bug is fixed + compile project(':shared') compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.10.0" - - // Support composite build against kotlin-native-shared. - // Gradle cannot substitute a dependency in buildSrc by an included build. - // See: https://github.com/gradle/gradle/issues/3768. - // Thus we have to compile sources of the included shared once more to use it in buildSrc. - if (project.hasProperty("sharedProjectPath")) { - compile project(':shared') - } else { - // If there is no composite build against shared, add a dependency on a published jar. - compile "org.jetbrains.kotlin:kotlin-native-shared:$sharedVersion" - } - } rootProject.dependencies { diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index b0728b4268a..9b10c7ee710 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -14,19 +14,5 @@ * limitations under the License. */ -import java.util.* - include(":plugins") - -// Read properties from the root gradle.properties to allow setting path to K/N shared there. -// We cannot store these properties here so we have to read them once more during configuration of projects. -val rootProperties = Properties().apply { - file("../gradle.properties").inputStream().use { load(it) } -} - -// Property specified using a command line option. -val sharedProjectPath: String? by settings - -if (sharedProjectPath != null || rootProperties.containsKey("sharedProjectPath")) { - include(":shared") -} \ No newline at end of file +include(":shared") \ No newline at end of file diff --git a/buildSrc/shared/build.gradle b/buildSrc/shared/build.gradle new file mode 100644 index 00000000000..1507eb816bc --- /dev/null +++ b/buildSrc/shared/build.gradle @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +buildscript { + ext { + gradleProperties = new Properties() + gradleProperties.load(new FileInputStream("$projectDir/../../gradle.properties")) + buildKotlinVersion = gradleProperties."buildKotlinVersion" + buildKotlinCompilerRepo = gradleProperties."buildKotlinCompilerRepo" + } + + apply from: '../../gradle/kotlinGradlePlugin.gradle' +} + +apply plugin: 'kotlin' + +// We reuse the source code from the Project in buildSrc. +sourceSets.main.kotlin.srcDirs = ["$projectDir/../../shared/src/main/kotlin"] + +repositories { + maven { + url buildKotlinCompilerRepo + } +} +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" +} + +rootProject.dependencies { + runtime project(path) +} diff --git a/cmd/run_konan b/cmd/run_konan index 7eeee03dd3d..5328c6f8d95 100755 --- a/cmd/run_konan +++ b/cmd/run_konan @@ -85,14 +85,13 @@ KOTLIN_SCRIPT_RUNTIME_JAR="${KONAN_HOME}/konan/lib/kotlin-script-runtime.jar" STUB_GENERATOR_JAR="${KONAN_HOME}/konan/lib/StubGenerator.jar" INTEROP_INDEXER_JAR="${KONAN_HOME}/konan/lib/Indexer.jar" INTEROP_JAR="${KONAN_HOME}/konan/lib/Runtime.jar" -SHARED_JAR="${KONAN_HOME}/konan/lib/kotlin-native-shared.jar" +SHARED_JAR="${KONAN_HOME}/konan/lib/shared.jar" EXTRACTED_METADATA_JAR="${KONAN_HOME}/konan/lib/konan.metadata.jar" EXTRACTED_SERIALIZER_JAR="${KONAN_HOME}/konan/lib/konan.serializer.jar" KLIB_JAR="${KONAN_HOME}/konan/lib/klib.jar" UTILITIES_JAR="${KONAN_HOME}/konan/lib/utilities.jar" TROVE_JAR="${KONAN_HOME}/konan/lib/trove4j.jar" -VERSION_JAR="${KONAN_HOME}/konan/lib/version.jar" -KONAN_CLASSPATH="$KOTLIN_JAR:$KOTLIN_STDLIB_JAR:$KOTLIN_REFLECT_JAR:$KOTLIN_SCRIPT_RUNTIME_JAR:$INTEROP_JAR:$STUB_GENERATOR_JAR:$INTEROP_INDEXER_JAR:$KONAN_JAR:$KLIB_JAR:$UTILITIES_JAR:$SHARED_JAR:$EXTRACTED_METADATA_JAR:$EXTRACTED_SERIALIZER_JAR:$TROVE_JAR:$VERSION_JAR" +KONAN_CLASSPATH="$KOTLIN_JAR:$KOTLIN_STDLIB_JAR:$KOTLIN_REFLECT_JAR:$KOTLIN_SCRIPT_RUNTIME_JAR:$INTEROP_JAR:$STUB_GENERATOR_JAR:$INTEROP_INDEXER_JAR:$KONAN_JAR:$KLIB_JAR:$UTILITIES_JAR:$SHARED_JAR:$EXTRACTED_METADATA_JAR:$EXTRACTED_SERIALIZER_JAR:$TROVE_JAR" TOOL_CLASS=org.jetbrains.kotlin.cli.utilities.MainKt LIBCLANG_DISABLE_CRASH_RECOVERY=1 \ diff --git a/cmd/run_konan.bat b/cmd/run_konan.bat index fbc368a0c3e..e33714ddbe9 100644 --- a/cmd/run_konan.bat +++ b/cmd/run_konan.bat @@ -54,7 +54,7 @@ if not "!ARG!" == "" ( set "NATIVE_LIB=%_KONAN_HOME%\konan\nativelib" set "KONAN_LIB=%_KONAN_HOME%\konan\lib" -set "SHARED_JAR=%KONAN_LIB%\kotlin-native-shared.jar" +set "SHARED_JAR=%KONAN_LIB%\shared.jar" set "EXTRACTED_METADATA_JAR=%KONAN_LIB%\konan.metadata.jar" set "EXTRACTED_SERIALIZER_JAR=%KONAN_LIB%\konan.serializer.jar" set "INTEROP_INDEXER_JAR=%KONAN_LIB%\Indexer.jar" @@ -68,9 +68,8 @@ set "KOTLIN_SCRIPT_RUNTIME_JAR=%KONAN_LIB%\kotlin-script-runtime.jar" set "STUB_GENERATOR_JAR=%KONAN_LIB%\StubGenerator.jar" set "UTILITIES_JAR=%KONAN_LIB%\utilities.jar" set TROVE_JAR="%KONAN_LIB%\lib\trove4j.jar" -set "VERSION_JAR=%KONAN_LIB%\version.jar" -set "KONAN_CLASSPATH=%KOTLIN_JAR%;%KOTLIN_STDLIB_JAR%;%KOTLIN_REFLECT_JAR%;%KOTLIN_SCRIPT_RUNTIME_JAR%;%INTEROP_RUNTIME_JAR%;%KONAN_JAR%;%STUB_GENERATOR_JAR%;%INTEROP_INDEXER_JAR%;%SHARED_JAR%;%EXTRACTED_METADATA_JAR%;%EXTRACTED_SERIALIZER_JAR%;%KLIB_JAR%;%UTILITIES_JAR%;%TROVE_JAR%;%VERSION_JAR%" +set "KONAN_CLASSPATH=%KOTLIN_JAR%;%KOTLIN_STDLIB_JAR%;%KOTLIN_REFLECT_JAR%;%KOTLIN_SCRIPT_RUNTIME_JAR%;%INTEROP_RUNTIME_JAR%;%KONAN_JAR%;%STUB_GENERATOR_JAR%;%INTEROP_INDEXER_JAR%;%SHARED_JAR%;%EXTRACTED_METADATA_JAR%;%EXTRACTED_SERIALIZER_JAR%;%KLIB_JAR%;%UTILITIES_JAR%;%TROVE_JAR%" set JAVA_OPTS=-ea ^ -Xmx3G ^ diff --git a/extracted/konan.serializer/build.gradle b/extracted/konan.serializer/build.gradle index 1d01f4293ef..8826e550619 100644 --- a/extracted/konan.serializer/build.gradle +++ b/extracted/konan.serializer/build.gradle @@ -33,15 +33,11 @@ repositories { maven { url buildKotlinCompilerRepo } - maven { - url sharedRepo - } } dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" - compile "org.jetbrains.kotlin:kotlin-native-shared:$sharedVersion" - compile "org.jetbrains.kotlin:kotlin-native-version" + compile "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" compile "org.jetbrains.kotlin:kotlin-compiler:$kotlinVersion" compile "org.jetbrains.kotlin:konan.metadata:$konanVersion" } diff --git a/gradle.properties b/gradle.properties index 01cf7c74dc9..cf6ec4015d4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,11 +23,7 @@ kotlinVersion=1.3.50-dev-560 kotlinStdlibRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_dev_Compiler),number:1.3.50-dev-560,branch:default:true,pinned:true/artifacts/content/maven kotlinStdlibVersion=1.3.50-dev-560 testKotlinCompilerVersion=1.3.50-dev-560 -# See https://teamcity.jetbrains.com/project.html?projectId=Kotlin_KotlinNativeShared&tab=projectOverview -sharedRepo=https://dl.bintray.com/jetbrains/kotlin-native-dependencies -sharedVersion=1.0-dev-99 konanVersion=1.3.50 org.gradle.jvmargs='-Dfile.encoding=UTF-8' org.gradle.workers.max=4 #kotlinProjectPath=/Users/jetbrains/IdeaProjects/kotlin -#sharedProjectPath=/Users/jetbrains/IdeaProjects/kotlin-native-shared diff --git a/gradle/kotlinNativeShared.gradle b/gradle/kotlinNativeShared.gradle deleted file mode 100644 index b96b58dbb1b..00000000000 --- a/gradle/kotlinNativeShared.gradle +++ /dev/null @@ -1,19 +0,0 @@ -if (!hasProperty('sharedVersion')) { - throw new GradleException('Please ensure the "sharedVersion" property is defined before applying this script.') -} - -if (!hasProperty('sharedRepo')) { - throw new GradleException('Please ensure the "sharedRepo" property is defined before applying this script.') -} - -project.buildscript { - repositories { - maven { url sharedRepo } - } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-native-shared:$sharedVersion" - // An included build with generated Kotlin/Native version. - classpath "org.jetbrains.kotlin:kotlin-native-version" - } -} diff --git a/libclangext/build.gradle b/libclangext/build.gradle index 42e1ea3ff16..32d3629a37e 100644 --- a/libclangext/build.gradle +++ b/libclangext/build.gradle @@ -20,10 +20,10 @@ buildscript { ext.rootBuildDirectory = file('..') apply from: "$rootBuildDirectory/gradle/kotlinGradlePlugin.gradle" - apply from: "$rootBuildDirectory/gradle/kotlinNativeShared.gradle" dependencies { classpath "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" + classpath "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" } } import org.jetbrains.kotlin.konan.target.ClangArgs diff --git a/llvmCoverageMappingC/build.gradle b/llvmCoverageMappingC/build.gradle index c5381693635..11ae9ebfb8e 100644 --- a/llvmCoverageMappingC/build.gradle +++ b/llvmCoverageMappingC/build.gradle @@ -17,8 +17,16 @@ apply plugin: "cpp" apply plugin: "c" -apply from: "${rootProject.projectDir}/gradle/kotlinGradlePlugin.gradle" +buildscript { + ext.rootBuildDirectory = file('..') + apply from: "$rootBuildDirectory/gradle/kotlinGradlePlugin.gradle" + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" + classpath "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" + } +} import org.jetbrains.kotlin.konan.target.ClangArgs model { diff --git a/llvmDebugInfoC/build.gradle b/llvmDebugInfoC/build.gradle index fa41d6195eb..58009e11305 100644 --- a/llvmDebugInfoC/build.gradle +++ b/llvmDebugInfoC/build.gradle @@ -21,10 +21,10 @@ buildscript { ext.rootBuildDirectory = file('..') apply from: "$rootBuildDirectory/gradle/kotlinGradlePlugin.gradle" - apply from: "$rootBuildDirectory/gradle/kotlinNativeShared.gradle" dependencies { classpath "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" + classpath "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" } } import org.jetbrains.kotlin.konan.target.ClangArgs diff --git a/settings.gradle b/settings.gradle index 7dd9b9548d8..685d04a4119 100644 --- a/settings.gradle +++ b/settings.gradle @@ -33,7 +33,7 @@ include ':utilities' //include ':tools:benchmarksAnalyzer' include ':platformLibs' -includeBuild 'version' +includeBuild 'shared' includeBuild 'extracted/konan.metadata' includeBuild 'extracted/konan.serializer' includeBuild 'tools/kotlin-native-gradle-plugin' diff --git a/shared/build.gradle b/shared/build.gradle index 7dc17f5d62e..0a8aca9ef1e 100644 --- a/shared/build.gradle +++ b/shared/build.gradle @@ -1,3 +1,65 @@ -plugins { - id 'base' -} \ No newline at end of file +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import org.jetbrains.kotlin.VersionGenerator + +buildscript { + ext.rootBuildDirectory = file('..') + + apply from: "$rootBuildDirectory/gradle/loadRootProperties.gradle" + apply from: "$rootBuildDirectory/gradle/kotlinGradlePlugin.gradle" +} + +apply plugin: 'kotlin' + +group = 'org.jetbrains.kotlin' +version = konanVersion + +repositories { + mavenCentral() + maven { + url buildKotlinCompilerRepo + } +} + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: drop generation of KonanVersion! +task generateCompilerVersion(type: VersionGenerator){} + +sourceSets { + main.kotlin { + srcDir 'src/main/kotlin' + srcDir 'src/library/kotlin' + srcDir generateCompilerVersion.versionSourceDirectory + } +} + +compileKotlin { + dependsOn('generateCompilerVersion') +} + +clean { + doFirst { + if (generateCompilerVersion.versionSourceDirectory.exists()) + generateCompilerVersion.versionSourceDirectory.delete() + } +} + +jar { + archiveName = "shared.jar" +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" +} diff --git a/version/buildSrc/build.gradle b/shared/buildSrc/build.gradle similarity index 99% rename from version/buildSrc/build.gradle rename to shared/buildSrc/build.gradle index 7da45bfe2eb..322e6303591 100644 --- a/version/buildSrc/build.gradle +++ b/shared/buildSrc/build.gradle @@ -20,6 +20,7 @@ buildscript { } + apply plugin: 'kotlin' repositories { diff --git a/shared/buildSrc/src/main/kotlin/org/jetbrains/kotlin/VersionGenerator.kt b/shared/buildSrc/src/main/kotlin/org/jetbrains/kotlin/VersionGenerator.kt new file mode 100644 index 00000000000..851cb5658c7 --- /dev/null +++ b/shared/buildSrc/src/main/kotlin/org/jetbrains/kotlin/VersionGenerator.kt @@ -0,0 +1,115 @@ +package org.jetbrains.kotlin + +import groovy.lang.Closure +import org.gradle.api.DefaultTask +import org.gradle.api.Task +import org.gradle.api.tasks.* +import java.io.File + + +open class VersionGenerator: DefaultTask() { + @OutputDirectory + val versionSourceDirectory = project.file("build/generated") + @OutputFile + val versionFile:File = project.file("${versionSourceDirectory.path}/org/jetbrains/kotlin/konan/KonanVersion.kt") + + val konanVersion: String + @Input get() = project.properties["konanVersion"].toString() + + val buildNumber: String? + // TeamCity passes all configuration parameters into a build script as project properties. + // Thus we can use them here instead of environment variables. + @Optional @Input get() = project.findProperty("build.number")?.toString() + + val meta: String + @Input get() = project.properties["konanMetaVersion"]?.let { + "MetaVersion.${it.toString().toUpperCase()}" + } ?: "MetaVersion.DEV" + + + override fun configure(closure: Closure<*>): Task { + val result = super.configure(closure) + doFirst { + val content = buildString { + operator fun String.unaryPlus() = this@buildString.append(this) + val version = konanVersion.split(".") + val major = version[0].toInt() + val minor = version[1].toInt() + val maintenance = if (version.size > 2) version[2].toInt() else 0 + project.logger.info("BUILD_NUMBER: $buildNumber") + val build = buildNumber?.let { + it.split("-")[2].toInt() //7-dev-buildcount + }?: -1 + + + """ + |package org.jetbrains.kotlin.konan + | + |import java.io.Serializable + | + |interface KonanVersion : Serializable { + | val meta: MetaVersion + | val major: Int + | val minor: Int + | val maintenance: Int + | val build: Int + | + | fun toString(showMeta: Boolean, showBuild: Boolean): String + | + | companion object { + | val CURRENT = KonanVersionImpl($meta, $major, $minor, $maintenance, $build) + | } + |} + | + |data class KonanVersionImpl( + | override val meta: MetaVersion = MetaVersion.DEV, + | override val major: Int, + | override val minor: Int, + | override val maintenance: Int, + | override val build: Int + |) : KonanVersion { + | + | override fun toString(showMeta: Boolean, showBuild: Boolean) = buildString { + | append(major) + | append('.') + | append(minor) + | if (maintenance != 0) { + | append('.') + | append(maintenance) + | } + | if (showMeta) { + | append('-') + | append(meta.metaString) + | } + | if (showBuild && build != -1) { + | append('-') + | append(build) + | } + | } + | + | private val isRelease: Boolean + | get() = meta == MetaVersion.RELEASE + | + | private val versionString by lazy { toString(!isRelease, !isRelease) } + | + | override fun toString() = versionString + |} + |fun String.parseKonanVersion(): KonanVersion { + | val (major, minor, maintenance, meta, build) = + | Regex(""${'"'}([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-(\p{Alpha}\p{Alnum}*))?(?:-([0-9]+))?""${'"'}) + | .matchEntire(this)!!.destructured + | return KonanVersionImpl( + | MetaVersion.findAppropriate(meta), + | major.toInt(), + | minor.toInt(), + | maintenance.toIntOrNull() ?: 0, + | build.toIntOrNull() ?: -1) + |} + """.trimMargin() + } + versionFile.printWriter().use { + it.println(content) + } + } + return result + } +} diff --git a/shared/settings.gradle b/shared/settings.gradle index e69de29bb2d..b536af31c76 100644 --- a/shared/settings.gradle +++ b/shared/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "kotlin-native-shared" \ No newline at end of file diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibrary.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibrary.kt new file mode 100644 index 00000000000..b99db471866 --- /dev/null +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibrary.kt @@ -0,0 +1,73 @@ +package org.jetbrains.kotlin.konan.library + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.properties.Properties +import org.jetbrains.kotlin.konan.properties.propertyList + +const val KLIB_PROPERTY_ABI_VERSION = "abi_version" +const val KLIB_PROPERTY_COMPILER_VERSION = "compiler_version" +const val KLIB_PROPERTY_DEPENDENCY_VERSION = "dependency_version" +const val KLIB_PROPERTY_LIBRARY_VERSION = "library_version" +const val KLIB_PROPERTY_UNIQUE_NAME = "unique_name" +const val KLIB_PROPERTY_LINKED_OPTS = "linkerOpts" +const val KLIB_PROPERTY_DEPENDS = "depends" +const val KLIB_PROPERTY_INTEROP = "interop" +const val KLIB_PROPERTY_PACKAGE = "package" +const val KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS = "exportForwardDeclarations" +const val KLIB_PROPERTY_INCLUDED_HEADERS = "includedHeaders" + +/** + * An abstraction for getting access to the information stored inside of Kotlin/Native library. + */ +interface KonanLibrary { + + val libraryName: String + val libraryFile: File + + // Whether this library is default (provided by Kotlin/Native distribution)? + val isDefault: Boolean + + // Properties: + val manifestProperties: Properties + val linkerOpts: List + + // Paths: + val bitcodePaths: List + val includedPaths: List + + val targetList: List + val versions: KonanLibraryVersioning + + val dataFlowGraph: ByteArray? + val moduleHeaderData: ByteArray + fun packageMetadataParts(fqName: String): Set + fun packageMetadata(fqName: String, partName: String): ByteArray + + val irHeader: ByteArray? + fun irDeclaration(index: Long, isLocal: Boolean): ByteArray +} + +val KonanLibrary.uniqueName + get() = manifestProperties.getProperty(KLIB_PROPERTY_UNIQUE_NAME)!! + +val KonanLibrary.unresolvedDependencies: List + get() = manifestProperties.propertyList(KLIB_PROPERTY_DEPENDS) + .map { + UnresolvedLibrary(it, manifestProperties.getProperty("dependency_version_$it")) + } + +val KonanLibrary.isInterop + get() = manifestProperties.getProperty(KLIB_PROPERTY_INTEROP) == "true" + +val KonanLibrary.packageFqName + get() = manifestProperties.getProperty(KLIB_PROPERTY_PACKAGE) + +val KonanLibrary.exportForwardDeclarations + get() = manifestProperties.getProperty(KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS) + .split(' ').asSequence() + .map { it.trim() } + .filter { it.isNotEmpty() } + .toList() + +val KonanLibrary.includedHeaders + get() = manifestProperties.getProperty(KLIB_PROPERTY_INCLUDED_HEADERS).split(' ') diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt new file mode 100644 index 00000000000..a860f255c53 --- /dev/null +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt @@ -0,0 +1,72 @@ +/** + * Copyright 2010-2019 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.library + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.target.KonanTarget + +/** + * This scheme describes the Kotlin/Native Library (KLIB) layout. + */ +interface KonanLibraryLayout { + + val libraryName: String + + val libDir: File + val target: KonanTarget? + // This is a default implementation. Can't make it an assignment. + get() = null + + val manifestFile + get() = File(libDir, "manifest") + val resourcesDir + get() = File(libDir, "resources") + + val targetsDir + get() = File(libDir, "targets") + val targetDir + get() = File(targetsDir, target!!.visibleName) + + val kotlinDir + get() = File(targetDir, "kotlin") + val nativeDir + get() = File(targetDir, "native") + val includedDir + get() = File(targetDir, "included") + + val linkdataDir + get() = File(libDir, "linkdata") + val moduleHeaderFile + get() = File(linkdataDir, "module") + val dataFlowGraphFile + get() = File(linkdataDir, "module_data_flow_graph") + + fun packageFragmentsDir(packageName: String) + = File(linkdataDir, if (packageName == "") "root_package" else "package_$packageName") + + fun packageFragmentFile(packageFqName: String, partName: String) = + File(packageFragmentsDir(packageFqName), "$partName$KLIB_METADATA_FILE_EXTENSION_WITH_DOT") + val irDir + get() = File(libDir, "ir") + val irFile + get() = File(irDir, "irCombined.knd") + val irHeader + get() = File(irDir, "irHeaders.kni") + val irIndex: File + get() = File(irDir, "uniqIdTableDump.txt") + +} diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt new file mode 100644 index 00000000000..d445fd20c0d --- /dev/null +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt @@ -0,0 +1,49 @@ +package org.jetbrains.kotlin.konan.library + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.file.file +import org.jetbrains.kotlin.konan.file.withMutableZipFileSystem +import org.jetbrains.kotlin.konan.library.impl.DefaultMetadataReaderImpl +import org.jetbrains.kotlin.konan.library.impl.KonanLibraryImpl +import org.jetbrains.kotlin.konan.library.impl.zippedKonanLibraryChecks +import org.jetbrains.kotlin.konan.target.KonanTarget + +const val KLIB_FILE_EXTENSION = "klib" +const val KLIB_FILE_EXTENSION_WITH_DOT = ".$KLIB_FILE_EXTENSION" + +const val KLIB_METADATA_FILE_EXTENSION = "knm" +const val KLIB_METADATA_FILE_EXTENSION_WITH_DOT = ".$KLIB_METADATA_FILE_EXTENSION" + +fun File.unpackZippedKonanLibraryTo(newDir: File) { + + // First, run validity checks for the given KLIB file. + zippedKonanLibraryChecks(this) + + if (newDir.exists) { + if (newDir.isDirectory) + newDir.deleteRecursively() + else + newDir.delete() + } + + this.withMutableZipFileSystem { + it.file("/").recursiveCopyTo(newDir) + } + check(newDir.exists) { "Could not unpack $this as $newDir." } +} + +val List.toUnresolvedLibraries + get() = this.map { + + val version = it.substringAfterLast('@', "") + .let { if (it.isEmpty()) null else it} + val name = it.substringBeforeLast('@') + UnresolvedLibrary(name, version) + } + +fun createKonanLibrary( + libraryFile: File, + target: KonanTarget? = null, + isDefault: Boolean = false, + metadataReader: MetadataReader = DefaultMetadataReaderImpl +): KonanLibrary = KonanLibraryImpl(libraryFile, target, isDefault, metadataReader) diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryVersioning.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryVersioning.kt new file mode 100644 index 00000000000..bb7ddf0b96d --- /dev/null +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryVersioning.kt @@ -0,0 +1,28 @@ +package org.jetbrains.kotlin.konan.library + +import org.jetbrains.kotlin.konan.KonanVersion +import org.jetbrains.kotlin.konan.parseKonanVersion +import org.jetbrains.kotlin.konan.KonanAbiVersion +import org.jetbrains.kotlin.konan.properties.Properties +import org.jetbrains.kotlin.konan.parseKonanAbiVersion + + +data class KonanLibraryVersioning( + val libraryVersion: String?, + val compilerVersion: KonanVersion?, + val abiVersion: KonanAbiVersion? +) + +fun Properties.writeKonanLibraryVersioning(versions: KonanLibraryVersioning) { + versions.abiVersion ?. let { this.setProperty(KLIB_PROPERTY_ABI_VERSION, it.toString()) } + versions.libraryVersion ?. let { this.setProperty(KLIB_PROPERTY_LIBRARY_VERSION, it) } + versions.compilerVersion ?. let { this.setProperty(KLIB_PROPERTY_COMPILER_VERSION, "${versions.compilerVersion.toString(true, true)}") } +} + +fun Properties.readKonanLibraryVersioning(): KonanLibraryVersioning { + val abiVersion = this.getProperty(KLIB_PROPERTY_ABI_VERSION)?.parseKonanAbiVersion() + val libraryVersion = this.getProperty(KLIB_PROPERTY_LIBRARY_VERSION) + val compilerVersion = this.getProperty(KLIB_PROPERTY_COMPILER_VERSION)?.parseKonanVersion() + + return KonanLibraryVersioning(abiVersion = abiVersion, libraryVersion = libraryVersion, compilerVersion = compilerVersion) +} diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt new file mode 100644 index 00000000000..57ae74f6e12 --- /dev/null +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt @@ -0,0 +1,6 @@ +package org.jetbrains.kotlin.konan.library + +interface MetadataReader { + fun loadSerializedModule(libraryLayout: KonanLibraryLayout): ByteArray + fun loadSerializedPackageFragment(libraryLayout: KonanLibraryLayout, fqName: String, partName: String): ByteArray +} diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt new file mode 100644 index 00000000000..0581220ff72 --- /dev/null +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt @@ -0,0 +1,244 @@ +package org.jetbrains.kotlin.konan.library + +import org.jetbrains.kotlin.konan.KonanAbiVersion +import org.jetbrains.kotlin.konan.KonanVersion +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.library.impl.KonanLibraryImpl +import org.jetbrains.kotlin.konan.target.Distribution +import org.jetbrains.kotlin.konan.target.KonanTarget +import org.jetbrains.kotlin.konan.util.* + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove the constants below: +const val KONAN_STDLIB_NAME = "stdlib" + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this interface! +interface SearchPathResolver : WithLogger { + val searchRoots: List + fun resolutionSequence(givenPath: String): Sequence + fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean = false): KonanLibraryImpl + fun resolve(givenPath: String): KonanLibraryImpl + fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List +} + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this interface! +interface SearchPathResolverWithTarget: SearchPathResolver { + val target: KonanTarget + val knownAbiVersions: List? + val knownCompilerVersions: List? +} + +fun defaultResolver( + repositories: List, + target: KonanTarget, + distribution: Distribution = Distribution() +): SearchPathResolverWithTarget = defaultResolver(repositories, emptyList(), target, distribution) + +fun defaultResolver( + repositories: List, + directLibs: List, + target: KonanTarget, + distribution: Distribution, + logger: Logger = ::dummyLogger, + skipCurrentDir: Boolean = false, + compatibleCompilerVersions: List = emptyList() +): SearchPathResolverWithTarget = KonanLibraryProperResolver( + repositories, + directLibs, + target, + listOf(KonanAbiVersion.CURRENT), + listOf(KonanVersion.CURRENT) + compatibleCompilerVersions, + distribution.klib, + distribution.localKonanDir.absolutePath, + skipCurrentDir, + logger) + +fun resolverByName( + repositories: List, + directLibs: List = emptyList(), + distributionKlib: String? = null, + localKonanDir: String? = null, + skipCurrentDir: Boolean = false +): SearchPathResolver = KonanLibrarySearchPathResolver(repositories, directLibs, distributionKlib, localKonanDir, skipCurrentDir) + +internal open class KonanLibrarySearchPathResolver( + repositories: List, + directLibs: List, + val distributionKlib: String?, + val localKonanDir: String?, + val skipCurrentDir: Boolean, + override val logger:Logger = ::dummyLogger +) : SearchPathResolver { + + val localHead: File? + get() = localKonanDir?.File()?.klib + + val distHead: File? + get() = distributionKlib?.File()?.child("common") + + open val distPlatformHead: File? = null + + val currentDirHead: File? + get() = if (!skipCurrentDir) File.userDir else null + + private val repoRoots: List by lazy { repositories.map { File(it) } } + + private val directLibraries: List by lazy { + directLibs.mapNotNull { found(File(it)) }.map { createKonanLibrary(it, null) } + } + + // This is the place where we specify the order of library search. + override val searchRoots: List by lazy { + (listOf(currentDirHead) + repoRoots + listOf(localHead, distHead, distPlatformHead)).filterNotNull() + } + + private fun found(candidate: File): File? { + fun check(file: File): Boolean = + file.exists && (file.isFile || File(file, "manifest").exists) + + val noSuffix = File(candidate.path.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT)) + val withSuffix = File(candidate.path.suffixIfNot(KLIB_FILE_EXTENSION_WITH_DOT)) + return when { + check(withSuffix) -> withSuffix + check(noSuffix) -> noSuffix + else -> null + } + } + + override fun resolutionSequence(givenPath: String): Sequence { + val given = File(givenPath) + val sequence = if (given.isAbsolute) { + sequenceOf(found(given)) + } else { + // Search among user-provided libraries by unique name. + // It's a workaround for maven publication. When a library is published without Gradle metadata, + // it has a complex file name (e.g. foo-macos_x64-1.0.klib). But a dependency on this lib in manifests + // of other libs uses its unique name written in the manifest (i.e just 'foo'). So we cannot resolve this + // library by its filename. But we have this library's file (we've downloaded it using maven dependency + // resolution) so we can pass it to the compiler directly. This code takes this into account and looks for + // a library dependencies also in libs passed to the compiler as files (passed to the resolver as the + // 'directLibraries' property). + val directLibs = directLibraries.asSequence().filter { + it.uniqueName == givenPath + }.map { + it.libraryFile + } + // Search among libraries in repositoreis by library filename. + val repoLibs = searchRoots.asSequence().map { + found(File(it, givenPath)) + } + directLibs + repoLibs + } + return sequence.filterNotNull() + } + + override fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean): KonanLibraryImpl { + val givenPath = unresolved.path + return resolutionSequence(givenPath).firstOrNull() ?. let { + createKonanLibrary(it, null, isDefaultLink) as KonanLibraryImpl + } ?: error("Could not find \"$givenPath\" in ${searchRoots.map { it.absolutePath }}.") + } + + override fun resolve(givenPath: String) = resolve(UnresolvedLibrary(givenPath, null), false) + + + private val File.klib + get() = File(this, "klib") + + // The libraries from the default root are linked automatically. + val defaultRoots: List + get() = listOfNotNull(distHead, distPlatformHead).filter { it.exists } + + override fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List { + + val result = mutableListOf() + + if (!noStdLib) { + result.add(resolve(UnresolvedLibrary(KONAN_STDLIB_NAME, null), true)) + } + + if (!noDefaultLibs) { + val defaultLibs = defaultRoots.flatMap { it.listFiles } + .filterNot { it.name.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT) == KONAN_STDLIB_NAME } + .map { UnresolvedLibrary(it.absolutePath, null) } + .map {resolve(it, isDefaultLink = true) } + result.addAll(defaultLibs) + } + + return result + } +} + +internal class KonanLibraryProperResolver( + repositories: List, + directLibs: List, + override val target: KonanTarget, + override val knownAbiVersions: List?, + override val knownCompilerVersions: List?, + distributionKlib: String?, + localKonanDir: String?, + skipCurrentDir: Boolean, + override val logger: Logger = ::dummyLogger +) : KonanLibrarySearchPathResolver(repositories, directLibs, distributionKlib, localKonanDir, skipCurrentDir, logger), + SearchPathResolverWithTarget +{ + override val distPlatformHead: File? + get() = distributionKlib?.File()?.child("platform")?.child(target.visibleName) + + override fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean): KonanLibraryImpl { + val givenPath = unresolved.path + val fileSequence = resolutionSequence(givenPath) + val matching = fileSequence.map { createKonanLibrary(it, target, isDefaultLink) as KonanLibraryImpl } + .map { it.takeIf { libraryMatch(it, unresolved) } } + .filterNotNull() + + return matching.firstOrNull() ?: error("Could not find \"$givenPath\" in ${searchRoots.map { it.absolutePath }}.") + } +} + +internal fun SearchPathResolverWithTarget.libraryMatch(candidate: KonanLibraryImpl, unresolved: UnresolvedLibrary): Boolean { + val resolverTarget = this.target + val candidatePath = candidate.libraryFile.absolutePath + + val candidateCompilerVersion = candidate.versions.compilerVersion + val candidateAbiVersion = candidate.versions.abiVersion + val candidateLibraryVersion = candidate.versions.libraryVersion + + if (!candidate.targetList.contains(resolverTarget.visibleName)) { + logger("skipping $candidatePath. The target doesn't match. Expected '$resolverTarget', found ${candidate.targetList}") + return false + } + + val abiVersionMatch = candidateAbiVersion != null && + knownAbiVersions != null && + knownAbiVersions!!.contains(candidateAbiVersion) + + val compilerVersionMatch = candidateCompilerVersion != null && + knownCompilerVersions != null && + knownCompilerVersions!!.any { it.compatible(candidateCompilerVersion) } + + if (!abiVersionMatch && !compilerVersionMatch) { + logger("skipping $candidatePath. The abi versions don't match. Expected '${knownAbiVersions}', found '${candidateAbiVersion}'") + + if (knownCompilerVersions != null) { + val expected = knownCompilerVersions?.map { it.toString(false, false) } + val found = candidateCompilerVersion?.toString(true, true) + logger("The compiler versions don't match either. Expected '${expected}', found '${found}'") + } + + return false + } + + if (candidateLibraryVersion != unresolved.libraryVersion && + candidateLibraryVersion != null && + unresolved.libraryVersion != null) { + logger("skipping $candidatePath. The library versions don't match. Expected '${unresolved.libraryVersion}', found '${candidateLibraryVersion}'") + return false + } + + return true +} + +private fun KonanVersion.compatible(other: KonanVersion) = + this.major == other.major + && this.minor == other.minor + && this.maintenance == other.maintenance diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/UnresolvedLibrary.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/UnresolvedLibrary.kt new file mode 100644 index 00000000000..d3ef4766f18 --- /dev/null +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/UnresolvedLibrary.kt @@ -0,0 +1,10 @@ +package org.jetbrains.kotlin.konan.library + +data class UnresolvedLibrary( + val path: String, + val libraryVersion: String?) { + + fun substitutePath(newPath: String): UnresolvedLibrary { + return UnresolvedLibrary(newPath, libraryVersion) + } +} \ No newline at end of file diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/CombinedIrFileAccessor.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/CombinedIrFileAccessor.kt new file mode 100644 index 00000000000..235f1312aa2 --- /dev/null +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/CombinedIrFileAccessor.kt @@ -0,0 +1,94 @@ +/** + * Copyright 2010-2019 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.library.impl + +import org.jetbrains.kotlin.konan.file.File +import java.io.RandomAccessFile +import java.nio.channels.FileChannel + +data class DeclarationId(val id: Long, val isLocal: Boolean) + +class CombinedIrFileReader(file: File) { + private val buffer = file.map(FileChannel.MapMode.READ_ONLY) + private val declarationToOffsetSize = mutableMapOf>() + + init { + val declarationsCount = buffer.int + for (i in 0 until declarationsCount) { + val id = buffer.long + val isLocal = buffer.int != 0 + val offset = buffer.int + val size = buffer.int + declarationToOffsetSize[DeclarationId(id, isLocal)] = offset to size + } + } + + fun declarationBytes(id: DeclarationId): ByteArray { + val offsetSize = declarationToOffsetSize[id] ?: throw Error("No declaration with $id here") + val result = ByteArray(offsetSize.second) + buffer.position(offsetSize.first) + buffer.get(result, 0, offsetSize.second) + return result + } +} + +private const val SINGLE_INDEX_RECORD_SIZE = 20 // sizeof(Long) + 3 * sizeof(Int). +private const val INDEX_HEADER_SIZE = 4 // sizeof(Int). + +class CombinedIrFileWriter(val declarationCount: Int) { + private var currentDeclaration = 0 + private var currentPosition = 0 + private val file = org.jetbrains.kotlin.konan.file.createTempFile("ir").deleteOnExit() + private val randomAccessFile = RandomAccessFile(file.path, "rw") + + init { + randomAccessFile.writeInt(declarationCount) + assert(randomAccessFile.filePointer.toInt() == INDEX_HEADER_SIZE) + for (i in 0 until declarationCount) { + randomAccessFile.writeLong(-1) // id + randomAccessFile.writeInt(-1) // isLocal + randomAccessFile.writeInt(-1) // offset + randomAccessFile.writeInt(-1) // size + } + currentPosition = randomAccessFile.filePointer.toInt() + assert(currentPosition == INDEX_HEADER_SIZE + SINGLE_INDEX_RECORD_SIZE * declarationCount) + } + + fun skipDeclaration() { + currentDeclaration++ + } + + fun addDeclaration(id: DeclarationId, bytes: ByteArray) { + randomAccessFile.seek((currentDeclaration * SINGLE_INDEX_RECORD_SIZE + INDEX_HEADER_SIZE).toLong()) + randomAccessFile.writeLong(id.id) + randomAccessFile.writeInt(if (id.isLocal) 1 else 0) + randomAccessFile.writeInt(currentPosition) + randomAccessFile.writeInt(bytes.size) + randomAccessFile.seek(currentPosition.toLong()) + randomAccessFile.write(bytes) + assert(randomAccessFile.filePointer < Int.MAX_VALUE.toLong()) + currentPosition = randomAccessFile.filePointer.toInt() + currentDeclaration++ + } + + fun finishWriting(): File { + assert(currentDeclaration == declarationCount) + randomAccessFile.close() + return file + } +} + diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt new file mode 100644 index 00000000000..5b1eb21e50c --- /dev/null +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt @@ -0,0 +1,14 @@ +package org.jetbrains.kotlin.konan.library.impl + +import org.jetbrains.kotlin.konan.library.KonanLibraryLayout +import org.jetbrains.kotlin.konan.library.MetadataReader + +internal object DefaultMetadataReaderImpl : MetadataReader { + + override fun loadSerializedModule(libraryLayout: KonanLibraryLayout): ByteArray = + libraryLayout.moduleHeaderFile.readBytes() + + override fun loadSerializedPackageFragment(libraryLayout: KonanLibraryLayout, fqName: String, partName: String): ByteArray = + libraryLayout.packageFragmentFile(fqName, partName).readBytes() + +} diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt new file mode 100644 index 00000000000..77bf2096ab8 --- /dev/null +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.library.impl + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.library.* +import org.jetbrains.kotlin.konan.properties.Properties +import org.jetbrains.kotlin.konan.properties.loadProperties +import org.jetbrains.kotlin.konan.properties.propertyList +import org.jetbrains.kotlin.konan.target.KonanTarget +import org.jetbrains.kotlin.konan.util.defaultTargetSubstitutions +import org.jetbrains.kotlin.konan.util.substitute + +class KonanLibraryImpl( + override val libraryFile: File, + internal val target: KonanTarget?, + override val isDefault: Boolean, + private val metadataReader: MetadataReader +) : KonanLibrary { + + // For the zipped libraries inPlace gives files from zip file system + // whereas realFiles extracts them to /tmp. + // For unzipped libraries inPlace and realFiles are the same + // providing files in the library directory. + + private val layout = createKonanLibraryLayout(libraryFile, target) + + override val libraryName: String by lazy { layout.inPlace { it.libraryName } } + + override val manifestProperties: Properties by lazy { + val properties = layout.inPlace { it.manifestFile.loadProperties() } + if (target != null) substitute(properties, defaultTargetSubstitutions(target)) + properties + } + + override val versions: KonanLibraryVersioning + get() = manifestProperties.readKonanLibraryVersioning() + + override val linkerOpts: List + get() = manifestProperties.propertyList(KLIB_PROPERTY_LINKED_OPTS) + + override val bitcodePaths: List + get() = layout.realFiles { (it.kotlinDir.listFilesOrEmpty + it.nativeDir.listFilesOrEmpty).map { it.absolutePath } } + + override val includedPaths: List + get() = layout.realFiles { it.includedDir.listFilesOrEmpty.map { it.absolutePath } } + + override val targetList by lazy { layout.inPlace { it.targetsDir.listFiles.map { it.name } } } + + override val dataFlowGraph by lazy { layout.inPlace { it.dataFlowGraphFile.let { if (it.exists) it.readBytes() else null } } } + + override val moduleHeaderData: ByteArray by lazy { layout.inPlace { metadataReader.loadSerializedModule(it) } } + + override fun packageMetadata(packageFqName: String, partName: String) = + layout.inPlace { metadataReader.loadSerializedPackageFragment(it, packageFqName, partName) } + + override fun packageMetadataParts(fqName: String): Set = + layout.inPlace { inPlaceLayout -> + val fileList = + inPlaceLayout.packageFragmentsDir(fqName) + .listFiles + .mapNotNull { + it.name + .substringBeforeLast(KLIB_METADATA_FILE_EXTENSION_WITH_DOT, missingDelimiterValue = "") + .takeIf { it.isNotEmpty() } + } + + fileList.toSortedSet().also { + require(it.size == fileList.size) { "Duplicated names: ${fileList.groupingBy { it }.eachCount().filter { (_, count) -> count > 1 }}" } + } + } + + override val irHeader: ByteArray? by lazy { layout.inPlace { library -> library.irHeader.let { + if (it.exists) loadIrHeader() else null } + }} + + override fun irDeclaration(index: Long, isLocal: Boolean) = loadIrDeclaraton(index, isLocal) + + override fun toString() = "$libraryName[default=$isDefault]" + + private val combinedDeclarations: CombinedIrFileReader by lazy { + CombinedIrFileReader(layout.realFiles { + it.irFile + }) + } + private fun loadIrHeader(): ByteArray = + layout.inPlace { it.irHeader.readBytes() } + + private fun loadIrDeclaraton(index: Long, isLocal: Boolean) = + combinedDeclarations.declarationBytes(DeclarationId(index, isLocal)) +} diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt new file mode 100644 index 00000000000..def00ea7053 --- /dev/null +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt @@ -0,0 +1,91 @@ +package org.jetbrains.kotlin.konan.library.impl + +import org.jetbrains.kotlin.konan.file.* +import org.jetbrains.kotlin.konan.library.KLIB_FILE_EXTENSION +import org.jetbrains.kotlin.konan.library.KLIB_FILE_EXTENSION_WITH_DOT +import org.jetbrains.kotlin.konan.library.KonanLibraryLayout +import org.jetbrains.kotlin.konan.target.KonanTarget +import org.jetbrains.kotlin.konan.util.removeSuffixIfPresent +import java.nio.file.FileSystem + +interface KonanLibraryLayoutImpl: KonanLibraryLayout { + fun inPlace(action: (KonanLibraryLayout) -> T): T + fun realFiles (action: (KonanLibraryLayout) -> T): T +} + +private class ZippedKonanLibraryLayout(val klibFile: File, override val target: KonanTarget?): KonanLibraryLayoutImpl { + + init { zippedKonanLibraryChecks(klibFile) } + + override val libraryName = klibFile.path.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT) + + override val libDir = File("/") + + override fun realFiles(action: (KonanLibraryLayout) -> T): T { + return action(FileExtractor(this))!! + } + + override fun inPlace(action: (KonanLibraryLayout) -> T): T { + return klibFile.withZipFileSystem { zipFileSystem -> + action(DirectFromZip(this, zipFileSystem)) + } + } +} + +internal fun zippedKonanLibraryChecks(klibFile: File) { + check(klibFile.exists) { "Could not find $klibFile." } + check(klibFile.isFile) { "Expected $klibFile to be a regular file." } + + val extension = klibFile.extension + check(extension.isEmpty() || extension == KLIB_FILE_EXTENSION) { "Unexpected file extension: $extension" } +} + +private class UnzippedKonanLibraryLayout(override val libDir: File, override val target: KonanTarget?): KonanLibraryLayoutImpl { + override val libraryName = libDir.path + + override fun inPlace(action: (KonanLibraryLayout) -> T): T = action(this) + override fun realFiles (action: (KonanLibraryLayout) -> T): T = inPlace(action) +} + +private class DirectFromZip(zippedLayout: ZippedKonanLibraryLayout, val zipFileSystem: FileSystem): KonanLibraryLayout { + override val libraryName = zippedLayout.libraryName + override val libDir = zipFileSystem.file(zippedLayout.libDir) +} + +/** + * This class automatically extracts pieces of the library on first access. Use it if you need + * to pass extracted files to an external tool. Otherwise, stick to [DirectFromZip]. + */ +private class FileExtractor(val zippedLibraryLayout: ZippedKonanLibraryLayout): KonanLibraryLayout by zippedLibraryLayout { + + override val manifestFile: File by lazy { extract(super.manifestFile) } + + override val resourcesDir: File by lazy { extractDir(super.resourcesDir) } + + override val includedDir: File by lazy { extractDir(super.includedDir) } + + override val kotlinDir: File by lazy { extractDir(super.kotlinDir) } + + override val nativeDir: File by lazy { extractDir(super.nativeDir) } + + override val linkdataDir: File by lazy { extractDir(super.linkdataDir) } + + override val irFile: File by lazy { extract(super.irFile) } + + fun extract(file: File): File = zippedLibraryLayout.klibFile.withZipFileSystem { zipFileSystem -> + val temporary = createTempFile(file.name) + zipFileSystem.file(file).copyTo(temporary) + temporary.deleteOnExit() + temporary + } + + fun extractDir(directory: File): File = zippedLibraryLayout.klibFile.withZipFileSystem { zipFileSystem -> + val temporary = createTempDir(directory.name) + zipFileSystem.file(directory).recursiveCopyTo(temporary) + temporary.deleteOnExitRecursively() + temporary + } +} + +internal fun createKonanLibraryLayout(klib: File, target: KonanTarget? = null) = + if (klib.isFile) ZippedKonanLibraryLayout(klib, target) else UnzippedKonanLibraryLayout(klib, target) diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/Exceptions.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/Exceptions.kt new file mode 100644 index 00000000000..3b779d3a918 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/Exceptions.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan + +/** + * This is a common ancestor of all Kotlin/Native exceptions. + */ +open class KonanException(message: String = "", cause: Throwable? = null) : Exception(message, cause) + +/** + * An error occured during external tool invocation. Such as non-zero exit code. + */ +class KonanExternalToolFailure(message: String, val toolName: String, cause: Throwable? = null) : KonanException(message, cause) + diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/KonanAbiVersion.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/KonanAbiVersion.kt new file mode 100644 index 00000000000..c7ea16e231c --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/KonanAbiVersion.kt @@ -0,0 +1,28 @@ +/** + * Copyright 2010-2019 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan + +fun String.parseKonanAbiVersion(): KonanAbiVersion { + return KonanAbiVersion(this.toInt()) +} + +data class KonanAbiVersion(val version: Int) { + companion object { + val CURRENT = KonanAbiVersion(8) + } + override fun toString() = "$version" +} diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/MetaVersion.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/MetaVersion.kt new file mode 100644 index 00000000000..30a275aedcf --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/MetaVersion.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove the whole file! + +/** + * https://en.wikipedia.org/wiki/Software_versioning + * scheme major.minor[.build[.revision]]. + */ + +enum class MetaVersion(val metaString: String) { + DEV("dev"), + EAP("eap"), + ALPHA("alpha"), + BETA("beta"), + RC1("rc1"), + RC2("rc2"), + RELEASE("release"); + + companion object { + + fun findAppropriate(metaString: String): MetaVersion { + return MetaVersion.values().find { it.metaString.equals(metaString, ignoreCase = true) } + ?: if (metaString.isBlank()) RELEASE else error("Unknown meta version: $metaString") + } + } +} diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/TempFiles.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/TempFiles.kt new file mode 100644 index 00000000000..5054d7131c5 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/TempFiles.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.kotlin.konan + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.file.* + +/** + * Creates and stores temporary compiler outputs + * If pathToTemporaryDir is given and is not empty then temporary outputs will be preserved + */ +class TempFiles(outputPath: String, pathToTemporaryDir: String? = null) { + private val outputName = File(outputPath).name + + val nativeBinaryFile by lazy { File(dir,"${outputName}.kt.bc") } + val cAdapterCpp by lazy { File(dir, "api.cpp") } + val cAdapterBitcode by lazy { File(dir, "api.bc") } + + val nativeBinaryFileName get() = nativeBinaryFile.absolutePath + val cAdapterCppName get() = cAdapterCpp.absolutePath + val cAdapterBitcodeName get() = cAdapterBitcode.absolutePath + + private val dir by lazy { if (pathToTemporaryDir == null || pathToTemporaryDir.isEmpty()) { + createTempDir("konan_temp").deleteOnExit() + } else { + createDirForTemporaryFiles(pathToTemporaryDir) + } + } + + private fun createDirForTemporaryFiles(path: String): File { + if (File(path).isFile) { + throw IllegalArgumentException("Given file is not a directory: $path") + } + return File(path).apply { + if (!exists) { mkdirs() } + } + } + + /** + * Create file named {name}{suffix} inside temporary dir + */ + fun create(prefix: String, suffix: String = ""): File = + File(dir, "$prefix$suffix") +} + diff --git a/buildSrc/shared/build.gradle.kts b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/VisibleNamed.kt similarity index 72% rename from buildSrc/shared/build.gradle.kts rename to shared/src/main/kotlin/org/jetbrains/kotlin/konan/VisibleNamed.kt index 29b70fc5c32..384d1f63232 100644 --- a/buildSrc/shared/build.gradle.kts +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/VisibleNamed.kt @@ -14,14 +14,12 @@ * limitations under the License. */ -plugins { - kotlin("jvm") -} - -val sharedProject = file(property("sharedProjectPath")!!).resolve("src") +package org.jetbrains.kotlin.konan.util -kotlin.sourceSets["main"].kotlin.srcDirs(sharedProject) +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove the whole file! -dependencies { - implementation(kotlin("stdlib")) +val > T.visibleName get() = name.toLowerCase() +interface Named { + val name: String + val visibleName get() = name } diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/exec/ExecuteCommand.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/exec/ExecuteCommand.kt new file mode 100644 index 00000000000..6d085468442 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/exec/ExecuteCommand.kt @@ -0,0 +1,111 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.exec + +import java.lang.ProcessBuilder +import java.lang.ProcessBuilder.Redirect +import org.jetbrains.kotlin.konan.KonanExternalToolFailure + + +open class Command(initialCommand: List) { + + constructor(tool: String) : this(listOf(tool)) + constructor(vararg command: String) : this(command.toList()) + protected val command = initialCommand.toMutableList() + + val args: List + get() = command.drop(1) + + operator fun String.unaryPlus(): Command { + command += this + return this@Command + } + + operator fun List.unaryPlus(): Command { + command.addAll(this) + return this@Command + } + + var logger: ((() -> String)->Unit)? = null + + fun logWith(newLogger: ((() -> String)->Unit)): Command { + logger = newLogger + return this + } + + open fun runProcess(): Int { + val builder = ProcessBuilder(command) + + builder.redirectOutput(Redirect.INHERIT) + builder.redirectInput(Redirect.INHERIT) + builder.redirectError(Redirect.INHERIT) + + val process = builder.start() + val exitCode = process.waitFor() + return exitCode + } + + open fun execute() { + log() + + val code = runProcess() + handleExitCode(code) + } + + /** + * If withErrors is true then output from error stream will be added + */ + fun getOutputLines(withErrors: Boolean = false): List = + getResult(withErrors, handleError = true).outputLines + + fun getResult(withErrors: Boolean, handleError: Boolean = false): Result { + log() + + val outputFile = createTempFile() + outputFile.deleteOnExit() + + try { + val builder = ProcessBuilder(command) + + builder.redirectInput(Redirect.INHERIT) + builder.redirectError(Redirect.INHERIT) + builder.redirectOutput(Redirect.to(outputFile)) + .redirectErrorStream(withErrors) + // Note: getting process output could be done without redirecting to temporary file, + // however this would require managing a thread to read `process.inputStream` because + // it may have limited capacity. + + val process = builder.start() + val code = process.waitFor() + if (handleError) handleExitCode(code) + + return Result(code, outputFile.readLines()) + } finally { + outputFile.delete() + } + } + + class Result(val exitCode: Int, val outputLines: List) + + private fun handleExitCode(code: Int) { + if (code != 0) throw KonanExternalToolFailure("The ${command[0]} command returned non-zero exit code: $code.", command[0]) + } + + private fun log() { + if (logger != null) logger!! { command.joinToString(" ") } + } +} diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt new file mode 100644 index 00000000000..d4119212479 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt @@ -0,0 +1,233 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.file + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove the whole file! + +import org.jetbrains.kotlin.konan.util.removeSuffixIfPresent +import java.io.BufferedReader +import java.io.InputStream +import java.io.InputStreamReader +import java.io.RandomAccessFile +import java.net.URI +import java.nio.MappedByteBuffer +import java.nio.channels.FileChannel +import java.nio.file.* +import java.nio.file.attribute.BasicFileAttributes + +data class File constructor(internal val javaPath: Path) { + constructor(parent: Path, child: String): this(parent.resolve(child)) + constructor(parent: File, child: String): this(parent.javaPath.resolve(child)) + constructor(path: String): this(Paths.get(path)) + constructor(parent: String, child: String): this(Paths.get(parent, child)) + + val path: String + get() = javaPath.toString() + val absolutePath: String + get() = javaPath.toAbsolutePath().toString() + val absoluteFile: File + get() = File(absolutePath) + val name: String + get() = javaPath.fileName.toString().removeSuffixIfPresent("/") // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8153248 + val extension: String + get() = name.substringAfterLast('.', "") + val parent: String + get() = javaPath.parent.toString() + val parentFile: File + get() = File(javaPath.parent) + + val exists + get() = Files.exists(javaPath) + val isDirectory + get() = Files.isDirectory(javaPath) + val isFile + get() = Files.isRegularFile(javaPath) + val isAbsolute + get() = javaPath.isAbsolute() + val listFiles: List + get() = Files.newDirectoryStream(javaPath).use { stream -> stream.map { File(it) } } + val listFilesOrEmpty: List + get() = if (exists) listFiles else emptyList() + + fun child(name: String) = File(this, name) + + fun copyTo(destination: File) { + Files.copy(javaPath, destination.javaPath, StandardCopyOption.REPLACE_EXISTING) + } + + fun recursiveCopyTo(destination: File) { + val sourcePath = javaPath + val destPath = destination.javaPath + sourcePath.recursiveCopyTo(destPath) + } + + fun mkdirs() = Files.createDirectories(javaPath) + fun delete() = Files.deleteIfExists(javaPath) + fun deleteRecursively() = postorder{Files.delete(it)} + fun deleteOnExitRecursively() = preorder{File(it).deleteOnExit()} + + fun preorder(task: (Path) -> Unit) { + if (!this.exists) return + + Files.walkFileTree(javaPath, object: SimpleFileVisitor() { + override fun visitFile(file: Path?, attrs: BasicFileAttributes?): FileVisitResult { + task(file!!) + return FileVisitResult.CONTINUE + } + + override fun preVisitDirectory(dir: Path?, attrs: BasicFileAttributes?): FileVisitResult { + task(dir!!) + return FileVisitResult.CONTINUE + } + }) + + } + + fun postorder(task: (Path) -> Unit) { + if (!this.exists) return + + Files.walkFileTree(javaPath, object: SimpleFileVisitor() { + override fun visitFile(file: Path?, attrs: BasicFileAttributes?): FileVisitResult { + task(file!!) + return FileVisitResult.CONTINUE + } + + override fun postVisitDirectory(dir: Path?, exc: java.io.IOException?): FileVisitResult { + task(dir!!) + return FileVisitResult.CONTINUE + } + }) + } + + fun map(mode: FileChannel.MapMode = FileChannel.MapMode.READ_ONLY, + start: Long = 0, size: Long = -1): MappedByteBuffer { + val file = RandomAccessFile(path, + if (mode == FileChannel.MapMode.READ_ONLY) "r" else "rw") + val fileSize = if (mode == FileChannel.MapMode.READ_ONLY) + file.length() else size.also { assert(size != -1L) } + return file.channel.map(mode, start, fileSize) // Shall we .also { file.close() }? + } + + fun deleteOnExit(): File { + // Works only on the default file system, + // but that's okay for now. + javaPath.toFile().deleteOnExit() + return this // Allow streaming. + } + fun readBytes() = Files.readAllBytes(javaPath) + fun writeBytes(bytes: ByteArray) = Files.write(javaPath, bytes) + fun appendBytes(bytes: ByteArray) + = Files.write(javaPath, bytes, StandardOpenOption.APPEND) + + fun writeLines(lines: Iterable) { + Files.write(javaPath, lines) + } + + fun writeText(text: String): Unit = writeLines(listOf(text)) + + fun forEachLine(action: (String) -> Unit) { + Files.lines(javaPath).use { lines -> + lines.forEach { action(it) } + } + } + + fun createAsSymlink(target: String) { + val targetPath = Paths.get(target) + if (Files.isSymbolicLink(this.javaPath) && Files.readSymbolicLink(javaPath) == targetPath) { + return + } + Files.createSymbolicLink(this.javaPath, targetPath) + } + + override fun toString() = path + + // TODO: Consider removeing these after konanazing java.util.Properties. + fun bufferedReader() = Files.newBufferedReader(javaPath) + fun outputStream() = Files.newOutputStream(javaPath) + fun printWriter() = javaPath.toFile().printWriter() + + companion object { + val userDir + get() = File(System.getProperty("user.dir")) + + val userHome + get() = File(System.getProperty("user.home")) + + val javaHome + get() = File(System.getProperty("java.home")) + val pathSeparator = java.io.File.pathSeparator + val separator = java.io.File.separator + } + + fun readStrings() = mutableListOf().also { list -> forEachLine{list.add(it)}} + + override fun equals(other: Any?): Boolean { + val otherFile = other as? File ?: return false + return otherFile.javaPath.toAbsolutePath() == javaPath.toAbsolutePath() + } + + override fun hashCode() = javaPath.toAbsolutePath().hashCode() +} + +fun String.File(): File = File(this) +fun Path.File(): File = File(this) + +fun createTempFile(name: String, suffix: String? = null) + = Files.createTempFile(name, suffix).File() +fun createTempDir(name: String): File + = Files.createTempDirectory(name).File() + +fun Path.recursiveCopyTo(destPath: Path) { + val sourcePath = this + Files.walk(sourcePath).forEach next@ { oldPath -> + + val relative = sourcePath.relativize(oldPath) + val destFs = destPath.getFileSystem() + // We are copying files between file systems, + // so pass the relative path through the String. + val newPath = destFs.getPath(destPath.toString(), relative.toString()) + + // File systems don't allow replacing an existing root. + if (newPath == newPath.getRoot()) return@next + if (Files.isDirectory(newPath)) { + Files.createDirectories(newPath) + } else { + Files.copy(oldPath, newPath, StandardCopyOption.REPLACE_EXISTING) + } + } +} + +fun bufferedReader(errorStream: InputStream?) = BufferedReader(InputStreamReader(errorStream)) + +// stdlib `use` function adapted for AutoCloseable. +inline fun T.use(block: (T) -> R): R { + var closed = false + try { + return block(this) + } catch (e: Exception) { + closed = true + try { + this?.close() + } catch (closeException: Exception) { + } + throw e + } finally { + if (!closed) { + this?.close() + } + } +} diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/NativeFileType.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/NativeFileType.kt new file mode 100644 index 00000000000..ce3af307385 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/NativeFileType.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.file + +val String.isJavaScript + get() = this.endsWith(".js") + +val String.isUnixStaticLib + get() = this.endsWith(".a") + +val String.isWindowsStaticLib + get() = this.endsWith(".lib") + +val String.isBitcode + get() = this.endsWith(".bc") diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/ZipUtil.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/ZipUtil.kt new file mode 100644 index 00000000000..2a9062724cf --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/ZipUtil.kt @@ -0,0 +1,52 @@ +package org.jetbrains.kotlin.konan.file + +import java.net.URI +import java.nio.file.* + +private val File.zipUri: URI + get() = URI.create("jar:${this.toPath().toUri()}") + +fun File.zipFileSystem(mutable: Boolean = false): FileSystem { + val zipUri = this.zipUri + val attributes = hashMapOf("create" to mutable.toString()) + return try { + FileSystems.newFileSystem(zipUri, attributes, null) + } catch (e: FileSystemAlreadyExistsException) { + FileSystems.getFileSystem(zipUri) + } +} + +fun FileSystem.file(file: File) = File(this.getPath(file.path)) + +fun FileSystem.file(path: String) = File(this.getPath(path)) + +private fun File.toPath() = Paths.get(this.path) + +fun File.zipDirAs(unixFile: File) { + unixFile.withMutableZipFileSystem() { + this.recursiveCopyTo(it.file("/")) + } +} + +fun Path.unzipTo(directory: Path) { + val zipUri = URI.create("jar:" + this.toUri()) + FileSystems.newFileSystem(zipUri, emptyMap(), null).use { zipfs -> + val zipPath = zipfs.getPath("/") + zipPath.recursiveCopyTo(directory) + } +} + +fun File.withZipFileSystem(mutable: Boolean = false, action: (FileSystem) -> T): T { + val zipFileSystem = this.zipFileSystem(mutable) + return try { + action(zipFileSystem) + } finally { + zipFileSystem.close() + } +} + +fun File.withZipFileSystem(action: (FileSystem) -> T): T + = this.withZipFileSystem(false, action) + +fun File.withMutableZipFileSystem(action: (FileSystem) -> T): T + = this.withZipFileSystem(true, action) \ No newline at end of file diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Apple.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Apple.kt new file mode 100644 index 00000000000..d223e58ced8 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Apple.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed -> in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.target + +import org.jetbrains.kotlin.konan.properties.KonanPropertiesLoader +import org.jetbrains.kotlin.konan.properties.Properties +import org.jetbrains.kotlin.konan.util.InternalServer + +class AppleConfigurablesImpl( + target: KonanTarget, + properties: Properties, + baseDir: String? +) : AppleConfigurables, KonanPropertiesLoader(target, properties, baseDir) { + + private val sdkDependency = this.targetSysRoot!! + private val toolchainDependency = this.targetToolchain!! + + override val absoluteTargetSysRoot: String get() = when (xcodePartsProvider) { + is XcodePartsProvider.Local -> when (target) { + KonanTarget.MACOS_X64 -> xcodePartsProvider.xcode.macosxSdk + KonanTarget.IOS_ARM32, KonanTarget.IOS_ARM64 -> xcodePartsProvider.xcode.iphoneosSdk + KonanTarget.IOS_X64 -> xcodePartsProvider.xcode.iphonesimulatorSdk + else -> error(target) + } + XcodePartsProvider.InternalServer -> absolute(sdkDependency) + } + + override val absoluteTargetToolchain: String get() = when (xcodePartsProvider) { + is XcodePartsProvider.Local -> xcodePartsProvider.xcode.toolchain + XcodePartsProvider.InternalServer -> absolute(toolchainDependency) + } + + override val dependencies get() = super.dependencies + when (xcodePartsProvider) { + is XcodePartsProvider.Local -> emptyList() + XcodePartsProvider.InternalServer -> listOf(sdkDependency, toolchainDependency) + } + + private val xcodePartsProvider = if (InternalServer.isAvailable) { + XcodePartsProvider.InternalServer + } else { + val xcode = Xcode.current + properties.getProperty("useFixedXcodeVersion")?.let { requiredXcodeVersion -> + val currentXcodeVersion = xcode.version + + if (properties.getProperty("ignoreXcodeVersionCheck") != "true" && + currentXcodeVersion != requiredXcodeVersion) { + error("expected Xcode version $requiredXcodeVersion, got $currentXcodeVersion, consider updating " + + "Xcode or use \"ignoreXcodeVersionCheck\" variable in konan.properties") + } + } + + XcodePartsProvider.Local(xcode) + } + + private sealed class XcodePartsProvider { + class Local(val xcode: Xcode) : XcodePartsProvider() + object InternalServer : XcodePartsProvider() + } +} diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt new file mode 100644 index 00000000000..38e3a1306c8 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt @@ -0,0 +1,317 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed -> in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.target + +import org.jetbrains.kotlin.konan.file.File + +class ClangArgs(private val configurables: Configurables) : Configurables by configurables { + + val targetArg = if (configurables is NonAppleConfigurables) + configurables.targetArg + else null + + val specificClangArgs: List + get() { + val result = when (target) { + KonanTarget.LINUX_X64 -> + listOf("--sysroot=$absoluteTargetSysRoot") + + if (target != host) listOf("-target", targetArg!!) else emptyList() + + KonanTarget.LINUX_ARM32_HFP -> + listOf("-target", targetArg!!, + "-mfpu=vfp", "-mfloat-abi=hard", + "--sysroot=$absoluteTargetSysRoot", + // TODO: those two are hacks. + "-I$absoluteTargetSysRoot/usr/include/c++/4.8.3", + "-I$absoluteTargetSysRoot/usr/include/c++/4.8.3/arm-linux-gnueabihf") + + KonanTarget.LINUX_MIPS32 -> + listOf("-target", targetArg!!, + "--sysroot=$absoluteTargetSysRoot", + "-I$absoluteTargetSysRoot/usr/include/c++/4.9.4", + "-I$absoluteTargetSysRoot/usr/include/c++/4.9.4/mips-unknown-linux-gnu") + + KonanTarget.LINUX_MIPSEL32 -> + listOf("-target", targetArg!!, + "--sysroot=$absoluteTargetSysRoot", + "-I$absoluteTargetSysRoot/usr/include/c++/4.9.4", + "-I$absoluteTargetSysRoot/usr/include/c++/4.9.4/mipsel-unknown-linux-gnu") + + KonanTarget.MINGW_X64, KonanTarget.MINGW_X86 -> + listOf("-target", targetArg!!, "--sysroot=$absoluteTargetSysRoot", "-Xclang", "-flto-visibility-public-std") + + KonanTarget.MACOS_X64 -> + listOf("--sysroot=$absoluteTargetSysRoot", "-mmacosx-version-min=10.11") + + KonanTarget.IOS_ARM32 -> + listOf("-stdlib=libc++", "-arch", "armv7", "-isysroot", absoluteTargetSysRoot, "-miphoneos-version-min=9.0.0") + + KonanTarget.IOS_ARM64 -> + listOf("-stdlib=libc++", "-arch", "arm64", "-isysroot", absoluteTargetSysRoot, "-miphoneos-version-min=9.0.0") + + KonanTarget.IOS_X64 -> + listOf("-stdlib=libc++", "-isysroot", absoluteTargetSysRoot, "-miphoneos-version-min=9.0.0") + + KonanTarget.ANDROID_ARM32 -> + listOf("-target", targetArg!!, + "--sysroot=$absoluteTargetSysRoot", + // HACKS! + "-I$absoluteTargetSysRoot/usr/include/c++/4.9.x", + "-I$absoluteTargetSysRoot/usr/include/c++/4.9.x/arm-linux-androideabi") + + KonanTarget.ANDROID_ARM64 -> + listOf("-target", targetArg!!, + "--sysroot=$absoluteTargetSysRoot", + // HACKS! + "-I$absoluteTargetSysRoot/usr/include/c++/4.9.x", + "-I$absoluteTargetSysRoot/usr/include/c++/4.9.x/aarch64-linux-android") + + // By default wasm target forces `hidden` visibility which causes + // linkage problems. + KonanTarget.WASM32 -> + listOf("-target", targetArg!!, + "-fno-rtti", + "-fno-exceptions", + "-fvisibility=default", + "-D_LIBCPP_ABI_VERSION=2", + "-D_LIBCPP_NO_EXCEPTIONS=1", + "-nostdinc", + "-Xclang", "-nobuiltininc", + "-Xclang", "-nostdsysteminc", + "-Xclang", "-isystem$absoluteTargetSysRoot/include/libcxx", + "-Xclang", "-isystem$absoluteTargetSysRoot/lib/libcxxabi/include", + "-Xclang", "-isystem$absoluteTargetSysRoot/include/compat", + "-Xclang", "-isystem$absoluteTargetSysRoot/include/libc") + + is KonanTarget.ZEPHYR -> + listOf("-target", targetArg!!, + "-fno-rtti", + "-fno-exceptions", + "-fno-asynchronous-unwind-tables", + "-fno-pie", + "-fno-pic", + "-fshort-enums", + "-nostdinc", + // TODO: make it a libGcc property? + // We need to get rid of wasm sysroot first. + "-isystem $targetToolchain/../lib/gcc/arm-none-eabi/7.2.1/include", + "-isystem $targetToolchain/../lib/gcc/arm-none-eabi/7.2.1/include-fixed", + "-isystem$absoluteTargetSysRoot/include/libcxx", + "-isystem$absoluteTargetSysRoot/include/libc" + ) + + (configurables as ZephyrConfigurables).boardSpecificClangFlags + + } + return result + } + + val clangArgsSpecificForKonanSources + get() = when (target) { + KonanTarget.LINUX_X64 -> + listOf("-DUSE_GCC_UNWIND=1", + "-DKONAN_LINUX=1", + "-DKONAN_X64=1", + "-DUSE_ELF_SYMBOLS=1", + "-DELFSIZE=64", + "-DKONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1") + + KonanTarget.LINUX_ARM32_HFP -> + listOf("-DUSE_GCC_UNWIND=1", + "-DKONAN_LINUX=1", + "-DKONAN_ARM32=1", + "-DUSE_ELF_SYMBOLS=1", + "-DELFSIZE=32", + "-DKONAN_NO_UNALIGNED_ACCESS=1") + + KonanTarget.LINUX_MIPS32 -> + listOf("-DUSE_GCC_UNWIND=1", + "-DKONAN_LINUX=1", + "-DKONAN_MIPS32=1", + "-DUSE_ELF_SYMBOLS=1", + "-DELFSIZE=32", + // TODO: reconsider, once target MIPS can do proper 64-bit load/store/CAS. + "-DKONAN_NO_64BIT_ATOMIC=1", + "-DKONAN_NO_UNALIGNED_ACCESS=1") + + KonanTarget.LINUX_MIPSEL32 -> + listOf("-DUSE_GCC_UNWIND=1", + "-DKONAN_LINUX=1", + "-DKONAN_MIPSEL32=1", + "-DUSE_ELF_SYMBOLS=1", + "-DELFSIZE=32", + // TODO: reconsider, once target MIPS can do proper 64-bit load/store/CAS. + "-DKONAN_NO_64BIT_ATOMIC=1", + "-DKONAN_NO_UNALIGNED_ACCESS=1") + + KonanTarget.MINGW_X64, KonanTarget.MINGW_X86 -> + listOf("-DUSE_GCC_UNWIND=1", + "-DUSE_PE_COFF_SYMBOLS=1", + "-DKONAN_WINDOWS=1", + if (target == KonanTarget.MINGW_X64) "-DKONAN_X64=1" else "-DKONAN_X86=1", + "-DKONAN_NO_MEMMEM=1", + "-DKONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1") + + KonanTarget.MACOS_X64 -> + listOf("-DKONAN_OSX=1", + "-DKONAN_MACOSX=1", + "-DKONAN_X64=1", + "-DKONAN_OBJC_INTEROP=1", + "-DKONAN_CORE_SYMBOLICATION=1", + "-DKONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1") + + KonanTarget.IOS_ARM32 -> + listOf("-DKONAN_OBJC_INTEROP=1", + "-DKONAN_IOS", + "-DKONAN_ARM32=1", + "-DKONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1", + "-DKONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG=1", + "-DMACHSIZE=32", + // While not 100% correct here, using atomic ops on iOS armv7 requires 8 byte alignment, + // and general ABI requires 4-byte alignment on 64-bit long fields as mentioned in + // https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARMv6FunctionCallingConventions.html#//apple_ref/doc/uid/TP40009021-SW1 + // See https://github.com/ktorio/ktor/issues/941 for the context. + "-DKONAN_NO_64BIT_ATOMIC=1", + "-DKONAN_NO_UNALIGNED_ACCESS=1") + + KonanTarget.IOS_ARM64 -> + listOf("-DKONAN_OBJC_INTEROP=1", + "-DKONAN_IOS=1", + "-DKONAN_ARM64=1", + "-DKONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1", + "-DKONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG=1", + "-DMACHSIZE=64") + + KonanTarget.IOS_X64 -> + listOf("-DKONAN_OBJC_INTEROP=1", + "-DKONAN_IOS=1", + "-DKONAN_X64=1", + "-DKONAN_CORE_SYMBOLICATION=1", + "-DKONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1") + + KonanTarget.ANDROID_ARM32 -> + listOf("-D__ANDROID__", + "-DUSE_GCC_UNWIND=1", + "-DUSE_ELF_SYMBOLS=1", + "-DELFSIZE=32", + "-DKONAN_ANDROID=1", + "-DKONAN_ARM32=1", + "-DKONAN_NO_UNALIGNED_ACCESS=1") + + KonanTarget.ANDROID_ARM64 -> + listOf("-D__ANDROID__", + "-DUSE_GCC_UNWIND=1", + "-DUSE_ELF_SYMBOLS=1", + "-DELFSIZE=64", + "-DKONAN_ANDROID=1", + "-DKONAN_ARM64=1", + "-DKONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1") + + KonanTarget.WASM32 -> + listOf("-DKONAN_WASM=1", + "-DKONAN_NO_FFI=1", + "-DKONAN_NO_THREADS=1", + "-DKONAN_NO_EXCEPTIONS=1", + "-DKONAN_INTERNAL_DLMALLOC=1", + "-DKONAN_INTERNAL_SNPRINTF=1", + "-DKONAN_INTERNAL_NOW=1", + "-DKONAN_NO_MEMMEM", + "-DKONAN_NO_CTORS_SECTION=1") + + is KonanTarget.ZEPHYR -> + listOf( "-DKONAN_ZEPHYR=1", + "-DKONAN_NO_FFI=1", + "-DKONAN_NO_THREADS=1", + "-DKONAN_NO_EXCEPTIONS=1", + "-DKONAN_NO_MATH=1", + "-DKONAN_INTERNAL_SNPRINTF=1", + "-DKONAN_INTERNAL_NOW=1", + "-DKONAN_NO_MEMMEM=1", + "-DKONAN_NO_CTORS_SECTION=1", + "-DKONAN_NO_UNALIGNED_ACCESS=1") + } + + private val host = HostManager.host + + private val binDir = when (host) { + KonanTarget.LINUX_X64 -> "$absoluteTargetToolchain/bin" + KonanTarget.MINGW_X64 -> "$absoluteTargetToolchain/bin" + KonanTarget.MACOS_X64 -> "$absoluteTargetToolchain/usr/bin" + else -> throw TargetSupportException("Unexpected host platform") + } + + private val extraHostClangArgs = + if (configurables is LinuxBasedConfigurables) { + listOf("--gcc-toolchain=${configurables.absoluteGccToolchain}") + } else { + emptyList() + } + + val commonClangArgs = listOf("-B$binDir", "-fno-stack-protector") + extraHostClangArgs + + val clangPaths = listOf("$absoluteLlvmHome/bin", binDir) + + private val jdkDir by lazy { + val home = File.javaHome.absoluteFile + if (home.child("include").exists) + home.absolutePath + else + home.parentFile.absolutePath + } + + val hostCompilerArgsForJni = listOf("", HostManager.jniHostPlatformIncludeDir).map { "-I$jdkDir/include/$it" }.toTypedArray() + + val clangArgs = (commonClangArgs + specificClangArgs).toTypedArray() + + val clangArgsForKonanSources = + clangArgs + clangArgsSpecificForKonanSources + + val targetLibclangArgs: List = + // libclang works not exactly the same way as the clang binary and + // (in particular) uses different default header search path. + // See e.g. http://lists.llvm.org/pipermail/cfe-dev/2013-November/033680.html + // We workaround the problem with -isystem flag below. + listOf("-isystem", "$absoluteLlvmHome/lib/clang/$llvmVersion/include", *clangArgs) + + val targetClangCmd + = listOf("${absoluteLlvmHome}/bin/clang") + clangArgs + + val targetClangXXCmd + = listOf("${absoluteLlvmHome}/bin/clang++") + clangArgs + + fun clangC(vararg userArgs: String) = targetClangCmd + userArgs.asList() + + fun clangCXX(vararg userArgs: String) = targetClangXXCmd + userArgs.asList() + + companion object { + @JvmStatic + fun filterGradleNativeSoftwareFlags(args: MutableList) { + args.remove("/usr/include") // HACK: over gradle-4.4. + args.remove("-nostdinc") // HACK: over gradle-5.1. + when (HostManager.host) { + KonanTarget.LINUX_X64 -> args.remove("/usr/include/x86_64-linux-gnu") // HACK: over gradle-4.4. + KonanTarget.MACOS_X64 -> { + val indexToRemove = args.indexOf(args.find { it.contains("MacOSX.platform")}) // HACK: over gradle-4.7. + if (indexToRemove != -1) { + args.removeAt(indexToRemove - 1) // drop -I. + args.removeAt(indexToRemove - 1) // drop /Application/Xcode.app/... + } + } + } + } + } +} + diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Configurables.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Configurables.kt new file mode 100644 index 00000000000..94c07099064 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Configurables.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed -> in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.target + +import org.jetbrains.kotlin.konan.properties.* + +interface Configurables : TargetableExternalStorage { + + val target: KonanTarget + + val llvmHome get() = hostString("llvmHome") + val llvmVersion get() = hostString("llvmVersion") + val libffiDir get() = hostString("libffiDir") + + // TODO: Delegate to a map? + val llvmLtoNooptFlags get() = targetList("llvmLtoNooptFlags") + val llvmLtoOptFlags get() = targetList("llvmLtoOptFlags") + val llvmLtoFlags get() = targetList("llvmLtoFlags") + val llvmLtoDynamicFlags get() = targetList("llvmLtoDynamicFlags") + val entrySelector get() = targetList("entrySelector") + val linkerOptimizationFlags get() = targetList("linkerOptimizationFlags") + val linkerKonanFlags get() = targetList("linkerKonanFlags") + val linkerNoDebugFlags get() = targetList("linkerNoDebugFlags") + val linkerDynamicFlags get() = targetList("linkerDynamicFlags") + val llvmDebugOptFlags get() = targetList("llvmDebugOptFlags") + val targetSysRoot get() = targetString("targetSysRoot") + + // Notice: these ones are host-target. + val targetToolchain get() = hostTargetString("targetToolchain") + + val absoluteTargetSysRoot get() = absolute(targetSysRoot) + val absoluteTargetToolchain get() = absolute(targetToolchain) + val absoluteLlvmHome get() = absolute(llvmHome) +} + +interface NonAppleConfigurables : Configurables { + val targetArg get() = targetString("quadruple") +} + +interface AppleConfigurables : Configurables { + val arch get() = targetString("arch")!! + val osVersionMin get() = targetString("osVersionMin")!! + val osVersionMinFlagLd get() = targetString("osVersionMinFlagLd")!! +} + +interface MingwConfigurables : NonAppleConfigurables + +interface LinuxBasedConfigurables : NonAppleConfigurables { + val gccToolchain get() = hostString("gccToolchain") + val absoluteGccToolchain get() = absolute(gccToolchain) + + val libGcc get() = targetString("libGcc")!! + val dynamicLinker get() = targetString("dynamicLinker")!! + val pluginOptimizationFlags get() = targetList("pluginOptimizationFlags") + val abiSpecificLibraries get() = targetList("abiSpecificLibraries") +} + +interface LinuxConfigurables : LinuxBasedConfigurables +interface LinuxMIPSConfigurables : LinuxBasedConfigurables +interface RaspberryPiConfigurables : LinuxBasedConfigurables +interface AndroidConfigurables : NonAppleConfigurables + +interface WasmConfigurables : NonAppleConfigurables { + + val llcFlags get() = targetList("llcFlags") + val llcNooptFlags get() = targetList("llcNooptFlags") + val llcOptFlags get() = targetList("llcOptFlags") + val llcDebugFlags get() = targetList("llcDebugFlags") + + val optFlags get() = targetList("optFlags") + val optNooptFlags get() = targetList("optNooptFlags") + val optOptFlags get() = targetList("optOptFlags") + val optDebugFlags get() = targetList("optDebugFlags") + + val lldFlags get() = targetList("lld") +} + +interface ZephyrConfigurables : NonAppleConfigurables { + val boardSpecificClangFlags get() = targetList("boardSpecificClangFlags") +} + diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ConfigurablesImpl.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ConfigurablesImpl.kt new file mode 100644 index 00000000000..471b5529da2 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ConfigurablesImpl.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed -> in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.target + +import org.jetbrains.kotlin.konan.properties.* + +class LinuxConfigurablesImpl(target: KonanTarget, properties: Properties, baseDir: String?) + : LinuxConfigurables, KonanPropertiesLoader(target, properties, baseDir) + +class LinuxMIPSConfigurablesImpl(target: KonanTarget, properties: Properties, baseDir: String?) + : LinuxMIPSConfigurables, KonanPropertiesLoader(target, properties, baseDir) + +class AndroidConfigurablesImpl(target: KonanTarget, properties: Properties, baseDir: String?) + : AndroidConfigurables, KonanPropertiesLoader(target, properties, baseDir) + +class MingwConfigurablesImpl(target: KonanTarget, properties: Properties, baseDir: String?) + : MingwConfigurables, KonanPropertiesLoader(target, properties, baseDir) + +class WasmConfigurablesImpl(target: KonanTarget, properties: Properties, baseDir: String?) + : WasmConfigurables, KonanPropertiesLoader(target, properties, baseDir) + +class ZephyrConfigurablesImpl(target: KonanTarget, properties: Properties, baseDir: String?) + : ZephyrConfigurables, KonanPropertiesLoader(target, properties, baseDir) + + +fun loadConfigurables(target: KonanTarget, properties: Properties, baseDir: String?) = when (target) { + KonanTarget.LINUX_X64, KonanTarget.LINUX_ARM32_HFP -> + LinuxConfigurablesImpl(target, properties, baseDir) + KonanTarget.LINUX_MIPS32, KonanTarget.LINUX_MIPSEL32 -> + LinuxMIPSConfigurablesImpl(target, properties, baseDir) + KonanTarget.MACOS_X64, KonanTarget.IOS_ARM32, KonanTarget.IOS_ARM64, KonanTarget.IOS_X64 -> + AppleConfigurablesImpl(target, properties, baseDir) + KonanTarget.ANDROID_ARM32, KonanTarget.ANDROID_ARM64 -> + AndroidConfigurablesImpl(target, properties, baseDir) + KonanTarget.MINGW_X64, KonanTarget.MINGW_X86 -> + MingwConfigurablesImpl(target, properties, baseDir) + KonanTarget.WASM32 -> + WasmConfigurablesImpl(target, properties, baseDir) + is KonanTarget.ZEPHYR -> + ZephyrConfigurablesImpl(target, properties, baseDir) + } + diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Distribution.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Distribution.kt new file mode 100644 index 00000000000..71c852543d0 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Distribution.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.target + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.properties.keepOnlyDefaultProfiles +import org.jetbrains.kotlin.konan.properties.loadProperties +import org.jetbrains.kotlin.konan.util.DependencyProcessor + +class Distribution( + private val onlyDefaultProfiles: Boolean = false, + private val konanHomeOverride: String? = null, + private val runtimeFileOverride: String? = null) { + + val localKonanDir = DependencyProcessor.localKonanDir + + private fun findKonanHome(): String { + if (konanHomeOverride != null) return konanHomeOverride + + val value = System.getProperty("konan.home", "dist") + val path = File(value).absolutePath + return path + } + + val konanHome = findKonanHome() + val konanSubdir = "$konanHome/konan" + val mainPropertyFileName = "$konanSubdir/konan.properties" + val experimentalEnabled by lazy { + File("$konanSubdir/experimentalTargetsEnabled").exists + } + + private fun propertyFilesFromConfigDir(configDir: String, genericName: String): List { + val directory = File(configDir, "platforms/$genericName") + return if (directory.exists && directory.isDirectory) + directory.listFiles + else + emptyList() + } + + private fun preconfiguredPropertyFiles(genericName: String) = + propertyFilesFromConfigDir(konanSubdir, genericName) + + private fun userPropertyFiles(genericName: String) = + propertyFilesFromConfigDir(localKonanDir.absolutePath, genericName) + + fun additionalPropertyFiles(genericName: String) = + preconfiguredPropertyFiles(genericName) + userPropertyFiles(genericName) + + val properties by lazy { + val loaded = File(mainPropertyFileName).loadProperties() + HostManager.knownTargetTemplates.forEach { + additionalPropertyFiles(it).forEach { + val additional = it.loadProperties() + loaded.putAll(additional) + } + } + if (onlyDefaultProfiles) { + loaded.keepOnlyDefaultProfiles() + } + loaded + } + + val klib = "$konanHome/klib" + val stdlib = "$klib/common/stdlib" + + fun defaultNatives(target: KonanTarget) = "$konanHome/konan/targets/${target.visibleName}/native" + + fun runtime(target: KonanTarget) = runtimeFileOverride ?: "$stdlib/targets/${target.visibleName}/native/runtime.bc" + + val launcherFiles = listOf("launcher.bc") + + val dependenciesDir = DependencyProcessor.defaultDependenciesRoot.absolutePath + + fun availableSubTarget(genericName: String) = + additionalPropertyFiles(genericName).map { it.name } +} + +fun buildDistribution(konanHomeOverride: String? = null) = Distribution(true, konanHomeOverride, null) + +fun customerDistribution(konanHomeOverride: String? = null) = Distribution(false, konanHomeOverride, null) diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/KonanProperties.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/KonanProperties.kt new file mode 100644 index 00000000000..9a7b4f0dedd --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/KonanProperties.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed -> in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.properties + +import org.jetbrains.kotlin.konan.target.KonanTarget +import org.jetbrains.kotlin.konan.target.Configurables +import org.jetbrains.kotlin.konan.util.DependencyProcessor + +interface TargetableExternalStorage { + fun targetString(key: String): String? + fun targetList(key: String): List + fun hostString(key: String): String? + fun hostList(key: String): List + fun hostTargetString(key: String): String? + fun hostTargetList(key: String): List + fun absolute(value: String?): String + fun downloadDependencies() +} + +abstract class KonanPropertiesLoader(override val target: KonanTarget, + val properties: Properties, + val baseDir: String? = null) : Configurables { + open val dependencies get() = hostTargetList("dependencies") + + override fun downloadDependencies() { + dependencyProcessor!!.run() + } + + override fun targetString(key: String): String? + = properties.targetString(key, target) + override fun targetList(key: String): List + = properties.targetList(key, target) + override fun hostString(key: String): String? + = properties.hostString(key) + override fun hostList(key: String): List + = properties.hostList(key) + override fun hostTargetString(key: String): String? + = properties.hostTargetString(key, target) + override fun hostTargetList(key: String): List + = properties.hostTargetList(key, target) + + override fun absolute(value: String?): String = + dependencyProcessor!!.resolveRelative(value!!).absolutePath + private val dependencyProcessor by lazy { + baseDir?.let { DependencyProcessor(java.io.File(it), this) } + } +} + +fun Properties.keepOnlyDefaultProfiles() { + val DEPENDENCY_PROFILES_KEY = "dependencyProfiles" + val dependencyProfiles = this.getProperty(DEPENDENCY_PROFILES_KEY) + if (dependencyProfiles != "default alt") + error("unexpected $DEPENDENCY_PROFILES_KEY value: expected 'default alt', got '$dependencyProfiles'") + + // Force build to use only 'default' profile: + this.setProperty(DEPENDENCY_PROFILES_KEY, "default") + // Force build to use fixed Xcode version: + this.setProperty("useFixedXcodeVersion", "10.1") + // TODO: it actually affects only resolution made in :dependencies, + // that's why we assume that 'default' profile comes first (and check this above). +} diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/KonanTarget.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/KonanTarget.kt new file mode 100644 index 00000000000..5702511bad1 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/KonanTarget.kt @@ -0,0 +1,317 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.target + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove the whole file! + +import org.jetbrains.kotlin.konan.target.KonanTarget.* +import org.jetbrains.kotlin.konan.util.Named +import java.io.Serializable + +enum class Family(val exeSuffix:String, val dynamicPrefix: String, val dynamicSuffix: String, + val staticPrefix: String, val staticSuffix: String) { + OSX ("kexe", "lib", "dylib", "lib", "a"), + IOS ("kexe", "lib", "dylib", "lib", "a"), + LINUX ("kexe", "lib", "so" , "lib", "a"), + MINGW ("exe" , "" , "dll" , "lib", "a"), + ANDROID ("so" , "lib", "so" , "lib", "a"), + WASM ("wasm", "" , "wasm" , "", "wasm"), + ZEPHYR ("o" , "lib", "a" , "lib", "a") +} + +enum class Architecture(val bitness: Int) { + X64(64), + X86(32), + ARM64(64), + ARM32(32), + MIPS32(32), + MIPSEL32(32), + WASM32(32); +} + +sealed class KonanTarget(override val name: String, val family: Family, val architecture: Architecture) : Named { + object ANDROID_ARM32 : KonanTarget( "android_arm32", Family.ANDROID, Architecture.ARM32) + object ANDROID_ARM64 : KonanTarget( "android_arm64", Family.ANDROID, Architecture.ARM64) + object IOS_ARM32 : KonanTarget( "ios_arm32", Family.IOS, Architecture.ARM32) + object IOS_ARM64 : KonanTarget( "ios_arm64", Family.IOS, Architecture.ARM64) + object IOS_X64 : KonanTarget( "ios_x64", Family.IOS, Architecture.X64) + object LINUX_X64 : KonanTarget( "linux_x64", Family.LINUX, Architecture.X64) + object MINGW_X86 : KonanTarget( "mingw_x86", Family.MINGW, Architecture.X86) + object MINGW_X64 : KonanTarget( "mingw_x64", Family.MINGW, Architecture.X64) + object MACOS_X64 : KonanTarget( "macos_x64", Family.OSX, Architecture.X64) + object LINUX_ARM32_HFP :KonanTarget( "linux_arm32_hfp", Family.LINUX, Architecture.ARM32) + object LINUX_MIPS32 : KonanTarget( "linux_mips32", Family.LINUX, Architecture.MIPS32) + object LINUX_MIPSEL32 : KonanTarget( "linux_mipsel32", Family.LINUX, Architecture.MIPSEL32) + object WASM32 : KonanTarget( "wasm32", Family.WASM, Architecture.WASM32) + + // Tunable targets + class ZEPHYR(val subName: String, val genericName: String = "zephyr") : KonanTarget("${genericName}_$subName", Family.ZEPHYR, Architecture.ARM32) + + override fun toString() = name +} + +fun hostTargetSuffix(host: KonanTarget, target: KonanTarget) = + if (target == host) host.name else "${host.name}-${target.name}" + +enum class CompilerOutputKind { + PROGRAM { + override fun suffix(target: KonanTarget?) = ".${target!!.family.exeSuffix}" + }, + DYNAMIC { + override fun suffix(target: KonanTarget?) = ".${target!!.family.dynamicSuffix}" + override fun prefix(target: KonanTarget?) = "${target!!.family.dynamicPrefix}" + }, + STATIC { + override fun suffix(target: KonanTarget?) = ".${target!!.family.staticSuffix}" + override fun prefix(target: KonanTarget?) = "${target!!.family.staticPrefix}" + }, + FRAMEWORK { + override fun suffix(target: KonanTarget?): String = ".framework" + }, + LIBRARY { + override fun suffix(target: KonanTarget?) = ".klib" + }, + BITCODE { + override fun suffix(target: KonanTarget?) = ".bc" + }; + + abstract fun suffix(target: KonanTarget? = null): String + open fun prefix(target: KonanTarget? = null): String = "" +} + +interface TargetManager { + val target: KonanTarget + val targetName : String + fun list() : Unit + val hostTargetSuffix: String + val targetSuffix: String +} + +private class TargetManagerImpl(val userRequest: String?, val hostManager: HostManager): TargetManager { + override val target = determineCurrent() + override val targetName + get() = target.visibleName + + override fun list() { + hostManager.enabled.forEach { + val isDefault = if (it == target) "(default)" else "" + val aliasList = HostManager.listAliases(it.visibleName).joinToString(", ") + println(String.format("%1$-30s%2$-10s%3\$s", "${it.visibleName}:", "$isDefault", aliasList)) + } + } + + fun determineCurrent(): KonanTarget { + return if (userRequest == null || userRequest == "host") { + HostManager.host + } else { + val resolvedAlias = HostManager.resolveAlias(userRequest) + hostManager.targets[hostManager.known(resolvedAlias)]!! + } + } + + override val hostTargetSuffix get() = hostTargetSuffix(HostManager.host, target) + override val targetSuffix get() = target.name +} + +open class HostManager(protected val distribution: Distribution = Distribution(), experimental: Boolean = false) { + + fun targetManager(userRequest: String? = null): TargetManager = TargetManagerImpl(userRequest, this) + + // TODO: need a better way to enumerated predefined targets. + private val predefinedTargets = listOf( + ANDROID_ARM32, ANDROID_ARM64, + IOS_ARM32, IOS_ARM64, IOS_X64, + LINUX_X64, LINUX_ARM32_HFP, LINUX_MIPS32, LINUX_MIPSEL32, + MINGW_X64, MINGW_X86, + MACOS_X64, + WASM32) + + private val zephyrSubtargets = distribution.availableSubTarget("zephyr").map { ZEPHYR(it) } + + private val experimentalEnabled = experimental || distribution.experimentalEnabled + + private val configurableSubtargets = zephyrSubtargets + + val targetValues: List by lazy { + predefinedTargets + configurableSubtargets + } + + val targets = targetValues.associate{ it.visibleName to it } + + fun toKonanTargets(names: Iterable): List { + return names.map { + if (it == "host") HostManager.host + else targets[known(resolveAlias(it))]!! + } + } + + fun known(name: String): String { + if (targets[name] == null) { + throw TargetSupportException("Unknown target: $name. Use -list_targets to see the list of available targets") + } + return name + } + + fun targetByName(name: String): KonanTarget { + if (name == "host") return host + val target = targets[resolveAlias(name)] + if (target == null) throw TargetSupportException("Unknown target name: $name") + return target + } + + val enabledRegular: List by lazy { + when (host) { + KonanTarget.LINUX_X64 -> listOf( + KonanTarget.LINUX_X64, + KonanTarget.LINUX_ARM32_HFP, + KonanTarget.LINUX_MIPS32, + KonanTarget.LINUX_MIPSEL32, + KonanTarget.ANDROID_ARM32, + KonanTarget.ANDROID_ARM64, + KonanTarget.WASM32 + ) + KonanTarget.MINGW_X64 -> listOf( + KonanTarget.MINGW_X64, + KonanTarget.MINGW_X86, + KonanTarget.LINUX_X64, + KonanTarget.LINUX_ARM32_HFP, + KonanTarget.ANDROID_ARM32, + // TODO: toolchain to be fixed for that to work. + // KonanTarget.ANDROID_ARM64, + KonanTarget.WASM32 + ) + KonanTarget.MACOS_X64 -> listOf( + KonanTarget.MACOS_X64, + KonanTarget.IOS_ARM32, + KonanTarget.IOS_ARM64, + KonanTarget.IOS_X64, + KonanTarget.LINUX_X64, + KonanTarget.LINUX_ARM32_HFP, + KonanTarget.ANDROID_ARM32, + KonanTarget.ANDROID_ARM64, + KonanTarget.WASM32 + ) + else -> + throw TargetSupportException("Unknown host platform: $host") + } + } + + val enabledExperimental: List by lazy { + when (host) { + KonanTarget.LINUX_X64 -> listOf( + KonanTarget.MINGW_X86, KonanTarget.MINGW_X64 + ) + zephyrSubtargets + KonanTarget.MACOS_X64 -> listOf( + KonanTarget.MINGW_X86, KonanTarget.MINGW_X64 + ) + zephyrSubtargets + KonanTarget.MINGW_X64 -> listOf( + ) + zephyrSubtargets + else -> throw TargetSupportException("Unknown host platform: $host") + } + } + + val enabled : List + get() = if (experimentalEnabled) enabledRegular + enabledExperimental else enabledRegular + + fun isEnabled(target: KonanTarget) = enabled.contains(target) + + companion object { + fun host_os(): String { + val javaOsName = System.getProperty("os.name") + return when { + javaOsName == "Mac OS X" -> "osx" + javaOsName == "Linux" -> "linux" + javaOsName.startsWith("Windows") -> "windows" + else -> throw TargetSupportException("Unknown operating system: ${javaOsName}") + } + } + + @JvmStatic + fun simpleOsName(): String { + val hostOs = host_os() + return if (hostOs == "osx") "macos" else hostOs + } + + val jniHostPlatformIncludeDir: String + get() = when(host) { + KonanTarget.MACOS_X64 -> "darwin" + KonanTarget.LINUX_X64 -> "linux" + KonanTarget.MINGW_X64 ->"win32" + else -> throw TargetSupportException("Unknown host: $host.") + } + + fun host_arch(): String { + val javaArch = System.getProperty("os.arch") + return when (javaArch) { + "x86_64" -> "x86_64" + "amd64" -> "x86_64" + "arm64" -> "arm64" + else -> throw TargetSupportException("Unknown hardware platform: ${javaArch}") + } + } + + val host: KonanTarget = when (host_os()) { + "osx" -> KonanTarget.MACOS_X64 + "linux" -> KonanTarget.LINUX_X64 + "windows" -> KonanTarget.MINGW_X64 + else -> throw TargetSupportException("Unknown host target: ${host_os()} ${host_arch()}") + } + + // Note Hotspot-specific VM option enforcing C1-only, critical for decent compilation speed. + val defaultJvmArgs = listOf("-XX:TieredStopAtLevel=1", "-ea", "-Dfile.encoding=UTF-8") + val regularJvmArgs = defaultJvmArgs + "-Xmx3G" + + val hostIsMac = (host.family == Family.OSX) + val hostIsLinux = (host.family == Family.LINUX) + val hostIsMingw = (host.family == Family.MINGW) + + val hostSuffix get() = host.name + @JvmStatic + val hostName get() = host.name + + val knownTargetTemplates = listOf("zephyr") + + private val targetAliasResolutions = mapOf( + "linux" to "linux_x64", + "macbook" to "macos_x64", + "macos" to "macos_x64", + "imac" to "macos_x64", + "raspberrypi" to "linux_arm32_hfp", + "iphone32" to "ios_arm32", + "iphone" to "ios_arm64", + "ipad" to "ios_arm64", + "ios" to "ios_arm64", + "iphone_sim" to "ios_x64", + "mingw" to "mingw_x64" + ) + + private val targetAliases: Map> by lazy { + val result = mutableMapOf>() + targetAliasResolutions.entries.forEach { + result.getOrPut(it.value, { mutableListOf() } ).add(it.key) + } + result + } + + fun resolveAlias(request: String): String = targetAliasResolutions[request] ?: request + + fun listAliases(target: String): List = targetAliases[target] ?: emptyList() + } +} + +class TargetSupportException (message: String = "", cause: Throwable? = null) : Exception(message, cause) + diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt new file mode 100644 index 00000000000..be8818ad682 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Linker.kt @@ -0,0 +1,426 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.target + +import java.lang.ProcessBuilder +import java.lang.ProcessBuilder.Redirect +import org.jetbrains.kotlin.konan.exec.Command +import org.jetbrains.kotlin.konan.file.* + +typealias ObjectFile = String +typealias ExecutableFile = String + +enum class LinkerOutputKind { + DYNAMIC_LIBRARY, + STATIC_LIBRARY, + EXECUTABLE +} + +// Here we take somewhat unexpected approach - we create the thin +// library, and then repack it during post-link phase. +// This way we ensure .a inputs are properly processed. +private fun staticGnuArCommands(ar: String, executable: ExecutableFile, + objectFiles: List, libraries: List) = when { + HostManager.hostIsMingw -> { + val temp = executable.replace('/', '\\') + "__" + val arWindows = ar.replace('/', '\\') + listOf( + Command(arWindows, "-rucT", temp).apply { + +objectFiles + +libraries + }, + Command("cmd", "/c").apply { + +"(echo create $executable & echo addlib ${temp} & echo save & echo end) | $arWindows -M" + }, + Command("cmd", "/c", "del", "/q", temp)) + } + HostManager.hostIsLinux || HostManager.hostIsMac -> listOf( + Command(ar, "cqT", executable).apply { + +objectFiles + +libraries + }, + Command("/bin/sh", "-c").apply { + +"printf 'create $executable\\naddlib $executable\\nsave\\nend' | $ar -M" + }) + else -> TODO("Unsupported host ${HostManager.host}") + } + +// Use "clang -v -save-temps" to write linkCommand() method +// for another implementation of this class. +abstract class LinkerFlags(val configurables: Configurables) +/* : Configurables by configurables */ { + + protected val llvmBin = "${configurables.absoluteLlvmHome}/bin" + protected val llvmLib = "${configurables.absoluteLlvmHome}/lib" + + private val libLTODir = when (HostManager.host) { + KonanTarget.MACOS_X64, KonanTarget.LINUX_X64 -> llvmLib + KonanTarget.MINGW_X64 -> llvmBin + else -> error("Don't know libLTO location for this platform.") + } + + open val useCompilerDriverAsLinker: Boolean get() = false // TODO: refactor. + + abstract fun linkCommands(objectFiles: List, executable: ExecutableFile, + libraries: List, linkerArgs: List, + optimize: Boolean, debug: Boolean, + kind: LinkerOutputKind, outputDsymBundle: String): List + + abstract fun filterStaticLibraries(binaries: List): List + + open fun linkStaticLibraries(binaries: List): List { + val libraries = filterStaticLibraries(binaries) + // Let's just pass them as absolute paths. + return libraries + } +} + +open class AndroidLinker(targetProperties: AndroidConfigurables) + : LinkerFlags(targetProperties), AndroidConfigurables by targetProperties { + + private val prefix = "$absoluteTargetToolchain/bin/" + private val clang = if (HostManager.hostIsMingw) "$prefix/clang.cmd" else "$prefix/clang" + private val ar = "$absoluteTargetToolchain/${targetProperties.targetArg}/bin/ar" + + override val useCompilerDriverAsLinker: Boolean get() = true + + override fun filterStaticLibraries(binaries: List) = binaries.filter { it.isUnixStaticLib } + + override fun linkCommands(objectFiles: List, executable: ExecutableFile, + libraries: List, linkerArgs: List, + optimize: Boolean, debug: Boolean, + kind: LinkerOutputKind, outputDsymBundle: String): List { + if (kind == LinkerOutputKind.STATIC_LIBRARY) + return staticGnuArCommands(ar, executable, objectFiles, libraries) + + val dynamic = kind == LinkerOutputKind.DYNAMIC_LIBRARY + // liblog.so must be linked in, as we use its functionality in runtime. + return listOf(Command(clang).apply { + +"-o" + +executable + +"-fPIC" + +"-shared" + +"-llog" + +objectFiles + if (optimize) +linkerOptimizationFlags + if (!debug) +linkerNoDebugFlags + if (dynamic) +linkerDynamicFlags + if (dynamic) +"-Wl,-soname,${File(executable).name}" + +linkerKonanFlags + +libraries + +linkerArgs + }) + } +} + +open class MacOSBasedLinker(targetProperties: AppleConfigurables) + : LinkerFlags(targetProperties), AppleConfigurables by targetProperties { + + private val libtool = "$absoluteTargetToolchain/usr/bin/libtool" + private val linker = "$absoluteTargetToolchain/usr/bin/ld" + private val dsymutil = "$absoluteLlvmHome/bin/llvm-dsymutil" + + private fun provideCompilerRtLibrary(libraryName: String): String? { + val suffix = if (libraryName.isNotEmpty() && target == KonanTarget.IOS_X64) { + "iossim" + } else { + when (val family = configurables.target.family) { + Family.OSX -> "osx" + Family.IOS -> "ios" + else -> error("Family $family is unsupported") + } + } + val dir = File("$absoluteTargetToolchain/usr/lib/clang/").listFiles.firstOrNull()?.absolutePath + val mangledName = if (libraryName.isEmpty()) "" else "${libraryName}_" + + return if (dir != null) "$dir/lib/darwin/libclang_rt.$mangledName$suffix.a" else null + } + + private val compilerRtLibrary: String? by lazy { + provideCompilerRtLibrary("") + } + + // Code coverage requires this library. + private val profileLibrary: String? by lazy { + provideCompilerRtLibrary("profile") + } + + private val osVersionMinFlags: List by lazy { + listOf(osVersionMinFlagLd, osVersionMin + ".0") + } + + override fun filterStaticLibraries(binaries: List) = binaries.filter { it.isUnixStaticLib } + + override fun linkCommands(objectFiles: List, executable: ExecutableFile, + libraries: List, linkerArgs: List, + optimize: Boolean, debug: Boolean, kind: LinkerOutputKind, + outputDsymBundle: String): List { + if (kind == LinkerOutputKind.STATIC_LIBRARY) + return listOf(Command(libtool).apply { + +"-static" + +listOf("-o", executable) + +objectFiles + +libraries + }) + val dynamic = kind == LinkerOutputKind.DYNAMIC_LIBRARY + return listOf(Command(linker).apply { + +"-demangle" + +listOf("-dynamic", "-arch", arch) + +osVersionMinFlags + +listOf("-syslibroot", absoluteTargetSysRoot, "-o", executable) + +objectFiles + if (optimize) +linkerOptimizationFlags + if (!debug) +linkerNoDebugFlags + if (dynamic) +linkerDynamicFlags + +linkerKonanFlags + if (compilerRtLibrary != null) +compilerRtLibrary!! + if (profileLibrary != null) +profileLibrary!! + +libraries + +linkerArgs + +rpath(dynamic) + }) + if (debug) listOf(dsymUtilCommand(executable, outputDsymBundle)) else emptyList() + } + + private fun rpath(dynamic: Boolean): List = listOfNotNull( + when (target.family) { + Family.OSX -> "@executable_path/../Frameworks" + Family.IOS -> "@executable_path/Frameworks" + else -> error(target) + }, + "@loader_path/Frameworks".takeIf { dynamic } + ).flatMap { listOf("-rpath", it) } + + fun dsymUtilCommand(executable: ExecutableFile, outputDsymBundle: String) = + object : Command(dsymutilCommand(executable, outputDsymBundle)) { + override fun runProcess(): Int = + executeCommandWithFilter(command) + } + + // TODO: consider introducing a better filtering directly in Command. + private fun executeCommandWithFilter(command: List): Int { + val builder = ProcessBuilder(command) + + // Inherit main process output streams. + val isDsymUtil = (command[0] == dsymutil) + + builder.redirectOutput(Redirect.INHERIT) + builder.redirectInput(Redirect.INHERIT) + if (!isDsymUtil) + builder.redirectError(Redirect.INHERIT) + + val process = builder.start() + if (isDsymUtil) { + /** + * llvm-lto has option -alias that lets tool to know which symbol we use instead of _main, + * llvm-dsym doesn't have such a option, so we ignore annoying warning manually. + */ + val errorStream = process.errorStream + val outputStream = bufferedReader(errorStream) + while (true) { + val line = outputStream.readLine() ?: break + if (!line.contains("warning: could not find object file symbol for symbol _main")) + System.err.println(line) + } + outputStream.close() + } + val exitCode = process.waitFor() + return exitCode + } + + open fun dsymutilCommand(executable: ExecutableFile, outputDsymBundle: String): List = + listOf(dsymutil, executable, "-o", outputDsymBundle) + + open fun dsymutilDryRunVerboseCommand(executable: ExecutableFile): List = + listOf(dsymutil, "-dump-debug-map", executable) +} + +open class LinuxBasedLinker(targetProperties: LinuxBasedConfigurables) + : LinkerFlags(targetProperties), LinuxBasedConfigurables by targetProperties { + + private val ar = if (HostManager.hostIsMac) "$absoluteTargetToolchain/bin/llvm-ar" + else "$absoluteTargetToolchain/bin/ar" + override val libGcc: String = "$absoluteTargetSysRoot/${super.libGcc}" + private val linker = "$absoluteLlvmHome/bin/ld.lld" + private val specificLibs = abiSpecificLibraries.map { "-L${absoluteTargetSysRoot}/$it" } + + override fun filterStaticLibraries(binaries: List) = binaries.filter { it.isUnixStaticLib } + + override fun linkCommands(objectFiles: List, executable: ExecutableFile, + libraries: List, linkerArgs: List, + optimize: Boolean, debug: Boolean, + kind: LinkerOutputKind, outputDsymBundle: String): List { + if (kind == LinkerOutputKind.STATIC_LIBRARY) + return staticGnuArCommands(ar, executable, objectFiles, libraries) + + val isMips = (configurables is LinuxMIPSConfigurables) + val dynamic = kind == LinkerOutputKind.DYNAMIC_LIBRARY + // TODO: Can we extract more to the konan.configurables? + return listOf(Command(linker).apply { + +"--sysroot=${absoluteTargetSysRoot}" + +"-export-dynamic" + +"-z" + +"relro" + +"--build-id" + +"--eh-frame-hdr" + +"-dynamic-linker" + +dynamicLinker + +"-o" + +executable + if (!dynamic) +"$absoluteTargetSysRoot/usr/lib64/crt1.o" + +"$absoluteTargetSysRoot/usr/lib64/crti.o" + +if (dynamic) "$libGcc/crtbeginS.o" else "$libGcc/crtbegin.o" + +"-L$llvmLib" + +"-L$libGcc" + if (!isMips) +"--hash-style=gnu" // MIPS doesn't support hash-style=gnu + +specificLibs + +listOf("-L$absoluteTargetSysRoot/../lib", "-L$absoluteTargetSysRoot/lib", "-L$absoluteTargetSysRoot/usr/lib") + if (optimize) +linkerOptimizationFlags + if (!debug) +linkerNoDebugFlags + if (dynamic) +linkerDynamicFlags + +objectFiles + +linkerKonanFlags + +listOf("-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed", + "-lc", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed") + +if (dynamic) "$libGcc/crtendS.o" else "$libGcc/crtend.o" + +"$absoluteTargetSysRoot/usr/lib64/crtn.o" + +libraries + +linkerArgs + }) + } +} + +open class MingwLinker(targetProperties: MingwConfigurables) + : LinkerFlags(targetProperties), MingwConfigurables by targetProperties { + + private val ar = "$absoluteTargetToolchain/bin/ar" + private val linker = "$absoluteTargetToolchain/bin/clang++" + + override val useCompilerDriverAsLinker: Boolean get() = true + + override fun filterStaticLibraries(binaries: List) = binaries.filter { it.isWindowsStaticLib || it.isUnixStaticLib } + + override fun linkCommands(objectFiles: List, executable: ExecutableFile, + libraries: List, linkerArgs: List, + optimize: Boolean, debug: Boolean, + kind: LinkerOutputKind, outputDsymBundle: String): List { + if (kind == LinkerOutputKind.STATIC_LIBRARY) + return staticGnuArCommands(ar, executable, objectFiles, libraries) + + val dynamic = kind == LinkerOutputKind.DYNAMIC_LIBRARY + return listOf(when { + HostManager.hostIsMingw -> Command(linker) + else -> Command("wine64", "$linker.exe") + }.apply { + +listOf("-o", executable) + +objectFiles + if (optimize) +linkerOptimizationFlags + if (!debug) +linkerNoDebugFlags + if (dynamic) +linkerDynamicFlags + +libraries + +linkerArgs + +linkerKonanFlags + }) + } +} + +open class WasmLinker(targetProperties: WasmConfigurables) + : LinkerFlags(targetProperties), WasmConfigurables by targetProperties { + + override val useCompilerDriverAsLinker: Boolean get() = false + + override fun filterStaticLibraries(binaries: List) = binaries.filter { it.isJavaScript } + + override fun linkCommands(objectFiles: List, executable: ExecutableFile, + libraries: List, linkerArgs: List, + optimize: Boolean, debug: Boolean, + kind: LinkerOutputKind, outputDsymBundle: String): List { + if (kind != LinkerOutputKind.EXECUTABLE) throw Error("Unsupported linker output kind") + + // TODO(horsh): maybe rethink it. + return listOf(object : Command() { + override fun execute() { + val src = File(objectFiles.single()) + val dst = File(executable) + src.recursiveCopyTo(dst) + javaScriptLink(libraries, executable) + } + + private fun javaScriptLink(jsFiles: List, executable: String): String { + val linkedJavaScript = File("$executable.js") + + val linkerHeader = "var konan = { libraries: [] };\n" + val linkerFooter = """|if (isBrowser()) { + | konan.moduleEntry([]); + |} else { + | konan.moduleEntry(arguments); + |}""".trimMargin() + + linkedJavaScript.writeText(linkerHeader) + + jsFiles.forEach { + linkedJavaScript.appendBytes(File(it).readBytes()) + } + + linkedJavaScript.appendBytes(linkerFooter.toByteArray()) + return linkedJavaScript.name + } + }) + } +} + +open class ZephyrLinker(targetProperties: ZephyrConfigurables) + : LinkerFlags(targetProperties), ZephyrConfigurables by targetProperties { + + private val linker = "$absoluteTargetToolchain/bin/ld" + + override val useCompilerDriverAsLinker: Boolean get() = false + + override fun filterStaticLibraries(binaries: List) = emptyList() + + override fun linkCommands(objectFiles: List, executable: ExecutableFile, + libraries: List, linkerArgs: List, + optimize: Boolean, debug: Boolean, + kind: LinkerOutputKind, outputDsymBundle: String): List { + if (kind != LinkerOutputKind.EXECUTABLE) throw Error("Unsupported linker output kind: $kind") + return listOf(Command(linker).apply { + +listOf("-r", "--gc-sections", "--entry", "main") + +listOf("-o", executable) + +objectFiles + +libraries + +linkerArgs + }) + } +} + +fun linker(configurables: Configurables): LinkerFlags = + when (configurables.target) { + KonanTarget.LINUX_X64, KonanTarget.LINUX_ARM32_HFP -> + LinuxBasedLinker(configurables as LinuxConfigurables) + KonanTarget.LINUX_MIPS32, KonanTarget.LINUX_MIPSEL32 -> + LinuxBasedLinker(configurables as LinuxMIPSConfigurables) + KonanTarget.MACOS_X64, KonanTarget.IOS_ARM32, KonanTarget.IOS_ARM64, KonanTarget.IOS_X64 -> + MacOSBasedLinker(configurables as AppleConfigurables) + KonanTarget.ANDROID_ARM32, KonanTarget.ANDROID_ARM64 -> + AndroidLinker(configurables as AndroidConfigurables) + KonanTarget.MINGW_X64, KonanTarget.MINGW_X86 -> + MingwLinker(configurables as MingwConfigurables) + KonanTarget.WASM32 -> + WasmLinker(configurables as WasmConfigurables) + is KonanTarget.ZEPHYR -> + ZephyrLinker(configurables as ZephyrConfigurables) + } + diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Platform.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Platform.kt new file mode 100644 index 00000000000..0ba5a07e0dd --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Platform.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.target + +import org.jetbrains.kotlin.konan.util.DependencyProcessor + +class Platform(val configurables: Configurables) + : Configurables by configurables { + + val clang by lazy { + ClangArgs(configurables) + } + val linker by lazy { + linker(configurables) + } +} + +class PlatformManager(distribution: Distribution = Distribution(), experimental: Boolean = false) : + HostManager(distribution, experimental) { + + private val loaders = enabled.map { + it to loadConfigurables(it, distribution.properties, DependencyProcessor.defaultDependenciesRoot.absolutePath) + }.toMap() + + private val platforms = loaders.map { + it.key to Platform(it.value) + }.toMap() + + fun platform(target: KonanTarget) = platforms.getValue(target) + val hostPlatform = platforms.getValue(host) + + fun loader(target: KonanTarget) = loaders.getValue(target) +} + diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Properties.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Properties.kt new file mode 100644 index 00000000000..eb886881b61 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Properties.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.properties + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove the whole file! + +import org.jetbrains.kotlin.konan.file.* + +typealias Properties = java.util.Properties + +fun File.loadProperties(): Properties { + val properties = java.util.Properties() + this.bufferedReader().use { reader -> + properties.load(reader) + } + return properties +} + +fun loadProperties(path: String): Properties = File(path).loadProperties() + +fun File.saveProperties(properties: Properties) { + this.outputStream().use { + properties.store(it, null) + } +} + +fun Properties.saveToFile(file: File) = file.saveProperties(this) + +fun Properties.propertyString(key: String, suffix: String? = null): String? = getProperty(key.suffix(suffix)) ?: this.getProperty(key) + +/** + * TODO: this method working with suffixes should be replaced with + * functionality borrowed from def file parser and unified for interop tool + * and kotlin compiler. + */ +fun Properties.propertyList(key: String, suffix: String? = null): List { + val value = this.getProperty(key.suffix(suffix)) ?: this.getProperty(key) + if (value?.isBlank() == true) return emptyList() + + return value?.split(Regex("\\s+")) ?: emptyList() +} + +fun Properties.hasProperty(key: String, suffix: String? = null): Boolean + = this.getProperty(key.suffix(suffix)) != null + +fun String.suffix(suf: String?): String = + if (suf == null) this + else "${this}.$suf" + + diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/TargetProperties.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/TargetProperties.kt new file mode 100644 index 00000000000..ee1c4634086 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/TargetProperties.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.properties + +import org.jetbrains.kotlin.konan.target.* + +fun Properties.hostString(name: String): String? + = this.propertyString(name, HostManager.hostName) + +fun Properties.hostList(name: String): List + = this.propertyList(name, HostManager.hostName) + +fun Properties.targetString(name: String, target: KonanTarget): String? + = this.propertyString(name, target.name) + +fun Properties.targetList(name: String, target: KonanTarget): List + = this.propertyList(name, target.name) + +fun Properties.hostTargetString(name: String, target: KonanTarget): String? + = this.propertyString(name, hostTargetSuffix(HostManager.host, target)) + +fun Properties.hostTargetList(name: String, target: KonanTarget): List + = this.propertyList(name, hostTargetSuffix(HostManager.host, target)) \ No newline at end of file diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Xcode.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Xcode.kt new file mode 100644 index 00000000000..ff1d581de0c --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/Xcode.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.target + +import org.jetbrains.kotlin.konan.KonanExternalToolFailure +import org.jetbrains.kotlin.konan.exec.Command +import org.jetbrains.kotlin.konan.file.File + +interface Xcode { + val toolchain: String + val macosxSdk: String + val iphoneosSdk: String + val iphonesimulatorSdk: String + val version: String + + companion object { + val current: Xcode by lazy { + CurrentXcode + } + } +} + +private object CurrentXcode : Xcode { + + override val toolchain by lazy { + val ldPath = xcrun("-f", "ld") // = $toolchain/usr/bin/ld + File(ldPath).parentFile.parentFile.parentFile.absolutePath + } + + override val macosxSdk by lazy { getSdkPath("macosx") } + override val iphoneosSdk by lazy { getSdkPath("iphoneos") } + override val iphonesimulatorSdk by lazy { getSdkPath("iphonesimulator") } + + override val version by lazy { + xcrun("xcodebuild", "-version") + .removePrefix("Xcode ") + } + + private fun xcrun(vararg args: String): String = + Command("/usr/bin/xcrun", *args).getOutputLines().first() // TODO: handle execution error + + private fun getSdkPath(sdk: String) = xcrun("--sdk", sdk, "--show-sdk-path") +} diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DefFile.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DefFile.kt new file mode 100644 index 00000000000..132832d332c --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DefFile.kt @@ -0,0 +1,156 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.util + +import java.io.File +import java.io.StringReader +import java.util.* + +class DefFile(val file:File?, val config:DefFileConfig, val manifestAddendProperties:Properties, val defHeaderLines:List) { + private constructor(file0:File?, triple: Triple>): this(file0, DefFileConfig(triple.first), triple.second, triple.third) + constructor(file:File?, substitutions: Map) : this(file, parseDefFile(file, substitutions)) + + val name by lazy { + file?.nameWithoutExtension ?: "" + } + class DefFileConfig(private val properties: Properties) { + val headers by lazy { + properties.getSpaceSeparated("headers") + } + + val modules by lazy { + properties.getSpaceSeparated("modules") + } + + val language by lazy { + properties.getProperty("language") + } + + val compilerOpts by lazy { + properties.getSpaceSeparated("compilerOpts") + } + + val excludeSystemLibs by lazy { + properties.getProperty("excludeSystemLibs")?.toBoolean() ?: false + } + + val excludeDependentModules by lazy { + properties.getProperty("excludeDependentModules")?.toBoolean() ?: false + } + + val entryPoints by lazy { + properties.getSpaceSeparated("entryPoint") + } + + val linkerOpts by lazy { + properties.getSpaceSeparated("linkerOpts") + } + + val linker by lazy { + properties.getProperty("linker", "clang") + } + + val excludedFunctions by lazy { + properties.getSpaceSeparated("excludedFunctions") + } + + val excludedMacros by lazy { + properties.getSpaceSeparated("excludedMacros") + } + + val staticLibraries by lazy { + properties.getSpaceSeparated("staticLibraries") + } + + val libraryPaths by lazy { + properties.getSpaceSeparated("libraryPaths") + } + + val packageName by lazy { + properties.getProperty("package") + } + + val headerFilter by lazy { + properties.getSpaceSeparated("headerFilter") + } + + val strictEnums by lazy { + properties.getSpaceSeparated("strictEnums") + } + + val nonStrictEnums by lazy { + properties.getSpaceSeparated("nonStrictEnums") + } + + val noStringConversion by lazy { + properties.getSpaceSeparated("noStringConversion") + } + + val depends by lazy { + properties.getSpaceSeparated("depends") + } + + val exportForwardDeclarations by lazy { + properties.getSpaceSeparated("exportForwardDeclarations") + } + + val disableDesignatedInitializerChecks by lazy { + properties.getProperty("disableDesignatedInitializerChecks")?.toBoolean() ?: false + } + } +} + +private fun Properties.getSpaceSeparated(name: String): List { + return this.getProperty(name)?.split(' ')?.filter { it.isNotEmpty() } ?: emptyList() +} + +private fun parseDefFile(file: File?, substitutions: Map): Triple> { + val properties = Properties() + + if (file == null) { + return Triple(properties, Properties(), emptyList()) + } + + val lines = file.readLines() + + val separator = "---" + val separatorIndex = lines.indexOf(separator) + + val propertyLines: List + val headerLines: List + + if (separatorIndex != -1) { + propertyLines = lines.subList(0, separatorIndex) + headerLines = lines.subList(separatorIndex + 1, lines.size) + } else { + propertyLines = lines + headerLines = emptyList() + } + + val propertiesReader = StringReader(propertyLines.joinToString(System.lineSeparator())) + properties.load(propertiesReader) + + // Pass unsubstituted copy of properties we have obtained from `.def` + // to compiler `-maniest`. + val manifestAddendProperties = properties.duplicate() + + substitute(properties, substitutions) + + return Triple(properties, manifestAddendProperties, headerLines) +} + +private fun Properties.duplicate() = Properties().apply { putAll(this@duplicate) } diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyDownloader.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyDownloader.kt new file mode 100644 index 00000000000..b00dc06856a --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyDownloader.kt @@ -0,0 +1,221 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.util + +import java.io.* +import java.net.HttpURLConnection +import java.net.URL +import java.net.URLConnection +import java.nio.file.Files +import java.nio.file.StandardCopyOption +import java.util.concurrent.* + +typealias ProgressCallback = (url: String, currentBytes: Long, totalBytes: Long) -> Unit + +class DependencyDownloader( + var maxAttempts: Int = DEFAULT_MAX_ATTEMPTS, + var attemptIntervalMs: Long = DEFAULT_ATTEMPT_INTERVAL_MS, + customProgressCallback: ProgressCallback? = null +) { + + private val progressCallback = customProgressCallback ?: { url, currentBytes, totalBytes -> + print("\rDownloading dependency: $url (${currentBytes.humanReadable}/${totalBytes.humanReadable}). ") + } + + val executor = ExecutorCompletionService(Executors.newSingleThreadExecutor(object : ThreadFactory { + override fun newThread(r: Runnable?): Thread { + val thread = Thread(r) + thread.name = "konan-dependency-downloader" + thread.isDaemon = true + + return thread + } + })) + + enum class ReplacingMode { + /** Redownload the file and replace the existing one. */ + REPLACE, + /** Throw FileAlreadyExistsException */ + THROW, + /** Don't download the file and return the existing one*/ + RETURN_EXISTING + } + + class HTTPResponseException(val url: URL, val responseCode: Int) + : IOException("Server returned HTTP response code: $responseCode for URL: $url") + + class DownloadingProgress(@Volatile var currentBytes: Long) { + fun update(readBytes: Int) { currentBytes += readBytes } + } + + private fun HttpURLConnection.checkHTTPResponse(expected: Int, originalUrl: URL = url) { + if (responseCode != expected) { + throw HTTPResponseException(originalUrl, responseCode) + } + } + + private fun HttpURLConnection.checkHTTPResponse(originalUrl: URL, predicate: (Int) -> Boolean) { + if (!predicate(responseCode)) { + throw HTTPResponseException(originalUrl, responseCode) + } + } + + private fun doDownload(originalUrl: URL, + connection: URLConnection, + tmpFile: File, + currentBytes: Long, + totalBytes: Long, + append: Boolean) { + val progress = DownloadingProgress(currentBytes) + + // TODO: Implement multi-thread downloading. + executor.submit { + connection.getInputStream().use { from -> + FileOutputStream(tmpFile, append).use { to -> + val buffer = ByteArray(DEFAULT_BUFFER_SIZE) + var read = from.read(buffer) + while (read != -1) { + if (Thread.interrupted()) { + throw InterruptedException() + } + to.write(buffer, 0, read) + progress.update(read) + read = from.read(buffer) + } + if (progress.currentBytes != totalBytes) { + throw EOFException("The stream closed before end of downloading.") + } + } + } + } + + var result: Future? + do { + progressCallback(originalUrl.toString(), progress.currentBytes, totalBytes) + result = executor.poll(1, TimeUnit.SECONDS) + } while(result == null) + progressCallback(originalUrl.toString(), progress.currentBytes, totalBytes) + + try { + result.get() + } catch (e: ExecutionException) { + throw e.cause ?: e + } + } + + private fun resumeDownload(originalUrl: URL, originalConnection: HttpURLConnection, tmpFile: File) { + originalConnection.connect() + val totalBytes = originalConnection.contentLengthLong + val currentBytes = tmpFile.length() + if (currentBytes >= totalBytes || originalConnection.getHeaderField("Accept-Ranges") != "bytes") { + // The temporary file is bigger then expected or the server doesn't support resuming downloading. + // Download the file from scratch. + doDownload(originalUrl, originalConnection, tmpFile, 0, totalBytes, false) + } else { + originalConnection.disconnect() + val rangeConnection = originalUrl.openConnection() as HttpURLConnection + rangeConnection.setRequestProperty("range", "bytes=$currentBytes-") + rangeConnection.connect() + rangeConnection.checkHTTPResponse(originalUrl) { + it == HttpURLConnection.HTTP_PARTIAL || it == HttpURLConnection.HTTP_OK + } + doDownload(originalUrl, rangeConnection, tmpFile, currentBytes, totalBytes, true) + } + } + + /** Performs an attempt to download a specified file into the specified location */ + private fun tryDownload(url: URL, tmpFile: File) { + val connection = url.openConnection() + + (connection as? HttpURLConnection)?.checkHTTPResponse(HttpURLConnection.HTTP_OK, url) + + if (connection is HttpURLConnection && tmpFile.exists()) { + resumeDownload(url, connection, tmpFile) + } else { + connection.connect() + val totalBytes = connection.contentLengthLong + doDownload(url, connection, tmpFile, 0, totalBytes, false) + } + } + + /** Downloads a file from [source] url to [destination]. Returns [destination]. */ + fun download(source: URL, + destination: File, + replace: ReplacingMode = ReplacingMode.RETURN_EXISTING): File { + + if (destination.exists()) { + when (replace) { + ReplacingMode.RETURN_EXISTING -> return destination + ReplacingMode.THROW -> throw FileAlreadyExistsException(destination) + ReplacingMode.REPLACE -> Unit // Just continue with downloading. + } + } + val tmpFile = File("${destination.canonicalPath}.$TMP_SUFFIX") + + check(!tmpFile.isDirectory) { + "A temporary file is a directory: ${tmpFile.canonicalPath}. Remove it and try again." + } + check(!destination.isDirectory) { + "The destination file is a directory: ${destination.canonicalPath}. Remove it and try again." + } + + var attempt = 1 + var waitTime = 0L + while (true) { + try { + tryDownload(source, tmpFile) + break + } catch (e: HTTPResponseException) { + throw e + } catch (e: IOException) { + if (attempt >= maxAttempts) { + throw e + } + attempt++ + waitTime += attemptIntervalMs + println("Cannot download a dependency: $e\n" + + "Waiting ${waitTime.toDouble() / 1000} sec and trying again (attempt: $attempt/$maxAttempts).") + // TODO: Wait better + Thread.sleep(waitTime) + } + } + + Files.move(tmpFile.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING) + println("Done.") + return destination + } + + private val Long.humanReadable: String + get() { + if (this < 0) { + return "-" + } + if (this < 1024) { + return "$this bytes" + } + val exp = (Math.log(this.toDouble()) / Math.log(1024.0)).toInt() + val prefix = "kMGTPE"[exp-1] + return "%.1f %siB".format(this / Math.pow(1024.0, exp.toDouble()), prefix) + } + + companion object { + const val DEFAULT_MAX_ATTEMPTS = 10 + const val DEFAULT_ATTEMPT_INTERVAL_MS = 3000L + + const val TMP_SUFFIX = "part" + } +} diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyExtractor.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyExtractor.kt new file mode 100644 index 00000000000..dd7c89c17e4 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyExtractor.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.util + +import org.jetbrains.kotlin.konan.file.unzipTo +import java.io.File +import java.util.concurrent.TimeUnit + + +class DependencyExtractor { + internal val useZip = System.getProperty("os.name").startsWith("Windows") + + internal val archiveExtension = if (useZip) { + "zip" + } else { + "tar.gz" + } + + private fun extractTarGz(tarGz: File, targetDirectory: File) { + val tarProcess = ProcessBuilder().apply { + command("tar", "-xzf", tarGz.canonicalPath) + directory(targetDirectory) + inheritIO() + }.start() + val finished = tarProcess.waitFor(extractionTimeout, extractionTimeoutUntis) + when { + finished && tarProcess.exitValue() != 0 -> + throw RuntimeException( + "Cannot extract archive with dependency: ${tarGz.canonicalPath}.\n" + + "Tar exit code: ${tarProcess.exitValue()}." + ) + !finished -> { + tarProcess.destroy() + throw RuntimeException( + "Cannot extract archive with dependency: ${tarGz.canonicalPath}.\n" + + "Tar process hasn't finished in ${extractionTimeoutUntis.toSeconds(extractionTimeout)} sec.") + } + } + } + + fun extract(archive: File, targetDirectory: File) { + if (useZip) { + archive.toPath().unzipTo(targetDirectory.toPath()) + } else { + extractTarGz(archive, targetDirectory) + } + } + + companion object { + val extractionTimeout = 3600L + val extractionTimeoutUntis = TimeUnit.SECONDS + } + +} \ No newline at end of file diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyProcessor.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyProcessor.kt new file mode 100644 index 00000000000..58105bce151 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyProcessor.kt @@ -0,0 +1,307 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.util + +import org.jetbrains.kotlin.konan.file.use +import org.jetbrains.kotlin.konan.properties.KonanPropertiesLoader +import org.jetbrains.kotlin.konan.properties.Properties +import org.jetbrains.kotlin.konan.properties.propertyList +import java.io.File +import java.io.FileNotFoundException +import java.io.RandomAccessFile +import java.net.InetAddress +import java.net.URL +import java.net.UnknownHostException +import java.nio.file.Paths +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.withLock + +private val Properties.dependenciesUrl : String + get() = getProperty("dependenciesUrl") + ?: throw IllegalStateException("No such property in konan.properties: dependenciesUrl") + +private val Properties.airplaneMode : Boolean + get() = getProperty("airplaneMode")?.toBoolean() ?: false + +private val Properties.downloadingAttempts : Int + get() = getProperty("downloadingAttempts")?.toInt() + ?: DependencyDownloader.DEFAULT_MAX_ATTEMPTS + +private val Properties.downloadingAttemptIntervalMs : Long + get() = getProperty("downloadingAttemptPauseMs")?.toLong() + ?: DependencyDownloader.DEFAULT_ATTEMPT_INTERVAL_MS + +private fun Properties.findCandidates(dependencies: List): Map> { + val dependencyProfiles = this.propertyList("dependencyProfiles") + return dependencies.map { dependency -> + dependency to dependencyProfiles.flatMap { profile -> + val candidateSpecs = propertyList("$dependency.$profile") + if (profile == "default" && candidateSpecs.isEmpty()) { + listOf(DependencySource.Remote.Public) + } else { + candidateSpecs.map { candidateSpec -> + when (candidateSpec) { + "remote:public" -> DependencySource.Remote.Public + "remote:internal" -> DependencySource.Remote.Internal + else -> DependencySource.Local(File(candidateSpec)) + } + } + } + } + }.toMap() +} + + +private val KonanPropertiesLoader.dependenciesUrl : String get() = properties.dependenciesUrl +private val KonanPropertiesLoader.airplaneMode : Boolean get() = properties.airplaneMode +private val KonanPropertiesLoader.downloadingAttempts : Int get() = properties.downloadingAttempts +private val KonanPropertiesLoader.downloadingAttemptIntervalMs : Long get() = properties.downloadingAttemptIntervalMs + +sealed class DependencySource { + data class Local(val path: File) : DependencySource() + + sealed class Remote : DependencySource() { + object Public : Remote() + object Internal : Remote() + } +} + +/** + * Inspects [dependencies] and downloads all the missing ones into [dependenciesDirectory] from [dependenciesUrl]. + * If [airplaneMode] is true will throw a RuntimeException instead of downloading. + */ +class DependencyProcessor(dependenciesRoot: File, + val dependenciesUrl: String, + dependencyToCandidates: Map>, + homeDependencyCache: File = defaultDependencyCacheDir, + val airplaneMode: Boolean = false, + maxAttempts: Int = DependencyDownloader.DEFAULT_MAX_ATTEMPTS, + attemptIntervalMs: Long = DependencyDownloader.DEFAULT_ATTEMPT_INTERVAL_MS, + customProgressCallback: ProgressCallback? = null, + val keepUnstable: Boolean = true, + val deleteArchives: Boolean = true) { + + val dependenciesDirectory = dependenciesRoot.apply { mkdirs() } + val cacheDirectory = homeDependencyCache.apply { mkdirs() } + + val lockFile = File(cacheDirectory, ".lock").apply { if (!exists()) createNewFile() } + + var showInfo = true + private var isInfoShown = false + + private val downloader = DependencyDownloader(maxAttempts, attemptIntervalMs, customProgressCallback) + private val extractor = DependencyExtractor() + + private val archiveExtension get() = extractor.archiveExtension + + constructor(dependenciesRoot: File, + properties: KonanPropertiesLoader, + dependenciesUrl: String = properties.dependenciesUrl, + keepUnstable:Boolean = true) : this( + dependenciesRoot, + properties.properties, + properties.dependencies, + dependenciesUrl, + keepUnstable = keepUnstable) + + constructor(dependenciesRoot: File, + properties: Properties, + dependencies: List, + dependenciesUrl: String = properties.dependenciesUrl, + keepUnstable:Boolean = true) : this( + dependenciesRoot, + dependenciesUrl, + dependencyToCandidates = properties.findCandidates(dependencies), + airplaneMode = properties.airplaneMode, + maxAttempts = properties.downloadingAttempts, + attemptIntervalMs = properties.downloadingAttemptIntervalMs, + keepUnstable = keepUnstable) + + + class DependencyFile(directory: File, fileName: String) { + val file = File(directory, fileName).apply { createNewFile() } + private val dependencies = file.readLines().toMutableSet() + + fun contains(dependency: String) = dependencies.contains(dependency) + fun add(dependency: String) = dependencies.add(dependency) + fun remove(dependency: String) = dependencies.remove(dependency) + + fun removeAndSave(dependency: String) { + remove(dependency) + save() + } + + fun addAndSave(dependency: String) { + add(dependency) + save() + } + + fun save() { + val writer = file.writer() + writer.use { + dependencies.forEach { + writer.write(it) + writer.write("\n") + } + } + } + } + + private fun downloadDependency(dependency: String, baseUrl: String) { + val depDir = File(dependenciesDirectory, dependency) + val depName = depDir.name + + val fileName = "$depName.$archiveExtension" + val archive = cacheDirectory.resolve(fileName) + val url = URL("$baseUrl/$fileName") + + val extractedDependencies = DependencyFile(dependenciesDirectory, ".extracted") + if (extractedDependencies.contains(depName) && + depDir.exists() && + depDir.isDirectory && + depDir.list().isNotEmpty()) { + + if (!keepUnstable && depDir.list().contains(".unstable")) { + // The downloaded version of the dependency is unstable -> redownload it. + depDir.deleteRecursively() + archive.delete() + extractedDependencies.removeAndSave(dependency) + } else { + return + } + } + + if (showInfo && !isInfoShown) { + println("Downloading native dependencies (LLVM, sysroot etc). This is a one-time action performed only on the first run of the compiler.") + isInfoShown = true + } + + if (!archive.exists()) { + if (airplaneMode) { + throw FileNotFoundException(""" + Cannot find a dependency locally: $dependency. + Set `airplaneMode = false` in konan.properties to download it. + """.trimIndent()) + } + downloader.download(url, archive) + } + println("Extracting dependency: $archive into $dependenciesDirectory") + extractor.extract(archive, dependenciesDirectory) + if (deleteArchives) { + archive.delete() + } + extractedDependencies.addAndSave(depName) + } + + companion object { + private val lock = ReentrantLock() + + val localKonanDir: File by lazy { + File(System.getenv("KONAN_DATA_DIR") ?: (System.getProperty("user.home") + File.separator + ".konan")) + } + + @JvmStatic + val defaultDependenciesRoot: File + get() = localKonanDir.resolve("dependencies") + + val defaultDependencyCacheDir: File + get() = localKonanDir.resolve("cache") + } + + private val resolvedDependencies = dependencyToCandidates.map { (dependency, candidates) -> + val candidate = candidates.asSequence().mapNotNull { candidate -> + when (candidate) { + is DependencySource.Local -> candidate.takeIf { it.path.exists() } + DependencySource.Remote.Public -> candidate + DependencySource.Remote.Internal -> candidate.takeIf { InternalServer.isAvailable } + } + }.firstOrNull() + + candidate ?: error("$dependency is not available; candidates:\n${candidates.joinToString("\n")}") + + dependency to candidate + }.toMap() + + private fun resolveDependency(dependency: String): File { + val candidate = resolvedDependencies[dependency] + return when (candidate) { + is DependencySource.Local -> candidate.path + is DependencySource.Remote -> File(dependenciesDirectory, dependency) + null -> error("$dependency not declared as dependency") + } + } + + fun resolveRelative(relative: String): File { + val path = Paths.get(relative) + if (path.isAbsolute) error("not a relative path: $relative") + + val dependency = path.first().toString() + return resolveDependency(dependency).let { + if (path.nameCount > 1) { + it.toPath().resolve(path.subpath(1, path.nameCount)).toFile() + } else { + it + } + } + } + + fun run() = lock.withLock { + RandomAccessFile(lockFile, "rw").channel.lock().use { + resolvedDependencies.forEach { (dependency, candidate) -> + val baseUrl = when (candidate) { + is DependencySource.Local -> null + DependencySource.Remote.Public -> dependenciesUrl + DependencySource.Remote.Internal -> InternalServer.url + } + // TODO: consider using different caches for different remotes. + if (baseUrl != null) { + downloadDependency(dependency, baseUrl) + } + } + } + } +} + +internal object InternalServer { + private val host = "repo.labs.intellij.net" + val url = "http://$host/kotlin-native" + + private val internalDomain = "labs.intellij.net" + + val isAvailable by lazy { + val envKey = "KONAN_USE_INTERNAL_SERVER" + val envValue = System.getenv(envKey) + when (envValue) { + "0" -> false + "1" -> true + null -> checkAccessible() + else -> error("unexpected environment: $envKey=$envValue") + } + } + + private fun checkAccessible() = try { + if (!InetAddress.getLocalHost().canonicalHostName.endsWith(".$internalDomain")) { + // Fast path: + false + } else { + InetAddress.getByName(host) + true + } + } catch (e: UnknownHostException) { + false + } +} diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/Helper0.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/Helper0.kt new file mode 100644 index 00000000000..ec2169040ae --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/Helper0.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.util + +import java.io.File +import java.util.* + +class Helper0(val dependenciesDir: String, + val properties: Properties, + val dependencies: List): Runnable { + + override fun run() { + DependencyProcessor(File(dependenciesDir), + properties, + dependencies, + properties.getProperty("dependenciesUrl", "https://download.jetbrains.com/kotlin/native") + ).run() + } +} \ No newline at end of file diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/Substitution.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/Substitution.kt new file mode 100644 index 00000000000..31e9f6b4009 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/Substitution.kt @@ -0,0 +1,29 @@ +package org.jetbrains.kotlin.konan.util + +import org.jetbrains.kotlin.konan.target.KonanTarget +import java.util.* + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove the whole file! + +fun defaultTargetSubstitutions(target: KonanTarget) = + mapOf( + "target" to target.visibleName, + "arch" to target.architecture.visibleName, + "family" to target.family.visibleName) + +// Performs substitution similar to: +// foo = ${foo} ${foo.${arch}} ${foo.${os}} +fun substitute(properties: Properties, substitutions: Map) { + for (key in properties.stringPropertyNames()) { + for (substitution in substitutions.values) { + val suffix = ".$substitution" + if (key.endsWith(suffix)) { + val baseKey = key.removeSuffix(suffix) + val oldValue = properties.getProperty(baseKey, "") + val appendedValue = properties.getProperty(key, "") + val newValue = if (oldValue != "") "$oldValue $appendedValue" else appendedValue + properties.setProperty(baseKey, newValue) + } + } + } +} diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/TargetDefFile.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/TargetDefFile.kt new file mode 100644 index 00000000000..860af6b16fd --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/TargetDefFile.kt @@ -0,0 +1,6 @@ +package org.jetbrains.kotlin.konan.util + +import org.jetbrains.kotlin.konan.target.KonanTarget +import java.io.File + +fun DefFile(file: File?, target: KonanTarget) = DefFile(file, defaultTargetSubstitutions(target)) diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/Util.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/Util.kt new file mode 100644 index 00000000000..d3f8d6bab50 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/Util.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.konan.util + +import kotlin.system.measureTimeMillis +import org.jetbrains.kotlin.konan.file.* + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this function: +fun printMillisec(message: String, body: () -> T): T { + var result: T? = null + val msec = measureTimeMillis{ + result = body() + } + println("$message: $msec msec") + return result!! +} + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this function: +fun profile(message: String, body: () -> Unit) = profileIf( + System.getProperty("konan.profile")?.equals("true") ?: false, + message, body) + +// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this function: +fun profileIf(condition: Boolean, message: String, body: () -> Unit) = + if (condition) printMillisec(message, body) else body() + +fun nTabs(amount: Int): String { + return String.format("%1$-${(amount+1)*4}s", "") +} + +fun String.prefixIfNot(prefix: String) = + if (this.startsWith(prefix)) this else "$prefix$this" + +fun String.prefixBaseNameIfNot(prefix: String): String { + val file = File(this).absoluteFile + val name = file.name + val directory = file.parent + return "$directory/${name.prefixIfNot(prefix)}" +} + +fun String.suffixIfNot(suffix: String) = + if (this.endsWith(suffix)) this else "$this$suffix" + +fun String.removeSuffixIfPresent(suffix: String) = + if (this.endsWith(suffix)) this.dropLast(suffix.length) else this + +fun Lazy.getValueOrNull(): T? = if (isInitialized()) value else null diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/WithLogger.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/WithLogger.kt new file mode 100644 index 00000000000..6a0908b8061 --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/WithLogger.kt @@ -0,0 +1,10 @@ +package org.jetbrains.kotlin.konan.util + +typealias Logger = (String) -> Unit + +interface WithLogger { + val logger: Logger +} + +fun dummyLogger(message: String) {} + diff --git a/tools/kotlin-native-gradle-plugin/build.gradle b/tools/kotlin-native-gradle-plugin/build.gradle index bb246b8b8a1..2f0049ed891 100644 --- a/tools/kotlin-native-gradle-plugin/build.gradle +++ b/tools/kotlin-native-gradle-plugin/build.gradle @@ -82,9 +82,6 @@ repositories { maven { url kotlinCompilerRepo } - maven { - url sharedRepo - } } configurations { @@ -105,8 +102,7 @@ dependencies { bundleDependencies "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion" bundleDependencies "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" bundleDependencies "org.jetbrains.kotlin:kotlin-gradle-plugin-api:$kotlinVersion" - bundleDependencies "org.jetbrains.kotlin:kotlin-native-shared:$sharedVersion" - bundleDependencies "org.jetbrains.kotlin:kotlin-native-version" + bundleDependencies "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion" testImplementation 'junit:junit:4.12' testImplementation "org.jetbrains.kotlin:kotlin-test:$buildKotlinVersion" diff --git a/tools/kotlin-native-gradle-plugin/settings.gradle b/tools/kotlin-native-gradle-plugin/settings.gradle index 2d2f198994e..391e1c9b290 100644 --- a/tools/kotlin-native-gradle-plugin/settings.gradle +++ b/tools/kotlin-native-gradle-plugin/settings.gradle @@ -1,2 +1,2 @@ rootProject.name = "kotlin-native-gradle-plugin" -includeBuild '../../version' \ No newline at end of file +includeBuild '../../shared' diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanLibrariesSpec.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanLibrariesSpec.kt index 4f5d366a66c..ca22cb82ac0 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanLibrariesSpec.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanLibrariesSpec.kt @@ -24,8 +24,6 @@ import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Internal import org.jetbrains.kotlin.gradle.plugin.tasks.KonanArtifactWithLibrariesTask import org.jetbrains.kotlin.gradle.plugin.tasks.KonanBuildingTask -import org.jetbrains.kotlin.konan.CURRENT -import org.jetbrains.kotlin.konan.KonanVersion import org.jetbrains.kotlin.konan.library.SearchPathResolver import org.jetbrains.kotlin.konan.library.defaultResolver import org.jetbrains.kotlin.konan.target.Distribution @@ -148,8 +146,7 @@ open class KonanLibrariesSpec(val task: KonanArtifactWithLibrariesTask, val proj defaultResolver( repos.map { it.absolutePath }, task.konanTarget, - Distribution(konanHomeOverride = project.konanHome), - listOf(KonanVersion.CURRENT) + Distribution(konanHomeOverride = project.konanHome) ) ) diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanPlugin.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanPlugin.kt index f928dc22938..78ca8fe51d2 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanPlugin.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanPlugin.kt @@ -34,7 +34,6 @@ import org.gradle.util.GradleVersion import org.jetbrains.kotlin.gradle.plugin.konan.KonanPlugin.Companion.COMPILE_ALL_TASK_NAME import org.jetbrains.kotlin.gradle.plugin.model.KonanToolingModelBuilder import org.jetbrains.kotlin.gradle.plugin.tasks.* -import org.jetbrains.kotlin.konan.CURRENT import org.jetbrains.kotlin.konan.KonanVersion import org.jetbrains.kotlin.konan.parseKonanVersion import org.jetbrains.kotlin.konan.target.HostManager diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanCompileTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanCompileTask.kt index 3171c4f0b0a..521c3248cea 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanCompileTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanCompileTask.kt @@ -25,8 +25,6 @@ import org.gradle.process.CommandLineArgumentProvider import org.jetbrains.kotlin.gradle.plugin.konan.* import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifact import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifactImpl -import org.jetbrains.kotlin.konan.CURRENT -import org.jetbrains.kotlin.konan.KonanVersion import org.jetbrains.kotlin.konan.library.defaultResolver import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.Distribution @@ -237,8 +235,7 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec { val resolver = defaultResolver( repos.map { it.absolutePath }, konanTarget, - Distribution(konanHomeOverride = project.konanHome), - listOf(KonanVersion.CURRENT) + Distribution(konanHomeOverride = project.konanHome) ) return KonanModelArtifactImpl( diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanInteropTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanInteropTask.kt index 39281476f91..5c2394ceb91 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanInteropTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/tasks/KonanInteropTask.kt @@ -27,8 +27,6 @@ import org.jetbrains.kotlin.gradle.plugin.konan.* import org.jetbrains.kotlin.gradle.plugin.konan.KonanInteropSpec.IncludeDirectoriesSpec import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifact import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifactImpl -import org.jetbrains.kotlin.konan.CURRENT -import org.jetbrains.kotlin.konan.KonanVersion import org.jetbrains.kotlin.konan.library.defaultResolver import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.Distribution @@ -173,8 +171,7 @@ open class KonanInteropTask @Inject constructor(val workerExecutor: WorkerExecut val resolver = defaultResolver( repos.map { it.absolutePath }, konanTarget, - Distribution(konanHomeOverride = project.konanHome), - listOf(KonanVersion.CURRENT) + Distribution(konanHomeOverride = project.konanHome) ) return KonanModelArtifactImpl( diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/model/KonanModelImpl.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/model/KonanModelImpl.kt index a7ec6217ace..1e7a7e544e4 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/model/KonanModelImpl.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/model/KonanModelImpl.kt @@ -23,7 +23,6 @@ import org.jetbrains.kotlin.gradle.plugin.konan.konanArtifactsContainer import org.jetbrains.kotlin.gradle.plugin.konan.konanExtension import org.jetbrains.kotlin.gradle.plugin.konan.konanHome import org.jetbrains.kotlin.gradle.plugin.konan.konanVersion -import org.jetbrains.kotlin.konan.CURRENT import org.jetbrains.kotlin.konan.KonanVersion import org.jetbrains.kotlin.konan.target.CompilerOutputKind import java.io.File diff --git a/tools/kotlin-native-gradle-plugin/src/test/kotlin/ToolingModelTests.kt b/tools/kotlin-native-gradle-plugin/src/test/kotlin/ToolingModelTests.kt index f240aeccdef..d458285bce6 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/kotlin/ToolingModelTests.kt +++ b/tools/kotlin-native-gradle-plugin/src/test/kotlin/ToolingModelTests.kt @@ -22,7 +22,6 @@ import org.gradle.testfixtures.ProjectBuilder import org.jetbrains.kotlin.gradle.plugin.konan.KonanPlugin import org.jetbrains.kotlin.gradle.plugin.konan.konanArtifactsContainer import org.jetbrains.kotlin.gradle.plugin.model.KonanToolingModelBuilder -import org.jetbrains.kotlin.konan.CURRENT import org.jetbrains.kotlin.konan.KonanVersion import org.junit.Rule import org.junit.rules.TemporaryFolder diff --git a/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/InteropCompiler.kt b/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/InteropCompiler.kt index e20d2c1dc8d..5d1cd3d4fa0 100644 --- a/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/InteropCompiler.kt +++ b/utilities/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/InteropCompiler.kt @@ -4,7 +4,6 @@ */ package org.jetbrains.kotlin.cli.utilities -import org.jetbrains.kotlin.konan.CURRENT import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.target.PlatformManager import org.jetbrains.kotlin.konan.library.* @@ -12,7 +11,6 @@ import org.jetbrains.kotlin.konan.target.Distribution import org.jetbrains.kotlin.native.interop.gen.jvm.interop import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kliopt.ArgParser -import org.jetbrains.kotlin.konan.KonanVersion import org.jetbrains.kotlin.native.interop.tool.* // TODO: this function should eventually be eliminated from 'utilities'. @@ -50,8 +48,7 @@ fun invokeInterop(flavor: String, args: Array): Array? { repos, libraries.filter { it.contains(File.separator) }, target, - Distribution(), - listOf(KonanVersion.CURRENT) + Distribution() ).libraryResolver() val allLibraries = resolver.resolveWithDependencies( libraries.toUnresolvedLibraries, noStdLib = true, noDefaultLibs = noDefaultLibs diff --git a/version/build.gradle b/version/build.gradle deleted file mode 100644 index 60176e4eeb8..00000000000 --- a/version/build.gradle +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2010-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import org.jetbrains.kotlin.VersionGenerator - -buildscript { - ext.rootBuildDirectory = file('..') - - apply from: "$rootBuildDirectory/gradle/loadRootProperties.gradle" - apply from: "$rootBuildDirectory/gradle/kotlinGradlePlugin.gradle" -} - -apply plugin: 'kotlin' - -group = 'org.jetbrains.kotlin' -version = konanVersion - -repositories { - mavenCentral() - maven { - url buildKotlinCompilerRepo - } - maven { - url sharedRepo - } -} - -// FIXME(ddol): KLIB-REFACTORING-CLEANUP: drop generation of KonanVersion! -task generateCompilerVersion(type: VersionGenerator){} - -sourceSets { - main.kotlin { - srcDir generateCompilerVersion.versionSourceDirectory - } -} - -compileKotlin { - dependsOn('generateCompilerVersion') -} - -clean { - doFirst { - if (generateCompilerVersion.versionSourceDirectory.exists()) - generateCompilerVersion.versionSourceDirectory.delete() - } -} - -jar { - archiveName = "version.jar" -} - -dependencies { - compile "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" - compile "org.jetbrains.kotlin:kotlin-native-shared:$sharedVersion" -} diff --git a/version/buildSrc/src/main/kotlin/org/jetbrains/kotlin/VersionGenerator.kt b/version/buildSrc/src/main/kotlin/org/jetbrains/kotlin/VersionGenerator.kt deleted file mode 100644 index 46f113b1ab8..00000000000 --- a/version/buildSrc/src/main/kotlin/org/jetbrains/kotlin/VersionGenerator.kt +++ /dev/null @@ -1,60 +0,0 @@ -package org.jetbrains.kotlin - -import groovy.lang.Closure -import org.gradle.api.DefaultTask -import org.gradle.api.Task -import org.gradle.api.tasks.* -import java.io.File - - -open class VersionGenerator: DefaultTask() { - @OutputDirectory - val versionSourceDirectory = project.file("build/generated") - @OutputFile - val versionFile:File = project.file("${versionSourceDirectory.path}/org/jetbrains/kotlin/konan/KonanVersionGenerated.kt") - - val konanVersion: String - @Input get() = project.properties["konanVersion"].toString() - - val buildNumber: String? - // TeamCity passes all configuration parameters into a build script as project properties. - // Thus we can use them here instead of environment variables. - @Optional @Input get() = project.findProperty("build.number")?.toString() - - val meta: String - @Input get() = project.properties["konanMetaVersion"]?.let { - "MetaVersion.${it.toString().toUpperCase()}" - } ?: "MetaVersion.DEV" - - - override fun configure(closure: Closure<*>): Task { - val result = super.configure(closure) - doFirst { - val content = buildString { - operator fun String.unaryPlus() = this@buildString.append(this) - val version = konanVersion.split(".") - val major = version[0].toInt() - val minor = version[1].toInt() - val maintenance = if (version.size > 2) version[2].toInt() else 0 - project.logger.info("BUILD_NUMBER: $buildNumber") - val build = buildNumber?.let { - it.split("-")[2].toInt() //7-dev-buildcount - }?: -1 - - + """ - |package org.jetbrains.kotlin.konan - | - |internal val currentKonanVersion: KonanVersion = - | KonanVersionImpl($meta, $major, $minor, $maintenance, $build) - | - |val KonanVersion.Companion.CURRENT: KonanVersion - | get() = currentKonanVersion - """.trimMargin() - } - versionFile.printWriter().use { - it.println(content) - } - } - return result - } -} diff --git a/version/settings.gradle b/version/settings.gradle deleted file mode 100644 index 763fe54145c..00000000000 --- a/version/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "kotlin-native-version" \ No newline at end of file