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