Kotlinc: catch FileNotFoundException caused by problematic destination.
This commit is contained in:
committed by
Alexander Udalov
parent
149282a888
commit
28452fc38e
@@ -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.MessageCollector
|
||||||
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
|
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
|
||||||
fun OutputFileCollection.writeAll(outputDir: File, report: ((file: OutputFile, sources: List<File>, output: File) -> Unit)?) {
|
fun OutputFileCollection.writeAll(outputDir: File, report: ((file: OutputFile, sources: List<File>, output: File) -> Unit)?) {
|
||||||
for (file in asList()) {
|
for (file in asList()) {
|
||||||
val sources = file.sourceFiles
|
val sources = file.sourceFiles
|
||||||
val output = File(outputDir, file.relativePath)
|
val output = File(outputDir, file.relativePath)
|
||||||
report?.invoke(file, sources, output)
|
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) {
|
fun OutputFileCollection.writeAll(outputDir: File, messageCollector: MessageCollector, reportOutputFiles: Boolean) {
|
||||||
if (!reportOutputFiles) writeAllTo(outputDir)
|
try {
|
||||||
else writeAll(outputDir) { _, sources, output ->
|
if (!reportOutputFiles) writeAllTo(outputDir)
|
||||||
messageCollector.report(CompilerMessageSeverity.OUTPUT, OutputMessageUtil.formatOutputMessage(sources, output))
|
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.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||||
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection;
|
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.messages.MessageCollector;
|
||||||
import org.jetbrains.kotlin.cli.common.modules.ModuleChunk;
|
import org.jetbrains.kotlin.cli.common.modules.ModuleChunk;
|
||||||
import org.jetbrains.kotlin.cli.common.modules.ModuleXmlParser;
|
import org.jetbrains.kotlin.cli.common.modules.ModuleXmlParser;
|
||||||
@@ -104,16 +105,24 @@ public class CompileEnvironmentUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void writeToJar(
|
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;
|
FileOutputStream outputStream = null;
|
||||||
try {
|
try {
|
||||||
|
// we should try to create the output dir first
|
||||||
|
jarPath.getParentFile().mkdirs();
|
||||||
outputStream = new FileOutputStream(jarPath);
|
outputStream = new FileOutputStream(jarPath);
|
||||||
doWriteToJar(outputFiles, outputStream, mainClass, jarRuntime, noReflect, resetJarTimestamps);
|
doWriteToJar(outputFiles, outputStream, mainClass, jarRuntime, noReflect, resetJarTimestamps);
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException e) {
|
catch (FileNotFoundException e) {
|
||||||
throw new CompileEnvironmentException("Invalid jar path " + jarPath, e);
|
messageCollector.report(CompilerMessageSeverity.ERROR, "Invalid jar path: " + jarPath, null);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
throw ExceptionUtilsKt.rethrow(e);
|
throw ExceptionUtilsKt.rethrow(e);
|
||||||
|
|||||||
+1
-1
@@ -88,7 +88,7 @@ object KotlinToJVMBytecodeCompiler {
|
|||||||
val includeRuntime = configuration.get(JVMConfigurationKeys.INCLUDE_RUNTIME, false)
|
val includeRuntime = configuration.get(JVMConfigurationKeys.INCLUDE_RUNTIME, false)
|
||||||
val noReflect = configuration.get(JVMConfigurationKeys.NO_REFLECT, false)
|
val noReflect = configuration.get(JVMConfigurationKeys.NO_REFLECT, false)
|
||||||
val resetJarTimestamps = !configuration.get(JVMConfigurationKeys.NO_RESET_JAR_TIMESTAMPS, 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) {
|
if (reportOutputFiles) {
|
||||||
val message = OutputMessageUtil.formatOutputMessage(outputFiles.asList().flatMap { it.sourceFiles }.distinct(), jarPath)
|
val message = OutputMessageUtil.formatOutputMessage(outputFiles.asList().flatMap { it.sourceFiles }.distinct(), jarPath)
|
||||||
messageCollector.report(OUTPUT, message)
|
messageCollector.report(OUTPUT, message)
|
||||||
|
|||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
ERR:
|
||||||
|
error: directory not found: [Temp]/clashingFile
|
||||||
|
|
||||||
|
Return code: 1
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
class Test
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
Return code: 0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
class Test
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
ERR:
|
||||||
|
error: error while writing [Temp]/noPermissionDir/Test.class (Permission denied)
|
||||||
|
|
||||||
|
Return code: 1
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
class Test
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
ERR:
|
||||||
|
error: invalid jar path: [Temp]/clashingFile/test.jar
|
||||||
|
|
||||||
|
Return code: 1
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
class Test
|
||||||
+4
@@ -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));
|
runCompiler("noReflect.compile", "-include-runtime", "-no-reflect", "noReflect.kt", "-d", jar));
|
||||||
run("noReflect.run", "-cp", jar, "noReflect.NoReflectKt");
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user