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.jvm.compiler;
018
019 import com.intellij.codeInsight.ExternalAnnotationsManager;
020 import com.intellij.core.CoreApplicationEnvironment;
021 import com.intellij.core.CoreJavaFileManager;
022 import com.intellij.core.JavaCoreApplicationEnvironment;
023 import com.intellij.core.JavaCoreProjectEnvironment;
024 import com.intellij.lang.java.JavaParserDefinition;
025 import com.intellij.mock.MockApplication;
026 import com.intellij.mock.MockProject;
027 import com.intellij.openapi.Disposable;
028 import com.intellij.openapi.components.ServiceManager;
029 import com.intellij.openapi.extensions.Extensions;
030 import com.intellij.openapi.fileTypes.PlainTextFileType;
031 import com.intellij.openapi.project.Project;
032 import com.intellij.openapi.util.Disposer;
033 import com.intellij.openapi.vfs.VirtualFile;
034 import com.intellij.psi.PsiElementFinder;
035 import com.intellij.psi.PsiManager;
036 import com.intellij.psi.impl.file.impl.JavaFileManager;
037 import kotlin.Function1;
038 import kotlin.Unit;
039 import org.jetbrains.annotations.NotNull;
040 import org.jetbrains.annotations.TestOnly;
041 import org.jetbrains.jet.CompilerModeProvider;
042 import org.jetbrains.jet.OperationModeProvider;
043 import org.jetbrains.jet.asJava.JavaElementFinder;
044 import org.jetbrains.jet.asJava.KotlinLightClassForPackage;
045 import org.jetbrains.jet.asJava.LightClassGenerationSupport;
046 import org.jetbrains.jet.cli.common.CLIConfigurationKeys;
047 import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation;
048 import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity;
049 import org.jetbrains.jet.cli.common.messages.MessageCollector;
050 import org.jetbrains.jet.cli.jvm.JVMConfigurationKeys;
051 import org.jetbrains.jet.config.CommonConfigurationKeys;
052 import org.jetbrains.jet.config.CompilerConfiguration;
053 import org.jetbrains.jet.lang.parsing.JetParserDefinition;
054 import org.jetbrains.jet.lang.parsing.JetScriptDefinitionProvider;
055 import org.jetbrains.jet.lang.psi.JetFile;
056 import org.jetbrains.jet.lang.resolve.kotlin.KotlinBinaryClassCache;
057 import org.jetbrains.jet.lang.resolve.kotlin.VirtualFileFinderFactory;
058 import org.jetbrains.jet.lang.resolve.lazy.declarations.CliDeclarationProviderFactoryService;
059 import org.jetbrains.jet.lang.resolve.lazy.declarations.DeclarationProviderFactoryService;
060 import org.jetbrains.jet.plugin.JetFileType;
061 import org.jetbrains.jet.utils.PathUtil;
062
063 import java.io.File;
064 import java.util.ArrayList;
065 import java.util.List;
066
067 import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.ERROR;
068 import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.WARNING;
069
070 @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod")
071 public class JetCoreEnvironment {
072
073 private static final Object APPLICATION_LOCK = new Object();
074 private static JavaCoreApplicationEnvironment ourApplicationEnvironment;
075 private static int ourProjectCount = 0;
076
077 @NotNull
078 public static JetCoreEnvironment createForProduction(
079 @NotNull Disposable parentDisposable,
080 @NotNull CompilerConfiguration configuration
081 ) {
082 // JPS may run many instances of the compiler in parallel (there's an option for compiling independent modules in parallel in IntelliJ)
083 // All projects share the same ApplicationEnvironment, and when the last project is disposed, the ApplicationEnvironment is disposed as well
084 Disposer.register(parentDisposable, new Disposable() {
085 @Override
086 public void dispose() {
087 synchronized (APPLICATION_LOCK) {
088 if (--ourProjectCount <= 0) {
089 disposeApplicationEnvironment();
090 }
091 }
092 }
093 });
094 JetCoreEnvironment environment =
095 new JetCoreEnvironment(parentDisposable, getOrCreateApplicationEnvironmentForProduction(), configuration);
096 synchronized (APPLICATION_LOCK) {
097 ourProjectCount++;
098 }
099 return environment;
100 }
101
102 @TestOnly
103 @NotNull
104 public static JetCoreEnvironment createForTests(@NotNull Disposable parentDisposable, @NotNull CompilerConfiguration configuration) {
105 // Tests are supposed to create a single project and dispose it right after use
106 return new JetCoreEnvironment(parentDisposable, createApplicationEnvironment(parentDisposable), configuration);
107 }
108
109 @NotNull
110 private static JavaCoreApplicationEnvironment getOrCreateApplicationEnvironmentForProduction() {
111 synchronized (APPLICATION_LOCK) {
112 if (ourApplicationEnvironment != null) return ourApplicationEnvironment;
113
114 Disposable parentDisposable = Disposer.newDisposable();
115 ourApplicationEnvironment = createApplicationEnvironment(parentDisposable);
116 ourProjectCount = 0;
117 Disposer.register(parentDisposable, new Disposable() {
118 @Override
119 public void dispose() {
120 synchronized (APPLICATION_LOCK) {
121 ourApplicationEnvironment = null;
122 }
123 }
124 });
125 return ourApplicationEnvironment;
126 }
127 }
128
129 public static void disposeApplicationEnvironment() {
130 synchronized (APPLICATION_LOCK) {
131 if (ourApplicationEnvironment == null) return;
132 JavaCoreApplicationEnvironment environment = ourApplicationEnvironment;
133 ourApplicationEnvironment = null;
134 Disposer.dispose(environment.getParentDisposable());
135 }
136 }
137
138 @NotNull
139 private static JavaCoreApplicationEnvironment createApplicationEnvironment(@NotNull Disposable parentDisposable) {
140 JavaCoreApplicationEnvironment applicationEnvironment = new JavaCoreApplicationEnvironment(parentDisposable);
141
142 registerApplicationServicesForCLI(applicationEnvironment);
143 registerApplicationServices(applicationEnvironment);
144
145 return applicationEnvironment;
146 }
147
148 private static void registerApplicationServicesForCLI(@NotNull JavaCoreApplicationEnvironment applicationEnvironment) {
149 // ability to get text from annotations xml files
150 applicationEnvironment.registerFileType(PlainTextFileType.INSTANCE, "xml");
151 applicationEnvironment.registerParserDefinition(new JavaParserDefinition());
152 }
153
154 // made public for Upsource
155 public static void registerApplicationServices(@NotNull JavaCoreApplicationEnvironment applicationEnvironment) {
156 applicationEnvironment.registerFileType(JetFileType.INSTANCE, "kt");
157 applicationEnvironment.registerFileType(JetFileType.INSTANCE, "ktm");
158 applicationEnvironment.registerFileType(JetFileType.INSTANCE, JetParserDefinition.STD_SCRIPT_SUFFIX); // should be renamed to kts
159 applicationEnvironment.registerParserDefinition(new JetParserDefinition());
160
161 applicationEnvironment.getApplication().registerService(OperationModeProvider.class, new CompilerModeProvider());
162 applicationEnvironment.getApplication().registerService(KotlinBinaryClassCache.class, new KotlinBinaryClassCache());
163 }
164
165 private final JavaCoreProjectEnvironment projectEnvironment;
166 private final List<JetFile> sourceFiles = new ArrayList<JetFile>();
167 private final ClassPath classPath = new ClassPath();
168
169 private final CoreExternalAnnotationsManager annotationsManager;
170
171 private final CompilerConfiguration configuration;
172
173 private JetCoreEnvironment(
174 @NotNull Disposable parentDisposable,
175 @NotNull JavaCoreApplicationEnvironment applicationEnvironment,
176 @NotNull CompilerConfiguration configuration
177 ) {
178 this.configuration = configuration.copy();
179 this.configuration.setReadOnly(true);
180
181 projectEnvironment = new JavaCoreProjectEnvironment(parentDisposable, applicationEnvironment);
182
183 MockProject project = projectEnvironment.getProject();
184 annotationsManager = new CoreExternalAnnotationsManager(project.getComponent(PsiManager.class));
185 project.registerService(ExternalAnnotationsManager.class, annotationsManager);
186 project.registerService(DeclarationProviderFactoryService.class, new CliDeclarationProviderFactoryService(sourceFiles));
187
188 registerProjectServicesForCLI(projectEnvironment);
189 registerProjectServices(projectEnvironment);
190
191 for (File path : configuration.getList(JVMConfigurationKeys.CLASSPATH_KEY)) {
192 addToClasspath(path);
193 }
194 for (File path : configuration.getList(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY)) {
195 addExternalAnnotationsRoot(path);
196 }
197 sourceFiles.addAll(
198 CompileEnvironmentUtil
199 .getJetFiles(getProject(), configuration.getList(CommonConfigurationKeys.SOURCE_ROOTS_KEY),
200 new Function1<String, Unit>() {
201 @Override
202 public Unit invoke(String s) {
203 report(ERROR, s);
204 return Unit.INSTANCE$;
205 }
206 }));
207 JetScriptDefinitionProvider.getInstance(project).addScriptDefinitions(
208 configuration.getList(CommonConfigurationKeys.SCRIPT_DEFINITIONS_KEY));
209
210 project.registerService(VirtualFileFinderFactory.class, new CliVirtualFileFinderFactory(classPath));
211 }
212
213 // made public for Upsource
214 public static void registerProjectServices(@NotNull JavaCoreProjectEnvironment projectEnvironment) {
215 MockProject project = projectEnvironment.getProject();
216 project.registerService(JetScriptDefinitionProvider.class, new JetScriptDefinitionProvider());
217
218 project.registerService(KotlinLightClassForPackage.FileStubCache.class, new KotlinLightClassForPackage.FileStubCache(project));
219 }
220
221 private static void registerProjectServicesForCLI(@NotNull JavaCoreProjectEnvironment projectEnvironment) {
222 MockProject project = projectEnvironment.getProject();
223 project.registerService(CoreJavaFileManager.class, (CoreJavaFileManager) ServiceManager.getService(project, JavaFileManager.class));
224 CliLightClassGenerationSupport cliLightClassGenerationSupport = new CliLightClassGenerationSupport();
225 project.registerService(LightClassGenerationSupport.class, cliLightClassGenerationSupport);
226 project.registerService(CliLightClassGenerationSupport.class, cliLightClassGenerationSupport);
227 Extensions.getArea(project)
228 .getExtensionPoint(PsiElementFinder.EP_NAME)
229 .registerExtension(new JavaElementFinder(project, cliLightClassGenerationSupport));
230 }
231
232 @NotNull
233 public CompilerConfiguration getConfiguration() {
234 return configuration;
235 }
236
237 @NotNull
238 private CoreApplicationEnvironment getMyApplicationEnvironment() {
239 return projectEnvironment.getEnvironment();
240 }
241
242 @NotNull
243 public MockApplication getApplication() {
244 return getMyApplicationEnvironment().getApplication();
245 }
246
247 @NotNull
248 public Project getProject() {
249 return projectEnvironment.getProject();
250 }
251
252 private void addExternalAnnotationsRoot(File path) {
253 if (!path.exists()) {
254 report(WARNING, "Annotations path entry points to a non-existent location: " + path);
255 return;
256 }
257 annotationsManager.addExternalAnnotationsRoot(PathUtil.jarFileOrDirectoryToVirtualFile(path));
258 }
259
260 private void addToClasspath(File path) {
261 if (path.isFile()) {
262 VirtualFile jarFile = getMyApplicationEnvironment().getJarFileSystem().findFileByPath(path + "!/");
263 if (jarFile == null) {
264 report(WARNING, "Classpath entry points to a file that is not a JAR archive: " + path);
265 return;
266 }
267 projectEnvironment.addJarToClassPath(path);
268 classPath.add(jarFile);
269 }
270 else {
271 VirtualFile root = getMyApplicationEnvironment().getLocalFileSystem().findFileByPath(path.getAbsolutePath());
272 if (root == null) {
273 report(WARNING, "Classpath entry points to a non-existent location: " + path);
274 return;
275 }
276 projectEnvironment.addSourcesToClasspath(root);
277 classPath.add(root);
278 }
279 }
280
281 @NotNull
282 public List<JetFile> getSourceFiles() {
283 return sourceFiles;
284 }
285
286 private void report(@NotNull CompilerMessageSeverity severity, @NotNull String message) {
287 MessageCollector messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY);
288 if (messageCollector != null) {
289 messageCollector.report(severity, message, CompilerMessageLocation.NO_LOCATION);
290 }
291 else {
292 throw new CompileEnvironmentException(message);
293 }
294 }
295 }