[K/N] Extract LlvmLink out of CompileToBitcode ^KT-53776
Merge-request: KT-MR-8120 Merged-by: Alexander Shabalin <Alexander.Shabalin@jetbrains.com>
This commit is contained in:
committed by
Space Team
parent
8e37a07af2
commit
9a2cb2609f
-252
@@ -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<CompileToBitcodeJob.Parameters> {
|
||||
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<String>
|
||||
val compilerExecutable: Property<String>
|
||||
val compilerWorkUnits: ListProperty<CompilerWorkUnit>
|
||||
val compilerArgs: ListProperty<String>
|
||||
val llvmLinkOutputFile: RegularFileProperty
|
||||
val llvmLinkArgs: ListProperty<String>
|
||||
val platformManager: Property<PlatformManager>
|
||||
}
|
||||
|
||||
@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<String>
|
||||
|
||||
/**
|
||||
* Extra arguments for `llvm-link`.
|
||||
*/
|
||||
@get:Input
|
||||
abstract val linkerArgs: ListProperty<String>
|
||||
|
||||
/**
|
||||
* Extra arguments for [compiler].
|
||||
*/
|
||||
// Marked as input via [compilerFlags].
|
||||
@get:Internal
|
||||
abstract val compilerArgs: ListProperty<String>
|
||||
|
||||
/**
|
||||
* 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<List<String>> = 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<String> = project.provider {
|
||||
compilerWorkingDirectory.get().asFile.absolutePath
|
||||
}
|
||||
|
||||
private val compilerWorkUnits: Provider<List<CompilerWorkUnit>> = project.provider {
|
||||
val workUnits = mutableListOf<CompilerWorkUnit>()
|
||||
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<List<File>> = 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<File>()
|
||||
// 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<PlatformManager>()
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
-14
@@ -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<ExecClang>()
|
||||
|
||||
/**
|
||||
* Task that produces [outputFile].
|
||||
* Compiles source files into bitcode files.
|
||||
*/
|
||||
val task = project.tasks.register<CompileToBitcode>("compileToBitcode${module.name.capitalized}${name.capitalized}${_target.toString().capitalized}", _target).apply {
|
||||
val compileTask = project.tasks.register<ClangFrontend>("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>("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<SourceSet>
|
||||
) : NamedDomainObjectContainer<SourceSet> by container {
|
||||
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.
|
||||
@@ -283,6 +302,9 @@ open class CompileToBitcodeExtension @Inject constructor(val project: Project) :
|
||||
fun main(action: Action<in SourceSet>): 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
|
||||
}
|
||||
|
||||
@@ -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<ClangFrontendJob.Parameters> {
|
||||
interface Parameters : WorkParameters {
|
||||
val workingDirectory: DirectoryProperty
|
||||
val targetName: Property<String>
|
||||
val inputFile: RegularFileProperty
|
||||
val outputFile: RegularFileProperty
|
||||
val compilerExecutable: Property<String>
|
||||
val arguments: ListProperty<String>
|
||||
val platformManager: Property<PlatformManager>
|
||||
}
|
||||
|
||||
@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<List<WorkUnit>> = project.provider {
|
||||
val result = mutableListOf<WorkUnit>()
|
||||
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<String>
|
||||
|
||||
/**
|
||||
* The compiler to be used.
|
||||
*
|
||||
* Currently only `clang` and `clang++` are supported.
|
||||
*/
|
||||
@get:Input
|
||||
abstract val compiler: Property<String>
|
||||
|
||||
/**
|
||||
* Extra arguments for [compiler].
|
||||
*/
|
||||
// Marked as input via [compilerFlags].
|
||||
@get:Internal
|
||||
abstract val arguments: ListProperty<String>
|
||||
|
||||
/**
|
||||
* 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<List<String>> = 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<String> = project.provider {
|
||||
workingDirectory.get().asFile.absolutePath
|
||||
}
|
||||
|
||||
/**
|
||||
* Computed header files used for task dependencies tracking.
|
||||
*/
|
||||
@get:InputFiles
|
||||
protected val headers: Provider<List<File>> = 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<File>()
|
||||
// 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<PlatformManager>()
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<LlvmLinkJob.Parameters> {
|
||||
interface Parameters : WorkParameters {
|
||||
val inputFiles: ConfigurableFileCollection
|
||||
val outputFile: RegularFileProperty
|
||||
val arguments: ListProperty<String>
|
||||
val platformManager: Property<PlatformManager>
|
||||
}
|
||||
|
||||
@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<String>
|
||||
|
||||
@get:Inject
|
||||
protected abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
private val platformManager = project.extensions.getByType<PlatformManager>()
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -59,8 +59,8 @@ open class RuntimeTestingPlugin : Plugin<Project> {
|
||||
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<Project> {
|
||||
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"),
|
||||
|
||||
Reference in New Issue
Block a user