001 /* 002 * Copyright 2010-2015 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.kotlin.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.annotations.Nullable; 026 import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments; 027 import org.jetbrains.kotlin.cli.common.messages.*; 028 import org.jetbrains.kotlin.cli.jvm.compiler.CompileEnvironmentException; 029 import org.jetbrains.kotlin.config.CompilerConfiguration; 030 import org.jetbrains.kotlin.config.Services; 031 032 import java.io.PrintStream; 033 import java.util.List; 034 035 import static org.jetbrains.kotlin.cli.common.ExitCode.*; 036 037 public abstract class CLICompiler<A extends CommonCompilerArguments> { 038 @NotNull 039 private List<CompilerPlugin> compilerPlugins = Lists.newArrayList(); 040 041 @NotNull 042 public List<CompilerPlugin> getCompilerPlugins() { 043 return compilerPlugins; 044 } 045 046 public void setCompilerPlugins(@NotNull List<CompilerPlugin> compilerPlugins) { 047 this.compilerPlugins = compilerPlugins; 048 } 049 050 @NotNull 051 public ExitCode exec(@NotNull PrintStream errStream, @NotNull String... args) { 052 return exec(errStream, Services.EMPTY, MessageRenderer.PLAIN_RELATIVE_PATHS, args); 053 } 054 055 @SuppressWarnings("UnusedDeclaration") // Used via reflection in CompilerRunnerUtil#invokeExecMethod 056 @NotNull 057 public ExitCode execAndOutputXml(@NotNull PrintStream errStream, @NotNull Services services, @NotNull String... args) { 058 return exec(errStream, services, MessageRenderer.XML, args); 059 } 060 061 @SuppressWarnings("UnusedDeclaration") // Used via reflection in KotlinCompilerBaseTask 062 @NotNull 063 public ExitCode execFullPathsInMessages(@NotNull PrintStream errStream, @NotNull String[] args) { 064 return exec(errStream, Services.EMPTY, MessageRenderer.PLAIN_FULL_PATHS, args); 065 } 066 067 @Nullable 068 private A parseArguments(@NotNull PrintStream errStream, @NotNull MessageRenderer messageRenderer, @NotNull String[] args) { 069 try { 070 A arguments = createArguments(); 071 arguments.freeArgs = Args.parse(arguments, args); 072 return arguments; 073 } 074 catch (IllegalArgumentException e) { 075 errStream.println(e.getMessage()); 076 usage(errStream, false); 077 } 078 catch (Throwable t) { 079 errStream.println(messageRenderer.render( 080 CompilerMessageSeverity.EXCEPTION, 081 OutputMessageUtil.renderException(t), 082 CompilerMessageLocation.NO_LOCATION) 083 ); 084 } 085 return null; 086 } 087 088 /** 089 * Allow derived classes to add additional command line arguments 090 */ 091 protected void usage(@NotNull PrintStream target, boolean extraHelp) { 092 Usage.print(target, createArguments(), extraHelp); 093 } 094 095 /** 096 * Strategy method to configure the environment, allowing compiler 097 * based tools to customise their own plugins 098 */ 099 protected void configureEnvironment(@NotNull CompilerConfiguration configuration, @NotNull A arguments) { 100 configuration.addAll(CLIConfigurationKeys.COMPILER_PLUGINS, compilerPlugins); 101 } 102 103 @NotNull 104 protected abstract A createArguments(); 105 106 @NotNull 107 private ExitCode exec( 108 @NotNull PrintStream errStream, 109 @NotNull Services services, 110 @NotNull MessageRenderer messageRenderer, 111 @NotNull String[] args 112 ) { 113 A arguments = parseArguments(errStream, messageRenderer, args); 114 if (arguments == null) { 115 return INTERNAL_ERROR; 116 } 117 118 if (arguments.help || arguments.extraHelp) { 119 usage(errStream, arguments.extraHelp); 120 return OK; 121 } 122 123 errStream.print(messageRenderer.renderPreamble()); 124 125 MessageCollector collector = new PrintingMessageCollector(errStream, messageRenderer, arguments.verbose); 126 127 try { 128 return exec(collector, services, arguments); 129 } 130 finally { 131 errStream.print(messageRenderer.renderConclusion()); 132 } 133 } 134 135 @NotNull 136 public ExitCode exec(@NotNull MessageCollector messageCollector, @NotNull Services services, @NotNull A arguments) { 137 printVersionIfNeeded(messageCollector, arguments); 138 139 if (arguments.suppressWarnings) { 140 messageCollector = new FilteringMessageCollector(messageCollector, Predicates.equalTo(CompilerMessageSeverity.WARNING)); 141 } 142 143 GroupingMessageCollector groupingCollector = new GroupingMessageCollector(messageCollector); 144 try { 145 Disposable rootDisposable = Disposer.newDisposable(); 146 try { 147 MessageSeverityCollector severityCollector = new MessageSeverityCollector(groupingCollector); 148 ExitCode code = doExecute(arguments, services, severityCollector, rootDisposable); 149 return severityCollector.anyReported(CompilerMessageSeverity.ERROR) ? COMPILATION_ERROR : code; 150 } 151 finally { 152 Disposer.dispose(rootDisposable); 153 } 154 } 155 catch (Throwable t) { 156 groupingCollector.report(CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(t), 157 CompilerMessageLocation.NO_LOCATION); 158 return INTERNAL_ERROR; 159 } 160 finally { 161 groupingCollector.flush(); 162 } 163 } 164 165 @NotNull 166 protected abstract ExitCode doExecute( 167 @NotNull A arguments, 168 @NotNull Services services, 169 @NotNull MessageCollector messageCollector, 170 @NotNull Disposable rootDisposable 171 ); 172 173 protected void printVersionIfNeeded(@NotNull MessageCollector messageCollector, @NotNull A arguments) { 174 if (!arguments.version) return; 175 176 messageCollector.report(CompilerMessageSeverity.INFO, 177 "Kotlin Compiler version " + KotlinVersion.VERSION, 178 CompilerMessageLocation.NO_LOCATION); 179 } 180 181 /** 182 * Useful main for derived command line tools 183 */ 184 public static void doMain(@NotNull CLICompiler compiler, @NotNull String[] args) { 185 // We depend on swing (indirectly through PSI or something), so we want to declare headless mode, 186 // to avoid accidentally starting the UI thread 187 System.setProperty("java.awt.headless", "true"); 188 ExitCode exitCode = doMainNoExit(compiler, args); 189 if (exitCode != OK) { 190 System.exit(exitCode.getCode()); 191 } 192 } 193 194 @NotNull 195 public static ExitCode doMainNoExit(@NotNull CLICompiler compiler, @NotNull String[] args) { 196 try { 197 return compiler.exec(System.err, args); 198 } 199 catch (CompileEnvironmentException e) { 200 System.err.println(e.getMessage()); 201 return INTERNAL_ERROR; 202 } 203 } 204 }