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