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.K2JVMCompilerArguments;
028    import org.jetbrains.jet.cli.common.messages.*;
029    import org.jetbrains.jet.cli.jvm.compiler.*;
030    import org.jetbrains.jet.cli.jvm.repl.ReplFromTerminal;
031    import org.jetbrains.jet.codegen.CompilationException;
032    import org.jetbrains.jet.config.CommonConfigurationKeys;
033    import org.jetbrains.jet.config.CompilerConfiguration;
034    import org.jetbrains.jet.lang.resolve.AnalyzerScriptParameter;
035    import org.jetbrains.jet.lang.types.lang.InlineUtil;
036    import org.jetbrains.jet.utils.KotlinPaths;
037    import org.jetbrains.jet.utils.KotlinPathsFromHomeDir;
038    import org.jetbrains.jet.utils.PathUtil;
039    
040    import java.io.File;
041    import java.util.Arrays;
042    import java.util.Collections;
043    import java.util.List;
044    
045    import static com.google.common.base.Predicates.in;
046    import static org.jetbrains.jet.cli.common.ExitCode.INTERNAL_ERROR;
047    import static org.jetbrains.jet.cli.common.ExitCode.OK;
048    
049    @SuppressWarnings("UseOfSystemOutOrSystemErr")
050    public class K2JVMCompiler extends CLICompiler<K2JVMCompilerArguments> {
051    
052        public static void main(String... args) {
053            doMain(new K2JVMCompiler(), args);
054        }
055    
056        @Override
057        @NotNull
058        protected ExitCode doExecute(
059                @NotNull K2JVMCompilerArguments arguments,
060                @NotNull MessageCollector messageCollector,
061                @NotNull Disposable rootDisposable
062        ) {
063            KotlinPaths paths = arguments.kotlinHome != null
064                                    ? new KotlinPathsFromHomeDir(new File(arguments.kotlinHome))
065                                    : PathUtil.getKotlinPathsForCompiler();
066    
067            messageCollector.report(CompilerMessageSeverity.LOGGING,
068                                    "Using Kotlin home directory " + paths.getHomePath(), CompilerMessageLocation.NO_LOCATION);
069    
070            CompilerConfiguration configuration = new CompilerConfiguration();
071    
072            try {
073                configuration.addAll(JVMConfigurationKeys.CLASSPATH_KEY, getClasspath(paths, arguments));
074                configuration.addAll(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY, getAnnotationsPath(paths, arguments));
075            }
076            catch (Throwable t) {
077                MessageCollectorUtil.reportException(messageCollector, t);
078                return INTERNAL_ERROR;
079            }
080    
081            if (!arguments.script &&
082                arguments.module == null &&
083                arguments.src == null &&
084                arguments.freeArgs.isEmpty()
085            ) {
086                ReplFromTerminal.run(rootDisposable, configuration);
087                return ExitCode.OK;
088            }
089            else if (arguments.module != null) {
090            }
091            else if (arguments.script) {
092                configuration.add(CommonConfigurationKeys.SOURCE_ROOTS_KEY, arguments.freeArgs.get(0));
093            }
094            else {
095                if (arguments.src != null) {
096                    List<String> sourcePathsSplitByPathSeparator
097                            = Arrays.asList(arguments.src.split(StringUtil.escapeToRegexp(File.pathSeparator)));
098                    configuration.addAll(CommonConfigurationKeys.SOURCE_ROOTS_KEY, sourcePathsSplitByPathSeparator);
099                }
100                for (String freeArg : arguments.freeArgs) {
101                    configuration.add(CommonConfigurationKeys.SOURCE_ROOTS_KEY, freeArg);
102                }
103            }
104    
105            configuration.put(JVMConfigurationKeys.SCRIPT_PARAMETERS, arguments.script
106                                                                              ? CommandLineScriptUtils.scriptParameters()
107                                                                              : Collections.<AnalyzerScriptParameter>emptyList());
108    
109            configuration.put(JVMConfigurationKeys.GENERATE_NOT_NULL_ASSERTIONS, arguments.notNullAssertions);
110            configuration.put(JVMConfigurationKeys.GENERATE_NOT_NULL_PARAMETER_ASSERTIONS, arguments.notNullParamAssertions);
111            configuration.put(JVMConfigurationKeys.ENABLE_INLINE, InlineUtil.optionToInlineFlag(arguments.enableInline));
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                File jar = arguments.jar != null ? new File(arguments.jar) : null;
121                File outputDir = arguments.outputDir != null ? new File(arguments.outputDir) : null;
122    
123                if (arguments.module != null) {
124                    MessageCollector sanitizedCollector = new FilteringMessageCollector(messageCollector, in(CompilerMessageSeverity.VERBOSE));
125                    ModuleChunk modules = CompileEnvironmentUtil.loadModuleDescriptions(paths, arguments.module, sanitizedCollector);
126    
127                    if (outputDir != null) {
128                        messageCollector.report(CompilerMessageSeverity.WARNING, "The '-output' option is ignored because '-module' is specified",
129                                                CompilerMessageLocation.NO_LOCATION);
130                    }
131    
132                    File directory = new File(arguments.module).getAbsoluteFile().getParentFile();
133                    KotlinToJVMBytecodeCompiler.compileModules(configuration, modules,
134                                                                          directory, jar,
135                                                                          arguments.includeRuntime);
136                }
137                else if (arguments.script) {
138                    List<String> scriptArgs = arguments.freeArgs.subList(1, arguments.freeArgs.size());
139                    JetCoreEnvironment environment = JetCoreEnvironment.createForProduction(rootDisposable, configuration);
140                    KotlinToJVMBytecodeCompiler.compileAndExecuteScript(paths, environment, scriptArgs);
141                }
142                else {
143                    JetCoreEnvironment environment = JetCoreEnvironment.createForProduction(rootDisposable, configuration);
144                    KotlinToJVMBytecodeCompiler.compileBunchOfSources(environment, jar, outputDir, arguments.includeRuntime);
145                }
146                return OK;
147            }
148            catch (CompilationException e) {
149                messageCollector.report(CompilerMessageSeverity.EXCEPTION, MessageRenderer.PLAIN.renderException(e),
150                                        MessageUtil.psiElementToMessageLocation(e.getElement()));
151                return INTERNAL_ERROR;
152            }
153        }
154    
155    
156        /**
157         * Allow derived classes to add additional command line arguments
158         */
159        @NotNull
160        @Override
161        protected K2JVMCompilerArguments createArguments() {
162            return new K2JVMCompilerArguments();
163        }
164    
165        @NotNull
166        private static List<File> getClasspath(@NotNull KotlinPaths paths, @NotNull K2JVMCompilerArguments arguments) {
167            List<File> classpath = Lists.newArrayList();
168            if (!arguments.noJdk) {
169                classpath.addAll(PathUtil.getJdkClassesRoots());
170            }
171            if (!arguments.noStdlib) {
172                classpath.add(paths.getRuntimePath());
173            }
174            if (arguments.classpath != null) {
175                for (String element : Splitter.on(File.pathSeparatorChar).split(arguments.classpath)) {
176                    classpath.add(new File(element));
177                }
178            }
179            return classpath;
180        }
181    
182        @NotNull
183        private static List<File> getAnnotationsPath(@NotNull KotlinPaths paths, @NotNull K2JVMCompilerArguments arguments) {
184            List<File> annotationsPath = Lists.newArrayList();
185            if (!arguments.noJdkAnnotations) {
186                annotationsPath.add(paths.getJdkAnnotationsPath());
187            }
188            if (arguments.annotations != null) {
189                for (String element : Splitter.on(File.pathSeparatorChar).split(arguments.annotations)) {
190                    annotationsPath.add(new File(element));
191                }
192            }
193            return annotationsPath;
194        }
195    
196        @Override
197        protected void checkArguments(@NotNull K2JVMCompilerArguments argument) {
198            super.checkArguments(argument);
199    
200            String inline = argument.enableInline;
201            if (inline != null) {
202                if (!"on".equalsIgnoreCase(inline) && !"off".equalsIgnoreCase(inline)) {
203                    throw new IllegalArgumentException("Wrong value for inline option: '" + inline + "'. Should be 'on' or 'off'");
204                }
205            }
206        }
207    }