Kotlinc: catch FileNotFoundException caused by problematic destination.

This commit is contained in:
scaventz
2021-01-28 11:53:27 +08:00
committed by Alexander Udalov
parent 149282a888
commit 28452fc38e
14 changed files with 103 additions and 7 deletions
@@ -23,13 +23,24 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
import java.io.File
import java.io.FileNotFoundException
fun OutputFileCollection.writeAll(outputDir: File, report: ((file: OutputFile, sources: List<File>, output: File) -> Unit)?) {
for (file in asList()) {
val sources = file.sourceFiles
val output = File(outputDir, file.relativePath)
report?.invoke(file, sources, output)
FileUtil.writeToFile(output, file.asByteArray())
try {
FileUtil.writeToFile(output, file.asByteArray())
} catch (e: FileNotFoundException) {
if (outputDir.isDirectory) {
// output directory exists
throw NoPermissionException("error while writing $output (Permission denied)", e)
}
// Failed to create directory, possibly due to lack of write permission or clash with existing file.
// In both cases, the directory will not exist, so for the sake of simplicity, we treat them same way.
throw e
}
}
}
@@ -38,8 +49,16 @@ fun OutputFileCollection.writeAllTo(outputDir: File) {
}
fun OutputFileCollection.writeAll(outputDir: File, messageCollector: MessageCollector, reportOutputFiles: Boolean) {
if (!reportOutputFiles) writeAllTo(outputDir)
else writeAll(outputDir) { _, sources, output ->
messageCollector.report(CompilerMessageSeverity.OUTPUT, OutputMessageUtil.formatOutputMessage(sources, output))
try {
if (!reportOutputFiles) writeAllTo(outputDir)
else writeAll(outputDir) { _, sources, output ->
messageCollector.report(CompilerMessageSeverity.OUTPUT, OutputMessageUtil.formatOutputMessage(sources, output))
}
} catch (e: NoPermissionException) {
messageCollector.report(CompilerMessageSeverity.ERROR, e.message!!)
} catch (e: FileNotFoundException) {
messageCollector.report(CompilerMessageSeverity.ERROR, "directory not found: $outputDir")
}
}
private class NoPermissionException(message: String, cause: Throwable?) : IllegalStateException(message, cause)
@@ -24,6 +24,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.output.OutputFile;
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection;
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity;
import org.jetbrains.kotlin.cli.common.messages.MessageCollector;
import org.jetbrains.kotlin.cli.common.modules.ModuleChunk;
import org.jetbrains.kotlin.cli.common.modules.ModuleXmlParser;
@@ -104,16 +105,24 @@ public class CompileEnvironmentUtil {
}
public static void writeToJar(
File jarPath, boolean jarRuntime, boolean noReflect, boolean resetJarTimestamps, FqName mainClass, OutputFileCollection outputFiles
File jarPath,
boolean jarRuntime,
boolean noReflect,
boolean resetJarTimestamps,
FqName mainClass,
OutputFileCollection outputFiles,
MessageCollector messageCollector
) {
FileOutputStream outputStream = null;
try {
// we should try to create the output dir first
jarPath.getParentFile().mkdirs();
outputStream = new FileOutputStream(jarPath);
doWriteToJar(outputFiles, outputStream, mainClass, jarRuntime, noReflect, resetJarTimestamps);
outputStream.close();
}
catch (FileNotFoundException e) {
throw new CompileEnvironmentException("Invalid jar path " + jarPath, e);
messageCollector.report(CompilerMessageSeverity.ERROR, "Invalid jar path: " + jarPath, null);
}
catch (IOException e) {
throw ExceptionUtilsKt.rethrow(e);
@@ -88,7 +88,7 @@ object KotlinToJVMBytecodeCompiler {
val includeRuntime = configuration.get(JVMConfigurationKeys.INCLUDE_RUNTIME, false)
val noReflect = configuration.get(JVMConfigurationKeys.NO_REFLECT, false)
val resetJarTimestamps = !configuration.get(JVMConfigurationKeys.NO_RESET_JAR_TIMESTAMPS, false)
CompileEnvironmentUtil.writeToJar(jarPath, includeRuntime, noReflect, resetJarTimestamps, mainClassFqName, outputFiles)
CompileEnvironmentUtil.writeToJar(jarPath, includeRuntime, noReflect, resetJarTimestamps, mainClassFqName, outputFiles, messageCollector)
if (reportOutputFiles) {
val message = OutputMessageUtil.formatOutputMessage(outputFiles.asList().flatMap { it.sourceFiles }.distinct(), jarPath)
messageCollector.report(OUTPUT, message)
@@ -0,0 +1,4 @@
ERR:
error: directory not found: [Temp]/clashingFile
Return code: 1
@@ -0,0 +1 @@
class Test
@@ -0,0 +1 @@
Return code: 0
@@ -0,0 +1 @@
class Test
@@ -0,0 +1,4 @@
ERR:
error: error while writing [Temp]/noPermissionDir/Test.class (Permission denied)
Return code: 1
@@ -0,0 +1 @@
class Test
@@ -0,0 +1,4 @@
ERR:
error: invalid jar path: [Temp]/clashingFile/test.jar
Return code: 1
@@ -0,0 +1 @@
class Test
@@ -0,0 +1,4 @@
ERR:
error: invalid jar path: [Temp]/noPermissionDir/test.jar
Return code: 1
@@ -0,0 +1 @@
class A
@@ -201,4 +201,50 @@ public class CompilerSmokeTest extends CompilerSmokeTestBase {
runCompiler("noReflect.compile", "-include-runtime", "-no-reflect", "noReflect.kt", "-d", jar));
run("noReflect.run", "-cp", jar, "noReflect.NoReflectKt");
}
// related to KT-14772, destination is not a jar, and clashing with an existing file
public void testDestinationDirClashingWithExistingFile() throws Exception {
String outputDir = tmpdir.getAbsolutePath() + File.separator + "clashingFile";
File file = new File(outputDir);
file.createNewFile();
runCompiler("test.compile", "test.kt", "-d", outputDir);
}
// related to KT-18184, destination is not a jar, and permission denied
public void testDestinationDirNoPermission() throws Exception {
String outputDir = tmpdir.getAbsolutePath() + File.separator + "noPermissionDir";
File file = new File(outputDir, "Test.class");
file.getParentFile().mkdirs();
file.createNewFile();
// file.getParentFile().setReadOnly(); // won't work on Windows
file.setReadOnly(); // So we use this one as an alternative
runCompiler("test.compile", "test.kt", "-d", outputDir);
}
// related to KT-18184, destination is a jar, and output directory does not exist, we should try to create
// output directory for it.
public void testDestinationDirDoesNotExist() throws Exception {
String jar = tmpdir.getAbsolutePath() + File.separator + "nonExistingDir" + File.separator + "test.jar";
runCompiler("test.compile", "test.kt", "-d", jar);
}
// related to KT-18184, destination is a jar, and output directory does not exist, and failed to created
// output directory due to clash with existing file.
public void testDestinationJarClashingWithExistingFile() throws Exception {
String jar = tmpdir.getAbsolutePath() + File.separator + "clashingFile" + File.separator + "test.jar";
File file = new File(jar);
file.getParentFile().createNewFile();
runCompiler("test.compile", "test.kt", "-d", jar);
}
// related to KT-18184, Destination is a jar, and output directory exist, and permission denied to write jar.
public void testDestinationJarNoPermission() throws Exception {
String outputDir = tmpdir.getAbsolutePath() + File.separator + "noPermissionDir";
File jar = new File(outputDir, "test.jar");
jar.getParentFile().mkdirs();
jar.createNewFile();
// jar.getParentFile().setReadOnly(); // won't work on Windows
jar.setReadOnly(); // So we use this one as an alternative
runCompiler("test.compile", "test.kt", "-d", jar.getCanonicalPath());
}
}