Rework compiler dependency management

And support for searching them locally and
downloading from internal server if KONAN_USE_INTERNAL_SERVER is set

Also:
Allow sysroot to change version minorly.
This commit is contained in:
Svyatoslav Scherbina
2017-12-08 14:14:50 +03:00
committed by SvyatoslavScherbina
parent 257f156e97
commit 228ec9d286
14 changed files with 173 additions and 33 deletions
@@ -0,0 +1,35 @@
/*
* 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.native.interop.indexer
import java.nio.file.Paths
class HeaderToIdMapper(sysRoot: String) {
private val headerPathToId = mutableMapOf<String, HeaderId>()
private val sysRoot = Paths.get(sysRoot).normalize()
internal fun getHeaderId(filePath: String) = headerPathToId.getOrPut(filePath) {
val path = Paths.get(filePath)
val headerIdValue = if (path.startsWith(sysRoot)) {
val relative = sysRoot.relativize(path)
relative.toString()
} else {
headerContentsHash(filePath)
}
HeaderId(headerIdValue)
}
}
@@ -111,18 +111,13 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
}
private val headerPathToId = mutableMapOf<String, HeaderId>()
internal fun getHeaderId(file: CXFile?): HeaderId {
if (file == null) {
return HeaderId("builtins")
}
val filePath = clang_getFileName(file).convertAndDispose()
return headerPathToId.getOrPut(filePath) {
val headerIdValue = headerContentsHash(filePath)
HeaderId(headerIdValue)
}
return library.headerToIdMapper.getHeaderId(filePath)
}
private fun getContainingFile(cursor: CValue<CXCursor>): CXFile? {
@@ -44,6 +44,7 @@ interface HeaderInclusionPolicy {
data class NativeLibrary(val includes: List<String>,
val additionalPreambleLines: List<String>,
val compilerArgs: List<String>,
val headerToIdMapper: HeaderToIdMapper,
val language: Language,
val excludeSystemLibs: Boolean, // TODO: drop?
val excludeDepdendentModules: Boolean,
@@ -28,7 +28,7 @@ fun KonanProperties.defaultCompilerOpts(): List<String> {
// TODO: eliminate this. below
val targetToolchain = absoluteTargetToolchain
val targetSysRoot = absoluteTargetSysRoot
val llvmHome = absolute(hostString("llvmHome"))
val llvmHome = absoluteLlvmHome
val llvmVersion = hostString("llvmVersion")!!
// StubGenerator passes the arguments to libclang which
@@ -40,10 +40,11 @@ class ToolConfig(userProvidedTargetName: String?, userProvidedKonanProperties: S
"target" to target.detailedName,
"arch" to target.architecture.userName)
fun downloadDependencies() = maybeExecuteHelper(dependencies.absolutePath,
properties, targetProperties.dependencies)
fun downloadDependencies() = targetProperties.downloadDependencies()
val llvmHome = targetProperties.absolute(targetProperties.hostString("llvmHome"))
val llvmHome = targetProperties.absoluteLlvmHome
val sysRoot get() = targetProperties.absoluteTargetSysRoot
val defaultCompilerOpts =
targetProperties.defaultCompilerOpts()
@@ -349,6 +349,7 @@ private fun processLib(args: Map<String, List<String>>,
includes = headerFiles,
additionalPreambleLines = def.defHeaderLines,
compilerArgs = compilerOpts,
headerToIdMapper = HeaderToIdMapper(sysRoot = tool.sysRoot),
language = language,
excludeSystemLibs = excludeSystemLibs,
excludeDepdendentModules = excludeDependentModules,
@@ -52,15 +52,8 @@ class Distribution(val targetManager: TargetManager,
val dependenciesDir = DependencyProcessor.defaultDependenciesRoot.absolutePath
val targetProperties = KonanProperties(target, properties, dependenciesDir)
val hostProperties = if (target == host) {
targetProperties
} else {
KonanProperties(host, properties, dependenciesDir)
}
val dependencies = targetProperties.dependencies
val llvmHome = hostProperties.absoluteLlvmHome
val hostSysRoot = hostProperties.absoluteTargetSysRoot
val llvmHome = targetProperties.absoluteLlvmHome
val llvmBin = "$llvmHome/bin"
val llvmLib = "$llvmHome/lib"
val llvmLto = "$llvmBin/llvm-lto"
@@ -54,7 +54,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
private fun Distribution.prepareDependencies(checkDependencies: Boolean) {
if (checkDependencies) {
DependencyProcessor(java.io.File(dependenciesDir), targetProperties).run()
targetProperties.downloadDependencies()
}
}
+3
View File
@@ -17,6 +17,9 @@
# TODO: Do we need a $variable substitution mechanism here?
dependenciesUrl = https://download.jetbrains.com/kotlin/native
# In order of preference:
dependencyProfiles = default alt
# Don't check dependencies on a server.
# If true, dependency downloader will throw an exception if a dependency isn't found in konan.home.
airplaneMode = false
+10
View File
@@ -26,6 +26,16 @@ ext {
konanPropertiesFile = project(':backend.native').file('konan.properties')
konanProperties = PropertiesKt.loadProperties(konanPropertiesFile.absolutePath)
String DEPENDENCY_PROFILES_KEY = "dependencyProfiles"
def dependencyProfiles = konanProperties.getProperty(DEPENDENCY_PROFILES_KEY)
if (dependencyProfiles != "default alt")
throw new Error("unexpected $DEPENDENCY_PROFILES_KEY value: expected 'default alt', got '$dependencyProfiles'")
// Force build to use only 'default' profile:
konanProperties.setProperty(DEPENDENCY_PROFILES_KEY, "default")
// TODO: it actually affects only resolution made in :dependencies,
// that's why we assume that 'default' profile comes first (and check this above).
distDir = file('dist')
dependenciesDir = DependencyProcessor.defaultDependenciesRoot
clangManager = new ClangManager(konanProperties, dependenciesDir.absolutePath)
+11 -4
View File
@@ -139,7 +139,7 @@ task update_kotlin_compiler(type: DefaultTask) {
abstract class NativeDep extends DefaultTask {
protected final String hostSystem = TargetManager.longerSystemName();
String baseUrl = "https://jetbrains.bintray.com/kotlin-native-dependencies"
static final String baseUrl = "https://jetbrains.bintray.com/kotlin-native-dependencies"
@Input
abstract String getFileName()
@@ -254,12 +254,19 @@ TargetManager.enabled.each { target ->
}
}
// Also resolves all dependencies:
final DependencyProcessor dependencyProcessor = new DependencyProcessor(
project.rootProject.ext.dependenciesDir,
rootProject.ext.konanProperties,
konanProperties.dependencies,
HelperNativeDep.baseUrl,
false
)
DependencyKind.values().each { kind ->
def dir = kind.getDirectory(konanProperties)
if (dir != null) {
def path = rootProject.ext.dependenciesDir.toPath()
.resolve(dir).toFile()
.canonicalPath
String path = dependencyProcessor.resolveRelative(dir).canonicalPath
rootProject.ext.set(kind.getPropertyName(target), path)
}
}
@@ -18,9 +18,14 @@ package org.jetbrains.kotlin.konan.properties
import org.jetbrains.kotlin.konan.file.*
import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.konan.util.DependencyProcessor
class KonanProperties(val target: KonanTarget, val properties: Properties, val baseDir: String? = null) {
fun downloadDependencies() {
dependencyProcessor!!.run()
}
fun targetString(key: String): String?
= properties.targetString(key, target)
fun targetList(key: String): List<String>
@@ -34,6 +39,8 @@ class KonanProperties(val target: KonanTarget, val properties: Properties, val b
fun hostTargetList(key: String): List<String>
= properties.hostTargetList(key, target)
val llvmHome get() = hostString("llvmHome")
// TODO: Delegate to a map?
val llvmLtoNooptFlags get() = targetList("llvmLtoNooptFlags")
val llvmLtoOptFlags get() = targetList("llvmLtoOptFlags")
@@ -51,12 +58,12 @@ class KonanProperties(val target: KonanTarget, val properties: Properties, val b
val libffiDir get() = targetString("libffiDir")
val gccToolchain get() = targetString("gccToolchain")
val targetArg get() = targetString("quadruple")
val llvmHome get() = targetString("llvmHome")
// Notice: these ones are host-target.
val targetToolchain get() = hostTargetString("targetToolchain")
val dependencies get() = hostTargetList("dependencies")
fun absolute(value: String?) = "${baseDir!!}/${value!!}"
private fun absolute(value: String?): String =
dependencyProcessor!!.resolveRelative(value!!).absolutePath
val absoluteTargetSysRoot get() = absolute(targetSysRoot)
val absoluteTargetToolchain get() = absolute(targetToolchain)
@@ -75,4 +82,7 @@ class KonanProperties(val target: KonanTarget, val properties: Properties, val b
}
val osVersionMin: String? get() = targetString("osVersionMin")
private val dependencyProcessor = baseDir?.let { DependencyProcessor(java.io.File(it), this) }
}
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.konan.util
import org.jetbrains.kotlin.konan.file.use
import org.jetbrains.kotlin.konan.properties.KonanProperties
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
@@ -45,6 +46,26 @@ private val Properties.downloadingAttemptIntervalMs : Long
private val Properties.homeDependencyCache : String
get() = getProperty("homeDependencyCache") ?: DependencyProcessor.DEFAULT_HOME_DEPENDENCY_CACHE
private fun Properties.findCandidates(dependencies: List<String>): Map<String, List<DependencySource>> {
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 KonanProperties.dependenciesUrl : String get() = properties.dependenciesUrl
private val KonanProperties.airplaneMode : Boolean get() = properties.airplaneMode
@@ -52,6 +73,17 @@ private val KonanProperties.downloadingAttempts : Int get() = properti
private val KonanProperties.downloadingAttemptIntervalMs : Long get() = properties.downloadingAttemptIntervalMs
private val KonanProperties.homeDependencyCache : String get() = properties.homeDependencyCache
private val internalDependenciesUrl = "http://repo.labs.intellij.net/kotlin-native"
sealed class DependencySource {
data class Local(val path: File) : DependencySource()
sealed class Remote : DependencySource() {
object Public : Remote()
object Internal : Remote()
}
}
// TODO: Try to use some dependency management system (Ivy?)
// TODO: Maybe rename.
/**
@@ -60,13 +92,13 @@ private val KonanProperties.homeDependencyCache : String get() = properti
*/
class DependencyProcessor(dependenciesRoot: File,
val dependenciesUrl: String,
val dependencies: Collection<String>,
dependencyToCandidates: Map<String, List<DependencySource>>,
homeDependencyCache: String = DEFAULT_HOME_DEPENDENCY_CACHE,
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 keepUnstable: Boolean = true) {
val dependenciesDirectory = dependenciesRoot.apply { mkdirs() }
val cacheDirectory = System.getProperty("user.home")?.let {
@@ -101,7 +133,7 @@ class DependencyProcessor(dependenciesRoot: File,
keepUnstable:Boolean = true) : this(
dependenciesRoot,
dependenciesUrl,
dependencies,
dependencyToCandidates = properties.findCandidates(dependencies),
airplaneMode = properties.airplaneMode,
maxAttempts = properties.downloadingAttempts,
attemptIntervalMs = properties.downloadingAttemptIntervalMs,
@@ -137,13 +169,13 @@ class DependencyProcessor(dependenciesRoot: File,
}
}
private fun processDependency(dependency: String) {
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("$dependenciesUrl/$fileName")
val url = URL("$baseUrl/$fileName")
val extractedDependencies = DependencyFile(dependenciesDirectory, ".extracted")
if (extractedDependencies.contains(depName) &&
@@ -190,10 +222,57 @@ class DependencyProcessor(dependenciesRoot: File,
get() = Paths.get(System.getProperty("user.home")).resolve(".konan/dependencies").toFile()
}
private fun internalServerIsAvailable(): Boolean = System.getenv("KONAN_USE_INTERNAL_SERVER") != null
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 { internalServerIsAvailable() }
}
}.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 {
dependencies.forEach {
processDependency(it)
resolvedDependencies.forEach { (dependency, candidate) ->
val baseUrl = when (candidate) {
is DependencySource.Local -> null
DependencySource.Remote.Public -> dependenciesUrl
DependencySource.Remote.Internal -> internalDependenciesUrl
}
// TODO: consider using different caches for different remotes.
if (baseUrl != null) {
downloadDependency(dependency, baseUrl)
}
}
}
}
@@ -22,6 +22,7 @@ import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.konan.util.DependencyProcessor
import org.jetbrains.kotlin.konan.util.DependencySource
import java.io.File
import java.io.IOException
@@ -60,7 +61,11 @@ open class KonanCompilerDownloadTask : DefaultTask() {
}
val konanCompiler = project.konanCompilerName()
logger.info("Downloading Kotlin/Native compiler from $downloadUrlDirectory/$konanCompiler into $KONAN_PARENT_DIR")
DependencyProcessor(File(KONAN_PARENT_DIR), downloadUrlDirectory, listOf(konanCompiler)).run()
DependencyProcessor(
File(KONAN_PARENT_DIR),
downloadUrlDirectory,
mapOf(konanCompiler to listOf(DependencySource.Remote.Public))
).run()
} catch (e: IOException) {
throw GradleScriptException("Cannot download Kotlin/Native compiler", e)
}