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.google.common.base.Predicates; 021 import com.intellij.openapi.Disposable; 022 import com.intellij.openapi.project.Project; 023 import com.intellij.openapi.util.io.FileUtil; 024 import com.intellij.openapi.vfs.VirtualFile; 025 import com.intellij.psi.PsiFile; 026 import com.intellij.util.Function; 027 import com.intellij.util.SmartList; 028 import com.intellij.util.containers.ContainerUtil; 029 import kotlin.Function0; 030 import kotlin.Function1; 031 import kotlin.Unit; 032 import org.jetbrains.annotations.NotNull; 033 import org.jetbrains.annotations.Nullable; 034 import org.jetbrains.kotlin.analyzer.AnalysisResult; 035 import org.jetbrains.kotlin.backend.common.output.OutputFileCollection; 036 import org.jetbrains.kotlin.cli.common.CLICompiler; 037 import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys; 038 import org.jetbrains.kotlin.cli.common.ExitCode; 039 import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments; 040 import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants; 041 import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport; 042 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation; 043 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity; 044 import org.jetbrains.kotlin.cli.common.messages.MessageCollector; 045 import org.jetbrains.kotlin.cli.common.output.outputUtils.OutputUtilsPackage; 046 import org.jetbrains.kotlin.cli.jvm.JVMConfigurationKeys; 047 import org.jetbrains.kotlin.cli.jvm.compiler.CompilerJarLocator; 048 import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles; 049 import org.jetbrains.kotlin.cli.jvm.compiler.JetCoreEnvironment; 050 import org.jetbrains.kotlin.config.CommonConfigurationKeys; 051 import org.jetbrains.kotlin.config.CompilerConfiguration; 052 import org.jetbrains.kotlin.config.Services; 053 import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS; 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 if (analyzeAndReportErrors(messageCollector, sourcesFiles, config)) { 134 return COMPILATION_ERROR; 135 } 136 137 File outputPrefixFile = null; 138 if (arguments.outputPrefix != null) { 139 outputPrefixFile = new File(arguments.outputPrefix); 140 if (!outputPrefixFile.exists()) { 141 messageCollector.report(CompilerMessageSeverity.ERROR, 142 "Output prefix file '" + arguments.outputPrefix + "' not found", 143 CompilerMessageLocation.NO_LOCATION); 144 return ExitCode.COMPILATION_ERROR; 145 } 146 } 147 148 File outputPostfixFile = null; 149 if (arguments.outputPostfix != null) { 150 outputPostfixFile = new File(arguments.outputPostfix); 151 if (!outputPostfixFile.exists()) { 152 messageCollector.report(CompilerMessageSeverity.ERROR, 153 "Output postfix file '" + arguments.outputPostfix + "' not found", 154 CompilerMessageLocation.NO_LOCATION); 155 return ExitCode.COMPILATION_ERROR; 156 } 157 } 158 159 MainCallParameters mainCallParameters = createMainCallParameters(arguments.main); 160 TranslationResult translationResult; 161 162 K2JSTranslator translator = new K2JSTranslator(config); 163 try { 164 //noinspection unchecked 165 translationResult = translator.translate(sourcesFiles, mainCallParameters); 166 } catch (Exception e) { 167 throw new RuntimeException(e); 168 } 169 170 AnalyzerWithCompilerReport.reportDiagnostics(translationResult.getDiagnostics(), messageCollector); 171 172 if (!(translationResult instanceof TranslationResult.Success)) return ExitCode.COMPILATION_ERROR; 173 174 TranslationResult.Success successResult = (TranslationResult.Success) translationResult; 175 OutputFileCollection outputFiles = successResult.getOutputFiles(outputFile, outputPrefixFile, outputPostfixFile); 176 177 if (outputFile.isDirectory()) { 178 messageCollector.report(CompilerMessageSeverity.ERROR, 179 "Cannot open output file '" + outputFile.getPath() + "': is a directory", 180 CompilerMessageLocation.NO_LOCATION); 181 return ExitCode.COMPILATION_ERROR; 182 } 183 184 File outputDir = outputFile.getParentFile(); 185 if (outputDir == null) { 186 outputDir = outputFile.getAbsoluteFile().getParentFile(); 187 } 188 OutputUtilsPackage.writeAll(outputFiles, outputDir, messageCollector); 189 190 return OK; 191 } 192 193 private static void reportCompiledSourcesList(@NotNull MessageCollector messageCollector, @NotNull List<JetFile> sourceFiles) { 194 Iterable<String> fileNames = ContainerUtil.map(sourceFiles, new Function<JetFile, String>() { 195 @Override 196 public String fun(@Nullable JetFile file) { 197 assert file != null; 198 VirtualFile virtualFile = file.getVirtualFile(); 199 if (virtualFile != null) { 200 return FileUtil.toSystemDependentName(virtualFile.getPath()); 201 } 202 return file.getName() + "(no virtual file)"; 203 } 204 }); 205 messageCollector.report(CompilerMessageSeverity.LOGGING, "Compiling source files: " + Joiner.on(", ").join(fileNames), 206 CompilerMessageLocation.NO_LOCATION); 207 } 208 209 private static boolean analyzeAndReportErrors(@NotNull MessageCollector messageCollector, 210 @NotNull final List<JetFile> sources, @NotNull final Config config) { 211 AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(messageCollector); 212 analyzerWithCompilerReport.analyzeAndReport(sources, new Function0<AnalysisResult>() { 213 @Override 214 public AnalysisResult invoke() { 215 return TopDownAnalyzerFacadeForJS.analyzeFiles(sources, Predicates.<PsiFile>alwaysTrue(), config); 216 } 217 }); 218 return analyzerWithCompilerReport.hasErrors(); 219 } 220 221 @NotNull 222 private static Config getConfig(@NotNull K2JSCompilerArguments arguments, @NotNull Project project) { 223 if (arguments.target != null) { 224 assert arguments.target == "v5" : "Unsupported ECMA version: " + arguments.target; 225 } 226 EcmaVersion ecmaVersion = EcmaVersion.defaultVersion(); 227 String moduleId = FileUtil.getNameWithoutExtension(new File(arguments.outputFile)); 228 boolean inlineEnabled = !arguments.noInline; 229 230 List<String> libraryFiles = new SmartList<String>(); 231 if (!arguments.noStdlib) { 232 libraryFiles.add(0, PathUtil.getKotlinPathsForCompiler().getJsStdLibJarPath().getAbsolutePath()); 233 } 234 235 if (arguments.libraryFiles != null) { 236 ContainerUtil.addAllNotNull(libraryFiles, arguments.libraryFiles); 237 } 238 239 return new LibrarySourcesConfig(project, moduleId, libraryFiles, ecmaVersion, arguments.sourceMap, inlineEnabled); 240 } 241 242 public static MainCallParameters createMainCallParameters(String main) { 243 if (K2JsArgumentConstants.NO_CALL.equals(main)) { 244 return MainCallParameters.noCall(); 245 } 246 else { 247 return MainCallParameters.mainWithoutArguments(); 248 } 249 } 250 }