[K/N][build] Download gtest like breakpad
diff --git a/kotlin-native/build-tools/build.gradle.kts b/kotlin-native/build-tools/build.gradle.kts index 4d4c9d3..8d17416 100644 --- a/kotlin-native/build-tools/build.gradle.kts +++ b/kotlin-native/build-tools/build.gradle.kts
@@ -77,10 +77,6 @@ id = "compile-to-bitcode" implementationClass = "org.jetbrains.kotlin.bitcode.CompileToBitcodePlugin" } - create("runtimeTesting") { - id = "runtime-testing" - implementationClass = "org.jetbrains.kotlin.testing.native.RuntimeTestingPlugin" - } create("compilationDatabase") { id = "compilation-database" implementationClass = "org.jetbrains.kotlin.cpp.CompilationDatabasePlugin"
diff --git a/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/bitcode/CompileToBitcodePlugin.kt b/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/bitcode/CompileToBitcodePlugin.kt index d26ca3e..433ff7a9a 100644 --- a/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/bitcode/CompileToBitcodePlugin.kt +++ b/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/bitcode/CompileToBitcodePlugin.kt
@@ -31,7 +31,6 @@ import org.jetbrains.kotlin.konan.target.* import org.jetbrains.kotlin.nativeDistribution.nativeProtoDistribution import org.jetbrains.kotlin.resolveLlvmUtility -import org.jetbrains.kotlin.testing.native.GoogleTestExtension import org.jetbrains.kotlin.utils.capitalized import java.io.File import java.time.Duration @@ -145,6 +144,11 @@ project.executorsClasspathConfiguration() } + /** + * GTest headers to be used in testFixtures and test source sets. + */ + val googleTestHeaders: ConfigurableFileCollection = project.objects.fileCollection() + private val allTestsTasks by lazy { val name = project.name.capitalized val platformManager = project.extensions.getByType<PlatformManager>() @@ -314,9 +318,6 @@ abstract class SourceSets @Inject constructor(private val module: Module, private val container: ExtensiblePolymorphicDomainObjectContainer<SourceSet>) : NamedDomainObjectContainer<SourceSet> by container { private val project by module::project - // googleTestExtension is only used if testFixtures or tests are used. - private val googleTestExtension by lazy { project.extensions.getByType<GoogleTestExtension>() } - /** * Get `main` source set if it was configured. */ @@ -349,14 +350,14 @@ */ fun testFixtures(action: Action<in SourceSet>): SourceSet = create(TEST_FIXTURES_SOURCE_SET_NAME) { this.inputFiles.include("**/*TestSupport.cpp", "**/*TestSupport.mm") - this.headersDirs.from(googleTestExtension.headersDirs) + this.headersDirs.from(module.owner.googleTestHeaders) // TODO: Must generally depend on googletest module headers which must itself depend on sources being present. - dependencies.add(project.tasks.named("downloadGoogleTest")) + dependencies.add(project.tasks.named("unpackGoogletest")) compileTask.configure { this.group = VERIFICATION_BUILD_TASK_GROUP // Without this explicit statement task dependency is not created even if it is requested in RuntimeTestingPlugin - dependsOn(project.tasks.named("downloadGoogleTest")) + dependsOn(project.tasks.named("unpackGoogletest")) } task.configure { this.group = VERIFICATION_BUILD_TASK_GROUP @@ -375,14 +376,14 @@ */ fun test(action: Action<in SourceSet>): SourceSet = create(TEST_SOURCE_SET_NAME) { this.inputFiles.include("**/*Test.cpp", "**/*Test.mm") - this.headersDirs.from(googleTestExtension.headersDirs) + this.headersDirs.from(module.owner.googleTestHeaders) // TODO: Must generally depend on googletest module headers which must itself depend on sources being present. - dependencies.add(project.tasks.named("downloadGoogleTest")) + dependencies.add(project.tasks.named("unpackGoogletest")) compileTask.configure { this.group = VERIFICATION_BUILD_TASK_GROUP // Without this explicit statement task dependency is not created even if it is requested in RuntimeTestingPlugin - dependsOn(project.tasks.named("downloadGoogleTest")) + dependsOn(project.tasks.named("unpackGoogletest")) } task.configure { this.group = VERIFICATION_BUILD_TASK_GROUP
diff --git a/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/testing/native/GitDownloadTask.kt b/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/testing/native/GitDownloadTask.kt deleted file mode 100644 index 725d705..0000000 --- a/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/testing/native/GitDownloadTask.kt +++ /dev/null
@@ -1,114 +0,0 @@ -/* - * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ - -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.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.net.URI -import java.util.* -import javax.inject.Inject - -/** - * Clones the given revision of the given Git repository to the given directory. - */ -@Suppress("UnstableApiUsage") -abstract class GitDownloadTask @Inject constructor( - objects: ObjectFactory, - private val execOperations: ExecOperations, - private val fileOperations: FileOperations, -) : DefaultTask() { - @get:Input - abstract val repository: Property<URI> - - @get:Input - abstract val revision: Property<String> - - @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") - @get:Input - val refresh: Property<Boolean> = objects.property<Boolean>().convention(false) - - init { - /** - * The download task should be executed in the following cases: - * - * - The output directory doesn't exist or is empty; - * - Repository or revision was changed since the last execution; - * - A user forced rerunning this tasks manually (see [GitDownloadTask.refresh]). - * - * In all other cases we consider the task UP-TO-DATE. - */ - 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 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. - outputDirectory.dir(".git").get().asFile.deleteRecursively() - } -}
diff --git a/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/testing/native/RuntimeTestingPlugin.kt b/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/testing/native/RuntimeTestingPlugin.kt deleted file mode 100644 index 6dca23d..0000000 --- a/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/testing/native/RuntimeTestingPlugin.kt +++ /dev/null
@@ -1,167 +0,0 @@ -/* - * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ - -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 java.net.URI -import javax.inject.Inject - -open class RuntimeTestingPlugin : Plugin<Project> { - override fun apply(target: Project): Unit = with(target) { - val extension = extensions.create(GOOGLE_TEST_EXTENSION_NAME, GoogleTestExtension::class.java) - val downloadTask = registerDownloadTask(extension) - - val googleTestRoot = extension.sourceDirectory - - createBitcodeTasks(project.objects.directoryProperty().value(googleTestRoot), downloadTask) - } - - 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: DirectoryProperty, - downloadTask: TaskProvider<GitDownloadTask> - ) { - pluginManager.withPlugin("compile-to-bitcode") { - val bitcodeExtension = project.extensions.getByType<CompileToBitcodeExtension>() - - bitcodeExtension.allTargets { - module("googletest") { - sourceSets { - testFixtures { - inputFiles.from(googleTestRoot.dir("googletest/src")) - // That's how googletest/CMakeLists.txt builds gtest library. - inputFiles.include("gtest-all.cc") - headersDirs.setFrom( - googleTestRoot.dir("googletest/include"), - googleTestRoot.dir("googletest") - ) - // Fix Gradle Configuration Cache: support this task being configured before googletest sources are actually downloaded. - compileTask.configure { - inputFiles.setFrom(googleTestRoot.dir("googletest/src/gtest-all.cc")) - dependsOn(downloadTask) - } - } - } - compilerArgs.set(listOf("-std=c++17", "-O2")) - this.dependencies.add(downloadTask) - } - - module("googlemock") { - sourceSets { - testFixtures { - inputFiles.from(googleTestRoot.dir("googlemock/src")) - // That's how googlemock/CMakeLists.txt builds gmock library. - inputFiles.include("gmock-all.cc") - headersDirs.setFrom( - googleTestRoot.dir("googlemock"), - googleTestRoot.dir("googlemock/include"), - googleTestRoot.dir("googletest/include"), - ) - // Fix Gradle Configuration Cache: support this task being configured before googletest sources are actually downloaded. - compileTask.configure { - inputFiles.setFrom(googleTestRoot.dir("googlemock/src/gmock-all.cc")) - dependsOn(downloadTask) - } - } - } - compilerArgs.set(listOf("-std=c++17", "-O2")) - this.dependencies.add(downloadTask) - } - } - } - } - - companion object { - internal const val GOOGLE_TEST_EXTENSION_NAME = "googletest" - } - -} - -/** - * A project extension to configure from where we get the GoogleTest framework. - */ -abstract class GoogleTestExtension @Inject constructor( - layout: ProjectLayout, - providers: ProviderFactory, - objects: ObjectFactory, -) { - - /** - * A repository to fetch GoogleTest from. - */ - val repository: Property<String> = objects.property<String>().convention("https://github.com/google/googletest.git") - - private var _revision: String? = null - - /** - * A particular revision in the [repository] to be fetched. It can be a branch, a tag or a commit hash. - */ - var revision: String - get() = _revision - ?: throw InvalidUserDataException( - "No value provided for property '${RuntimeTestingPlugin.GOOGLE_TEST_EXTENSION_NAME}.revision'. " + - "Please specify it in the buildscript." - ) - set(value) { _revision = value } - - /** - * Fetch the [revision] even if the [fetchDirectory] already contains it. Overwrite all changes manually made in the output directory. - */ - val refresh: Property<Boolean> = objects.property<Boolean>().convention(false) - - /** - * A directory to fetch the [revision] to. - */ - 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. - */ - 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 [localSourceRoot]) or [fetchDirectory] otherwise. - */ - 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 = layout.files( - sourceDirectory.map { it.dir("googletest/include")}, - sourceDirectory.map { it.dir("googlemock/include")} - ) -} -
diff --git a/kotlin-native/gradle.properties b/kotlin-native/gradle.properties index 6828c85..136a787 100644 --- a/kotlin-native/gradle.properties +++ b/kotlin-native/gradle.properties
@@ -20,12 +20,6 @@ # A version of Xcode required to build the Kotlin/Native compiler. xcodeMajorVersion=26 -# A GTest revision used to test the runtime. -# The latest release GTest (1.10.0) doesn't properly register skipped tests in an XML-report. -# Therefore we use a fixed commit form the master branch where this problem is already fixed. -# https://github.com/google/googletest/commit/07f4869221012b16b7f9ee685d94856e1fc9f361 -gtestRevision=07f4869221012b16b7f9ee685d94856e1fc9f361 - org.gradle.jvmargs='-Dfile.encoding=UTF-8' org.gradle.workers.max=4 slackApiVersion=1.2.0
diff --git a/kotlin-native/runtime/build.gradle.kts b/kotlin-native/runtime/build.gradle.kts index 6868431..1c8e4e2 100644 --- a/kotlin-native/runtime/build.gradle.kts +++ b/kotlin-native/runtime/build.gradle.kts
@@ -18,19 +18,27 @@ plugins { id("base") id("compile-to-bitcode") - id("runtime-testing") } repositories { githubTag("google", "breakpad") + githubCommit("google", "googletest") } val breakpad = configurations.dependencyScope("breakpad") val breakpadClasspath = configurations.resolvable("breakpadClasspath") { extendsFrom(breakpad.get()) } +val googletest = configurations.dependencyScope("googletest") +val googletestClasspath = configurations.resolvable("googletestClasspath") { + extendsFrom(googletest.get()) +} dependencies { breakpad("google:breakpad:2024.02.16@zip") + // GTest 1.10.0 doesn't properly register skipped tests in an XML-report. + // Therefore we use a fixed commit form the master branch where this problem is already fixed. + // https://github.com/google/googletest/commit/07f4869221012b16b7f9ee685d94856e1fc9f361 + googletest("google:googletest:07f4869221012b16b7f9ee685d94856e1fc9f361@zip") } if (HostManager.host == KonanTarget.MACOS_ARM64) { @@ -60,15 +68,28 @@ add(breakpadSources.name, unpackBreakpad) } -googletest { - revision = project.property("gtestRevision") as String - refresh = project.hasProperty("refresh-gtest") +val unpackGoogletest = tasks.register<Sync>("unpackGoogletest") { + from(googletestClasspath.map { zipTree(it.singleFile) }) + eachFile { + relativePath = RelativePath(true, *relativePath.segments.drop(1).toTypedArray()) + } + includeEmptyDirs = false + into(layout.buildDirectory.dir("googletest")) +} + +val googleTestRoot = objects.directoryProperty().apply { + set(layout.dir(unpackGoogletest.map { (it.destinationDir) })) } val targetList = enabledTargets(extensions.getByType<PlatformManager>()) // NOTE: the list of modules is duplicated in `RuntimeModule.kt` bitcode { + googleTestHeaders.from( + googleTestRoot.map { it.dir("googletest/include") }, + googleTestRoot.map { it.dir("googlemock/include") } + ) + allTargets { val fixBrokenMacroExpansionInXcode15_3: List<String> = when (target) { KonanTarget.MACOS_ARM64, KonanTarget.MACOS_X64 -> hashMapOf( @@ -378,6 +399,49 @@ } } + module("googletest") { + sourceSets { + testFixtures { + inputFiles.from(googleTestRoot.dir("googletest/src")) + // That's how googletest/CMakeLists.txt builds gtest library. + inputFiles.include("gtest-all.cc") + headersDirs.setFrom( + googleTestRoot.dir("googletest/include"), + googleTestRoot.dir("googletest") + ) + // Fix Gradle Configuration Cache: support this task being configured before googletest sources are actually downloaded. + compileTask.configure { + inputFiles.setFrom(googleTestRoot.dir("googletest/src/gtest-all.cc")) + dependsOn(unpackGoogletest) + } + } + } + compilerArgs.set(listOf("-std=c++17", "-O2")) + this.dependencies.add(unpackGoogletest) + } + + module("googlemock") { + sourceSets { + testFixtures { + inputFiles.from(googleTestRoot.dir("googlemock/src")) + // That's how googlemock/CMakeLists.txt builds gmock library. + inputFiles.include("gmock-all.cc") + headersDirs.setFrom( + googleTestRoot.dir("googlemock"), + googleTestRoot.dir("googlemock/include"), + googleTestRoot.dir("googletest/include"), + ) + // Fix Gradle Configuration Cache: support this task being configured before googletest sources are actually downloaded. + compileTask.configure { + inputFiles.setFrom(googleTestRoot.dir("googlemock/src/gmock-all.cc")) + dependsOn(unpackGoogletest) + } + } + } + compilerArgs.set(listOf("-std=c++17", "-O2")) + this.dependencies.add(unpackGoogletest) + } + module("test_support") { headersDirs.from(files("src/externalCallsChecker/common/cpp", "src/objcExport/cpp", "src/main/cpp")) sourceSets {