diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLITool.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLITool.kt index ceda801e555..394fdfce355 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLITool.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLITool.kt @@ -42,21 +42,8 @@ abstract class CLITool { ): ExitCode { K2JVMCompiler.resetInitStartTime() - val parseArgumentsCollector = PrintingMessageCollector(errStream, messageRenderer, false) - val arguments = try { - parseArguments(parseArgumentsCollector, args) ?: return ExitCode.INTERNAL_ERROR - } - catch (e: IllegalArgumentException) { - parseArgumentsCollector.report(CompilerMessageSeverity.ERROR, e.message!!, null) - parseArgumentsCollector.report(CompilerMessageSeverity.INFO, "Use -help for more information", null) - return ExitCode.COMPILATION_ERROR - } - - if (arguments.help || arguments.extraHelp) { - Usage.print(errStream, this, arguments) - return ExitCode.OK - } - + val arguments = createArguments() + parseCommandLineArguments(args, arguments) val collector = PrintingMessageCollector(errStream, messageRenderer, arguments.verbose) try { @@ -65,6 +52,19 @@ abstract class CLITool { } errStream.print(messageRenderer.renderPreamble()) + + val errorMessage = validateArguments(arguments.errors) + if (errorMessage != null) { + collector.report(CompilerMessageSeverity.ERROR, errorMessage, null) + collector.report(CompilerMessageSeverity.INFO, "Use -help for more information", null) + return ExitCode.COMPILATION_ERROR + } + + if (arguments.help || arguments.extraHelp) { + errStream.print(messageRenderer.renderUsage(Usage.render(this, arguments))) + return ExitCode.OK + } + return exec(collector, services, arguments) } finally { @@ -93,19 +93,6 @@ abstract class CLITool { // Used in kotlin-maven-plugin (KotlinCompileMojoBase) protected abstract fun execImpl(messageCollector: MessageCollector, services: Services, arguments: A): ExitCode - private fun parseArguments(messageCollector: MessageCollector, args: Array): A? { - return try { - createArguments().also { parseArguments(args, it) } - } - catch (e: IllegalArgumentException) { - throw e - } - catch (t: Throwable) { - messageCollector.report(CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(t), null) - null - } - } - abstract fun createArguments(): A // Used in kotlin-maven-plugin (KotlinCompileMojoBase) and in kotlin-gradle-plugin (KotlinJvmOptionsImpl, KotlinJsOptionsImpl) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/Usage.java b/compiler/cli/src/org/jetbrains/kotlin/cli/common/Usage.java index d217174075c..6a901736721 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/Usage.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/Usage.java @@ -18,46 +18,43 @@ package org.jetbrains.kotlin.cli.common; import kotlin.text.StringsKt; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.cli.common.arguments.Argument; import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments; import org.jetbrains.kotlin.cli.common.arguments.ParseCommandLineArgumentsKt; -import java.io.PrintStream; import java.lang.reflect.Field; -class Usage { +public class Usage { // The magic number 29 corresponds to the similar padding width in javac and scalac command line compilers private static final int OPTION_NAME_PADDING_WIDTH = 29; - public static void print( - @NotNull PrintStream target, @NotNull CLITool compiler, @NotNull A arguments - ) { - target.println("Usage: " + compiler.executableScriptFileName() + " "); - target.println("where " + (arguments.extraHelp ? "advanced" : "possible") + " options include:"); + @NotNull + public static String render(@NotNull CLITool tool, @NotNull A arguments) { + StringBuilder sb = new StringBuilder(); + appendln(sb, "Usage: " + tool.executableScriptFileName() + " "); + appendln(sb, "where " + (arguments.extraHelp ? "advanced" : "possible") + " options include:"); for (Class clazz = arguments.getClass(); clazz != null; clazz = clazz.getSuperclass()) { for (Field field : clazz.getDeclaredFields()) { - String usage = fieldUsage(field, arguments.extraHelp); - if (usage != null) { - target.println(usage); - } + fieldUsage(sb, field, arguments.extraHelp); } } if (arguments.extraHelp) { - target.println(); - target.println("Advanced options are non-standard and may be changed or removed without any notice."); + appendln(sb, ""); + appendln(sb, "Advanced options are non-standard and may be changed or removed without any notice."); } + + return sb.toString(); } - @Nullable - private static String fieldUsage(@NotNull Field field, boolean extraHelp) { + private static void fieldUsage(@NotNull StringBuilder sb, @NotNull Field field, boolean extraHelp) { Argument argument = field.getAnnotation(Argument.class); - if (argument == null) return null; + if (argument == null) return; - if (extraHelp != ParseCommandLineArgumentsKt.isAdvanced(argument)) return null; + if (extraHelp != ParseCommandLineArgumentsKt.isAdvanced(argument)) return; - StringBuilder sb = new StringBuilder(" "); + int startLength = sb.length(); + sb.append(" "); sb.append(argument.value()); if (!argument.shortName().isEmpty()) { @@ -71,17 +68,21 @@ class Usage { sb.append(argument.valueDescription()); } - int width = OPTION_NAME_PADDING_WIDTH - 1; - if (sb.length() >= width + 5) { // Break the line if it's too long + int margin = startLength + OPTION_NAME_PADDING_WIDTH - 1; + if (sb.length() >= margin + 5) { // Break the line if it's too long sb.append("\n"); - width += sb.length(); + margin += sb.length() - startLength; } - while (sb.length() < width) { + while (sb.length() < margin) { sb.append(" "); } sb.append(" "); - sb.append(argument.description().replace("\n", "\n" + StringsKt.repeat(" ", OPTION_NAME_PADDING_WIDTH))); - return sb.toString(); + appendln(sb, argument.description().replace("\n", "\n" + StringsKt.repeat(" ", OPTION_NAME_PADDING_WIDTH))); + } + + private static void appendln(@NotNull StringBuilder sb, @NotNull String string) { + sb.append(string); + StringsKt.appendln(sb); } } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/MessageRenderer.java b/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/MessageRenderer.java index 4f00ba3e920..d10664f8f6b 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/MessageRenderer.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/MessageRenderer.java @@ -56,5 +56,7 @@ public interface MessageRenderer { String render(@NotNull CompilerMessageSeverity severity, @NotNull String message, @Nullable CompilerMessageLocation location); + String renderUsage(@NotNull String usage); + String renderConclusion(); } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/PlainTextMessageRenderer.java b/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/PlainTextMessageRenderer.java index 9103f664c2d..03edd893fd1 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/PlainTextMessageRenderer.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/PlainTextMessageRenderer.java @@ -157,6 +157,11 @@ public abstract class PlainTextMessageRenderer implements MessageRenderer { @Nullable protected abstract String getPath(@NotNull CompilerMessageLocation location); + @Override + public String renderUsage(@NotNull String usage) { + return usage; + } + @Override public String renderConclusion() { return ""; diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/XmlMessageRenderer.java b/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/XmlMessageRenderer.java index fb8d07944c1..b5702fce182 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/XmlMessageRenderer.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/XmlMessageRenderer.java @@ -48,6 +48,11 @@ public class XmlMessageRenderer implements MessageRenderer { return StringUtil.escapeXml(str); } + @Override + public String renderUsage(@NotNull String usage) { + return render(CompilerMessageSeverity.STRONG_WARNING, usage, null); + } + @Override public String renderConclusion() { return ""; diff --git a/jps-plugin/jps-tests/jps-tests.iml b/jps-plugin/jps-tests/jps-tests.iml index 1772d1a64c3..4cec7e15e41 100644 --- a/jps-plugin/jps-tests/jps-tests.iml +++ b/jps-plugin/jps-tests/jps-tests.iml @@ -19,5 +19,6 @@ + \ No newline at end of file diff --git a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt index 944ed4380cd..d8f87476e2e 100644 --- a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt @@ -51,6 +51,9 @@ import org.jetbrains.jps.model.java.JpsJavaSdkType import org.jetbrains.jps.model.library.JpsOrderRootType import org.jetbrains.jps.model.module.JpsModule import org.jetbrains.jps.util.JpsPathUtil +import org.jetbrains.kotlin.cli.common.Usage +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.JvmCodegenUtil import org.jetbrains.kotlin.config.KotlinCompilerVersion.TEST_IS_PRE_RELEASE_SYSTEM_PROPERTY @@ -787,6 +790,26 @@ class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() { ) } + fun testHelp() { + initProject() + + val result = buildAllModules() + result.assertSuccessful() + val warning = result.getMessages(BuildMessage.Kind.WARNING).single() + + Assert.assertEquals(Usage.render(K2JVMCompiler(), K2JVMCompilerArguments()), warning.messageText) + } + + fun testWrongArgument() { + initProject() + + val result = buildAllModules() + result.assertFailed() + val errors = result.getMessages(BuildMessage.Kind.ERROR).joinToString("\n\n") { it.messageText } + + Assert.assertEquals("Invalid argument: -abcdefghij-invalid-argument", errors) + } + fun testCodeInKotlinPackage() { initProject(JVM_MOCK_RUNTIME) diff --git a/jps-plugin/testData/general/Help/kotlinProject.iml b/jps-plugin/testData/general/Help/kotlinProject.iml new file mode 100644 index 00000000000..a0cbe548242 --- /dev/null +++ b/jps-plugin/testData/general/Help/kotlinProject.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/jps-plugin/testData/general/Help/kotlinProject.ipr b/jps-plugin/testData/general/Help/kotlinProject.ipr new file mode 100644 index 00000000000..102bdf0ea20 --- /dev/null +++ b/jps-plugin/testData/general/Help/kotlinProject.ipr @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jps-plugin/testData/general/Help/src/test.kt b/jps-plugin/testData/general/Help/src/test.kt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/jps-plugin/testData/general/WrongArgument/kotlinProject.iml b/jps-plugin/testData/general/WrongArgument/kotlinProject.iml new file mode 100644 index 00000000000..a0cbe548242 --- /dev/null +++ b/jps-plugin/testData/general/WrongArgument/kotlinProject.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/jps-plugin/testData/general/WrongArgument/kotlinProject.ipr b/jps-plugin/testData/general/WrongArgument/kotlinProject.ipr new file mode 100644 index 00000000000..a9c7f866ea1 --- /dev/null +++ b/jps-plugin/testData/general/WrongArgument/kotlinProject.ipr @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jps-plugin/testData/general/WrongArgument/src/test.kt b/jps-plugin/testData/general/WrongArgument/src/test.kt new file mode 100644 index 00000000000..e69de29bb2d