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 }