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.intellij.openapi.Disposable; 021 import com.intellij.openapi.util.Disposer; 022 import com.sampullara.cli.Args; 023 import org.jetbrains.annotations.NotNull; 024 import org.jetbrains.jet.cli.common.messages.*; 025 import org.jetbrains.jet.cli.jvm.compiler.CompileEnvironmentException; 026 import org.jetbrains.jet.cli.jvm.compiler.CompileEnvironmentUtil; 027 import org.jetbrains.jet.config.CompilerConfiguration; 028 029 import java.io.PrintStream; 030 031 import static org.jetbrains.jet.cli.common.ExitCode.*; 032 033 public abstract class CLICompiler<A extends CompilerArguments> { 034 035 @NotNull 036 public ExitCode exec(@NotNull PrintStream errStream, @NotNull String... args) { 037 A arguments = createArguments(); 038 if (!parseArguments(errStream, arguments, args)) { 039 return INTERNAL_ERROR; 040 } 041 return exec(errStream, arguments); 042 } 043 044 /** 045 * Returns true if the arguments can be parsed correctly 046 */ 047 protected boolean parseArguments(@NotNull PrintStream errStream, @NotNull A arguments, @NotNull String[] args) { 048 try { 049 arguments.freeArgs = Args.parse(arguments, args); 050 return true; 051 } 052 catch (IllegalArgumentException e) { 053 errStream.println(e.getMessage()); 054 usage(errStream); 055 } 056 catch (Throwable t) { 057 // Always use tags 058 errStream.println(MessageRenderer.TAGS.renderException(t)); 059 } 060 return false; 061 } 062 063 /** 064 * Allow derived classes to add additional command line arguments 065 */ 066 protected void usage(@NotNull PrintStream target) { 067 // We should say something like 068 // Args.usage(target, K2JVMCompilerArguments.class); 069 // but currently cli-parser we are using does not support that 070 // a corresponding patch has been sent to the authors 071 // For now, we are using this: 072 PrintStream oldErr = System.err; 073 System.setErr(target); 074 try { 075 // TODO: use proper argv0 076 Args.usage(createArguments()); 077 } 078 finally { 079 System.setErr(oldErr); 080 } 081 } 082 083 /** 084 * Strategy method to configure the environment, allowing compiler 085 * based tools to customise their own plugins 086 */ 087 //TODO: add parameter annotations when KT-1863 is resolved 088 protected void configureEnvironment(@NotNull CompilerConfiguration configuration, @NotNull A arguments) { 089 configuration.addAll(CLIConfigurationKeys.COMPILER_PLUGINS, arguments.getCompilerPlugins()); 090 } 091 092 @NotNull 093 protected abstract A createArguments(); 094 095 /** 096 * Executes the compiler on the parsed arguments 097 */ 098 @NotNull 099 public ExitCode exec(@NotNull PrintStream errStream, @NotNull A arguments) { 100 if (arguments.isHelp()) { 101 usage(errStream); 102 return OK; 103 } 104 105 MessageRenderer messageRenderer = getMessageRenderer(arguments); 106 errStream.print(messageRenderer.renderPreamble()); 107 108 printVersionIfNeeded(errStream, arguments, messageRenderer); 109 110 MessageCollector collector = new PrintingMessageCollector(errStream, messageRenderer, arguments.isVerbose()); 111 112 if (arguments.suppressAllWarnings()) { 113 collector = new FilteringMessageCollector(collector, Predicates.equalTo(CompilerMessageSeverity.WARNING)); 114 } 115 116 try { 117 return exec(collector, arguments); 118 } 119 finally { 120 errStream.print(messageRenderer.renderConclusion()); 121 } 122 } 123 124 @NotNull 125 public ExitCode exec(@NotNull MessageCollector messageCollector, @NotNull A arguments) { 126 GroupingMessageCollector groupingCollector = new GroupingMessageCollector(messageCollector); 127 try { 128 Disposable rootDisposable = CompileEnvironmentUtil.createMockDisposable(); 129 try { 130 MessageSeverityCollector severityCollector = new MessageSeverityCollector(groupingCollector); 131 ExitCode code = doExecute(arguments, severityCollector, rootDisposable); 132 return severityCollector.anyReported(CompilerMessageSeverity.ERROR) ? COMPILATION_ERROR : code; 133 } 134 finally { 135 Disposer.dispose(rootDisposable); 136 } 137 } 138 catch (Throwable t) { 139 groupingCollector.report(CompilerMessageSeverity.EXCEPTION, MessageRenderer.PLAIN.renderException(t), 140 CompilerMessageLocation.NO_LOCATION); 141 return INTERNAL_ERROR; 142 } 143 finally { 144 groupingCollector.flush(); 145 } 146 } 147 148 //TODO: can't declare parameters as not null due to KT-1863 149 @NotNull 150 protected abstract ExitCode doExecute(A arguments, MessageCollector messageCollector, Disposable rootDisposable); 151 152 //TODO: can we make it private? 153 @NotNull 154 protected MessageRenderer getMessageRenderer(@NotNull A arguments) { 155 return arguments.isTags() ? MessageRenderer.TAGS : MessageRenderer.PLAIN; 156 } 157 158 protected void printVersionIfNeeded(@NotNull PrintStream errStream, 159 @NotNull A arguments, 160 @NotNull MessageRenderer messageRenderer) { 161 if (arguments.isVersion()) { 162 String versionMessage = messageRenderer.render(CompilerMessageSeverity.INFO, 163 "Kotlin Compiler version " + KotlinVersion.VERSION, 164 CompilerMessageLocation.NO_LOCATION); 165 errStream.println(versionMessage); 166 } 167 } 168 169 /** 170 * Useful main for derived command line tools 171 */ 172 public static void doMain(@NotNull CLICompiler compiler, @NotNull String[] args) { 173 // We depend on swing (indirectly through PSI or something), so we want to declare headless mode, 174 // to avoid accidentally starting the UI thread 175 System.setProperty("java.awt.headless", "true"); 176 ExitCode exitCode = doMainNoExit(compiler, args); 177 if (exitCode != OK) { 178 System.exit(exitCode.getCode()); 179 } 180 } 181 182 @NotNull 183 public static ExitCode doMainNoExit(@NotNull CLICompiler compiler, @NotNull String[] args) { 184 try { 185 ExitCode rc = compiler.exec(System.out, args); 186 if (rc != OK) { 187 System.err.println("exec() finished with " + rc + " return code"); 188 } 189 if (Boolean.parseBoolean(System.getProperty("kotlin.print.cmd.args"))) { 190 System.out.println("Command line arguments:"); 191 for (String arg : args) { 192 System.out.println(arg); 193 } 194 } 195 return rc; 196 } 197 catch (CompileEnvironmentException e) { 198 System.err.println(e.getMessage()); 199 return INTERNAL_ERROR; 200 } 201 } 202 }