KT-39670: -Xdebug-prefix-map for path remapping

This introduces a new `-Xdebug-prefix-map` flag. When compiling files
with directories prefixed by the map key, the prefix will be changed to
the map value. This allows for reproducable builds by replacing
absolute paths with relative ones.

(cherry picked from commit bba4ce1c2c5ee06b9f5f65148ac8ef883715474a)
This commit is contained in:
Martin Petrov
2020-06-18 09:38:53 -04:00
committed by Vasily Levchenko
parent f135220444
commit c1f0a6048b
6 changed files with 39 additions and 6 deletions
@@ -235,6 +235,7 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
put(LIBRARIES_TO_COVER, arguments.coveredLibraries.toNonNullList())
arguments.coverageFile?.let { put(PROFRAW_PATH, it) }
put(OBJC_GENERICS, !arguments.noObjcGenerics)
put(DEBUG_PREFIX_MAP, parseDebugPrefixMap(arguments, configuration))
put(LIBRARIES_TO_CACHE, parseLibrariesToCache(arguments, configuration, outputKind))
val libraryToAddToCache = parseLibraryToAddToCache(arguments, configuration, outputKind)
@@ -422,5 +423,22 @@ private fun parseShortModuleName(
}
}
private fun parseDebugPrefixMap(
arguments: K2NativeCompilerArguments,
configuration: CompilerConfiguration
): Map<String, String> = arguments.debugPrefixMap?.asList().orEmpty().mapNotNull {
val libraryAndCache = it.split("=")
if (libraryAndCache.size != 2) {
configuration.report(
ERROR,
"incorrect debug prefix map format: expected '<old>=<new>', got '$it'"
)
null
} else {
libraryAndCache[0] to libraryAndCache[1]
}
}.toMap()
fun main(args: Array<String>) = K2Native.main(args)
fun mainNoExitWithGradleRenderer(args: Array<String>) = K2Native.mainNoExitWithGradleRenderer(args)
@@ -265,6 +265,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
@Argument(value = "-Xmetadata-klib", description = "Produce a klib that only contains the declarations metadata")
var metadataKlib: Boolean = false
@Argument(value = "-Xdebug-prefix-map", valueDescription = "<old1=new1,old2=new2,...>", description = "Remap file source directory paths in debug info")
var debugPrefixMap: Array<String>? = null
override fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> =
super.configureAnalysisFlags(collector).also {
val useExperimental = it[AnalysisFlags.useExperimental] as List<*>
@@ -138,6 +138,8 @@ class KonanConfigKeys {
= CompilerConfigurationKey.create("path to *.profraw coverage output")
val OBJC_GENERICS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("write objc header with generics support")
val DEBUG_PREFIX_MAP: CompilerConfigurationKey<Map<String, String>>
= CompilerConfigurationKey.create("remap file source paths in debug info")
}
}
@@ -38,11 +38,13 @@ internal val contextLLVMSetupPhase = makeKonanModuleOpPhase(
// we don't split path to filename and directory to provide enough level uniquely for dsymutil to avoid symbol
// clashing, which happens on linking with libraries produced from intercepting sources.
val filePath = context.config.outputFile.toFileAndFolder(context).path()
context.debugInfo.compilationUnit = if (context.shouldContainLocationDebugInfo()) DICreateCompilationUnit(
builder = context.debugInfo.builder,
lang = DWARF.language(context.config),
File = File(context.config.outputFile).absolutePath,
dir = "-",
File = filePath,
dir = "",
producer = DWARF.producer,
isOptimized = 0,
flags = "",
@@ -117,16 +117,24 @@ internal data class FileAndFolder(val file: String, val folder: String) {
fun path() = if (this == NOFILE) file else "$folder/$file"
}
internal fun String?.toFileAndFolder():FileAndFolder {
internal fun String?.toFileAndFolder(context: Context):FileAndFolder {
this ?: return FileAndFolder.NOFILE
val file = File(this).absoluteFile
return FileAndFolder(file.name, file.parent)
var parent = file.parent
context.configuration.get(KonanConfigKeys.DEBUG_PREFIX_MAP)?.let { debugPrefixMap ->
for ((key, value) in debugPrefixMap) {
if (parent.startsWith(key)) {
parent = value + parent.removePrefix(key)
}
}
}
return FileAndFolder(file.name, parent)
}
internal fun generateDebugInfoHeader(context: Context) {
if (context.shouldContainAnyDebugInfo()) {
val path = context.config.outputFile
.toFileAndFolder()
.toFileAndFolder(context)
@Suppress("UNCHECKED_CAST")
context.debugInfo.module = DICreateModule(
builder = context.debugInfo.builder,
@@ -1896,7 +1896,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
//-------------------------------------------------------------------------//
private fun IrFile.file(): DIFileRef {
return context.debugInfo.files.getOrPut(this.fileEntry.name) {
val path = this.fileEntry.name.toFileAndFolder()
val path = this.fileEntry.name.toFileAndFolder(context)
DICreateFile(context.debugInfo.builder, path.file, path.folder)!!
}
}