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