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