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