diff --git a/compiler/cli/cli-runner/src/org/jetbrains/kotlin/runner/Main.kt b/compiler/cli/cli-runner/src/org/jetbrains/kotlin/runner/Main.kt index f8db5648b85..c0410260e75 100644 --- a/compiler/cli/cli-runner/src/org/jetbrains/kotlin/runner/Main.kt +++ b/compiler/cli/cli-runner/src/org/jetbrains/kotlin/runner/Main.kt @@ -144,7 +144,19 @@ object Main { break } else { - setRunner(MainClassRunner(arg)) + val workingDir = File(".") + val classFile = File(arg) + + // Allow running class files with '.class' extension. + // In order to infer its fully qualified name, it should be located in the current working directory or a subdirectory of it + val className = + if (arg.endsWith(".class") && classFile.exists() && classFile.canonicalPath.contains(workingDir.canonicalPath)) { + classFile.canonicalFile.toRelativeString(workingDir.canonicalFile) + .removeSuffix(".class") + .replace(File.separatorChar, '.') + } else arg + + setRunner(MainClassRunner(className)) restAsArguments() break } diff --git a/compiler/cli/cli-runner/src/org/jetbrains/kotlin/runner/runners.kt b/compiler/cli/cli-runner/src/org/jetbrains/kotlin/runner/runners.kt index d92dbab9a30..0f106c1a9a0 100644 --- a/compiler/cli/cli-runner/src/org/jetbrains/kotlin/runner/runners.kt +++ b/compiler/cli/cli-runner/src/org/jetbrains/kotlin/runner/runners.kt @@ -25,7 +25,7 @@ import java.net.URLClassLoader import java.util.jar.Attributes import java.util.jar.JarFile -class RunnerException(message: String) : RuntimeException(message) +class RunnerException(message: String, cause: Throwable? = null) : RuntimeException(message, cause) abstract class AbstractRunner : Runner { protected abstract val className: String @@ -40,6 +40,12 @@ abstract class AbstractRunner : Runner { } catch (e: ClassNotFoundException) { throw RunnerException("could not find or load main class $className") + } catch (e: NoClassDefFoundError) { + val message = """ + could not find or load main class $className + Caused by: $e + """.trimIndent() + throw RunnerException(message) } val main = try { diff --git a/compiler/testData/launcher/defaultPackage.kt b/compiler/testData/launcher/defaultPackage.kt new file mode 100644 index 00000000000..bc7fb0bfcb2 --- /dev/null +++ b/compiler/testData/launcher/defaultPackage.kt @@ -0,0 +1,3 @@ +fun main() { + print("ok") +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/cli/LauncherScriptTest.kt b/compiler/tests/org/jetbrains/kotlin/cli/LauncherScriptTest.kt index 87ab9a1acdb..674d1a27d4e 100644 --- a/compiler/tests/org/jetbrains/kotlin/cli/LauncherScriptTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/cli/LauncherScriptTest.kt @@ -354,4 +354,104 @@ compiler/testData/launcher/noInline.myscript:1:7: error: unresolved reference: C expectedExitCode = 1 ) } + + fun testNoClassDefFoundErrorWhenClassInDefaultPackage() { + val testDir = File("$tmpdir/test") + + runProcess("kotlinc", "$testDataDirectory/defaultPackage.kt", "-d", testDir.path) + assertExists(File("${testDir.path}/DefaultPackageKt.class")) + + runProcess( + "kotlin", "test.DefaultPackageKt", workDirectory = tmpdir, expectedExitCode = 1, + expectedStderr = """ + error: could not find or load main class test.DefaultPackageKt + Caused by: java.lang.NoClassDefFoundError: test/DefaultPackageKt (wrong name: DefaultPackageKt) + + """.trimIndent() + ) + } + + fun testNoClassDefFoundErrorWhenClassNotInDefaultPackage() { + val testDir = File("$tmpdir/test") + + runProcess("kotlinc", "$testDataDirectory/helloWorld.kt", "-d", tmpdir.path) + assertExists(File("${testDir.path}/HelloWorldKt.class")) + + runProcess( + "kotlin", "HelloWorldKt", workDirectory = testDir, expectedExitCode = 1, + expectedStderr = """ + error: could not find or load main class HelloWorldKt + Caused by: java.lang.NoClassDefFoundError: HelloWorldKt (wrong name: test/HelloWorldKt) + + """.trimIndent() + ) + } + + /** + * A class whose full qualified name is `DefaultPackageKt` and is located in path `$tmpdir/test/DefaultPackageKt.class` + */ + fun testRunClassFileWithExtensionInDefaultPackage() { + val subDir = File("$tmpdir/test/sub").apply { mkdirs() } + val testDir = File("$tmpdir/test") + + runProcess("kotlinc", "$testDataDirectory/defaultPackage.kt", "-d", testDir.path) + assertExists(File("${testDir.path}/DefaultPackageKt.class")) + + runProcess( + "kotlin", "test/DefaultPackageKt.class", workDirectory = tmpdir, expectedExitCode = 1, + expectedStderr = """ + error: could not find or load main class test.DefaultPackageKt + Caused by: java.lang.NoClassDefFoundError: test/DefaultPackageKt (wrong name: DefaultPackageKt) + + """.trimIndent() + ) + + runProcess("kotlin", "DefaultPackageKt.class", expectedStdout = "ok", workDirectory = testDir) + runProcess("kotlin", "./sub/../DefaultPackageKt.class", expectedStdout = "ok", workDirectory = testDir) + runProcess( + "kotlin", "../DefaultPackageKt.class", expectedExitCode = 1, + expectedStderr = "error: could not find or load main class ../DefaultPackageKt.class\n", + workDirectory = subDir + ) + } + + /** + * A class whose full qualified name is `test.HelloWorldKt` and is located in path `$tmpdir/test/HelloWorldKt.class` + */ + fun testRunClassFileWithExtensionNotInDefaultPackage() { + val subDir = File("$tmpdir/test/sub").apply { mkdirs() } + val testDir = File("$tmpdir/test") + + runProcess("kotlinc", "$testDataDirectory/helloWorld.kt", "-d", tmpdir.path) + assertExists(File("${testDir.path}/HelloWorldKt.class")) + + runProcess("kotlin", "test/HelloWorldKt.class", expectedStdout = "Hello!\n", workDirectory = tmpdir) + runProcess( + "kotlin", "test.HelloWorldKt.class", expectedExitCode = 1, + expectedStderr = "error: could not find or load main class test.HelloWorldKt.class\n", + workDirectory = tmpdir + ) + runProcess("kotlin", "test/sub/../../test/HelloWorldKt.class", expectedStdout = "Hello!\n", workDirectory = tmpdir) + runProcess( + "kotlin", "./HelloWorldKt.class", workDirectory = testDir, expectedExitCode = 1, + expectedStderr = """ + error: could not find or load main class HelloWorldKt + Caused by: java.lang.NoClassDefFoundError: HelloWorldKt (wrong name: test/HelloWorldKt) + + """.trimIndent() + ) + runProcess( + "kotlin", "HelloWorldKt.class", workDirectory = testDir, expectedExitCode = 1, + expectedStderr = """ + error: could not find or load main class HelloWorldKt + Caused by: java.lang.NoClassDefFoundError: HelloWorldKt (wrong name: test/HelloWorldKt) + + """.trimIndent() + ) + runProcess( + "kotlin", "../HelloWorldKt.class", expectedExitCode = 1, + expectedStderr = "error: could not find or load main class ../HelloWorldKt.class\n", + workDirectory = subDir + ) + } }