001 /*
002 * Copyright 2010-2015 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.kotlin.cli.js;
018
019 import com.google.common.base.Joiner;
020 import com.intellij.openapi.Disposable;
021 import com.intellij.openapi.project.Project;
022 import com.intellij.openapi.util.io.FileUtil;
023 import com.intellij.openapi.vfs.VirtualFile;
024 import com.intellij.util.Function;
025 import com.intellij.util.SmartList;
026 import com.intellij.util.containers.ContainerUtil;
027 import kotlin.Unit;
028 import kotlin.jvm.functions.Function0;
029 import kotlin.jvm.functions.Function1;
030 import org.jetbrains.annotations.NotNull;
031 import org.jetbrains.annotations.Nullable;
032 import org.jetbrains.kotlin.analyzer.AnalysisResult;
033 import org.jetbrains.kotlin.backend.common.output.OutputFileCollection;
034 import org.jetbrains.kotlin.cli.common.CLICompiler;
035 import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys;
036 import org.jetbrains.kotlin.cli.common.ExitCode;
037 import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments;
038 import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants;
039 import org.jetbrains.kotlin.cli.common.messages.*;
040 import org.jetbrains.kotlin.cli.common.output.outputUtils.OutputUtilsPackage;
041 import org.jetbrains.kotlin.cli.jvm.compiler.CompilerJarLocator;
042 import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
043 import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
044 import org.jetbrains.kotlin.cli.jvm.config.JVMConfigurationKeys;
045 import org.jetbrains.kotlin.config.CompilerConfiguration;
046 import org.jetbrains.kotlin.config.Services;
047 import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus;
048 import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS;
049 import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult;
050 import org.jetbrains.kotlin.js.config.Config;
051 import org.jetbrains.kotlin.js.config.EcmaVersion;
052 import org.jetbrains.kotlin.js.config.LibrarySourcesConfig;
053 import org.jetbrains.kotlin.js.facade.K2JSTranslator;
054 import org.jetbrains.kotlin.js.facade.MainCallParameters;
055 import org.jetbrains.kotlin.js.facade.TranslationResult;
056 import org.jetbrains.kotlin.psi.JetFile;
057 import org.jetbrains.kotlin.utils.PathUtil;
058
059 import java.io.File;
060 import java.util.List;
061
062 import static org.jetbrains.kotlin.cli.common.ExitCode.COMPILATION_ERROR;
063 import static org.jetbrains.kotlin.cli.common.ExitCode.OK;
064 import static org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation.NO_LOCATION;
065 import static org.jetbrains.kotlin.config.ConfigPackage.addKotlinSourceRoots;
066
067 public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
068
069 public static void main(String... args) {
070 doMain(new K2JSCompiler(), args);
071 }
072
073 @NotNull
074 @Override
075 protected K2JSCompilerArguments createArguments() {
076 return new K2JSCompilerArguments();
077 }
078
079
080 @NotNull
081 @Override
082 protected ExitCode doExecute(
083 @NotNull K2JSCompilerArguments arguments,
084 @NotNull Services services,
085 @NotNull MessageCollector messageCollector,
086 @NotNull Disposable rootDisposable
087 ) {
088 final MessageSeverityCollector messageSeverityCollector = new MessageSeverityCollector(messageCollector);
089
090 if (arguments.freeArgs.isEmpty()) {
091 messageSeverityCollector.report(CompilerMessageSeverity.ERROR, "Specify at least one source file or directory", NO_LOCATION);
092 return ExitCode.COMPILATION_ERROR;
093 }
094
095 CompilerConfiguration configuration = new CompilerConfiguration();
096 configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageSeverityCollector);
097
098 CompilerJarLocator locator = services.get(CompilerJarLocator.class);
099 if (locator != null) {
100 configuration.put(JVMConfigurationKeys.COMPILER_JAR_LOCATOR, locator);
101 }
102
103 addKotlinSourceRoots(configuration, arguments.freeArgs);
104 KotlinCoreEnvironment environmentForJS =
105 KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JS_CONFIG_FILES);
106
107 Project project = environmentForJS.getProject();
108 List<JetFile> sourcesFiles = environmentForJS.getSourceFiles();
109
110 if (arguments.outputFile == null) {
111 messageSeverityCollector.report(CompilerMessageSeverity.ERROR, "Specify output file via -output", CompilerMessageLocation.NO_LOCATION);
112 return ExitCode.COMPILATION_ERROR;
113 }
114
115 if (messageSeverityCollector.anyReported(CompilerMessageSeverity.ERROR)) {
116 return ExitCode.COMPILATION_ERROR;
117 }
118
119 if (sourcesFiles.isEmpty()) {
120 messageSeverityCollector.report(CompilerMessageSeverity.ERROR, "No source files", CompilerMessageLocation.NO_LOCATION);
121 return COMPILATION_ERROR;
122 }
123
124 if (arguments.verbose) {
125 reportCompiledSourcesList(messageSeverityCollector, sourcesFiles);
126 }
127
128 File outputFile = new File(arguments.outputFile);
129
130 Config config = getConfig(arguments, project);
131 if (config.checkLibFilesAndReportErrors(new Function1<String, Unit>() {
132 @Override
133 public Unit invoke(String message) {
134 messageSeverityCollector.report(CompilerMessageSeverity.ERROR, message, CompilerMessageLocation.NO_LOCATION);
135 return Unit.INSTANCE$;
136 }
137 })) {
138 return COMPILATION_ERROR;
139 }
140
141 AnalyzerWithCompilerReport analyzerWithCompilerReport = analyzeAndReportErrors(messageSeverityCollector, sourcesFiles, config);
142 if (analyzerWithCompilerReport.hasErrors()) {
143 return COMPILATION_ERROR;
144 }
145
146 ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
147
148 AnalysisResult analysisResult = analyzerWithCompilerReport.getAnalysisResult();
149 assert analysisResult instanceof JsAnalysisResult : "analysisResult should be instance of JsAnalysisResult, but " + analysisResult;
150 JsAnalysisResult jsAnalysisResult = (JsAnalysisResult) analysisResult;
151
152 File outputPrefixFile = null;
153 if (arguments.outputPrefix != null) {
154 outputPrefixFile = new File(arguments.outputPrefix);
155 if (!outputPrefixFile.exists()) {
156 messageSeverityCollector.report(CompilerMessageSeverity.ERROR,
157 "Output prefix file '" + arguments.outputPrefix + "' not found",
158 CompilerMessageLocation.NO_LOCATION);
159 return ExitCode.COMPILATION_ERROR;
160 }
161 }
162
163 File outputPostfixFile = null;
164 if (arguments.outputPostfix != null) {
165 outputPostfixFile = new File(arguments.outputPostfix);
166 if (!outputPostfixFile.exists()) {
167 messageSeverityCollector.report(CompilerMessageSeverity.ERROR,
168 "Output postfix file '" + arguments.outputPostfix + "' not found",
169 CompilerMessageLocation.NO_LOCATION);
170 return ExitCode.COMPILATION_ERROR;
171 }
172 }
173
174 MainCallParameters mainCallParameters = createMainCallParameters(arguments.main);
175 TranslationResult translationResult;
176
177 K2JSTranslator translator = new K2JSTranslator(config);
178 try {
179 //noinspection unchecked
180 translationResult = translator.translate(sourcesFiles, mainCallParameters, jsAnalysisResult);
181 } catch (Exception e) {
182 throw new RuntimeException(e);
183 }
184
185 ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
186
187 AnalyzerWithCompilerReport.reportDiagnostics(translationResult.getDiagnostics(), messageSeverityCollector);
188
189 if (!(translationResult instanceof TranslationResult.Success)) return ExitCode.COMPILATION_ERROR;
190
191 TranslationResult.Success successResult = (TranslationResult.Success) translationResult;
192 OutputFileCollection outputFiles = successResult.getOutputFiles(outputFile, outputPrefixFile, outputPostfixFile);
193
194 if (outputFile.isDirectory()) {
195 messageSeverityCollector.report(CompilerMessageSeverity.ERROR,
196 "Cannot open output file '" + outputFile.getPath() + "': is a directory",
197 CompilerMessageLocation.NO_LOCATION);
198 return ExitCode.COMPILATION_ERROR;
199 }
200
201 File outputDir = outputFile.getParentFile();
202 if (outputDir == null) {
203 outputDir = outputFile.getAbsoluteFile().getParentFile();
204 }
205
206 ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
207
208 OutputUtilsPackage.writeAll(outputFiles, outputDir, messageSeverityCollector);
209
210 return OK;
211 }
212
213 private static void reportCompiledSourcesList(@NotNull MessageCollector messageCollector, @NotNull List<JetFile> sourceFiles) {
214 Iterable<String> fileNames = ContainerUtil.map(sourceFiles, new Function<JetFile, String>() {
215 @Override
216 public String fun(@Nullable JetFile file) {
217 assert file != null;
218 VirtualFile virtualFile = file.getVirtualFile();
219 if (virtualFile != null) {
220 return FileUtil.toSystemDependentName(virtualFile.getPath());
221 }
222 return file.getName() + "(no virtual file)";
223 }
224 });
225 messageCollector.report(CompilerMessageSeverity.LOGGING, "Compiling source files: " + Joiner.on(", ").join(fileNames),
226 CompilerMessageLocation.NO_LOCATION);
227 }
228
229 private static AnalyzerWithCompilerReport analyzeAndReportErrors(@NotNull MessageCollector messageCollector,
230 @NotNull final List<JetFile> sources, @NotNull final Config config) {
231 AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(messageCollector);
232 analyzerWithCompilerReport.analyzeAndReport(sources, new Function0<AnalysisResult>() {
233 @Override
234 public AnalysisResult invoke() {
235 return TopDownAnalyzerFacadeForJS.analyzeFiles(sources, config);
236 }
237 });
238 return analyzerWithCompilerReport;
239 }
240
241 @NotNull
242 private static Config getConfig(@NotNull K2JSCompilerArguments arguments, @NotNull Project project) {
243 if (arguments.target != null) {
244 assert arguments.target == "v5" : "Unsupported ECMA version: " + arguments.target;
245 }
246 EcmaVersion ecmaVersion = EcmaVersion.defaultVersion();
247 String moduleId = FileUtil.getNameWithoutExtension(new File(arguments.outputFile));
248 boolean inlineEnabled = !arguments.noInline;
249
250 List<String> libraryFiles = new SmartList<String>();
251 if (!arguments.noStdlib) {
252 libraryFiles.add(0, PathUtil.getKotlinPathsForCompiler().getJsStdLibJarPath().getAbsolutePath());
253 }
254
255 if (arguments.libraryFiles != null) {
256 ContainerUtil.addAllNotNull(libraryFiles, arguments.libraryFiles);
257 }
258
259 return new LibrarySourcesConfig.Builder(project, moduleId, libraryFiles)
260 .ecmaVersion(ecmaVersion)
261 .sourceMap(arguments.sourceMap)
262 .inlineEnabled(inlineEnabled)
263 .metaInfo(arguments.metaInfo)
264 .build();
265 }
266
267 public static MainCallParameters createMainCallParameters(String main) {
268 if (K2JsArgumentConstants.NO_CALL.equals(main)) {
269 return MainCallParameters.noCall();
270 }
271 else {
272 return MainCallParameters.mainWithoutArguments();
273 }
274 }
275 }