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    }