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.arguments.CompilerArgumentsUtil;
028    import org.jetbrains.jet.cli.common.arguments.K2JVMCompilerArguments;
029    import org.jetbrains.jet.cli.common.messages.*;
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.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.util.Arrays;
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.src == null &&
088                arguments.freeArgs.isEmpty() &&
089                !arguments.version
090            ) {
091                ReplFromTerminal.run(rootDisposable, configuration);
092                return ExitCode.OK;
093            }
094            else if (arguments.module != null) {
095            }
096            else if (arguments.script) {
097                configuration.add(CommonConfigurationKeys.SOURCE_ROOTS_KEY, arguments.freeArgs.get(0));
098            }
099            else {
100                if (arguments.src != null) {
101                    List<String> sourcePathsSplitByPathSeparator
102                            = Arrays.asList(arguments.src.split(StringUtil.escapeToRegexp(File.pathSeparator)));
103                    configuration.addAll(CommonConfigurationKeys.SOURCE_ROOTS_KEY, sourcePathsSplitByPathSeparator);
104                }
105                for (String freeArg : arguments.freeArgs) {
106                    configuration.add(CommonConfigurationKeys.SOURCE_ROOTS_KEY, freeArg);
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            configuration.put(JVMConfigurationKeys.ENABLE_INLINE,
117                              CompilerArgumentsUtil.optionToBooleanFlag(arguments.inline, InlineCodegenUtil.DEFAULT_INLINE_FLAG));
118    
119            configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector);
120    
121            messageCollector.report(CompilerMessageSeverity.LOGGING, "Configuring the compilation environment",
122                                    CompilerMessageLocation.NO_LOCATION);
123            try {
124                configureEnvironment(configuration, arguments);
125    
126                File jar = arguments.jar != null ? new File(arguments.jar) : null;
127                File outputDir = arguments.outputDir != null ? new File(arguments.outputDir) : null;
128    
129                if (arguments.module != null) {
130                    MessageCollector sanitizedCollector = new FilteringMessageCollector(messageCollector, in(CompilerMessageSeverity.VERBOSE));
131                    CompileEnvironmentUtil.ModuleScriptData moduleScript = CompileEnvironmentUtil.loadModuleDescriptions(
132                            paths, arguments.module, sanitizedCollector);
133                    if (moduleScript.getIncrementalCacheDir() != null) {
134                        configuration.put(JVMConfigurationKeys.INCREMENTAL_CACHE_BASE_DIR, new File(moduleScript.getIncrementalCacheDir()));
135                    }
136    
137                    if (outputDir != null) {
138                        messageCollector.report(CompilerMessageSeverity.WARNING, "The '-output' option is ignored because '-module' is specified",
139                                                CompilerMessageLocation.NO_LOCATION);
140                    }
141    
142                    File directory = new File(arguments.module).getAbsoluteFile().getParentFile();
143                    KotlinToJVMBytecodeCompiler.compileModules(configuration, moduleScript.getModules(),
144                                                                          directory, jar,
145                                                                          arguments.includeRuntime);
146                }
147                else if (arguments.script) {
148                    List<String> scriptArgs = arguments.freeArgs.subList(1, arguments.freeArgs.size());
149                    JetCoreEnvironment environment = JetCoreEnvironment.createForProduction(rootDisposable, configuration);
150                    KotlinToJVMBytecodeCompiler.compileAndExecuteScript(paths, environment, scriptArgs);
151                }
152                else {
153                    JetCoreEnvironment environment = JetCoreEnvironment.createForProduction(rootDisposable, configuration);
154                    KotlinToJVMBytecodeCompiler.compileBunchOfSources(environment, jar, outputDir, arguments.includeRuntime);
155                }
156                return OK;
157            }
158            catch (CompilationException e) {
159                messageCollector.report(CompilerMessageSeverity.EXCEPTION, MessageRenderer.PLAIN.renderException(e),
160                                        MessageUtil.psiElementToMessageLocation(e.getElement()));
161                return INTERNAL_ERROR;
162            }
163        }
164    
165    
166        /**
167         * Allow derived classes to add additional command line arguments
168         */
169        @NotNull
170        @Override
171        protected K2JVMCompilerArguments createArguments() {
172            return new K2JVMCompilerArguments();
173        }
174    
175        @NotNull
176        private static List<File> getClasspath(@NotNull KotlinPaths paths, @NotNull K2JVMCompilerArguments arguments) {
177            List<File> classpath = Lists.newArrayList();
178            if (!arguments.noJdk) {
179                classpath.addAll(PathUtil.getJdkClassesRoots());
180            }
181            if (!arguments.noStdlib) {
182                classpath.add(paths.getRuntimePath());
183            }
184            if (arguments.classpath != null) {
185                for (String element : Splitter.on(File.pathSeparatorChar).split(arguments.classpath)) {
186                    classpath.add(new File(element));
187                }
188            }
189            return classpath;
190        }
191    
192        @NotNull
193        private static List<File> getAnnotationsPath(@NotNull KotlinPaths paths, @NotNull K2JVMCompilerArguments arguments) {
194            List<File> annotationsPath = Lists.newArrayList();
195            if (!arguments.noJdkAnnotations) {
196                annotationsPath.add(paths.getJdkAnnotationsPath());
197            }
198            if (arguments.annotations != null) {
199                for (String element : Splitter.on(File.pathSeparatorChar).split(arguments.annotations)) {
200                    annotationsPath.add(new File(element));
201                }
202            }
203            return annotationsPath;
204        }
205    
206        @Override
207        protected void checkArguments(@NotNull K2JVMCompilerArguments argument) {
208            super.checkArguments(argument);
209    
210            if (!CompilerArgumentsUtil.checkOption(argument.inline)) {
211                throw new IllegalArgumentException(CompilerArgumentsUtil.getWrongInlineOptionErrorMessage(argument.inline));
212            }
213        }
214    
215    }