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