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 }