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