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 com.intellij.openapi.util.text.StringUtil;
023    import jet.modules.Module;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.jet.cli.common.CLICompiler;
026    import org.jetbrains.jet.cli.common.CLIConfigurationKeys;
027    import org.jetbrains.jet.cli.common.ExitCode;
028    import org.jetbrains.jet.cli.common.messages.*;
029    import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity;
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.BuiltinToJavaTypesMapping;
036    import org.jetbrains.jet.codegen.CompilationException;
037    import org.jetbrains.jet.config.CommonConfigurationKeys;
038    import org.jetbrains.jet.config.CompilerConfiguration;
039    import org.jetbrains.jet.lang.resolve.AnalyzerScriptParameter;
040    import org.jetbrains.jet.utils.KotlinPaths;
041    import org.jetbrains.jet.utils.KotlinPathsFromHomeDir;
042    import org.jetbrains.jet.utils.PathUtil;
043    
044    import java.io.File;
045    import java.io.PrintStream;
046    import java.util.Arrays;
047    import java.util.Collections;
048    import java.util.List;
049    
050    import static org.jetbrains.jet.cli.common.ExitCode.*;
051    
052    @SuppressWarnings("UseOfSystemOutOrSystemErr")
053    public class K2JVMCompiler extends CLICompiler<K2JVMCompilerArguments> {
054    
055        public static void main(String... args) {
056            doMain(new K2JVMCompiler(), args);
057        }
058    
059        @Override
060        @NotNull
061        protected ExitCode doExecute(K2JVMCompilerArguments arguments, MessageCollector messageCollector, Disposable rootDisposable) {
062            KotlinPaths paths = arguments.kotlinHome != null
063                                    ? new KotlinPathsFromHomeDir(new File(arguments.kotlinHome))
064                                    : PathUtil.getKotlinPathsForCompiler();
065    
066            messageCollector.report(CompilerMessageSeverity.LOGGING,
067                                    "Using Kotlin home directory " + paths.getHomePath(), CompilerMessageLocation.NO_LOCATION);
068    
069            CompilerConfiguration configuration = new CompilerConfiguration();
070    
071            try {
072                configuration.addAll(JVMConfigurationKeys.CLASSPATH_KEY, getClasspath(paths, arguments));
073                configuration.addAll(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY, getAnnotationsPath(paths, arguments));
074            }
075            catch (Throwable t) {
076                MessageCollectorUtil.reportException(messageCollector, t);
077                return INTERNAL_ERROR;
078            }
079    
080            List<String> argumentsSourceDirs = arguments.getSourceDirs();
081            if (!arguments.script &&
082                arguments.module == null &&
083                arguments.src == null &&
084                arguments.freeArgs.isEmpty() &&
085                (argumentsSourceDirs == null || argumentsSourceDirs.size() == 0)) {
086    
087                ReplFromTerminal.run(rootDisposable, configuration);
088                return ExitCode.OK;
089            }
090            else if (arguments.module != null) {
091            }
092            else if (arguments.script) {
093                configuration.add(CommonConfigurationKeys.SOURCE_ROOTS_KEY, arguments.freeArgs.get(0));
094            }
095            else {
096                // TODO ideally we'd unify to just having a single field that supports multiple files/dirs
097                if (arguments.getSourceDirs() != null) {
098                    for (String source : arguments.getSourceDirs()) {
099                        configuration.add(CommonConfigurationKeys.SOURCE_ROOTS_KEY, source);
100                    }
101                }
102                else {
103                    if (arguments.src != null) {
104                        List<String> sourcePathsSplitByPathSeparator
105                                = Arrays.asList(arguments.src.split(StringUtil.escapeToRegexp(File.pathSeparator)));
106                        configuration.addAll(CommonConfigurationKeys.SOURCE_ROOTS_KEY, sourcePathsSplitByPathSeparator);
107                    }
108                    for (String freeArg : arguments.freeArgs) {
109                        configuration.add(CommonConfigurationKeys.SOURCE_ROOTS_KEY, freeArg);
110                    }
111                }
112            }
113    
114            boolean builtins = arguments.builtins;
115    
116            configuration.put(JVMConfigurationKeys.SCRIPT_PARAMETERS, arguments.script
117                                                                              ? CommandLineScriptUtils.scriptParameters()
118                                                                              : Collections.<AnalyzerScriptParameter>emptyList());
119            configuration.put(JVMConfigurationKeys.STUBS, builtins);
120            configuration.put(JVMConfigurationKeys.BUILTIN_TO_JAVA_TYPES_MAPPING_KEY,
121                                      builtins ? BuiltinToJavaTypesMapping.DISABLED : BuiltinToJavaTypesMapping.ENABLED);
122    
123            configuration.put(JVMConfigurationKeys.GENERATE_NOT_NULL_ASSERTIONS, arguments.notNullAssertions);
124            configuration.put(JVMConfigurationKeys.GENERATE_NOT_NULL_PARAMETER_ASSERTIONS, arguments.notNullParamAssertions);
125    
126            configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector);
127    
128            messageCollector.report(CompilerMessageSeverity.LOGGING, "Configuring the compilation environment",
129                                    CompilerMessageLocation.NO_LOCATION);
130            try {
131                configureEnvironment(configuration, arguments);
132    
133                File jar = arguments.jar != null ? new File(arguments.jar) : null;
134                File outputDir = arguments.outputDir != null ? new File(arguments.outputDir) : null;
135    
136                if (arguments.module != null) {
137                    MessageCollector sanitizedCollector = new FilteringMessageCollector(messageCollector);
138                    List<Module> modules = CompileEnvironmentUtil.loadModuleDescriptions(paths, arguments.module, sanitizedCollector);
139    
140                    File directory = new File(arguments.module).getAbsoluteFile().getParentFile();
141                    KotlinToJVMBytecodeCompiler.compileModules(configuration, modules,
142                                                                          directory, jar, outputDir,
143                                                                          arguments.includeRuntime);
144                }
145                else if (arguments.script) {
146                    List<String> scriptArgs = arguments.freeArgs.subList(1, arguments.freeArgs.size());
147                    JetCoreEnvironment environment = new JetCoreEnvironment(rootDisposable, configuration);
148                    KotlinToJVMBytecodeCompiler.compileAndExecuteScript(paths, environment, scriptArgs);
149                }
150                else {
151                    JetCoreEnvironment environment = new JetCoreEnvironment(rootDisposable, configuration);
152                    KotlinToJVMBytecodeCompiler.compileBunchOfSources(environment, jar, outputDir, arguments.includeRuntime);
153                }
154                return OK;
155            }
156            catch (CompilationException e) {
157                messageCollector.report(CompilerMessageSeverity.EXCEPTION, MessageRenderer.PLAIN.renderException(e),
158                                        MessageUtil.psiElementToMessageLocation(e.getElement()));
159                return INTERNAL_ERROR;
160            }
161        }
162    
163    
164        /**
165         * Allow derived classes to add additional command line arguments
166         */
167        @NotNull
168        @Override
169        protected K2JVMCompilerArguments createArguments() {
170            return new K2JVMCompilerArguments();
171        }
172    
173        // TODO this method is here only to workaround KT-2498
174        @Override
175        protected void configureEnvironment(@NotNull CompilerConfiguration configuration, @NotNull K2JVMCompilerArguments arguments) {
176            super.configureEnvironment(configuration, arguments);
177        }
178    
179        //TODO: Hacked! Be sure that our kotlin stuff builds correctly before you remove.
180        // our compiler throws method not found error
181        // probably relates to KT-1863... well, may be not
182        @NotNull
183        @Override
184        public ExitCode exec(@NotNull PrintStream errStream, @NotNull K2JVMCompilerArguments arguments) {
185            return super.exec(errStream, arguments);
186        }
187    
188        @NotNull
189        private static List<File> getClasspath(@NotNull KotlinPaths paths, @NotNull K2JVMCompilerArguments arguments) {
190            List<File> classpath = Lists.newArrayList();
191            if (!arguments.noJdk) {
192                classpath.add(PathUtil.findRtJar());
193            }
194            if (!arguments.noStdlib) {
195                classpath.add(paths.getRuntimePath());
196            }
197            if (arguments.classpath != null) {
198                for (String element : Splitter.on(File.pathSeparatorChar).split(arguments.classpath)) {
199                    classpath.add(new File(element));
200                }
201            }
202            return classpath;
203        }
204    
205        @NotNull
206        private static List<File> getAnnotationsPath(@NotNull KotlinPaths paths, @NotNull K2JVMCompilerArguments arguments) {
207            List<File> annotationsPath = Lists.newArrayList();
208            if (!arguments.noJdkAnnotations) {
209                annotationsPath.add(paths.getJdkAnnotationsPath());
210            }
211            if (arguments.annotations != null) {
212                for (String element : Splitter.on(File.pathSeparatorChar).split(arguments.annotations)) {
213                    annotationsPath.add(new File(element));
214                }
215            }
216            return annotationsPath;
217        }
218    
219        private static class FilteringMessageCollector implements MessageCollector {
220            private final MessageCollector messageCollector;
221    
222            public FilteringMessageCollector(@NotNull MessageCollector messageCollector) {
223                this.messageCollector = messageCollector;
224            }
225    
226            @Override
227            public void report(
228                    @NotNull CompilerMessageSeverity severity, @NotNull String message, @NotNull CompilerMessageLocation location
229            ) {
230                if (!CompilerMessageSeverity.VERBOSE.contains(severity)) {
231                    messageCollector.report(severity, message, location);
232                }
233            }
234        }
235    }