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