001 /* 002 * Copyright 2010-2013 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.jetbrains.jet.cli.common; 018 019 import com.google.common.base.Predicates; 020 import com.google.common.collect.Lists; 021 import com.intellij.openapi.Disposable; 022 import com.intellij.openapi.util.Disposer; 023 import com.sampullara.cli.Args; 024 import org.jetbrains.annotations.NotNull; 025 import org.jetbrains.jet.cli.common.arguments.CommonCompilerArguments; 026 import org.jetbrains.jet.cli.common.messages.*; 027 import org.jetbrains.jet.cli.jvm.compiler.CompileEnvironmentException; 028 import org.jetbrains.jet.config.CompilerConfiguration; 029 030 import java.io.PrintStream; 031 import java.util.List; 032 033 import static org.jetbrains.jet.cli.common.ExitCode.*; 034 035 public abstract class CLICompiler<A extends CommonCompilerArguments> { 036 037 @NotNull 038 private List<CompilerPlugin> compilerPlugins = Lists.newArrayList(); 039 040 @NotNull 041 public List<CompilerPlugin> getCompilerPlugins() { 042 return compilerPlugins; 043 } 044 045 public void setCompilerPlugins(@NotNull List<CompilerPlugin> compilerPlugins) { 046 this.compilerPlugins = compilerPlugins; 047 } 048 049 @NotNull 050 public ExitCode exec(@NotNull PrintStream errStream, @NotNull String... args) { 051 A arguments = createArguments(); 052 if (!parseArguments(errStream, arguments, args)) { 053 return INTERNAL_ERROR; 054 } 055 return exec(errStream, arguments); 056 } 057 058 /** 059 * Returns true if the arguments can be parsed correctly 060 */ 061 protected boolean parseArguments(@NotNull PrintStream errStream, @NotNull A arguments, @NotNull String[] args) { 062 try { 063 arguments.freeArgs = Args.parse(arguments, args); 064 checkArguments(arguments); 065 return true; 066 } 067 catch (IllegalArgumentException e) { 068 errStream.println(e.getMessage()); 069 usage(errStream); 070 } 071 catch (Throwable t) { 072 // Always use tags 073 errStream.println(MessageRenderer.TAGS.renderException(t)); 074 } 075 return false; 076 } 077 078 protected void checkArguments(@NotNull A argument) { 079 080 } 081 082 /** 083 * Allow derived classes to add additional command line arguments 084 */ 085 protected void usage(@NotNull PrintStream target) { 086 Usage.print(target, createArguments()); 087 } 088 089 /** 090 * Strategy method to configure the environment, allowing compiler 091 * based tools to customise their own plugins 092 */ 093 protected void configureEnvironment(@NotNull CompilerConfiguration configuration, @NotNull A arguments) { 094 configuration.addAll(CLIConfigurationKeys.COMPILER_PLUGINS, compilerPlugins); 095 } 096 097 @NotNull 098 protected abstract A createArguments(); 099 100 /** 101 * Executes the compiler on the parsed arguments 102 */ 103 @NotNull 104 public ExitCode exec(@NotNull PrintStream errStream, @NotNull A arguments) { 105 if (arguments.help) { 106 usage(errStream); 107 return OK; 108 } 109 110 MessageRenderer messageRenderer = getMessageRenderer(arguments); 111 errStream.print(messageRenderer.renderPreamble()); 112 113 printVersionIfNeeded(errStream, arguments, messageRenderer); 114 115 MessageCollector collector = new PrintingMessageCollector(errStream, messageRenderer, arguments.verbose); 116 117 if (arguments.suppressAllWarnings()) { 118 collector = new FilteringMessageCollector(collector, Predicates.equalTo(CompilerMessageSeverity.WARNING)); 119 } 120 121 try { 122 return exec(collector, arguments); 123 } 124 finally { 125 errStream.print(messageRenderer.renderConclusion()); 126 } 127 } 128 129 @NotNull 130 public ExitCode exec(@NotNull MessageCollector messageCollector, @NotNull A arguments) { 131 GroupingMessageCollector groupingCollector = new GroupingMessageCollector(messageCollector); 132 try { 133 Disposable rootDisposable = Disposer.newDisposable(); 134 try { 135 MessageSeverityCollector severityCollector = new MessageSeverityCollector(groupingCollector); 136 ExitCode code = doExecute(arguments, severityCollector, rootDisposable); 137 return severityCollector.anyReported(CompilerMessageSeverity.ERROR) ? COMPILATION_ERROR : code; 138 } 139 finally { 140 Disposer.dispose(rootDisposable); 141 } 142 } 143 catch (Throwable t) { 144 groupingCollector.report(CompilerMessageSeverity.EXCEPTION, MessageRenderer.PLAIN.renderException(t), 145 CompilerMessageLocation.NO_LOCATION); 146 return INTERNAL_ERROR; 147 } 148 finally { 149 groupingCollector.flush(); 150 } 151 } 152 153 @NotNull 154 protected abstract ExitCode doExecute(@NotNull A arguments, @NotNull MessageCollector messageCollector, @NotNull Disposable rootDisposable); 155 156 //TODO: can we make it private? 157 @NotNull 158 protected MessageRenderer getMessageRenderer(@NotNull A arguments) { 159 return arguments.tags ? MessageRenderer.TAGS : MessageRenderer.PLAIN; 160 } 161 162 protected void printVersionIfNeeded( 163 @NotNull PrintStream errStream, 164 @NotNull A arguments, 165 @NotNull MessageRenderer messageRenderer 166 ) { 167 if (arguments.version) { 168 String versionMessage = messageRenderer.render(CompilerMessageSeverity.INFO, 169 "Kotlin Compiler version " + KotlinVersion.VERSION, 170 CompilerMessageLocation.NO_LOCATION); 171 errStream.println(versionMessage); 172 } 173 } 174 175 /** 176 * Useful main for derived command line tools 177 */ 178 public static void doMain(@NotNull CLICompiler compiler, @NotNull String[] args) { 179 // We depend on swing (indirectly through PSI or something), so we want to declare headless mode, 180 // to avoid accidentally starting the UI thread 181 System.setProperty("java.awt.headless", "true"); 182 ExitCode exitCode = doMainNoExit(compiler, args); 183 if (exitCode != OK) { 184 System.exit(exitCode.getCode()); 185 } 186 } 187 188 @NotNull 189 public static ExitCode doMainNoExit(@NotNull CLICompiler compiler, @NotNull String[] args) { 190 try { 191 return compiler.exec(System.err, args); 192 } 193 catch (CompileEnvironmentException e) { 194 System.err.println(e.getMessage()); 195 return INTERNAL_ERROR; 196 } 197 } 198 }