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 }