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