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