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