Make :kotlin-native:runtime:hostRuntimeTests CC compatible
Required for KTI-1553
This commit is contained in:
committed by
Space Team
parent
09dc9c6743
commit
8f1f6ca9c9
@@ -146,6 +146,9 @@ abstract class RunGTest : DefaultTask() {
|
||||
@get:Input
|
||||
abstract val executionTimeout: Property<Duration>
|
||||
|
||||
@get:Input
|
||||
val platformManagerExtension: PlatformManager = project.extensions.getByType<PlatformManager>()
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val workQueue = workerExecutor.noIsolation()
|
||||
@@ -157,7 +160,7 @@ abstract class RunGTest : DefaultTask() {
|
||||
reportFileUnprocessed.set(this@RunGTest.reportFileUnprocessed)
|
||||
filter.set(this@RunGTest.filter)
|
||||
tsanSuppressionsFile.set(this@RunGTest.tsanSuppressionsFile)
|
||||
platformManager.set(project.extensions.getByType<PlatformManager>())
|
||||
platformManager.set(platformManagerExtension)
|
||||
targetName.set(this@RunGTest.target.get().name)
|
||||
executionTimeout.set(this@RunGTest.executionTimeout)
|
||||
}
|
||||
|
||||
+68
-129
@@ -6,16 +6,20 @@
|
||||
package org.jetbrains.kotlin.testing.native
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.internal.file.FileOperations
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.options.Option
|
||||
import org.gradle.kotlin.dsl.property
|
||||
import org.gradle.process.ExecOperations
|
||||
import org.gradle.process.ExecResult
|
||||
import org.gradle.process.ExecSpec
|
||||
import org.jetbrains.kotlin.isNotEmpty
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.net.URI
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -23,112 +27,27 @@ import javax.inject.Inject
|
||||
* Clones the given revision of the given Git repository to the given directory.
|
||||
*/
|
||||
@Suppress("UnstableApiUsage")
|
||||
open class GitDownloadTask @Inject constructor(
|
||||
private val repositoryProvider: Provider<URL>,
|
||||
private val revisionProvider: Provider<String>,
|
||||
private val outputDirectoryProvider: Provider<File>
|
||||
abstract class GitDownloadTask @Inject constructor(
|
||||
objects: ObjectFactory,
|
||||
private val execOperations: ExecOperations,
|
||||
private val fileOperations: FileOperations,
|
||||
) : DefaultTask() {
|
||||
@get:Input
|
||||
abstract val repository: Property<URI>
|
||||
|
||||
private val repository: URL
|
||||
get() = repositoryProvider.get()
|
||||
@get:Input
|
||||
abstract val revision: Property<String>
|
||||
|
||||
private val revision: String
|
||||
get() = revisionProvider.get()
|
||||
|
||||
private val outputDirectory: File
|
||||
get() = outputDirectoryProvider.get()
|
||||
@get:OutputDirectory
|
||||
abstract val outputDirectory: DirectoryProperty
|
||||
|
||||
@Option(option = "refresh",
|
||||
description = "Fetch and checkout the revision even if the output directory already contains it. " +
|
||||
"All changes in the output directory will be overwritten")
|
||||
@Input
|
||||
val refresh: Property<Boolean> = project.objects.property(Boolean::class.java).apply {
|
||||
set(false)
|
||||
}
|
||||
|
||||
private val upToDateChecker = UpToDateChecker()
|
||||
@get:Input
|
||||
val refresh: Property<Boolean> = objects.property<Boolean>().convention(false)
|
||||
|
||||
init {
|
||||
outputs.upToDateWhen { upToDateChecker.isUpToDate() }
|
||||
}
|
||||
|
||||
private fun git(
|
||||
vararg args: String,
|
||||
ignoreExitValue: Boolean = false,
|
||||
execConfiguration: ExecSpec.() -> Unit = {}
|
||||
): ExecResult =
|
||||
project.exec {
|
||||
executable = "git"
|
||||
args(*args)
|
||||
isIgnoreExitValue = ignoreExitValue
|
||||
execConfiguration()
|
||||
}
|
||||
|
||||
private fun tryCloneBranch(): Boolean {
|
||||
val execResult = git(
|
||||
"clone", repository.toString(),
|
||||
outputDirectory.absolutePath,
|
||||
"--depth", "1",
|
||||
"--branch", revision,
|
||||
ignoreExitValue = true
|
||||
)
|
||||
return execResult.exitValue == 0
|
||||
}
|
||||
|
||||
private fun fetchByHash() {
|
||||
git("init", outputDirectory.absolutePath)
|
||||
git("fetch", repository.toString(), "--depth", "1", revision) {
|
||||
workingDir(outputDirectory)
|
||||
}
|
||||
git("reset", "--hard", revision) {
|
||||
workingDir(outputDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun clone() {
|
||||
// Gradle ignores outputs.upToDateWhen { ... } and reruns a task if the classpath of the task was changed
|
||||
// So we have to perform the up-to-date check manually one more time in the task action.
|
||||
if (upToDateChecker.isUpToDate()) {
|
||||
logger.info("Skip cloning to avoid rewriting possible debug changes in ${outputDirectory.absolutePath}.")
|
||||
return
|
||||
}
|
||||
|
||||
project.delete {
|
||||
delete(outputDirectory)
|
||||
}
|
||||
|
||||
if (!tryCloneBranch()) {
|
||||
logger.info("Cannot use the revision '$revision' to clone the repository. Trying to use init && fetch instead.")
|
||||
fetchByHash()
|
||||
}
|
||||
|
||||
// Store info about used revision for the manual up-to-date check.
|
||||
upToDateChecker.storeRevisionInfo()
|
||||
|
||||
// Delete the .git directory of the cloned repo to avoid adding it to IDEA's VCS roots.
|
||||
outputDirectory.resolve(".git").deleteRecursively()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This class performs manual UP-TO-DATE checking.
|
||||
*
|
||||
* We want to be able to edit downloaded sources for debug purposes. Thus this task should not rewrite changes in
|
||||
* the output directory.
|
||||
*
|
||||
* Gradle does allow us to provide a custom logic to determine if task outputs are UP-TO-DATE or not (see
|
||||
* Task.outputs.upToDateWhen). But Gradle still reruns a task if its classpath was changed. This may lead
|
||||
* to rewriting our manual debug changes in downloaded sources if there are some changes in the
|
||||
* `build-tools` project.
|
||||
*
|
||||
* So we have to manually check up-to-dateness in upToDateWhen and as a first step of the task action.
|
||||
*/
|
||||
private inner class UpToDateChecker() {
|
||||
|
||||
private val revisionInfoFile: File
|
||||
get() = outputDirectory.resolve(".revision")
|
||||
|
||||
/**
|
||||
* The download task should be executed in the following cases:
|
||||
*
|
||||
@@ -138,38 +57,58 @@ open class GitDownloadTask @Inject constructor(
|
||||
*
|
||||
* In all other cases we consider the task UP-TO-DATE.
|
||||
*/
|
||||
fun isUpToDate(): Boolean {
|
||||
return !refresh.get() &&
|
||||
outputDirectory.let { it.exists() && project.fileTree(it).isNotEmpty } &&
|
||||
noRevisionChanges()
|
||||
}
|
||||
|
||||
private fun noRevisionChanges(): Boolean =
|
||||
revisionInfoFile.exists() && loadRevisionInfo() == RevisionInfo(repository, revision)
|
||||
|
||||
fun storeRevisionInfo() {
|
||||
val properties = Properties()
|
||||
properties["repository"] = repository.toString()
|
||||
properties["revision"] = revision
|
||||
revisionInfoFile.bufferedWriter().use {
|
||||
properties.store(it, null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadRevisionInfo(): RevisionInfo? {
|
||||
return try {
|
||||
val properties = Properties()
|
||||
revisionInfoFile.bufferedReader().use {
|
||||
properties.load(it)
|
||||
}
|
||||
RevisionInfo(properties.getProperty("repository"), properties.getProperty("revision"))
|
||||
} catch (_ : Exception) {
|
||||
null
|
||||
outputs.upToDateWhen {
|
||||
val upToDate = !refresh.get() && outputDirectory.asFileTree.isNotEmpty
|
||||
if (upToDate) {
|
||||
logger.info("Skip cloning to avoid rewriting possible debug changes in ${outputDirectory.get().asFile.absolutePath}.")
|
||||
}
|
||||
upToDate
|
||||
}
|
||||
}
|
||||
|
||||
private data class RevisionInfo(val repository: String, val revision: String) {
|
||||
constructor(repository: URL, revision: String): this(repository.toString(), revision)
|
||||
private fun git(
|
||||
vararg args: String,
|
||||
ignoreExitValue: Boolean = false,
|
||||
execConfiguration: ExecSpec.() -> Unit = {}
|
||||
): ExecResult =
|
||||
execOperations.exec {
|
||||
executable = "git"
|
||||
args(*args)
|
||||
isIgnoreExitValue = ignoreExitValue
|
||||
execConfiguration()
|
||||
}
|
||||
|
||||
private fun tryCloneBranch(): Boolean {
|
||||
val execResult = git(
|
||||
"clone", repository.get().toString(),
|
||||
outputDirectory.get().asFile.absolutePath,
|
||||
"--depth", "1",
|
||||
"--branch", revision.get(),
|
||||
ignoreExitValue = true
|
||||
)
|
||||
return execResult.exitValue == 0
|
||||
}
|
||||
|
||||
private fun fetchByHash() {
|
||||
git("init", outputDirectory.get().asFile.absolutePath)
|
||||
git("fetch", repository.get().toString(), "--depth", "1", revision.get()) {
|
||||
workingDir(outputDirectory)
|
||||
}
|
||||
git("reset", "--hard", revision.get()) {
|
||||
workingDir(outputDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun clone() {
|
||||
fileOperations.delete(outputDirectory)
|
||||
|
||||
if (!tryCloneBranch()) {
|
||||
logger.info("Cannot use the revision '${revision.get()}' to clone the repository. Trying to use init && fetch instead.")
|
||||
fetchByHash()
|
||||
}
|
||||
|
||||
// Delete the .git directory of the cloned repo to avoid adding it to IDEA's VCS roots.
|
||||
fileOperations.delete(outputDirectory.dir(".git"))
|
||||
}
|
||||
}
|
||||
|
||||
+47
-53
@@ -8,46 +8,47 @@ package org.jetbrains.kotlin.testing.native
|
||||
import org.gradle.api.InvalidUserDataException
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.Directory
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.file.ProjectLayout
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.property
|
||||
import org.jetbrains.kotlin.bitcode.CompileToBitcodeExtension
|
||||
import org.jetbrains.kotlin.resolve
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.net.URI
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
open class RuntimeTestingPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
val extension = extensions.create(GOOGLE_TEST_EXTENSION_NAME, GoogleTestExtension::class.java, target)
|
||||
val extension = extensions.create(GOOGLE_TEST_EXTENSION_NAME, GoogleTestExtension::class.java)
|
||||
val downloadTask = registerDownloadTask(extension)
|
||||
|
||||
val googleTestRoot = project.provider { extension.sourceDirectory }
|
||||
val googleTestRoot = extension.sourceDirectory
|
||||
|
||||
createBitcodeTasks(googleTestRoot, listOf(downloadTask))
|
||||
createBitcodeTasks(project.objects.directoryProperty().value(googleTestRoot), listOf(downloadTask))
|
||||
}
|
||||
|
||||
private fun Project.registerDownloadTask(extension: GoogleTestExtension): TaskProvider<GitDownloadTask> {
|
||||
val task = tasks.register(
|
||||
"downloadGoogleTest",
|
||||
GitDownloadTask::class.java,
|
||||
provider { URL(extension.repository) },
|
||||
provider { extension.revision },
|
||||
provider { extension.fetchDirectory }
|
||||
)
|
||||
task.configure {
|
||||
refresh.set(provider { extension.refresh })
|
||||
onlyIf { extension.localSourceRoot == null }
|
||||
description = "Retrieves GoogleTest from the given repository"
|
||||
group = "Google Test"
|
||||
}
|
||||
return task
|
||||
}
|
||||
private fun Project.registerDownloadTask(extension: GoogleTestExtension): TaskProvider<GitDownloadTask> =
|
||||
tasks.register("downloadGoogleTest", GitDownloadTask::class.java) {
|
||||
description = "Retrieves GoogleTest from the given repository"
|
||||
group = "Google Test"
|
||||
onlyIf {
|
||||
!extension.hasLocalSourceRoot.get()
|
||||
}
|
||||
repository.set(extension.repository.map { URI.create(it) })
|
||||
revision.set(extension.revision)
|
||||
outputDirectory.set(extension.sourceDirectory)
|
||||
refresh.set(extension.refresh)
|
||||
}
|
||||
|
||||
private fun Project.createBitcodeTasks(
|
||||
googleTestRoot: Provider<File>,
|
||||
googleTestRoot: DirectoryProperty,
|
||||
dependencies: Iterable<TaskProvider<*>>
|
||||
) {
|
||||
pluginManager.withPlugin("compile-to-bitcode") {
|
||||
@@ -57,12 +58,12 @@ open class RuntimeTestingPlugin : Plugin<Project> {
|
||||
module("googletest") {
|
||||
sourceSets {
|
||||
testFixtures {
|
||||
inputFiles.from(googleTestRoot.resolve("googletest/src"))
|
||||
inputFiles.from(googleTestRoot.dir("googletest/src"))
|
||||
// That's how googletest/CMakeLists.txt builds gtest library.
|
||||
inputFiles.include("gtest-all.cc")
|
||||
headersDirs.setFrom(
|
||||
googleTestRoot.resolve("googletest/include"),
|
||||
googleTestRoot.resolve("googletest")
|
||||
googleTestRoot.dir("googletest/include"),
|
||||
googleTestRoot.dir("googletest")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -73,13 +74,13 @@ open class RuntimeTestingPlugin : Plugin<Project> {
|
||||
module("googlemock") {
|
||||
sourceSets {
|
||||
testFixtures {
|
||||
inputFiles.from(googleTestRoot.resolve("googlemock/src"))
|
||||
inputFiles.from(googleTestRoot.dir("googlemock/src"))
|
||||
// That's how googlemock/CMakeLists.txt builds gmock library.
|
||||
inputFiles.include("gmock-all.cc")
|
||||
headersDirs.setFrom(
|
||||
googleTestRoot.resolve("googlemock"),
|
||||
googleTestRoot.resolve("googlemock/include"),
|
||||
googleTestRoot.resolve("googletest/include"),
|
||||
googleTestRoot.dir("googlemock"),
|
||||
googleTestRoot.dir("googlemock/include"),
|
||||
googleTestRoot.dir("googletest/include"),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -99,12 +100,16 @@ open class RuntimeTestingPlugin : Plugin<Project> {
|
||||
/**
|
||||
* A project extension to configure from where we get the GoogleTest framework.
|
||||
*/
|
||||
open class GoogleTestExtension @Inject constructor(private val project: Project) {
|
||||
abstract class GoogleTestExtension @Inject constructor(
|
||||
layout: ProjectLayout,
|
||||
providers: ProviderFactory,
|
||||
objects: ObjectFactory,
|
||||
) {
|
||||
|
||||
/**
|
||||
* A repository to fetch GoogleTest from.
|
||||
*/
|
||||
var repository: String = "https://github.com/google/googletest.git"
|
||||
val repository: Property<String> = objects.property<String>().convention("https://github.com/google/googletest.git")
|
||||
|
||||
private var _revision: String? = null
|
||||
|
||||
@@ -122,43 +127,32 @@ open class GoogleTestExtension @Inject constructor(private val project: Project)
|
||||
/**
|
||||
* Fetch the [revision] even if the [fetchDirectory] already contains it. Overwrite all changes manually made in the output directory.
|
||||
*/
|
||||
var refresh: Boolean = false
|
||||
val refresh: Property<Boolean> = objects.property<Boolean>().convention(false)
|
||||
|
||||
/**
|
||||
* A directory to fetch the [revision] to.
|
||||
*/
|
||||
var fetchDirectory: File = project.file("googletest")
|
||||
|
||||
internal var localSourceRoot: File? = null
|
||||
private val fetchDirectory: Directory = layout.projectDirectory.dir("googletest")
|
||||
|
||||
/**
|
||||
* Use a local [directory] with GoogleTest instead of the fetched one. If set, the download task will not be executed.
|
||||
* Use a local directory with GoogleTest instead of the fetched one. If set, the download task will not be executed.
|
||||
*/
|
||||
fun useLocalSources(directory: File) {
|
||||
localSourceRoot = directory
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a local [directory] with GoogleTest instead of the fetched one. If set, the download task will not be executed.
|
||||
*/
|
||||
fun useLocalSources(directory: String) {
|
||||
localSourceRoot = project.file(directory)
|
||||
}
|
||||
abstract val localSourceRoot: DirectoryProperty
|
||||
val hasLocalSourceRoot: Provider<Boolean> = providers.provider { localSourceRoot.isPresent }
|
||||
|
||||
/**
|
||||
* A getter for directory that contains the GTest sources.
|
||||
* Returns a local source directory if it's specified (see [useLocalSources]) or [fetchDirectory] otherwise.
|
||||
* Returns a local source directory if it's specified (see [localSourceRoot]) or [fetchDirectory] otherwise.
|
||||
*/
|
||||
val sourceDirectory: File
|
||||
get() = localSourceRoot ?: fetchDirectory
|
||||
val sourceDirectory: Provider<Directory> = localSourceRoot.orElse(fetchDirectory)
|
||||
|
||||
/**
|
||||
* A file collection with header directories for GoogleTest and GoogleMock.
|
||||
* Useful to configure compilation against GTest.
|
||||
*/
|
||||
val headersDirs: FileCollection = project.files(
|
||||
project.provider { sourceDirectory.resolve("googletest/include") },
|
||||
project.provider { sourceDirectory.resolve("googlemock/include") }
|
||||
val headersDirs: FileCollection = layout.files(
|
||||
sourceDirectory.map { it.dir("googletest/include")},
|
||||
sourceDirectory.map { it.dir("googlemock/include")}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user