jps: common modules metadata compiler

(cherry picked from commit cde333d)
This commit is contained in:
Sergey Rostov
2018-04-10 19:05:30 +03:00
parent 41dcc9ad4d
commit 91b703d529
9 changed files with 133 additions and 120 deletions
@@ -62,6 +62,8 @@ import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.withIC
import org.jetbrains.kotlin.jps.model.JpsKotlinCompilerSettings
import org.jetbrains.kotlin.jps.build.KotlinJpsBuildTest.LibraryDependency.*
import org.jetbrains.kotlin.jps.model.kotlinCommonCompilerArguments
import org.jetbrains.kotlin.jps.model.kotlinCompilerArguments
import org.jetbrains.kotlin.jps.platforms.KotlinJsModuleBuildTarget
import org.jetbrains.kotlin.jps.platforms.clearKotlinModuleBuildTargetDataBindings
import org.jetbrains.kotlin.jps.platforms.kotlinData
@@ -525,9 +527,9 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() {
assertEquals(1, myProject.modules.size)
val module = myProject.modules.first()
val args = JpsKotlinCompilerSettings.getCommonCompilerArguments(module)
val args = module.kotlinCompilerArguments
args.apiVersion = "1.2"
JpsKotlinCompilerSettings.setCommonCompilerArguments(myProject, args)
myProject.kotlinCommonCompilerArguments = args
buildAllModules().assertSuccessful()
}
@@ -27,6 +27,8 @@ import kotlin.reflect.KMutableProperty1
import org.jetbrains.kotlin.daemon.common.COMPILE_DAEMON_CUSTOM_RUN_FILES_PATH_FOR_TESTS
import org.jetbrains.kotlin.daemon.common.COMPILE_DAEMON_ENABLED_PROPERTY
import org.jetbrains.kotlin.daemon.common.isDaemonEnabled
import org.jetbrains.kotlin.jps.model.kotlinCommonCompilerArguments
import org.jetbrains.kotlin.jps.model.kotlinCompilerArguments
import java.io.File
class KotlinJpsBuildTestIncremental : KotlinJpsBuildTest() {
@@ -138,11 +140,11 @@ class KotlinJpsBuildTestIncremental : KotlinJpsBuildTest() {
assertEquals(1, myProject.modules.size)
val module = myProject.modules.first()
val args = JpsKotlinCompilerSettings.getCommonCompilerArguments(module)
val args = module.kotlinCompilerArguments
fun setVersion(newVersion: String) {
versionProperty.set(args, newVersion)
JpsKotlinCompilerSettings.setCommonCompilerArguments(myProject, args)
myProject.kotlinCommonCompilerArguments = args
}
assertNull(args.apiVersion)
@@ -93,7 +93,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
val arguments = mergeBeans(commonArguments, XmlSerializerUtil.createCopy(k2MetadataArguments))
arguments.freeArgs = sourceFiles.map { it.absolutePath }
withCompilerSettings(compilerSettings) {
runCompiler(K2JVM_COMPILER, arguments, environment)
runCompiler(K2METADATA_COMPILER, arguments, environment)
}
}
@@ -117,7 +117,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
compilerSettings: CompilerSettings,
environment: JpsCompilerEnvironment,
sourceFiles: Collection<File>,
sourceRoots: Collection<File>,
sourceMapRoots: Collection<File>,
libraries: List<String>,
friendModules: List<String>,
outputFile: File
@@ -130,7 +130,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner<JpsCompilerEnvironment>() {
setupK2JsArguments(outputFile, sourceFiles, libraries, friendModules, arguments)
if (arguments.sourceMap) {
arguments.sourceMapBaseDirs = sourceRoots.joinToString(File.pathSeparator) { it.path }
arguments.sourceMapBaseDirs = sourceMapRoots.joinToString(File.pathSeparator) { it.path }
}
log.debug("K2JS: arguments after setup" + ArgumentUtils.convertArgumentsToStringList(arguments))
@@ -274,7 +274,8 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
outputConsumer: ModuleLevelBuilder.OutputConsumer
): ModuleLevelBuilder.ExitCode {
if (chunk.isDummy(context)) return NOTHING_DONE
if (chunk.isDummy(context))
return NOTHING_DONE
val messageCollector = MessageCollectorAdapter(context)
val fsOperations = FSOperationsHelper(context, chunk, LOG)
@@ -359,7 +360,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
val start = System.nanoTime()
val outputItemCollector = doCompileModuleChunk(
allCompiledFiles, chunk, commonArguments, context, dirtyFilesHolder,
environment, filesToCompile, incrementalCaches, project
environment, filesToCompile, incrementalCaches, fsOperations
)
statisticsLogger.registerStatistic(chunk, System.nanoTime() - start)
@@ -510,10 +511,15 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
chunk.representativeTarget().module.kotlinCompilerArguments
private fun doCompileModuleChunk(
allCompiledFiles: MutableSet<File>, chunk: ModuleChunk, commonArguments: CommonCompilerArguments, context: CompileContext,
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>, environment: JpsCompilerEnvironment,
filesToCompile: MultiMap<ModuleBuildTarget, File>, incrementalCaches: Map<ModuleBuildTarget, IncrementalJvmCache>,
project: JpsProject
allCompiledFiles: MutableSet<File>,
chunk: ModuleChunk,
commonArguments: CommonCompilerArguments,
context: CompileContext,
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
environment: JpsCompilerEnvironment,
filesToCompile: MultiMap<ModuleBuildTarget, File>,
incrementalCaches: Map<ModuleBuildTarget, IncrementalJvmCache>,
fsOperations: FSOperationsHelper
): OutputItemsCollector? {
val representativeTarget = chunk.representativeTarget()
@@ -545,7 +551,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
val isDoneSomething = representativeTarget.kotlinData?.compileModuleChunk(
allCompiledFiles, chunk, commonArguments, context,
dirtyFilesHolder, environment, filesToCompile
dirtyFilesHolder, environment, filesToCompile, fsOperations
) ?: false
return if (isDoneSomething) environment.outputItemsCollector else null
@@ -561,7 +567,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
register(LookupTracker::class.java, lookupTracker)
register(
IncrementalCompilationComponents::class.java,
IncrementalCompilationComponentsImpl(incrementalCaches.mapKeys { it.key.kotlinData!!.moduleId })
IncrementalCompilationComponentsImpl(incrementalCaches.mapKeys { it.key.kotlinData!!.targetId })
)
register(CompilationCanceledStatus::class.java, object : CompilationCanceledStatus {
override fun checkCanceled() {
@@ -628,9 +634,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
fun SimpleOutputItem.target() =
sourceFiles.firstOrNull()?.let { sourceToTarget[it] } ?: chunk.targets.singleOrNull {
it.outputDir?.let {
outputFile.startsWith(
it
)
outputFile.startsWith(it)
} ?: false
} ?: representativeTarget
@@ -870,14 +874,15 @@ fun getAllCompiledFilesContainer(context: CompileContext): MutableSet<File> {
// TODO: investigate thread safety
private val PROCESSED_TARGETS_WITH_REMOVED_FILES = Key.create<MutableSet<ModuleBuildTarget>>("_processed_targets_with_removed_files_")
fun getProcessedTargetsWithRemovedFilesContainer(context: CompileContext): MutableSet<ModuleBuildTarget> {
var set = PROCESSED_TARGETS_WITH_REMOVED_FILES.get(context)
if (set == null) {
set = HashSet()
PROCESSED_TARGETS_WITH_REMOVED_FILES.set(context, set)
val CompileContext.processedTargetsWithRemovedFilesContainer: MutableSet<ModuleBuildTarget>
get() {
var set = PROCESSED_TARGETS_WITH_REMOVED_FILES.get(this)
if (set == null) {
set = HashSet()
PROCESSED_TARGETS_WITH_REMOVED_FILES.set(this, set)
}
return set
}
return set
}
private fun hasKotlinDirtyOrRemovedFiles(
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
@@ -50,11 +50,6 @@ object KotlinSourceFileCollector {
.getRemovedFiles(target)
.mapNotNull { if (FileUtilRt.extensionEquals(it, "kt")) File(it) else null }
private fun getRelevantSourceRoots(target: ModuleBuildTarget): Iterable<JpsModuleSourceRoot> {
val sourceRootType = if (target.isTests) JavaSourceRootType.TEST_SOURCE else JavaSourceRootType.SOURCE
return target.module.getSourceRoots<JavaSourceRootProperties>(sourceRootType)
}
internal fun isKotlinSourceFile(file: File): Boolean {
return FileUtilRt.extensionEquals(file.name, "kt")
}
@@ -10,10 +10,13 @@ import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.builders.DirtyFilesHolder
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.jps.incremental.FSOperations
import org.jetbrains.jps.incremental.ModuleBuildTarget
import org.jetbrains.jps.incremental.fs.CompilationRound
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment
import org.jetbrains.kotlin.compilerRunner.JpsKotlinCompilerRunner
import org.jetbrains.kotlin.jps.build.FSOperationsHelper
import org.jetbrains.kotlin.jps.model.k2MetadataCompilerArguments
import org.jetbrains.kotlin.jps.model.kotlinCompilerSettings
import java.io.File
@@ -28,11 +31,15 @@ class KotlinCommonModuleBuildTarget(jpsModuleBuildTarget: ModuleBuildTarget) :
context: CompileContext,
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
environment: JpsCompilerEnvironment,
filesToCompile: MultiMap<ModuleBuildTarget, File>
filesToCompile: MultiMap<ModuleBuildTarget, File>,
fsOperations: FSOperationsHelper
): Boolean {
require(chunk.representativeTarget() == jpsModuleBuildTarget)
if (reportAndSkipCircular(chunk, environment)) return false
// Incremental compilation is not supported, so mark all dependents as dirty
FSOperations.markDirtyRecursively(context, CompilationRound.CURRENT, chunk)
JpsKotlinCompilerRunner().runK2MetadataCompiler(
commonArguments,
module.k2MetadataCompilerArguments,
@@ -41,7 +48,6 @@ class KotlinCommonModuleBuildTarget(jpsModuleBuildTarget: ModuleBuildTarget) :
sources
)
// TODO: Compile metadata
return false
return true
}
}
@@ -17,6 +17,7 @@ import org.jetbrains.jps.util.JpsPathUtil
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment
import org.jetbrains.kotlin.compilerRunner.JpsKotlinCompilerRunner
import org.jetbrains.kotlin.jps.build.FSOperationsHelper
import org.jetbrains.kotlin.jps.model.k2JsCompilerArguments
import org.jetbrains.kotlin.jps.model.kotlinCompilerSettings
import org.jetbrains.kotlin.jps.model.productionOutputFilePath
@@ -28,26 +29,93 @@ import java.io.File
import java.net.URI
class KotlinJsModuleBuildTarget(jpsModuleBuildTarget: ModuleBuildTarget) : KotlinModuleBuilderTarget(jpsModuleBuildTarget) {
override fun compileModuleChunk(
allCompiledFiles: MutableSet<File>,
chunk: ModuleChunk,
commonArguments: CommonCompilerArguments,
context: CompileContext,
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
environment: JpsCompilerEnvironment,
filesToCompile: MultiMap<ModuleBuildTarget, File>,
fsOperations: FSOperationsHelper
): Boolean {
require(chunk.representativeTarget() == jpsModuleBuildTarget)
if (reportAndSkipCircular(chunk, environment)) return false
val sources = sources
if (sources.isEmpty()) return false
val libraries = libraryFiles + dependenciesMetaFiles
JpsKotlinCompilerRunner().runK2JsCompiler(
commonArguments,
module.k2JsCompilerArguments,
module.kotlinCompilerSettings,
environment,
sources,
sourceMapRoots,
libraries,
friendBuildTargetsMetaFiles,
outputFile
)
return true
}
override fun doAfterBuild() {
copyJsLibraryFilesIfNeeded()
}
private fun copyJsLibraryFilesIfNeeded() {
if (module.kotlinCompilerSettings.copyJsLibraryFiles) {
val outputLibraryRuntimeDirectory = File(outputDir, module.kotlinCompilerSettings.outputDirectoryForJsLibraryFiles).absolutePath
JsLibraryUtils.copyJsFilesFromLibraries(
libraryFiles, outputLibraryRuntimeDirectory,
copySourceMap = module.k2JsCompilerArguments.sourceMap
)
}
}
private val sourceMapRoots: List<File>
get() {
// Compiler starts to produce path relative to base dirs in source maps if at least one statement is true:
// 1) base dirs are specified;
// 2) prefix is specified (i.e. non-empty)
// Otherwise compiler produces paths relative to source maps location.
// We don't have UI to configure base dirs, but we have UI to configure prefix.
// If prefix is not specified (empty) in UI, we want to produce paths relative to source maps location
return if (module.k2JsCompilerArguments.sourceMapPrefix.isNullOrBlank()) emptyList()
else module.contentRootsList.urls
.map { URI.create(it) }
.filter { it.scheme == "file" }
.map { File(it.path) }
}
val friendBuildTargetsMetaFiles
get() = friendBuildTargets.mapNotNull {
(it as? KotlinJsModuleBuildTarget)?.outputMetaFile?.absoluteFile?.toString()
}
val outputFile
get() = explicitOutputPath?.let { File(it) } ?: implicitOutputFile
val explicitOutputPath
private val explicitOutputPath
get() = if (isTests) module.testOutputFilePath else module.productionOutputFilePath
val implicitOutputFile: File
private val implicitOutputFile: File
get() {
val suffix = if (isTests) "_test" else ""
return File(outputDir, module.name + suffix + JS_EXT)
}
val outputFileBaseName: String
private val outputFileBaseName: String
get() = outputFile.path.substringBeforeLast(".")
val outputMetaFile: File
get() = File(outputFileBaseName + META_JS_SUFFIX)
val libraryFiles: List<String>
private val libraryFiles: List<String>
get() = mutableListOf<String>().also { result ->
for (library in allDependencies.libraries) {
for (root in library.getRoots(JpsOrderRootType.COMPILED)) {
@@ -56,12 +124,12 @@ class KotlinJsModuleBuildTarget(jpsModuleBuildTarget: ModuleBuildTarget) : Kotli
}
}
val dependenciesMetaFiles: List<String>
private val dependenciesMetaFiles: List<String>
get() = mutableListOf<String>().also { result ->
allDependencies.processModules { module ->
if (isTests) addDependencyMetaFile(module, result, isTests = true)
// production targets should be also added as dependency to test targets
// note: production targets should be also added as dependency to test targets
addDependencyMetaFile(module, result, isTests = false)
}
}
@@ -83,72 +151,4 @@ class KotlinJsModuleBuildTarget(jpsModuleBuildTarget: ModuleBuildTarget) : Kotli
}
}
}
override fun compileModuleChunk(
allCompiledFiles: MutableSet<File>,
chunk: ModuleChunk,
commonArguments: CommonCompilerArguments,
context: CompileContext,
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
environment: JpsCompilerEnvironment,
filesToCompile: MultiMap<ModuleBuildTarget, File>
): Boolean {
require(chunk.representativeTarget() == jpsModuleBuildTarget)
if (reportAndSkipCircular(chunk, environment)) return false
val sources = sources
if (sources.isEmpty()) return false
val k2JsCompilerArguments = module.k2JsCompilerArguments
// Compiler starts to produce path relative to base dirs in source maps if at least one statement is true:
// 1) base dirs are specified;
// 2) prefix is specified (i.e. non-empty)
// Otherwise compiler produces paths relative to source maps location.
// We don't have UI to configure base dirs, but we have UI to configure prefix.
// If prefix is not specified (empty) in UI, we want to produce paths relative to source maps location
val sourceRoots = if (k2JsCompilerArguments.sourceMapPrefix.isNullOrBlank()) {
emptyList()
} else {
module.contentRootsList.urls
.map { URI.create(it) }
.filter { it.scheme == "file" }
.map { File(it.path) }
}
val friendPaths = friendBuildTargets.mapNotNull {
(it as? KotlinJsModuleBuildTarget)?.outputMetaFile?.absoluteFile?.toString()
}
val libraries = libraryFiles + dependenciesMetaFiles
val compilerRunner = JpsKotlinCompilerRunner()
compilerRunner.runK2JsCompiler(
commonArguments,
k2JsCompilerArguments,
module.kotlinCompilerSettings,
environment,
sources,
sourceRoots,
libraries,
friendPaths,
outputFile
)
return true
}
override fun doAfterBuild() {
copyJsLibraryFilesIfNeeded()
}
private fun copyJsLibraryFilesIfNeeded() {
if (module.kotlinCompilerSettings.copyJsLibraryFiles) {
val outputLibraryRuntimeDirectory = File(outputDir, module.kotlinCompilerSettings.outputDirectoryForJsLibraryFiles).absolutePath
JsLibraryUtils.copyJsFilesFromLibraries(
libraryFiles, outputLibraryRuntimeDirectory,
copySourceMap = module.k2JsCompilerArguments.sourceMap
)
}
}
}
@@ -24,7 +24,10 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment
import org.jetbrains.kotlin.compilerRunner.JpsKotlinCompilerRunner
import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.jps.build.*
import org.jetbrains.kotlin.jps.build.FSOperationsHelper
import org.jetbrains.kotlin.jps.build.KotlinBuilder
import org.jetbrains.kotlin.jps.build.KotlinSourceFileCollector
import org.jetbrains.kotlin.jps.build.processedTargetsWithRemovedFilesContainer
import org.jetbrains.kotlin.jps.model.k2JvmCompilerArguments
import org.jetbrains.kotlin.jps.model.kotlinCompilerSettings
import org.jetbrains.kotlin.modules.KotlinModuleXmlBuilder
@@ -39,7 +42,8 @@ class KotlinJvmModuleBuildTarget(jpsModuleBuildTarget: ModuleBuildTarget) : Kotl
context: CompileContext,
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
environment: JpsCompilerEnvironment,
filesToCompile: MultiMap<ModuleBuildTarget, File>
filesToCompile: MultiMap<ModuleBuildTarget, File>,
fsOperations: FSOperationsHelper
): Boolean {
if (chunk.modules.size > 1) {
environment.messageCollector.report(
@@ -52,7 +56,7 @@ class KotlinJvmModuleBuildTarget(jpsModuleBuildTarget: ModuleBuildTarget) : Kotl
allCompiledFiles.addAll(filesToCompile.values())
val processedTargetsWithRemoved = getProcessedTargetsWithRemovedFilesContainer(context)
val processedTargetsWithRemoved = context.processedTargetsWithRemovedFilesContainer
var totalRemovedFiles = 0
for (target in chunk.targets) {
@@ -131,7 +135,7 @@ class KotlinJvmModuleBuildTarget(jpsModuleBuildTarget: ModuleBuildTarget) : Kotl
}
}
val kotlinModuleId = target.moduleId
val kotlinModuleId = target.targetId
builder.addModule(
kotlinModuleId.name,
outputDir.absolutePath,
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.jps.platforms
import com.intellij.openapi.util.io.FileUtilRt
import com.intellij.util.containers.MultiMap
import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.builders.DirtyFilesHolder
@@ -21,6 +20,7 @@ import org.jetbrains.jps.util.JpsPathUtil
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment
import org.jetbrains.kotlin.jps.build.FSOperationsHelper
import org.jetbrains.kotlin.jps.build.KotlinSourceFileCollector
import org.jetbrains.kotlin.jps.model.kotlinFacet
import org.jetbrains.kotlin.jps.model.productionOutputFilePath
@@ -29,6 +29,9 @@ import org.jetbrains.kotlin.modules.TargetId
import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.File
/**
* Properties and actions for Kotlin test / production module build target.
*/
abstract class KotlinModuleBuilderTarget(val jpsModuleBuildTarget: ModuleBuildTarget) {
val module: JpsModule
get() = jpsModuleBuildTarget.module
@@ -36,7 +39,7 @@ abstract class KotlinModuleBuilderTarget(val jpsModuleBuildTarget: ModuleBuildTa
val isTests: Boolean
get() = jpsModuleBuildTarget.isTests
val moduleId: TargetId
val targetId: TargetId
get() {
// Since IDEA 2016 each gradle source root is imported as a separate module.
// One gradle module X is imported as two JPS modules:
@@ -75,7 +78,7 @@ abstract class KotlinModuleBuilderTarget(val jpsModuleBuildTarget: ModuleBuildTa
JpsJavaExtensionService.getInstance().getOutputDirectory(it.module, false)
}
val relatedProductionModule: JpsModule?
private val relatedProductionModule: JpsModule?
get() = JpsJavaExtensionService.getInstance().getTestModuleProperties(module)?.productionModule
val allDependencies by lazy {
@@ -95,11 +98,6 @@ abstract class KotlinModuleBuilderTarget(val jpsModuleBuildTarget: ModuleBuildTa
.map { ModuleBuildTarget(it, isTests).kotlinData!! }
}
fun getRemovedKotlinFiles(dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>): List<File> =
dirtyFilesHolder
.getRemovedFiles(jpsModuleBuildTarget)
.mapNotNull { if (FileUtilRt.extensionEquals(it, "kt")) File(it) else null }
val sources by lazy {
mutableListOf<File>().also { result ->
// add all common libs sources
@@ -139,7 +137,8 @@ abstract class KotlinModuleBuilderTarget(val jpsModuleBuildTarget: ModuleBuildTa
context: CompileContext,
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
environment: JpsCompilerEnvironment,
filesToCompile: MultiMap<ModuleBuildTarget, File>
filesToCompile: MultiMap<ModuleBuildTarget, File>,
fsOperations: FSOperationsHelper
): Boolean
protected fun reportAndSkipCircular(