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.Function; 027 import com.intellij.util.containers.ContainerUtil; 028 import jet.Function0; 029 import org.jetbrains.annotations.NotNull; 030 import org.jetbrains.annotations.Nullable; 031 import org.jetbrains.jet.OutputFileCollection; 032 import org.jetbrains.jet.analyzer.AnalyzeExhaust; 033 import org.jetbrains.jet.cli.common.CLICompiler; 034 import org.jetbrains.jet.cli.common.ExitCode; 035 import org.jetbrains.jet.cli.common.arguments.K2JSCompilerArguments; 036 import org.jetbrains.jet.cli.common.arguments.K2JsArgumentConstants; 037 import org.jetbrains.jet.cli.common.messages.AnalyzerWithCompilerReport; 038 import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation; 039 import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity; 040 import org.jetbrains.jet.cli.common.messages.MessageCollector; 041 import org.jetbrains.jet.cli.common.output.OutputDirector; 042 import org.jetbrains.jet.cli.common.output.SingleDirectoryDirector; 043 import org.jetbrains.jet.cli.common.output.outputUtils.OutputUtilsPackage; 044 import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; 045 import org.jetbrains.jet.config.CommonConfigurationKeys; 046 import org.jetbrains.jet.config.CompilerConfiguration; 047 import org.jetbrains.jet.lang.psi.JetFile; 048 import org.jetbrains.k2js.analyze.AnalyzerFacadeForJS; 049 import org.jetbrains.k2js.config.*; 050 import org.jetbrains.k2js.facade.K2JSTranslator; 051 import org.jetbrains.k2js.facade.MainCallParameters; 052 053 import java.io.File; 054 import java.util.Arrays; 055 import java.util.List; 056 057 import static org.jetbrains.jet.cli.common.ExitCode.COMPILATION_ERROR; 058 import static org.jetbrains.jet.cli.common.ExitCode.OK; 059 import static org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.NO_LOCATION; 060 061 public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> { 062 063 public static void main(String... args) { 064 doMain(new K2JSCompiler(), args); 065 } 066 067 @NotNull 068 @Override 069 protected K2JSCompilerArguments createArguments() { 070 return new K2JSCompilerArguments(); 071 } 072 073 074 @NotNull 075 @Override 076 protected ExitCode doExecute( 077 @NotNull K2JSCompilerArguments arguments, 078 @NotNull MessageCollector messageCollector, 079 @NotNull Disposable rootDisposable 080 ) { 081 if (arguments.sourceFiles == null) { 082 messageCollector.report(CompilerMessageSeverity.ERROR, "Specify sources location via -sourceFiles", NO_LOCATION); 083 return ExitCode.INTERNAL_ERROR; 084 } 085 086 CompilerConfiguration configuration = new CompilerConfiguration(); 087 configuration.addAll(CommonConfigurationKeys.SOURCE_ROOTS_KEY, Arrays.asList(arguments.sourceFiles)); 088 JetCoreEnvironment environmentForJS = JetCoreEnvironment.createForProduction(rootDisposable, configuration); 089 090 Project project = environmentForJS.getProject(); 091 List<JetFile> sourcesFiles = environmentForJS.getSourceFiles(); 092 093 ClassPathLibrarySourcesLoader sourceLoader = new ClassPathLibrarySourcesLoader(project); 094 List<JetFile> additionalSourceFiles = sourceLoader.findSourceFiles(); 095 sourcesFiles.addAll(additionalSourceFiles); 096 097 if (arguments.verbose) { 098 reportCompiledSourcesList(messageCollector, sourcesFiles); 099 } 100 101 if (arguments.outputFile == null) { 102 messageCollector.report(CompilerMessageSeverity.ERROR, "Specify output file via -output", CompilerMessageLocation.NO_LOCATION); 103 return ExitCode.INTERNAL_ERROR; 104 } 105 106 File outputFile = new File(arguments.outputFile); 107 108 Config config = getConfig(arguments, project); 109 if (analyzeAndReportErrors(messageCollector, sourcesFiles, config)) { 110 return COMPILATION_ERROR; 111 } 112 113 File outputPrefixFile = null; 114 if (arguments.outputPrefix != null) { 115 outputPrefixFile = new File(arguments.outputPrefix); 116 if (!outputPrefixFile.exists()) { 117 messageCollector.report(CompilerMessageSeverity.ERROR, 118 "Output prefix file '" + arguments.outputPrefix + "' not found", 119 CompilerMessageLocation.NO_LOCATION); 120 return ExitCode.COMPILATION_ERROR; 121 } 122 } 123 124 File outputPostfixFile = null; 125 if (arguments.outputPostfix != null) { 126 outputPostfixFile = new File(arguments.outputPostfix); 127 if (!outputPostfixFile.exists()) { 128 messageCollector.report(CompilerMessageSeverity.ERROR, 129 "Output postfix file '" + arguments.outputPostfix + "' not found", 130 CompilerMessageLocation.NO_LOCATION); 131 return ExitCode.COMPILATION_ERROR; 132 } 133 } 134 135 MainCallParameters mainCallParameters = createMainCallParameters(arguments.main); 136 137 OutputFileCollection outputFiles = translate(mainCallParameters, config, sourcesFiles, outputFile, outputPrefixFile, outputPostfixFile); 138 139 OutputDirector outputDirector = new SingleDirectoryDirector(outputFile.getParentFile()); 140 OutputUtilsPackage.writeAll(outputFiles, outputDirector, messageCollector); 141 142 return OK; 143 } 144 145 private static void reportCompiledSourcesList(@NotNull MessageCollector messageCollector, @NotNull List<JetFile> sourceFiles) { 146 Iterable<String> fileNames = ContainerUtil.map(sourceFiles, new Function<JetFile, String>() { 147 @Override 148 public String fun(@Nullable JetFile file) { 149 assert file != null; 150 VirtualFile virtualFile = file.getVirtualFile(); 151 if (virtualFile != null) { 152 return FileUtil.toSystemIndependentName(virtualFile.getPath()); 153 } 154 return file.getName() + "(no virtual file)"; 155 } 156 }); 157 messageCollector.report(CompilerMessageSeverity.LOGGING, "Compiling source files: " + Joiner.on(", ").join(fileNames), 158 CompilerMessageLocation.NO_LOCATION); 159 } 160 161 private static OutputFileCollection translate( 162 @NotNull MainCallParameters mainCall, 163 @NotNull Config config, 164 @NotNull List<JetFile> sourceFiles, 165 @NotNull File outputFile, 166 @Nullable File outputPrefix, 167 @Nullable File outputPostfix 168 ) { 169 try { 170 return K2JSTranslator.translateWithMainCallParameters(mainCall, sourceFiles, outputFile, outputPrefix, outputPostfix, config); 171 } 172 catch (Exception e) { 173 throw new RuntimeException(e); 174 } 175 } 176 177 private static boolean analyzeAndReportErrors(@NotNull MessageCollector messageCollector, 178 @NotNull final List<JetFile> sources, @NotNull final Config config) { 179 AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(messageCollector); 180 analyzerWithCompilerReport.analyzeAndReport(new Function0<AnalyzeExhaust>() { 181 @Override 182 public AnalyzeExhaust invoke() { 183 return AnalyzerFacadeForJS.analyzeFiles(sources, Predicates.<PsiFile>alwaysTrue(), config); 184 } 185 }, sources); 186 return analyzerWithCompilerReport.hasErrors(); 187 } 188 189 @NotNull 190 private static Config getConfig(@NotNull K2JSCompilerArguments arguments, @NotNull Project project) { 191 if (arguments.target != null) { 192 assert arguments.target == "v5" : "Unsupported ECMA version: " + arguments.target; 193 } 194 EcmaVersion ecmaVersion = EcmaVersion.defaultVersion(); 195 String moduleId = FileUtil.getNameWithoutExtension(new File(arguments.outputFile)); 196 if (arguments.libraryFiles != null) { 197 return new LibrarySourcesConfig(project, moduleId, Arrays.asList(arguments.libraryFiles), ecmaVersion, arguments.sourcemap); 198 } 199 else { 200 // lets discover the JS library definitions on the classpath 201 return new ClassPathLibraryDefintionsConfig(project, moduleId, ecmaVersion, arguments.sourcemap); 202 } 203 } 204 205 public static MainCallParameters createMainCallParameters(String main) { 206 if (K2JsArgumentConstants.NO_CALL.equals(main)) { 207 return MainCallParameters.noCall(); 208 } 209 else { 210 return MainCallParameters.mainWithoutArguments(); 211 } 212 } 213 }