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
017package org.jetbrains.jet.cli.jvm;
018
019import com.google.common.base.Splitter;
020import com.google.common.collect.Lists;
021import com.intellij.openapi.Disposable;
022import com.intellij.openapi.util.text.StringUtil;
023import jet.modules.Module;
024import org.jetbrains.annotations.NotNull;
025import org.jetbrains.jet.cli.common.CLICompiler;
026import org.jetbrains.jet.cli.common.CLIConfigurationKeys;
027import org.jetbrains.jet.cli.common.ExitCode;
028import org.jetbrains.jet.cli.common.messages.*;
029import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity;
030import org.jetbrains.jet.cli.jvm.compiler.CommandLineScriptUtils;
031import org.jetbrains.jet.cli.jvm.compiler.CompileEnvironmentUtil;
032import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment;
033import org.jetbrains.jet.cli.jvm.compiler.KotlinToJVMBytecodeCompiler;
034import org.jetbrains.jet.cli.jvm.repl.ReplFromTerminal;
035import org.jetbrains.jet.codegen.BuiltinToJavaTypesMapping;
036import org.jetbrains.jet.codegen.CompilationException;
037import org.jetbrains.jet.config.CommonConfigurationKeys;
038import org.jetbrains.jet.config.CompilerConfiguration;
039import org.jetbrains.jet.lang.resolve.AnalyzerScriptParameter;
040import org.jetbrains.jet.utils.KotlinPaths;
041import org.jetbrains.jet.utils.KotlinPathsFromHomeDir;
042import org.jetbrains.jet.utils.PathUtil;
043
044import java.io.File;
045import java.io.PrintStream;
046import java.util.Arrays;
047import java.util.Collections;
048import java.util.List;
049
050import static org.jetbrains.jet.cli.common.ExitCode.*;
051
052@SuppressWarnings("UseOfSystemOutOrSystemErr")
053public 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}