JS: change the way how DCE resolves input paths

This commit is contained in:
Alexey Andreev
2017-11-17 18:05:08 +03:00
parent 8514a7706f
commit ffdebfab45
13 changed files with 156 additions and 99 deletions
@@ -24,28 +24,45 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.js.dce.*
import java.io.File
import java.util.zip.ZipFile
class K2JSDce : CLITool<K2JSDceArguments>() {
override fun createArguments(): K2JSDceArguments = K2JSDceArguments()
override fun execImpl(messageCollector: MessageCollector, services: Services, arguments: K2JSDceArguments): ExitCode {
val baseDir = File(arguments.outputDirectory ?: "min")
val files = arguments.freeArgs.map { arg ->
val parts = arg.split(File.pathSeparator, ignoreCase = false, limit = 2)
val inputName = parts[0]
val moduleName = parts.getOrNull(1) ?: ""
val resolvedModuleName = if (!moduleName.isEmpty()) moduleName else File(inputName).nameWithoutExtension
val pathToSourceMapCandidate = "$inputName.map"
val pathToSourceMap = if (File(pathToSourceMapCandidate).exists()) pathToSourceMapCandidate else null
InputFile(inputName, pathToSourceMap, File(baseDir, resolvedModuleName + ".js").absolutePath, resolvedModuleName)
var hasErrors = false
val files = arguments.freeArgs.flatMap { arg ->
val files = collectInputFiles(baseDir, arg, messageCollector)
if (files != null) {
files
}
else {
hasErrors = true
emptyList()
}
}
if (hasErrors) return ExitCode.COMPILATION_ERROR
if (files.isEmpty() && !arguments.version) {
messageCollector.report(CompilerMessageSeverity.ERROR, "no source files")
return ExitCode.COMPILATION_ERROR
}
if (!checkSourceFiles(messageCollector, files)) {
return ExitCode.COMPILATION_ERROR
val existingFiles = mutableMapOf<String, InputFile>()
for (file in files) {
existingFiles[file.outputPath]?.let {
messageCollector.report(
CompilerMessageSeverity.ERROR,
"duplicate target file will be created for '${file.resource.name}' and '${it.resource.name}'")
return ExitCode.COMPILATION_ERROR
}
existingFiles[file.outputPath] = file
if (File(file.outputPath).isDirectory) {
messageCollector.report(CompilerMessageSeverity.ERROR, "cannot open output file '${file.outputPath}': it is a directory")
return ExitCode.COMPILATION_ERROR
}
}
val includedDeclarations = arguments.declarationsToKeep.orEmpty().toSet()
@@ -72,35 +89,83 @@ class K2JSDce : CLITool<K2JSDceArguments>() {
return ExitCode.OK
}
private fun checkSourceFiles(messageCollector: MessageCollector, files: List<InputFile>): Boolean {
return files.fold(true) { ok, file ->
val inputFile = File(file.path)
val outputFile = File(file.outputPath)
val inputOk = when {
!inputFile.exists() -> {
messageCollector.report(CompilerMessageSeverity.ERROR, "source file or directory not found: " + file.path)
false
}
inputFile.isDirectory -> {
messageCollector.report(CompilerMessageSeverity.ERROR, "input file '" + file.path + "' is a directory")
false
}
else -> true
private fun collectInputFiles(baseDir: File, fileName: String, messageCollector: MessageCollector): List<InputFile>? {
val file = File(fileName)
return when {
file.isDirectory -> {
collectInputFilesFromDirectory(baseDir, fileName)
}
val outputOk = when {
outputFile.exists() && outputFile.isDirectory -> {
messageCollector.report(CompilerMessageSeverity.ERROR, "cannot open output file '${outputFile.path}': is a directory")
false
file.isFile -> {
when {
fileName.endsWith(".js") -> {
listOf(singleInputFile(baseDir, fileName))
}
fileName.endsWith(".zip") || fileName.endsWith(".jar") -> {
collectInputFilesFromZip(baseDir, fileName)
}
else -> {
messageCollector.report(CompilerMessageSeverity.ERROR,
"invalid file name '$fileName'; must end either with '.js', '.zip' or '.jar'")
null
}
}
else -> true
}
ok and inputOk and outputOk
else -> {
messageCollector.report(CompilerMessageSeverity.ERROR, "source file or directory not found: $fileName")
null
}
}
}
private fun singleInputFile(baseDir: File, path: String): InputFile {
val moduleName = getModuleNameFromPath(path)
val pathToSourceMapCandidate = "$path.map"
val pathToSourceMap = if (File(pathToSourceMapCandidate).exists()) pathToSourceMapCandidate else null
return InputFile(InputResource.file(path), pathToSourceMap?.let { InputResource.file(it) },
File(baseDir, "$moduleName.js").absolutePath, moduleName)
}
private fun collectInputFilesFromZip(baseDir: File, path: String): List<InputFile> {
return ZipFile(path).use { zipFile ->
zipFile.entries().asSequence()
.filter { !it.isDirectory }
.filter { it.name.endsWith(".js") }
.filter { zipFile.getEntry(it.name.metaJs()) != null }
.distinctBy { it.name }
.map { entry ->
val moduleName = getModuleNameFromPath(entry.name)
val pathToSourceMapCandidate = "${entry.name}.map"
val pathToSourceMap = if (zipFile.getEntry(pathToSourceMapCandidate) != null) pathToSourceMapCandidate else null
InputFile(InputResource.zipFile(path, entry.name), pathToSourceMap?.let { InputResource.zipFile(path, it) },
File(baseDir, "$moduleName.js").absolutePath, moduleName)
}
.toList()
}
}
private fun collectInputFilesFromDirectory(baseDir: File, path: String): List<InputFile> {
return File(path).walkTopDown().asSequence()
.filter { !it.isDirectory }
.filter { it.name.endsWith(".js") }
.filter { File(it.path.metaJs()).exists() }
.map { entry ->
val moduleName = getModuleNameFromPath(entry.name)
val pathToSourceMapCandidate = "${entry.path}.map"
val pathToSourceMap = if (File(pathToSourceMapCandidate).exists()) pathToSourceMapCandidate else null
InputFile(InputResource.file(entry.path), pathToSourceMap?.let { InputResource.file(it) },
File(baseDir, "$moduleName.js").absolutePath, moduleName)
}
.toList()
}
private fun String.metaJs() = removeSuffix(".js") + ".meta.js"
private fun getModuleNameFromPath(path: String): String {
val dotIndex = path.lastIndexOf('.')
val slashIndex = maxOf(path.lastIndexOf('/'), path.lastIndexOf('\\'))
return path.substring(slashIndex + 1, if (dotIndex < 0) path.length else dotIndex)
}
override fun executableScriptFileName(): String = "kotlin-dce-js"
companion object {