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 }