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 .build(); 268 } 269 270 public static MainCallParameters createMainCallParameters(String main) { 271 if (K2JsArgumentConstants.NO_CALL.equals(main)) { 272 return MainCallParameters.noCall(); 273 } 274 else { 275 return MainCallParameters.mainWithoutArguments(); 276 } 277 } 278 }