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 }