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.CliOptionProcessingException;
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 (CliOptionProcessingException e) {
098                messageCollector.report(CompilerMessageSeverity.ERROR, e.getMessage(), CompilerMessageLocation.NO_LOCATION);
099                return INTERNAL_ERROR;
100            }
101            catch (Throwable t) {
102                MessageCollectorUtil.reportException(messageCollector, t);
103                return INTERNAL_ERROR;
104            }
105    
106            if (arguments.script) {
107                if (arguments.freeArgs.isEmpty()) {
108                    messageCollector.report(CompilerMessageSeverity.ERROR, "Specify script source path to evaluate",
109                                            CompilerMessageLocation.NO_LOCATION);
110                    return COMPILATION_ERROR;
111                }
112                configuration.add(CommonConfigurationKeys.SOURCE_ROOTS_KEY, arguments.freeArgs.get(0));
113            }
114            else if (arguments.module == null) {
115                configuration.addAll(CommonConfigurationKeys.SOURCE_ROOTS_KEY, arguments.freeArgs);
116    
117                // Adding all directory sources to classpath to resolve Java symbols from Kotlin
118                for (String source : arguments.freeArgs) {
119                    File file = new File(source);
120                    if (file.isDirectory()) {
121                        configuration.add(JVMConfigurationKeys.CLASSPATH_KEY, file);
122                    }
123                }
124            }
125    
126            configuration.addAll(JVMConfigurationKeys.CLASSPATH_KEY, getClasspath(paths, arguments));
127            configuration.addAll(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY, getAnnotationsPath(paths, arguments));
128    
129            if (arguments.module == null && arguments.freeArgs.isEmpty() && !arguments.version) {
130                ReplFromTerminal.run(rootDisposable, configuration);
131                return ExitCode.OK;
132            }
133    
134            configuration.put(JVMConfigurationKeys.SCRIPT_PARAMETERS, arguments.script
135                                                                      ? CommandLineScriptUtils.scriptParameters()
136                                                                      : Collections.<AnalyzerScriptParameter>emptyList());
137    
138            putAdvancedOptions(configuration, arguments);
139    
140            messageCollector.report(CompilerMessageSeverity.LOGGING, "Configuring the compilation environment",
141                                    CompilerMessageLocation.NO_LOCATION);
142            try {
143                configureEnvironment(configuration, arguments);
144    
145                String destination = arguments.destination;
146    
147                File jar;
148                File outputDir;
149                if (destination != null) {
150                    boolean isJar = destination.endsWith(".jar");
151                    jar = isJar ? new File(destination) : null;
152                    outputDir = isJar ? null : new File(destination);
153                }
154                else {
155                    jar = null;
156                    outputDir = null;
157                }
158    
159                if (arguments.module != null) {
160                    MessageCollector sanitizedCollector = new FilteringMessageCollector(messageCollector, in(CompilerMessageSeverity.VERBOSE));
161                    ModuleScriptData moduleScript = CompileEnvironmentUtil.loadModuleDescriptions(
162                            paths, arguments.module, sanitizedCollector);
163    
164                    if (outputDir != null) {
165                        messageCollector.report(CompilerMessageSeverity.WARNING,
166                                                "The '-d' option with a directory destination is ignored because '-module' is specified",
167                                                CompilerMessageLocation.NO_LOCATION);
168                    }
169    
170                    File directory = new File(arguments.module).getAbsoluteFile().getParentFile();
171                    KotlinToJVMBytecodeCompiler.compileModules(
172                            configuration, moduleScript.getModules(), directory, jar, arguments.includeRuntime
173                    );
174                }
175                else if (arguments.script) {
176                    List<String> scriptArgs = arguments.freeArgs.subList(1, arguments.freeArgs.size());
177                    JetCoreEnvironment environment =
178                            JetCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
179                    KotlinToJVMBytecodeCompiler.compileAndExecuteScript(paths, environment, scriptArgs);
180                }
181                else {
182                    JetCoreEnvironment environment =
183                            JetCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
184                    KotlinToJVMBytecodeCompiler.compileBunchOfSources(environment, jar, outputDir, arguments.includeRuntime);
185                }
186                return OK;
187            }
188            catch (CompilationException e) {
189                messageCollector.report(CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(e),
190                                        MessageUtil.psiElementToMessageLocation(e.getElement()));
191                return INTERNAL_ERROR;
192            }
193        }
194    
195        private static void putAdvancedOptions(@NotNull CompilerConfiguration configuration, @NotNull K2JVMCompilerArguments arguments) {
196            configuration.put(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, arguments.noCallAssertions);
197            configuration.put(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, arguments.noParamAssertions);
198            configuration.put(JVMConfigurationKeys.DISABLE_INLINE, arguments.noInline);
199            configuration.put(JVMConfigurationKeys.DISABLE_OPTIMIZATION, arguments.noOptimize);
200        }
201    
202        /**
203         * Allow derived classes to add additional command line arguments
204         */
205        @NotNull
206        @Override
207        protected K2JVMCompilerArguments createArguments() {
208            return new K2JVMCompilerArguments();
209        }
210    
211        @NotNull
212        private static List<File> getClasspath(@NotNull KotlinPaths paths, @NotNull K2JVMCompilerArguments arguments) {
213            List<File> classpath = Lists.newArrayList();
214            if (arguments.classpath != null) {
215                for (String element : Splitter.on(File.pathSeparatorChar).split(arguments.classpath)) {
216                    classpath.add(new File(element));
217                }
218            }
219            if (!arguments.noStdlib) {
220                classpath.add(paths.getRuntimePath());
221            }
222            return classpath;
223        }
224    
225        @NotNull
226        private static List<File> getAnnotationsPath(@NotNull KotlinPaths paths, @NotNull K2JVMCompilerArguments arguments) {
227            List<File> annotationsPath = Lists.newArrayList();
228            if (arguments.annotations != null) {
229                for (String element : Splitter.on(File.pathSeparatorChar).split(arguments.annotations)) {
230                    annotationsPath.add(new File(element));
231                }
232            }
233            if (!arguments.noJdkAnnotations) {
234                annotationsPath.add(paths.getJdkAnnotationsPath());
235            }
236            return annotationsPath;
237        }
238    }