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
017package org.jetbrains.jet.cli.js;
018
019import com.google.common.base.Function;
020import com.google.common.base.Joiner;
021import com.google.common.base.Predicates;
022import com.google.common.collect.Iterables;
023import com.intellij.openapi.Disposable;
024import com.intellij.openapi.project.Project;
025import com.intellij.openapi.util.io.FileUtil;
026import com.intellij.openapi.vfs.VirtualFile;
027import com.intellij.psi.PsiFile;
028import jet.Function0;
029import org.jetbrains.annotations.NotNull;
030import org.jetbrains.annotations.Nullable;
031import org.jetbrains.jet.analyzer.AnalyzeExhaust;
032import org.jetbrains.jet.cli.common.CLICompiler;
033import org.jetbrains.jet.cli.common.ExitCode;
034import org.jetbrains.jet.cli.common.messages.AnalyzerWithCompilerReport;
035import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation;
036import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity;
037import org.jetbrains.jet.cli.common.messages.MessageCollector;
038import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment;
039import org.jetbrains.jet.config.CommonConfigurationKeys;
040import org.jetbrains.jet.config.CompilerConfiguration;
041import org.jetbrains.jet.lang.psi.JetFile;
042import org.jetbrains.k2js.analyze.AnalyzerFacadeForJS;
043import org.jetbrains.k2js.config.*;
044import org.jetbrains.k2js.facade.K2JSTranslator;
045import org.jetbrains.k2js.facade.MainCallParameters;
046
047import java.io.File;
048import java.util.Arrays;
049import java.util.List;
050
051import static org.jetbrains.jet.cli.common.ExitCode.COMPILATION_ERROR;
052import static org.jetbrains.jet.cli.common.ExitCode.OK;
053import static org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.NO_LOCATION;
054
055public 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}