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