CommandLineProcessor infrastructure added

This commit is contained in:
Andrey Breslav
2014-09-23 18:19:48 +04:00
committed by Yan Zhulanow
parent 565ce5a781
commit d961e2f5b4
5 changed files with 165 additions and 5 deletions
@@ -23,6 +23,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.List;
public abstract class CommonCompilerArguments {
public static final String PLUGIN_OPTION_FORMAT = "<pluginId>:<optionName>=<value>";
@Argument(value = "nowarn", description = "Generate no warnings")
public boolean suppressWarnings;
@@ -41,6 +43,14 @@ public abstract class CommonCompilerArguments {
@Argument(value = "Xno-inline", description = "Disable method inlining")
public boolean noInline;
@Argument(value = "Xplugin", description = "Load a plugin from the given classpath")
@ValueDescription("<path>")
public String[] pluginClasspaths;
@Argument(value = "P", description = "Pass an option to a plugin")
@ValueDescription(PLUGIN_OPTION_FORMAT)
public String[] pluginOptions;
public List<String> freeArgs = new SmartList<String>();
@NotNull
@@ -0,0 +1,109 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.cli.jvm
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
import org.jetbrains.jet.cli.common.arguments.CommonCompilerArguments
import kotlin.platform.*
import java.util.jar.JarFile
import java.util.jar.Attributes
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
import java.util.regex.Pattern
import org.jetbrains.jet.utils.valuesToMap
import org.jetbrains.jet.config.CompilerConfiguration
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.compiler.plugin.CliOption
public object PluginCliParser {
public val PLUGIN_ARGUMENT_PREFIX: String = "plugin:"
private fun getCommandLineProcessors(arguments: CommonCompilerArguments): Collection<CommandLineProcessor> {
return arguments.pluginClasspaths!!.map {
loadCommandLineProcessor(JarFile(it).getManifest()?.getAttributes("org.jetbrains.kotlin.compiler.plugin"))
}.filterNotNull()
}
private fun loadCommandLineProcessor(attributes: Attributes?): CommandLineProcessor? {
if (attributes == null) return null
val processorClassName = attributes.getValue("CommandLineProcessor")
if (processorClassName == null) return null
try {
val processorClass = Class.forName(processorClassName)
return processorClass.newInstance() as CommandLineProcessor
}
catch (e: Throwable) {
throw CliOptionProcessingException("Loading plugin component failed: $processorClassName", e)
}
}
[platformStatic]
fun processPluginOptions(arguments: CommonCompilerArguments, configuration: CompilerConfiguration) {
val optionValuesByPlugin = arguments.pluginOptions!!.map { parsePluginOption(it) }.groupBy {
if (it == null) throw CliOptionProcessingException("Wrong plugin option format: $it, should be ${CommonCompilerArguments.PLUGIN_OPTION_FORMAT}")
it.optionName
}
val processors = getCommandLineProcessors(arguments)
for (processor in processors) {
val declaredOptions = processor.pluginOptions.valuesToMap { it.name }
val optionsToValues = MultiMap<CliOption, PluginOptionValue>()
for (optionValue in optionValuesByPlugin[processor.pluginId].orEmpty()) {
val option = declaredOptions[optionValue!!.optionName]
if (option == null) {
throw CliOptionProcessingException("Unsupported plugin option: $optionValue")
}
optionsToValues.putValue(option, optionValue)
}
for (option in processor.pluginOptions) {
val values = optionsToValues[option]
if (option.required && values.isEmpty()) {
throw CliOptionProcessingException("Required plugin option not present: ${processor.pluginId}:${option.name}")
}
if (!option.allowMultipleOccurrences && values.size() > 1) {
throw CliOptionProcessingException("Multiple values not allowed for plugin option ${processor.pluginId}:${option.name}")
}
for (value in values) {
processor.processOption(option, value.value, configuration)
}
}
}
}
private class PluginOptionValue(
val pluginId: String,
val optionName: String,
val value: String
) {
override fun toString() = "$pluginId:$optionName=$value"
}
private fun parsePluginOption(argumentValue: String): PluginOptionValue? {
val pattern = Pattern.compile("""^([^:]*):([^=]*)=(.*)$""")
val matcher = pattern.matcher(argumentValue)
if (matcher.matches()) {
return PluginOptionValue(matcher.group(1)!!, matcher.group(2)!!, matcher.group(3)!!)
}
return null
}
}
@@ -20,6 +20,7 @@ import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.intellij.openapi.Disposable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.cli.jvm.PluginCliParser;
import org.jetbrains.kotlin.cli.common.CLICompiler;
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys;
import org.jetbrains.kotlin.cli.common.ExitCode;
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.cli.common.modules.ModuleScriptData;
import org.jetbrains.kotlin.cli.jvm.compiler.*;
import org.jetbrains.kotlin.cli.jvm.repl.ReplFromTerminal;
import org.jetbrains.kotlin.codegen.CompilationException;
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException;
import org.jetbrains.kotlin.config.CommonConfigurationKeys;
import org.jetbrains.kotlin.config.CompilerConfiguration;
import org.jetbrains.kotlin.config.Services;
@@ -90,12 +92,13 @@ public class K2JVMCompiler extends CLICompiler<K2JVMCompilerArguments> {
return INTERNAL_ERROR;
}
if (arguments.androidRes != null) {
configuration.put(JVMConfigurationKeys.ANDROID_RES_PATH, arguments.androidRes);
try {
PluginCliParser.processPluginOptions(arguments, configuration);
}
if (arguments.androidManifest != null) {
configuration.put(JVMConfigurationKeys.ANDROID_MANIFEST, arguments.androidManifest);
catch (CliOptionProcessingException e) {
// TODO Print usage?
messageCollector.report(CompilerMessageSeverity.ERROR, e.getMessage(), CompilerMessageLocation.NO_LOCATION);
return INTERNAL_ERROR;
}
if (arguments.script) {
+1
View File
@@ -8,6 +8,7 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="intellij-core" level="project" />
<orderEntry type="library" name="kotlin-runtime" level="project" />
</component>
</module>
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.compiler.plugin
import org.jetbrains.jet.config.CompilerConfiguration
public class CliOption(
public val name: String,
public val valueDescription: String,
public val description: String,
public val required: Boolean = true,
public val allowMultipleOccurrences: Boolean = false
)
public class CliOptionProcessingException(message: String, cause: Throwable? = null): RuntimeException(message, cause)
public trait CommandLineProcessor {
public val pluginId: String
public val pluginOptions: Collection<CliOption>
[throws(javaClass<CliOptionProcessingException>())]
public fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration)
}