From 9a2cb2609f7ebd542575b97e52bc01b3874575d9 Mon Sep 17 00:00:00 2001 From: Alexander Shabalin Date: Wed, 18 Jan 2023 21:41:17 +0000 Subject: [PATCH] [K/N] Extract LlvmLink out of CompileToBitcode ^KT-53776 Merge-request: KT-MR-8120 Merged-by: Alexander Shabalin --- .../kotlin/bitcode/CompileToBitcode.kt | 252 ------------------ .../kotlin/bitcode/CompileToBitcodePlugin.kt | 56 +++- .../org/jetbrains/kotlin/cpp/ClangFrontend.kt | 208 +++++++++++++++ .../org/jetbrains/kotlin/cpp/LlvmLink.kt | 84 ++++++ .../testing/native/RuntimeTestingPlugin.kt | 8 +- 5 files changed, 338 insertions(+), 270 deletions(-) delete mode 100644 kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/bitcode/CompileToBitcode.kt create mode 100644 kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/cpp/ClangFrontend.kt create mode 100644 kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/cpp/LlvmLink.kt diff --git a/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/bitcode/CompileToBitcode.kt b/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/bitcode/CompileToBitcode.kt deleted file mode 100644 index 31fdd536595..00000000000 --- a/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/bitcode/CompileToBitcode.kt +++ /dev/null @@ -1,252 +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/LICENSE.txt file. - */ - -package org.jetbrains.kotlin.bitcode - -import org.gradle.api.DefaultTask -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.ConfigurableFileTree -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.* -import org.gradle.kotlin.dsl.getByType -import org.gradle.process.ExecOperations -import org.gradle.workers.WorkAction -import org.gradle.workers.WorkParameters -import org.gradle.workers.WorkerExecutor -import org.jetbrains.kotlin.ExecClang -import org.jetbrains.kotlin.execLlvmUtility -import org.jetbrains.kotlin.konan.target.PlatformManager -import org.jetbrains.kotlin.konan.target.SanitizerKind -import org.jetbrains.kotlin.konan.target.TargetWithSanitizer -import java.io.File -import javax.inject.Inject - -private data class CompilerWorkUnit( - val input: File, - val output: File, -) : java.io.Serializable - -private abstract class CompileToBitcodeJob : WorkAction { - interface Parameters : WorkParameters { - val workingDirectory: DirectoryProperty - // TODO: Figure out a way to pass KonanTarget, but it is used as a key into PlatformManager, - // so object identity matters, and platform managers are different between project and worker sides. - val targetName: Property - val compilerExecutable: Property - val compilerWorkUnits: ListProperty - val compilerArgs: ListProperty - val llvmLinkOutputFile: RegularFileProperty - val llvmLinkArgs: ListProperty - val platformManager: Property - } - - @get:Inject - abstract val objects: ObjectFactory - - @get:Inject - abstract val execOperations: ExecOperations - - override fun execute() { - with(parameters) { - val execClang = ExecClang.create(objects, platformManager.get()) - - val baseDir = workingDirectory.asFile.get() - - compilerWorkUnits.get().forEach { workUnit -> - workUnit.output.parentFile.mkdirs() - val inputRelativePath = baseDir.toPath().relativize(workUnit.input.toPath()) - execClang.execKonanClang(targetName.get()) { - workingDir = baseDir - executable = compilerExecutable.get() - args = compilerArgs.get() + listOf(inputRelativePath.toString(), "-o", workUnit.output.absolutePath) - } - } - - // TODO: Extract llvm-link out. This will allow parallelizing clang compilation. - execOperations.execLlvmUtility(platformManager.get(), "llvm-link") { - args = listOf("-o", llvmLinkOutputFile.asFile.get().absolutePath) + llvmLinkArgs.get() + compilerWorkUnits.get().map { it.output.absolutePath } - } - } - } -} - -/** - * Compiling files with clang into LLVM bitcode. - * - * Compiles [inputFiles] into [outputFile] with [compiler] and `llvm-link`. - * - * @see CompileToBitcodePlugin - */ -abstract class CompileToBitcode @Inject constructor( - private val _target: TargetWithSanitizer, -) : DefaultTask() { - /** - * Target for which to compile - */ - @get:Input - val target by _target::target - - /** - * Optional [sanitizer][SanitizerKind] for [target]. - */ - @get:Input - @get:Optional - val sanitizer by _target::sanitizer - - /** - * Final output file. - */ - @get:OutputFile - abstract val outputFile: RegularFileProperty - - /** - * Where to put bitcode files generated by clang. - */ - @get:Internal - abstract val outputDirectory: DirectoryProperty - - /** - * The compiler to be used. - * - * Currently only `clang` and `clang++` are supported. - */ - @get:Input - abstract val compiler: Property - - /** - * Extra arguments for `llvm-link`. - */ - @get:Input - abstract val linkerArgs: ListProperty - - /** - * Extra arguments for [compiler]. - */ - // Marked as input via [compilerFlags]. - @get:Internal - abstract val compilerArgs: ListProperty - - /** - * Locations to search for headers. - * - * Will be passed to the compiler as `-I…` and will also be used to compute task dependencies: recompile if the headers change. - */ - // Marked as input via [headers] and [compilerFlags]. - @get:Internal - abstract val headersDirs: ConfigurableFileCollection - - /** - * Final computed compiler arguments. - */ - @get:Input - val compilerFlags: Provider> = project.provider { - listOfNotNull( - "-c", - "-emit-llvm" - ) + headersDirs.map { "-I${it.absolutePath}" } + when (sanitizer) { - null -> listOf() - SanitizerKind.ADDRESS -> listOf("-fsanitize=address") - SanitizerKind.THREAD -> listOf("-fsanitize=thread") - } + compilerArgs.get() - } - - /** - * Source files to compile from. - */ - @get:SkipWhenEmpty - @get:InputFiles - abstract val inputFiles: ConfigurableFileTree - - /** - * Working directory for the compiler. - * - * All inputs will be passed to the compiler as relative paths to this directory. - */ - @get:Internal - abstract val compilerWorkingDirectory: DirectoryProperty - - @get:Input - protected val compilerWorkingDirectoryPath: Provider = project.provider { - compilerWorkingDirectory.get().asFile.absolutePath - } - - private val compilerWorkUnits: Provider> = project.provider { - val workUnits = mutableListOf() - inputFiles.visit { - if (!isDirectory) { - val output = outputDirectory.file(relativePath.parent.append(true, "${file.nameWithoutExtension}.bc").pathString) - workUnits.add(CompilerWorkUnit(file, output.get().asFile)) - } - } - workUnits - } - - /** - * Output files from the [compiler]. - */ - // TODO: Make it OutputFiles. Currently clashes with `kotlinNativeInterop` `linkOutputs` in kotlin-native/backend.native/build.gradle Can be fixed when the task is split into clang and llvm-link. - @get:Internal - val compilerOutputFiles = compilerWorkUnits.map { it.map { it.output } } - - /** - * Computed header files used for task dependencies tracking. - */ - @get:InputFiles - protected val headers: Provider> = project.provider { - // Not using clang's -M* flags because there's a problem with our current include system: - // We allow includes relative to the current directory and also pass -I for each imported module - // Given file tree: - // a: - // header.hpp - // b: - // impl.cpp - // Assume module b adds a to its include path. - // If b/impl.cpp has #include "header.hpp", it'll be included from a/header.hpp. If we add another file - // header.hpp into b/, the next compilation of b/impl.cpp will include b/header.hpp. -M flags, however, - // won't generate a dependency on b/header.hpp, so incremental compilation will be broken. - // TODO: Apart from dependency generation this also makes it awkward to have two files with - // the same name (e.g. Utils.h) in directories a/ and b/: For the b/impl.cpp to include a/header.hpp - // it needs to have #include "../a/header.hpp" - - val dirs = mutableSetOf() - // First add dirs with sources, as clang by default adds directory with the source to the include path. - inputFiles.forEach { - dirs.add(it.parentFile) - } - // Now add manually given header dirs. - dirs.addAll(headersDirs.files) - dirs.flatMap { dir -> - project.fileTree(dir) { - include("**/*.h", "**/*.hpp") - }.files - } - } - - @get:Inject - protected abstract val workerExecutor: WorkerExecutor - - private val platformManager = project.extensions.getByType() - - @TaskAction - fun compile() { - val workQueue = workerExecutor.noIsolation() - - workQueue.submit(CompileToBitcodeJob::class.java) { - workingDirectory.set(this@CompileToBitcode.compilerWorkingDirectory) - targetName.set(this@CompileToBitcode.target.name) - compilerExecutable.set(this@CompileToBitcode.compiler) - compilerArgs.set(this@CompileToBitcode.compilerFlags) - compilerWorkUnits.set(this@CompileToBitcode.compilerWorkUnits) - llvmLinkOutputFile.set(this@CompileToBitcode.outputFile) - llvmLinkArgs.set(this@CompileToBitcode.linkerArgs) - platformManager.set(this@CompileToBitcode.platformManager) - } - } -} 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 467ce92ecf8..8d47706009f 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 @@ -10,7 +10,10 @@ import org.gradle.api.* import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ConfigurationVariant import org.gradle.api.attributes.Usage -import org.gradle.api.file.* +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.ConfigurableFileTree +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider @@ -204,21 +207,25 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) : private val execClang = project.extensions.getByType() /** - * Task that produces [outputFile]. + * Compiles source files into bitcode files. */ - val task = project.tasks.register("compileToBitcode${module.name.capitalized}${name.capitalized}${_target.toString().capitalized}", _target).apply { + val compileTask = project.tasks.register("clangFrontend${module.name.capitalized}${name.capitalized}${_target.toString().capitalized}").apply { configure { - this.description = "Compiles '${module.name}' (${this@SourceSet.name} sources) to bitcode for ${_target}" - this.outputFile.set(this@SourceSet.outputFile) + this.description = "Compiles '${module.name}' (${this@SourceSet.name} sources) to bitcode for $_target" this.outputDirectory.set(this@SourceSet.outputDirectory) + this.targetName.set(target.name) this.compiler.set(module.compiler) - this.linkerArgs.set(module.linkerArgs) - this.compilerArgs.set(module.compilerArgs) + this.arguments.set(module.compilerArgs) + this.arguments.addAll(when (sanitizer) { + null -> emptyList() + SanitizerKind.ADDRESS -> listOf("-fsanitize=address") + SanitizerKind.THREAD -> listOf("-fsanitize=thread") + }) this.headersDirs.from(this@SourceSet.headersDirs) this.inputFiles.from(this@SourceSet.inputFiles.dir) this.inputFiles.setIncludes(this@SourceSet.inputFiles.includes) this.inputFiles.setExcludes(this@SourceSet.inputFiles.excludes) - this.compilerWorkingDirectory.set(module.compilerWorkingDirectory) + this.workingDirectory.set(module.compilerWorkingDirectory) // TODO: Should depend only on the toolchain needed to build for the _target dependsOn(":kotlin-native:dependencies:update") dependsOn(this@SourceSet.dependencies) @@ -230,11 +237,11 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) : entry { val compileTask = this@apply.get() val args = listOf(execClang.resolveExecutable(compileTask.compiler.get())) + compileTask.compilerFlags.get() + execClang.clangArgsForCppRuntime(target.name) - directory.set(compileTask.compilerWorkingDirectory) + directory.set(compileTask.workingDirectory) files.setFrom(compileTask.inputFiles) arguments.set(args) // Only the location of output file matters, compdb does not depend on the compilation result. - output.set(compileTask.outputFile.locationOnly.map { it.asFile.absolutePath }) + output.set(compileTask.outputDirectory.locationOnly.map { it.asFile.absolutePath }) } task.configure { // Compile task depends on the toolchain (including headers) and on the source code (e.g. googletest). @@ -245,6 +252,21 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) : dependsOn(this@SourceSet.dependencies) } } + } + + /** + * Links bitcode files together. + */ + val task = project.tasks.register("llvmLink${module.name.capitalized}${name.capitalized}${_target.toString().capitalized}").apply { + configure { + this.description = "Link '${module.name}' bitcode files (${this@SourceSet.name} sources) into a single bitcode file for $_target" + this.inputFiles.from(compileTask) + this.outputFile.set(this@SourceSet.outputFile) + this.arguments.set(module.linkerArgs) + onlyIf { + this@SourceSet.onlyIf.get().all { it.isSatisfiedBy(this@SourceSet) } + } + } project.compileBitcodeElements(this@SourceSet.name).targetVariant(_target).artifact(this) project.moduleCompileBitcodeElements(module.name, this@SourceSet.name).targetVariant(_target).artifact(this) } @@ -262,10 +284,7 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) : * * 3 source sets are well known: [main], [testFixtures] and [test]. */ - abstract class SourceSets @Inject constructor( - private val module: Module, - private val container: ExtensiblePolymorphicDomainObjectContainer - ) : NamedDomainObjectContainer by container { + abstract class SourceSets @Inject constructor(private val module: Module, private val container: ExtensiblePolymorphicDomainObjectContainer) : NamedDomainObjectContainer by container { private val project by module::project // googleTestExtension is only used if testFixtures or tests are used. @@ -283,6 +302,9 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) : fun main(action: Action): SourceSet = create(MAIN_SOURCE_SET_NAME) { this.inputFiles.include("**/*.cpp", "**/*.mm") this.inputFiles.exclude("**/*Test.cpp", "**/*TestSupport.cpp", "**/*Test.mm", "**/*TestSupport.mm") + compileTask.configure { + this.group = BUILD_TASK_GROUP + } task.configure { this.group = BUILD_TASK_GROUP } @@ -303,6 +325,9 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) : this.headersDirs.from(googleTestExtension.headersDirs) // TODO: Must generally depend on googletest module headers which must itself depend on sources being present. dependencies.add(project.tasks.named("downloadGoogleTest")) + compileTask.configure { + this.group = VERIFICATION_BUILD_TASK_GROUP + } task.configure { this.group = VERIFICATION_BUILD_TASK_GROUP } @@ -323,6 +348,9 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) : this.headersDirs.from(googleTestExtension.headersDirs) // TODO: Must generally depend on googletest module headers which must itself depend on sources being present. dependencies.add(project.tasks.named("downloadGoogleTest")) + compileTask.configure { + this.group = VERIFICATION_BUILD_TASK_GROUP + } task.configure { this.group = VERIFICATION_BUILD_TASK_GROUP } diff --git a/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/cpp/ClangFrontend.kt b/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/cpp/ClangFrontend.kt new file mode 100644 index 00000000000..1d5f9858124 --- /dev/null +++ b/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/cpp/ClangFrontend.kt @@ -0,0 +1,208 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.cpp + +import org.gradle.api.DefaultTask +import org.gradle.api.file.* +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.getByType +import org.gradle.workers.WorkAction +import org.gradle.workers.WorkParameters +import org.gradle.workers.WorkerExecutor +import org.jetbrains.kotlin.ExecClang +import org.jetbrains.kotlin.bitcode.CompileToBitcodePlugin +import org.jetbrains.kotlin.konan.target.PlatformManager +import java.io.File +import javax.inject.Inject + +private abstract class ClangFrontendJob : WorkAction { + interface Parameters : WorkParameters { + val workingDirectory: DirectoryProperty + val targetName: Property + val inputFile: RegularFileProperty + val outputFile: RegularFileProperty + val compilerExecutable: Property + val arguments: ListProperty + val platformManager: Property + } + + @get:Inject + abstract val objects: ObjectFactory + + override fun execute() { + with(parameters) { + val execClang = ExecClang.create(objects, platformManager.get()) + + val baseDir = workingDirectory.asFile.get() + outputFile.get().asFile.parentFile.mkdirs() + val inputRelativePath = baseDir.toPath().relativize(inputFile.get().asFile.toPath()) + execClang.execKonanClang(targetName.get()) { + workingDir = baseDir + executable = compilerExecutable.get() + args = arguments.get() + listOf(inputRelativePath.toString(), "-o", outputFile.get().asFile.absolutePath) + } + } + } +} + +/** + * Compiling [inputFiles] with clang into LLVM bitcode in [outputFiles]. + * + * @see CompileToBitcodePlugin + */ +abstract class ClangFrontend : DefaultTask() { + private data class WorkUnit( + val inputFile: File, + val outputFile: File, + ) + + private val workUnits: Provider> = project.provider { + val result = mutableListOf() + inputFiles.visit { + if (!isDirectory) { + val inputFile = this.file + val outputFile = outputDirectory.file(relativePath.parent.append(true, "${file.nameWithoutExtension}.bc").pathString).get().asFile + result.add(WorkUnit(inputFile, outputFile)) + } + } + result + } + + /** + * Where to put bitcode files generated by clang. + */ + // Marked as output via [outputFiles] + @get:Internal + abstract val outputDirectory: DirectoryProperty + + /** + * Source files to compile from. + */ + @get:SkipWhenEmpty + @get:InputFiles + abstract val inputFiles: ConfigurableFileTree + + /** + * Bitcode files generated by clang. + */ + @get:OutputFiles + val outputFiles: FileCollection = project.files(workUnits.map { it.map { workUnit -> workUnit.outputFile } }) + + /** + * Will select the appropriate compiler and additional flags. + */ + // TODO: Consider specifying full clang execution here and configure it from the plugin. + @get:Input + abstract val targetName: Property + + /** + * The compiler to be used. + * + * Currently only `clang` and `clang++` are supported. + */ + @get:Input + abstract val compiler: Property + + /** + * Extra arguments for [compiler]. + */ + // Marked as input via [compilerFlags]. + @get:Internal + abstract val arguments: ListProperty + + /** + * Locations to search for headers. + * + * Will be passed to the compiler as `-I…` and will also be used to compute task dependencies: recompile if the headers change. + */ + // Marked as input via [headers] and [compilerFlags]. + @get:Internal + abstract val headersDirs: ConfigurableFileCollection + + /** + * Final computed compiler arguments. + */ + @get:Input + val compilerFlags: Provider> = project.provider { + listOfNotNull( + "-c", + "-emit-llvm" + ) + headersDirs.map { "-I${it.absolutePath}" } + arguments.get() + } + + /** + * Working directory for [compiler]. + * + * All inputs will be passed to the compiler as relative paths to this directory. + */ + @get:Internal + abstract val workingDirectory: DirectoryProperty + + @get:Input + protected val workingDirectoryPath: Provider = project.provider { + workingDirectory.get().asFile.absolutePath + } + + /** + * Computed header files used for task dependencies tracking. + */ + @get:InputFiles + protected val headers: Provider> = project.provider { + // Not using clang's -M* flags because there's a problem with our current include system: + // We allow includes relative to the current directory and also pass -I for each imported module + // Given file tree: + // a: + // header.hpp + // b: + // impl.cpp + // Assume module b adds a to its include path. + // If b/impl.cpp has #include "header.hpp", it'll be included from a/header.hpp. If we add another file + // header.hpp into b/, the next compilation of b/impl.cpp will include b/header.hpp. -M flags, however, + // won't generate a dependency on b/header.hpp, so incremental compilation will be broken. + // TODO: Apart from dependency generation this also makes it awkward to have two files with + // the same name (e.g. Utils.h) in directories a/ and b/: For the b/impl.cpp to include a/header.hpp + // it needs to have #include "../a/header.hpp" + + val dirs = mutableSetOf() + // First add dirs with sources, as clang by default adds directory with the source to the include path. + workUnits.get().forEach { + dirs.add(it.inputFile) + } + // Now add manually given header dirs. + dirs.addAll(headersDirs.files) + dirs.flatMap { dir -> + project.fileTree(dir) { + include("**/*.h", "**/*.hpp") + }.files + } + } + + @get:Inject + protected abstract val workerExecutor: WorkerExecutor + + private val platformManager = project.extensions.getByType() + + @TaskAction + fun compile() { + val workQueue = workerExecutor.noIsolation() + + workUnits.get().forEach { workUnit -> + workQueue.submit(ClangFrontendJob::class.java) { + workingDirectory.set(this@ClangFrontend.workingDirectory) + targetName.set(this@ClangFrontend.targetName) + inputFile.set(workUnit.inputFile) + outputFile.set(workUnit.outputFile) + compilerExecutable.set(this@ClangFrontend.compiler) + arguments.set(this@ClangFrontend.compilerFlags) + platformManager.set(this@ClangFrontend.platformManager) + } + } + } +} diff --git a/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/cpp/LlvmLink.kt b/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/cpp/LlvmLink.kt new file mode 100644 index 00000000000..94ee9764e68 --- /dev/null +++ b/kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/cpp/LlvmLink.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.cpp + +import org.gradle.api.DefaultTask +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.getByType +import org.gradle.process.ExecOperations +import org.gradle.workers.WorkAction +import org.gradle.workers.WorkParameters +import org.gradle.workers.WorkerExecutor +import org.jetbrains.kotlin.execLlvmUtility +import org.jetbrains.kotlin.konan.target.PlatformManager +import javax.inject.Inject + +private abstract class LlvmLinkJob : WorkAction { + interface Parameters : WorkParameters { + val inputFiles: ConfigurableFileCollection + val outputFile: RegularFileProperty + val arguments: ListProperty + val platformManager: Property + } + + @get:Inject + abstract val execOperations: ExecOperations + + override fun execute() { + with(parameters) { + execOperations.execLlvmUtility(platformManager.get(), "llvm-link") { + args = listOf("-o", outputFile.asFile.get().absolutePath) + arguments.get() + inputFiles.map { it.absolutePath } + } + } + } +} + +/** + * Run `llvm-link` on [inputFiles] with extra [arguments] and produce [outputFile] + */ +abstract class LlvmLink : DefaultTask() { + /** + * Bitcode files to link together. + */ + @get:InputFiles + abstract val inputFiles: ConfigurableFileCollection + + /** + * Output file. + */ + @get:OutputFile + abstract val outputFile: RegularFileProperty + + /** + * Extra arguments for `llvm-link`. + */ + @get:Input + abstract val arguments: ListProperty + + @get:Inject + protected abstract val workerExecutor: WorkerExecutor + + private val platformManager = project.extensions.getByType() + + @TaskAction + fun link() { + val workQueue = workerExecutor.noIsolation() + + workQueue.submit(LlvmLinkJob::class.java) { + inputFiles.from(this@LlvmLink.inputFiles) + outputFile.set(this@LlvmLink.outputFile) + arguments.set(this@LlvmLink.arguments) + platformManager.set(this@LlvmLink.platformManager) + } + } +} \ No newline at end of file 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 index b441b172ed2..7e3c5ee9688 100644 --- 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 @@ -59,8 +59,8 @@ open class RuntimeTestingPlugin : Plugin { sourceSets { testFixtures { inputFiles.from(googleTestRoot.resolve("googletest/src")) - inputFiles.include("*.cc") - inputFiles.exclude("gtest-all.cc", "gtest_main.cc") + // That's how googletest/CMakeLists.txt builds gtest library. + inputFiles.include("gtest-all.cc") headersDirs.setFrom( googleTestRoot.resolve("googletest/include"), googleTestRoot.resolve("googletest") @@ -75,8 +75,8 @@ open class RuntimeTestingPlugin : Plugin { sourceSets { testFixtures { inputFiles.from(googleTestRoot.resolve("googlemock/src")) - inputFiles.include("*.cc") - inputFiles.exclude("gmock-all.cc", "gmock_main.cc") + // That's how googlemock/CMakeLists.txt builds gmock library. + inputFiles.include("gmock-all.cc") headersDirs.setFrom( googleTestRoot.resolve("googlemock"), googleTestRoot.resolve("googlemock/include"),