001    /*
002     * Copyright 2010-2013 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.jet.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.jet.cli.common.CLICompiler;
024    import org.jetbrains.jet.cli.common.CLIConfigurationKeys;
025    import org.jetbrains.jet.cli.common.ExitCode;
026    import org.jetbrains.jet.cli.common.arguments.CompilerArgumentsUtil;
027    import org.jetbrains.jet.cli.common.arguments.K2JVMCompilerArguments;
028    import org.jetbrains.jet.cli.common.messages.*;
029    import org.jetbrains.jet.cli.common.modules.ModuleScriptData;
030    import org.jetbrains.jet.cli.jvm.compiler.CommandLineScriptUtils;
031    import org.jetbrains.jet.cli.jvm.compiler.CompileEnvironmentUtil;
032    import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment;
033    import org.jetbrains.jet.cli.jvm.compiler.KotlinToJVMBytecodeCompiler;
034    import org.jetbrains.jet.cli.jvm.repl.ReplFromTerminal;
035    import org.jetbrains.jet.codegen.CompilationException;
036    import org.jetbrains.jet.codegen.inline.InlineCodegenUtil;
037    import org.jetbrains.jet.codegen.optimization.OptimizationUtils;
038    import org.jetbrains.jet.config.CommonConfigurationKeys;
039    import org.jetbrains.jet.config.CompilerConfiguration;
040    import org.jetbrains.jet.lang.resolve.AnalyzerScriptParameter;
041    import org.jetbrains.jet.utils.KotlinPaths;
042    import org.jetbrains.jet.utils.KotlinPathsFromHomeDir;
043    import org.jetbrains.jet.utils.PathUtil;
044    
045    import java.io.File;
046    import java.util.Collections;
047    import java.util.List;
048    
049    import static com.google.common.base.Predicates.in;
050    import static org.jetbrains.jet.cli.common.ExitCode.INTERNAL_ERROR;
051    import static org.jetbrains.jet.cli.common.ExitCode.OK;
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 MessageCollector messageCollector,
065                @NotNull Disposable rootDisposable
066        ) {
067            KotlinPaths paths = arguments.kotlinHome != null
068                                    ? new KotlinPathsFromHomeDir(new File(arguments.kotlinHome))
069                                    : PathUtil.getKotlinPathsForCompiler();
070    
071            messageCollector.report(CompilerMessageSeverity.LOGGING,
072                                    "Using Kotlin home directory " + paths.getHomePath(), CompilerMessageLocation.NO_LOCATION);
073    
074            CompilerConfiguration configuration = new CompilerConfiguration();
075    
076            try {
077                configuration.addAll(JVMConfigurationKeys.CLASSPATH_KEY, getClasspath(paths, arguments));
078                configuration.addAll(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY, getAnnotationsPath(paths, arguments));
079            }
080            catch (Throwable t) {
081                MessageCollectorUtil.reportException(messageCollector, t);
082                return INTERNAL_ERROR;
083            }
084    
085            if (!arguments.script &&
086                arguments.module == null &&
087                arguments.freeArgs.isEmpty() &&
088                !arguments.version
089            ) {
090                ReplFromTerminal.run(rootDisposable, configuration);
091                return ExitCode.OK;
092            }
093            else if (arguments.module != null) {
094            }
095            else if (arguments.script) {
096                configuration.add(CommonConfigurationKeys.SOURCE_ROOTS_KEY, arguments.freeArgs.get(0));
097            }
098            else {
099                configuration.addAll(CommonConfigurationKeys.SOURCE_ROOTS_KEY, arguments.freeArgs);
100            }
101    
102            configuration.put(JVMConfigurationKeys.SCRIPT_PARAMETERS, arguments.script
103                                                                      ? CommandLineScriptUtils.scriptParameters()
104                                                                      : Collections.<AnalyzerScriptParameter>emptyList());
105    
106            configuration.put(JVMConfigurationKeys.GENERATE_NOT_NULL_ASSERTIONS, arguments.notNullAssertions);
107            configuration.put(JVMConfigurationKeys.GENERATE_NOT_NULL_PARAMETER_ASSERTIONS, arguments.notNullParamAssertions);
108            configuration.put(JVMConfigurationKeys.ENABLE_INLINE,
109                              CompilerArgumentsUtil.optionToBooleanFlag(arguments.inline, InlineCodegenUtil.DEFAULT_INLINE_FLAG));
110            configuration.put(JVMConfigurationKeys.ENABLE_OPTIMIZATION,
111                              CompilerArgumentsUtil.optionToBooleanFlag(arguments.optimize, OptimizationUtils.DEFAULT_OPTIMIZATION_FLAG));
112    
113            configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector);
114    
115            messageCollector.report(CompilerMessageSeverity.LOGGING, "Configuring the compilation environment",
116                                    CompilerMessageLocation.NO_LOCATION);
117            try {
118                configureEnvironment(configuration, arguments);
119    
120                String destination = arguments.destination;
121    
122                File jar;
123                File outputDir;
124                if (destination != null) {
125                    boolean isJar = destination.endsWith(".jar");
126                    jar = isJar ? new File(destination) : null;
127                    outputDir = isJar ? null : new File(destination);
128                }
129                else {
130                    jar = null;
131                    outputDir = null;
132                }
133    
134                if (arguments.module != null) {
135                    MessageCollector sanitizedCollector = new FilteringMessageCollector(messageCollector, in(CompilerMessageSeverity.VERBOSE));
136                    ModuleScriptData moduleScript = CompileEnvironmentUtil.loadModuleDescriptions(
137                            paths, arguments.module, sanitizedCollector);
138                    if (moduleScript.getIncrementalCacheDir() != null) {
139                        configuration.put(JVMConfigurationKeys.INCREMENTAL_CACHE_BASE_DIR, new File(moduleScript.getIncrementalCacheDir()));
140                    }
141    
142                    if (outputDir != null) {
143                        messageCollector.report(CompilerMessageSeverity.WARNING,
144                                                "The '-d' option with a directory destination is ignored because '-module' is specified",
145                                                CompilerMessageLocation.NO_LOCATION);
146                    }
147    
148                    File directory = new File(arguments.module).getAbsoluteFile().getParentFile();
149                    KotlinToJVMBytecodeCompiler.compileModules(
150                            configuration, moduleScript.getModules(), directory, jar, arguments.includeRuntime
151                    );
152                }
153                else if (arguments.script) {
154                    List<String> scriptArgs = arguments.freeArgs.subList(1, arguments.freeArgs.size());
155                    JetCoreEnvironment environment = JetCoreEnvironment.createForProduction(rootDisposable, configuration);
156                    KotlinToJVMBytecodeCompiler.compileAndExecuteScript(paths, environment, scriptArgs);
157                }
158                else {
159                    JetCoreEnvironment environment = JetCoreEnvironment.createForProduction(rootDisposable, configuration);
160                    KotlinToJVMBytecodeCompiler.compileBunchOfSources(environment, jar, outputDir, arguments.includeRuntime);
161                }
162                return OK;
163            }
164            catch (CompilationException e) {
165                messageCollector.report(CompilerMessageSeverity.EXCEPTION, MessageRenderer.PLAIN.renderException(e),
166                                        MessageUtil.psiElementToMessageLocation(e.getElement()));
167                return INTERNAL_ERROR;
168            }
169        }
170    
171    
172        /**
173         * Allow derived classes to add additional command line arguments
174         */
175        @NotNull
176        @Override
177        protected K2JVMCompilerArguments createArguments() {
178            return new K2JVMCompilerArguments();
179        }
180    
181        @NotNull
182        private static List<File> getClasspath(@NotNull KotlinPaths paths, @NotNull K2JVMCompilerArguments arguments) {
183            List<File> classpath = Lists.newArrayList();
184            if (!arguments.noJdk) {
185                classpath.addAll(PathUtil.getJdkClassesRoots());
186            }
187            if (!arguments.noStdlib) {
188                classpath.add(paths.getRuntimePath());
189            }
190            if (arguments.classpath != null) {
191                for (String element : Splitter.on(File.pathSeparatorChar).split(arguments.classpath)) {
192                    classpath.add(new File(element));
193                }
194            }
195            return classpath;
196        }
197    
198        @NotNull
199        private static List<File> getAnnotationsPath(@NotNull KotlinPaths paths, @NotNull K2JVMCompilerArguments arguments) {
200            List<File> annotationsPath = Lists.newArrayList();
201            if (!arguments.noJdkAnnotations) {
202                annotationsPath.add(paths.getJdkAnnotationsPath());
203            }
204            if (arguments.annotations != null) {
205                for (String element : Splitter.on(File.pathSeparatorChar).split(arguments.annotations)) {
206                    annotationsPath.add(new File(element));
207                }
208            }
209            return annotationsPath;
210        }
211    
212        @Override
213        protected void checkArguments(@NotNull K2JVMCompilerArguments argument) {
214            super.checkArguments(argument);
215    
216            if (!CompilerArgumentsUtil.checkOption(argument.inline)) {
217                throw new IllegalArgumentException(CompilerArgumentsUtil.getWrongCheckOptionErrorMessage("inline", argument.inline));
218            }
219    
220            if (!CompilerArgumentsUtil.checkOption(argument.optimize)) {
221                throw new IllegalArgumentException(CompilerArgumentsUtil.getWrongCheckOptionErrorMessage("optimize", argument.optimize));
222            }
223        }
224    
225    }