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