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.jvm;
018
019 import com.google.common.base.Splitter;
020 import com.google.common.collect.Lists;
021 import com.intellij.openapi.Disposable;
022 import org.jetbrains.annotations.NotNull;
023 import org.jetbrains.kotlin.cli.common.CLICompiler;
024 import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys;
025 import org.jetbrains.kotlin.cli.common.ExitCode;
026 import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments;
027 import org.jetbrains.kotlin.cli.common.messages.*;
028 import org.jetbrains.kotlin.cli.common.modules.ModuleScriptData;
029 import org.jetbrains.kotlin.cli.jvm.compiler.*;
030 import org.jetbrains.kotlin.cli.jvm.config.JVMConfigurationKeys;
031 import org.jetbrains.kotlin.cli.jvm.repl.ReplFromTerminal;
032 import org.jetbrains.kotlin.codegen.CompilationException;
033 import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException;
034 import org.jetbrains.kotlin.compiler.plugin.PluginCliOptionProcessingException;
035 import org.jetbrains.kotlin.compiler.plugin.PluginPackage;
036 import org.jetbrains.kotlin.config.CompilerConfiguration;
037 import org.jetbrains.kotlin.config.Services;
038 import org.jetbrains.kotlin.load.kotlin.incremental.cache.IncrementalCacheProvider;
039 import org.jetbrains.kotlin.resolve.AnalyzerScriptParameter;
040 import org.jetbrains.kotlin.utils.KotlinPaths;
041 import org.jetbrains.kotlin.utils.KotlinPathsFromHomeDir;
042 import org.jetbrains.kotlin.utils.PathUtil;
043
044 import java.io.File;
045 import java.util.Collections;
046 import java.util.List;
047
048 import static com.google.common.base.Predicates.in;
049 import static org.jetbrains.kotlin.cli.common.ExitCode.*;
050 import static org.jetbrains.kotlin.cli.jvm.config.ConfigPackage.*;
051 import static org.jetbrains.kotlin.config.ConfigPackage.addKotlinSourceRoot;
052
053 @SuppressWarnings("UseOfSystemOutOrSystemErr")
054 public class K2JVMCompiler extends CLICompiler<K2JVMCompilerArguments> {
055
056 public static void main(String... args) {
057 doMain(new K2JVMCompiler(), args);
058 }
059
060 @Override
061 @NotNull
062 protected ExitCode doExecute(
063 @NotNull K2JVMCompilerArguments arguments,
064 @NotNull Services services,
065 @NotNull MessageCollector messageCollector,
066 @NotNull Disposable rootDisposable
067 ) {
068 KotlinPaths paths = arguments.kotlinHome != null
069 ? new KotlinPathsFromHomeDir(new File(arguments.kotlinHome))
070 : PathUtil.getKotlinPathsForCompiler();
071
072 messageCollector.report(CompilerMessageSeverity.LOGGING,
073 "Using Kotlin home directory " + paths.getHomePath(), CompilerMessageLocation.NO_LOCATION);
074
075 CompilerConfiguration configuration = new CompilerConfiguration();
076 configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector);
077
078 IncrementalCacheProvider incrementalCacheProvider = services.get(IncrementalCacheProvider.class);
079 if (incrementalCacheProvider != null) {
080 configuration.put(JVMConfigurationKeys.INCREMENTAL_CACHE_PROVIDER, incrementalCacheProvider);
081 }
082
083 CompilerJarLocator locator = services.get(CompilerJarLocator.class);
084 if (locator != null) {
085 configuration.put(JVMConfigurationKeys.COMPILER_JAR_LOCATOR, locator);
086 }
087
088 try {
089 if (!arguments.noJdk) {
090 addJvmClasspathRoots(configuration, PathUtil.getJdkClassesRoots());
091 }
092 }
093 catch (Throwable t) {
094 MessageCollectorUtil.reportException(messageCollector, t);
095 return INTERNAL_ERROR;
096 }
097
098 try {
099 PluginCliParser.loadPlugins(arguments, configuration);
100 }
101 catch (PluginCliOptionProcessingException e) {
102 String message = e.getMessage() + "\n\n" + PluginPackage.cliPluginUsageString(e.getPluginId(), e.getOptions());
103 messageCollector.report(CompilerMessageSeverity.ERROR, message, CompilerMessageLocation.NO_LOCATION);
104 return INTERNAL_ERROR;
105 }
106 catch (CliOptionProcessingException e) {
107 messageCollector.report(CompilerMessageSeverity.ERROR, e.getMessage(), CompilerMessageLocation.NO_LOCATION);
108 return INTERNAL_ERROR;
109 }
110 catch (Throwable t) {
111 MessageCollectorUtil.reportException(messageCollector, t);
112 return INTERNAL_ERROR;
113 }
114
115 if (arguments.script) {
116 if (arguments.freeArgs.isEmpty()) {
117 messageCollector.report(CompilerMessageSeverity.ERROR, "Specify script source path to evaluate",
118 CompilerMessageLocation.NO_LOCATION);
119 return COMPILATION_ERROR;
120 }
121 addKotlinSourceRoot(configuration, arguments.freeArgs.get(0));
122 }
123 else if (arguments.module == null) {
124 for (String arg : arguments.freeArgs) {
125 addKotlinSourceRoot(configuration, arg);
126 File file = new File(arg);
127 if (file.isDirectory()) {
128 addJavaSourceRoot(configuration, file);
129 }
130 }
131 }
132
133 addJvmClasspathRoots(configuration, getClasspath(paths, arguments));
134
135 configuration.addAll(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY, getAnnotationsPath(paths, arguments));
136
137 if (arguments.module == null && arguments.freeArgs.isEmpty() && !arguments.version) {
138 ReplFromTerminal.run(rootDisposable, configuration);
139 return ExitCode.OK;
140 }
141
142 configuration.put(JVMConfigurationKeys.SCRIPT_PARAMETERS, arguments.script
143 ? CommandLineScriptUtils.scriptParameters()
144 : Collections.<AnalyzerScriptParameter>emptyList());
145
146 putAdvancedOptions(configuration, arguments);
147
148 messageCollector.report(CompilerMessageSeverity.LOGGING, "Configuring the compilation environment",
149 CompilerMessageLocation.NO_LOCATION);
150 try {
151 configureEnvironment(configuration, arguments);
152
153 String destination = arguments.destination;
154
155 File jar;
156 File outputDir;
157 if (destination != null) {
158 boolean isJar = destination.endsWith(".jar");
159 jar = isJar ? new File(destination) : null;
160 outputDir = isJar ? null : new File(destination);
161 }
162 else {
163 jar = null;
164 outputDir = null;
165 }
166
167 if (arguments.module != null) {
168 MessageCollector sanitizedCollector = new FilteringMessageCollector(messageCollector, in(CompilerMessageSeverity.VERBOSE));
169 ModuleScriptData moduleScript = CompileEnvironmentUtil.loadModuleDescriptions(arguments.module, sanitizedCollector);
170
171 if (outputDir != null) {
172 messageCollector.report(CompilerMessageSeverity.WARNING,
173 "The '-d' option with a directory destination is ignored because '-module' is specified",
174 CompilerMessageLocation.NO_LOCATION);
175 }
176
177 File directory = new File(arguments.module).getAbsoluteFile().getParentFile();
178 KotlinToJVMBytecodeCompiler.compileModules(
179 configuration, moduleScript.getModules(), directory, jar, arguments.includeRuntime
180 );
181 }
182 else if (arguments.script) {
183 List<String> scriptArgs = arguments.freeArgs.subList(1, arguments.freeArgs.size());
184 KotlinCoreEnvironment environment =
185 KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
186 KotlinToJVMBytecodeCompiler.compileAndExecuteScript(configuration, paths, environment, scriptArgs);
187 }
188 else {
189 KotlinCoreEnvironment environment =
190 KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
191 KotlinToJVMBytecodeCompiler.compileBunchOfSources(environment, jar, outputDir, arguments.includeRuntime);
192 }
193 return OK;
194 }
195 catch (CompilationException e) {
196 messageCollector.report(CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(e),
197 MessageUtil.psiElementToMessageLocation(e.getElement()));
198 return INTERNAL_ERROR;
199 }
200 }
201
202 private static void putAdvancedOptions(@NotNull CompilerConfiguration configuration, @NotNull K2JVMCompilerArguments arguments) {
203 configuration.put(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, arguments.noCallAssertions);
204 configuration.put(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, arguments.noParamAssertions);
205 configuration.put(JVMConfigurationKeys.DISABLE_INLINE, arguments.noInline);
206 configuration.put(JVMConfigurationKeys.DISABLE_OPTIMIZATION, arguments.noOptimize);
207 }
208
209 /**
210 * Allow derived classes to add additional command line arguments
211 */
212 @NotNull
213 @Override
214 protected K2JVMCompilerArguments createArguments() {
215 return new K2JVMCompilerArguments();
216 }
217
218 @NotNull
219 private static List<File> getClasspath(@NotNull KotlinPaths paths, @NotNull K2JVMCompilerArguments arguments) {
220 List<File> classpath = Lists.newArrayList();
221 if (arguments.classpath != null) {
222 for (String element : Splitter.on(File.pathSeparatorChar).split(arguments.classpath)) {
223 classpath.add(new File(element));
224 }
225 }
226 if (!arguments.noStdlib) {
227 classpath.add(paths.getRuntimePath());
228 }
229 return classpath;
230 }
231
232 @NotNull
233 private static List<File> getAnnotationsPath(@NotNull KotlinPaths paths, @NotNull K2JVMCompilerArguments arguments) {
234 List<File> annotationsPath = Lists.newArrayList();
235 if (arguments.annotations != null) {
236 for (String element : Splitter.on(File.pathSeparatorChar).split(arguments.annotations)) {
237 annotationsPath.add(new File(element));
238 }
239 }
240 if (!arguments.noJdkAnnotations) {
241 annotationsPath.add(paths.getJdkAnnotationsPath());
242 }
243 return annotationsPath;
244 }
245 }