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