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.js;
018    
019    import com.google.common.base.Function;
020    import com.google.common.base.Joiner;
021    import com.google.common.base.Predicates;
022    import com.google.common.collect.Iterables;
023    import com.intellij.openapi.Disposable;
024    import com.intellij.openapi.project.Project;
025    import com.intellij.openapi.util.io.FileUtil;
026    import com.intellij.openapi.vfs.VirtualFile;
027    import com.intellij.psi.PsiFile;
028    import jet.Function0;
029    import org.jetbrains.annotations.NotNull;
030    import org.jetbrains.annotations.Nullable;
031    import org.jetbrains.jet.analyzer.AnalyzeExhaust;
032    import org.jetbrains.jet.cli.common.CLICompiler;
033    import org.jetbrains.jet.cli.common.ExitCode;
034    import org.jetbrains.jet.cli.common.arguments.K2JSCompilerArguments;
035    import org.jetbrains.jet.cli.common.arguments.K2JsArgumentConstants;
036    import org.jetbrains.jet.cli.common.messages.AnalyzerWithCompilerReport;
037    import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation;
038    import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity;
039    import org.jetbrains.jet.cli.common.messages.MessageCollector;
040    import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment;
041    import org.jetbrains.jet.config.CommonConfigurationKeys;
042    import org.jetbrains.jet.config.CompilerConfiguration;
043    import org.jetbrains.jet.lang.psi.JetFile;
044    import org.jetbrains.k2js.analyze.AnalyzerFacadeForJS;
045    import org.jetbrains.k2js.config.*;
046    import org.jetbrains.k2js.facade.K2JSTranslator;
047    import org.jetbrains.k2js.facade.MainCallParameters;
048    
049    import java.io.File;
050    import java.util.Arrays;
051    import java.util.List;
052    
053    import static org.jetbrains.jet.cli.common.ExitCode.COMPILATION_ERROR;
054    import static org.jetbrains.jet.cli.common.ExitCode.OK;
055    import static org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.NO_LOCATION;
056    
057    public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
058    
059        public static void main(String... args) {
060            doMain(new K2JSCompiler(), args);
061        }
062    
063        @NotNull
064        @Override
065        protected K2JSCompilerArguments createArguments() {
066            return new K2JSCompilerArguments();
067        }
068    
069    
070        @NotNull
071        @Override
072        protected ExitCode doExecute(
073                @NotNull K2JSCompilerArguments arguments,
074                @NotNull MessageCollector messageCollector,
075                @NotNull Disposable rootDisposable
076        ) {
077            if (arguments.sourceFiles == null) {
078                messageCollector.report(CompilerMessageSeverity.ERROR, "Specify sources location via -sourceFiles", NO_LOCATION);
079                return ExitCode.INTERNAL_ERROR;
080            }
081    
082            CompilerConfiguration configuration = new CompilerConfiguration();
083            configuration.addAll(CommonConfigurationKeys.SOURCE_ROOTS_KEY, Arrays.asList(arguments.sourceFiles));
084            JetCoreEnvironment environmentForJS = JetCoreEnvironment.createForProduction(rootDisposable, configuration);
085    
086            Project project = environmentForJS.getProject();
087    
088            ClassPathLibrarySourcesLoader sourceLoader = new ClassPathLibrarySourcesLoader(project);
089            List<JetFile> sourceFiles = sourceLoader.findSourceFiles();
090            environmentForJS.getSourceFiles().addAll(sourceFiles);
091    
092            if (arguments.verbose) {
093                reportCompiledSourcesList(messageCollector, environmentForJS);
094            }
095    
096            Config config = getConfig(arguments, project);
097            if (analyzeAndReportErrors(messageCollector, environmentForJS.getSourceFiles(), config)) {
098                return COMPILATION_ERROR;
099            }
100    
101            String outputFile = arguments.outputFile;
102            if (outputFile == null) {
103                messageCollector.report(CompilerMessageSeverity.ERROR, "Specify output file via -output", CompilerMessageLocation.NO_LOCATION);
104                return ExitCode.INTERNAL_ERROR;
105            }
106    
107            MainCallParameters mainCallParameters = createMainCallParameters(arguments.main);
108            return translateAndGenerateOutputFile(mainCallParameters, environmentForJS, config, outputFile);
109        }
110    
111        private static void reportCompiledSourcesList(@NotNull MessageCollector messageCollector,
112                @NotNull JetCoreEnvironment environmentForJS) {
113            List<JetFile> files = environmentForJS.getSourceFiles();
114            Iterable<String> fileNames = Iterables.transform(files, new Function<JetFile, String>() {
115                @Override
116                public String apply(@Nullable JetFile file) {
117                    assert file != null;
118                    VirtualFile virtualFile = file.getVirtualFile();
119                    if (virtualFile != null) {
120                        return FileUtil.toSystemIndependentName(virtualFile.getPath());
121                    }
122                    return file.getName() + "(no virtual file)";
123                }
124            });
125            messageCollector.report(CompilerMessageSeverity.LOGGING, "Compiling source files: " + Joiner.on(", ").join(fileNames),
126                                    CompilerMessageLocation.NO_LOCATION);
127        }
128    
129        @NotNull
130        private static ExitCode translateAndGenerateOutputFile(
131                @NotNull MainCallParameters mainCall,
132                @NotNull JetCoreEnvironment environmentForJS,
133                @NotNull Config config,
134                @NotNull String outputFile
135        ) {
136            try {
137                K2JSTranslator.translateWithMainCallParametersAndSaveToFile(mainCall, environmentForJS.getSourceFiles(), outputFile, config);
138            }
139            catch (Exception e) {
140                throw new RuntimeException(e);
141            }
142            return OK;
143        }
144    
145        private static boolean analyzeAndReportErrors(@NotNull MessageCollector messageCollector,
146                @NotNull final List<JetFile> sources, @NotNull final Config config) {
147            AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(messageCollector);
148            analyzerWithCompilerReport.analyzeAndReport(new Function0<AnalyzeExhaust>() {
149                @Override
150                public AnalyzeExhaust invoke() {
151                    return AnalyzerFacadeForJS.analyzeFiles(sources, Predicates.<PsiFile>alwaysTrue(), config);
152                }
153            }, sources);
154            return analyzerWithCompilerReport.hasErrors();
155        }
156    
157        @NotNull
158        private static Config getConfig(@NotNull K2JSCompilerArguments arguments, @NotNull Project project) {
159            if (arguments.target != null) {
160                assert arguments.target == "v5" : "Unsupported ECMA version: " + arguments.target;
161            }
162            EcmaVersion ecmaVersion = EcmaVersion.defaultVersion();
163            String moduleId = FileUtil.getNameWithoutExtension(new File(arguments.outputFile));
164            if (arguments.libraryFiles != null) {
165                return new LibrarySourcesConfig(project, moduleId, Arrays.asList(arguments.libraryFiles), ecmaVersion, arguments.sourcemap);
166            }
167            else {
168                // lets discover the JS library definitions on the classpath
169                return new ClassPathLibraryDefintionsConfig(project, moduleId, ecmaVersion, arguments.sourcemap);
170            }
171        }
172    
173        public static MainCallParameters createMainCallParameters(String main) {
174            if (K2JsArgumentConstants.NO_CALL.equals(main)) {
175                return MainCallParameters.noCall();
176            }
177            else {
178                return MainCallParameters.mainWithoutArguments();
179            }
180        }
181    }