001 /*
002 * Copyright 2010-2015 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.kotlin.cli.js;
018
019 import com.google.common.base.Joiner;
020 import com.intellij.openapi.Disposable;
021 import com.intellij.openapi.project.Project;
022 import com.intellij.openapi.util.io.FileUtil;
023 import com.intellij.openapi.vfs.VirtualFile;
024 import com.intellij.util.Function;
025 import com.intellij.util.SmartList;
026 import com.intellij.util.containers.ContainerUtil;
027 import kotlin.Unit;
028 import kotlin.jvm.functions.Function0;
029 import kotlin.jvm.functions.Function1;
030 import org.jetbrains.annotations.NotNull;
031 import org.jetbrains.annotations.Nullable;
032 import org.jetbrains.kotlin.analyzer.AnalysisResult;
033 import org.jetbrains.kotlin.backend.common.output.OutputFileCollection;
034 import org.jetbrains.kotlin.cli.common.CLICompiler;
035 import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys;
036 import org.jetbrains.kotlin.cli.common.ExitCode;
037 import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments;
038 import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants;
039 import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport;
040 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation;
041 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity;
042 import org.jetbrains.kotlin.cli.common.messages.MessageCollector;
043 import org.jetbrains.kotlin.cli.common.output.outputUtils.OutputUtilsPackage;
044 import org.jetbrains.kotlin.cli.jvm.compiler.CompilerJarLocator;
045 import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
046 import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
047 import org.jetbrains.kotlin.cli.jvm.config.JVMConfigurationKeys;
048 import org.jetbrains.kotlin.config.CompilerConfiguration;
049 import org.jetbrains.kotlin.config.Services;
050 import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS;
051 import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult;
052 import org.jetbrains.kotlin.js.config.Config;
053 import org.jetbrains.kotlin.js.config.EcmaVersion;
054 import org.jetbrains.kotlin.js.config.LibrarySourcesConfig;
055 import org.jetbrains.kotlin.js.facade.K2JSTranslator;
056 import org.jetbrains.kotlin.js.facade.MainCallParameters;
057 import org.jetbrains.kotlin.js.facade.TranslationResult;
058 import org.jetbrains.kotlin.psi.JetFile;
059 import org.jetbrains.kotlin.utils.PathUtil;
060
061 import java.io.File;
062 import java.util.List;
063
064 import static org.jetbrains.kotlin.cli.common.ExitCode.COMPILATION_ERROR;
065 import static org.jetbrains.kotlin.cli.common.ExitCode.OK;
066 import static org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation.NO_LOCATION;
067 import static org.jetbrains.kotlin.config.ConfigPackage.addKotlinSourceRoots;
068
069 public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
070
071 public static void main(String... args) {
072 doMain(new K2JSCompiler(), args);
073 }
074
075 @NotNull
076 @Override
077 protected K2JSCompilerArguments createArguments() {
078 return new K2JSCompilerArguments();
079 }
080
081
082 @NotNull
083 @Override
084 protected ExitCode doExecute(
085 @NotNull K2JSCompilerArguments arguments,
086 @NotNull Services services,
087 @NotNull final MessageCollector messageCollector,
088 @NotNull Disposable rootDisposable
089 ) {
090 if (arguments.freeArgs.isEmpty()) {
091 messageCollector.report(CompilerMessageSeverity.ERROR, "Specify at least one source file or directory", NO_LOCATION);
092 return ExitCode.INTERNAL_ERROR;
093 }
094
095 CompilerConfiguration configuration = new CompilerConfiguration();
096 configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector);
097
098 CompilerJarLocator locator = services.get(CompilerJarLocator.class);
099 if (locator != null) {
100 configuration.put(JVMConfigurationKeys.COMPILER_JAR_LOCATOR, locator);
101 }
102
103 addKotlinSourceRoots(configuration, arguments.freeArgs);
104 KotlinCoreEnvironment environmentForJS =
105 KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JS_CONFIG_FILES);
106
107 Project project = environmentForJS.getProject();
108 List<JetFile> sourcesFiles = environmentForJS.getSourceFiles();
109
110 if (arguments.verbose) {
111 reportCompiledSourcesList(messageCollector, sourcesFiles);
112 }
113
114 if (arguments.outputFile == null) {
115 messageCollector.report(CompilerMessageSeverity.ERROR, "Specify output file via -output", CompilerMessageLocation.NO_LOCATION);
116 return ExitCode.INTERNAL_ERROR;
117 }
118
119 File outputFile = new File(arguments.outputFile);
120
121 Config config = getConfig(arguments, project);
122 if (config.checkLibFilesAndReportErrors(new Function1<String, Unit>() {
123 @Override
124 public Unit invoke(String message) {
125 messageCollector.report(CompilerMessageSeverity.ERROR, message, CompilerMessageLocation.NO_LOCATION);
126 return Unit.INSTANCE$;
127 }
128 })) {
129 return COMPILATION_ERROR;
130 }
131
132 AnalyzerWithCompilerReport analyzerWithCompilerReport = analyzeAndReportErrors(messageCollector, sourcesFiles, config);
133 if (analyzerWithCompilerReport.hasErrors()) {
134 return COMPILATION_ERROR;
135 }
136
137 AnalysisResult analysisResult = analyzerWithCompilerReport.getAnalysisResult();
138 assert analysisResult instanceof JsAnalysisResult : "analysisResult should be instance of JsAnalysisResult, but " + analysisResult;
139 JsAnalysisResult jsAnalysisResult = (JsAnalysisResult) analysisResult;
140
141 File outputPrefixFile = null;
142 if (arguments.outputPrefix != null) {
143 outputPrefixFile = new File(arguments.outputPrefix);
144 if (!outputPrefixFile.exists()) {
145 messageCollector.report(CompilerMessageSeverity.ERROR,
146 "Output prefix file '" + arguments.outputPrefix + "' not found",
147 CompilerMessageLocation.NO_LOCATION);
148 return ExitCode.COMPILATION_ERROR;
149 }
150 }
151
152 File outputPostfixFile = null;
153 if (arguments.outputPostfix != null) {
154 outputPostfixFile = new File(arguments.outputPostfix);
155 if (!outputPostfixFile.exists()) {
156 messageCollector.report(CompilerMessageSeverity.ERROR,
157 "Output postfix file '" + arguments.outputPostfix + "' not found",
158 CompilerMessageLocation.NO_LOCATION);
159 return ExitCode.COMPILATION_ERROR;
160 }
161 }
162
163 MainCallParameters mainCallParameters = createMainCallParameters(arguments.main);
164 TranslationResult translationResult;
165
166 K2JSTranslator translator = new K2JSTranslator(config);
167 try {
168 //noinspection unchecked
169 translationResult = translator.translate(sourcesFiles, mainCallParameters, jsAnalysisResult);
170 } catch (Exception e) {
171 throw new RuntimeException(e);
172 }
173
174 AnalyzerWithCompilerReport.reportDiagnostics(translationResult.getDiagnostics(), messageCollector);
175
176 if (!(translationResult instanceof TranslationResult.Success)) return ExitCode.COMPILATION_ERROR;
177
178 TranslationResult.Success successResult = (TranslationResult.Success) translationResult;
179 OutputFileCollection outputFiles = successResult.getOutputFiles(outputFile, outputPrefixFile, outputPostfixFile);
180
181 if (outputFile.isDirectory()) {
182 messageCollector.report(CompilerMessageSeverity.ERROR,
183 "Cannot open output file '" + outputFile.getPath() + "': is a directory",
184 CompilerMessageLocation.NO_LOCATION);
185 return ExitCode.COMPILATION_ERROR;
186 }
187
188 File outputDir = outputFile.getParentFile();
189 if (outputDir == null) {
190 outputDir = outputFile.getAbsoluteFile().getParentFile();
191 }
192 OutputUtilsPackage.writeAll(outputFiles, outputDir, messageCollector);
193
194 return OK;
195 }
196
197 private static void reportCompiledSourcesList(@NotNull MessageCollector messageCollector, @NotNull List<JetFile> sourceFiles) {
198 Iterable<String> fileNames = ContainerUtil.map(sourceFiles, new Function<JetFile, String>() {
199 @Override
200 public String fun(@Nullable JetFile file) {
201 assert file != null;
202 VirtualFile virtualFile = file.getVirtualFile();
203 if (virtualFile != null) {
204 return FileUtil.toSystemDependentName(virtualFile.getPath());
205 }
206 return file.getName() + "(no virtual file)";
207 }
208 });
209 messageCollector.report(CompilerMessageSeverity.LOGGING, "Compiling source files: " + Joiner.on(", ").join(fileNames),
210 CompilerMessageLocation.NO_LOCATION);
211 }
212
213 private static AnalyzerWithCompilerReport analyzeAndReportErrors(@NotNull MessageCollector messageCollector,
214 @NotNull final List<JetFile> sources, @NotNull final Config config) {
215 AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(messageCollector);
216 analyzerWithCompilerReport.analyzeAndReport(sources, new Function0<AnalysisResult>() {
217 @Override
218 public AnalysisResult invoke() {
219 return TopDownAnalyzerFacadeForJS.analyzeFiles(sources, config);
220 }
221 });
222 return analyzerWithCompilerReport;
223 }
224
225 @NotNull
226 private static Config getConfig(@NotNull K2JSCompilerArguments arguments, @NotNull Project project) {
227 if (arguments.target != null) {
228 assert arguments.target == "v5" : "Unsupported ECMA version: " + arguments.target;
229 }
230 EcmaVersion ecmaVersion = EcmaVersion.defaultVersion();
231 String moduleId = FileUtil.getNameWithoutExtension(new File(arguments.outputFile));
232 boolean inlineEnabled = !arguments.noInline;
233
234 List<String> libraryFiles = new SmartList<String>();
235 if (!arguments.noStdlib) {
236 libraryFiles.add(0, PathUtil.getKotlinPathsForCompiler().getJsStdLibJarPath().getAbsolutePath());
237 }
238
239 if (arguments.libraryFiles != null) {
240 ContainerUtil.addAllNotNull(libraryFiles, arguments.libraryFiles);
241 }
242
243 return new LibrarySourcesConfig.Builder(project, moduleId, libraryFiles)
244 .ecmaVersion(ecmaVersion)
245 .sourceMap(arguments.sourceMap)
246 .inlineEnabled(inlineEnabled)
247 .metaInfo(arguments.metaInfo)
248 .build();
249 }
250
251 public static MainCallParameters createMainCallParameters(String main) {
252 if (K2JsArgumentConstants.NO_CALL.equals(main)) {
253 return MainCallParameters.noCall();
254 }
255 else {
256 return MainCallParameters.mainWithoutArguments();
257 }
258 }
259 }