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