[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:
Alexander Shabalin
2023-01-18 21:41:17 +00:00
committed by Space Team
parent 8e37a07af2
commit 9a2cb2609f
5 changed files with 338 additions and 270 deletions
@@ -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)
}
}
}
@@ -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)
}
}
}
@@ -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"),